mirror of
https://github.com/nuxt/nuxt.git
synced 2025-01-22 11:22:43 +00:00
Merge branch 'main' into feat/nuxt_async_context
This commit is contained in:
commit
97af928232
@ -1,4 +1,4 @@
|
||||
FROM node:lts@sha256:0e910f435308c36ea60b4cfd7b80208044d77a074d16b768a81901ce938a62dc
|
||||
FROM node:lts@sha256:99981c3d1aac0d98cd9f03f74b92dddf30f30ffb0b34e6df8bd96283f62f12c6
|
||||
|
||||
RUN apt-get update && \
|
||||
apt-get install -fy libnss3 libnspr4 libatk1.0-0 libatk-bridge2.0-0 libcups2 libdbus-1-3 libdrm2 libxkbcommon0 libatspi2.0-0 libxcomposite1 libxdamage1 libxfixes3 libxrandr2 libgbm1 libasound2 && \
|
||||
|
2
.github/workflows/ci.yml
vendored
2
.github/workflows/ci.yml
vendored
@ -60,7 +60,7 @@ jobs:
|
||||
run: pnpm test:attw
|
||||
|
||||
- name: Cache dist
|
||||
uses: actions/upload-artifact@6f51ac03b9356f520e9adb1b1b7802705f340c2b # v4.5.0
|
||||
uses: actions/upload-artifact@65c4c4a1ddee5b72f698fdd19549f0f0fb45cf08 # v4.6.0
|
||||
with:
|
||||
retention-days: 3
|
||||
name: dist
|
||||
|
2
.github/workflows/scorecards.yml
vendored
2
.github/workflows/scorecards.yml
vendored
@ -59,7 +59,7 @@ jobs:
|
||||
# Upload the results as artifacts (optional). Commenting out will disable uploads of run results in SARIF
|
||||
# format to the repository Actions tab.
|
||||
- name: "Upload artifact"
|
||||
uses: actions/upload-artifact@6f51ac03b9356f520e9adb1b1b7802705f340c2b # v4.5.0
|
||||
uses: actions/upload-artifact@65c4c4a1ddee5b72f698fdd19549f0f0fb45cf08 # v4.6.0
|
||||
if: github.repository == 'nuxt/nuxt' && success()
|
||||
with:
|
||||
name: SARIF file
|
||||
|
@ -147,7 +147,7 @@ If you have not fetched data on the server (for example, with `server: false`),
|
||||
|
||||
```ts [Signature]
|
||||
function useFetch<DataT, ErrorT>(
|
||||
url: string | Request | Ref<string | Request> | (() => string) | Request,
|
||||
url: string | Request | Ref<string | Request> | (() => string | Request),
|
||||
options?: UseFetchOptions<DataT>
|
||||
): Promise<AsyncData<DataT, ErrorT>>
|
||||
|
||||
|
@ -63,7 +63,7 @@ Each active version has its own nightly releases which are generated automatical
|
||||
|
||||
Release | | Initial release | End Of Life | Docs
|
||||
----------------------------------------|---------------------------------------------------------------------------------------------------|-----------------|--------------|-------
|
||||
**4.x** (scheduled) | | 2024 Q3 | |
|
||||
**4.x** (scheduled) | | approximately 1 month after release of nitro v3 | |
|
||||
**3.x** (stable) | <a href="https://npmjs.com/package/nuxt"><img alt="Nuxt latest 3.x version" src="https://flat.badgen.net/npm/v/nuxt?label=" class="not-prose"></a> | 2022-11-16 | TBA | [nuxt.com](/docs)
|
||||
**2.x** (unsupported) | <a href="https://www.npmjs.com/package/nuxt?activeTab=versions"><img alt="Nuxt 2.x version" src="https://flat.badgen.net/npm/v/nuxt/2x?label=" class="not-prose"></a> | 2018-09-21 | 2024-06-30 | [v2.nuxt.com](https://v2.nuxt.com/docs)
|
||||
**1.x** (unsupported) | <a href="https://www.npmjs.com/package/nuxt?activeTab=versions"><img alt="Nuxt 1.x version" src="https://flat.badgen.net/npm/v/nuxt/1x?label=" class="not-prose"></a> | 2018-01-08 | 2019-09-21 |
|
||||
|
34
package.json
34
package.json
@ -40,11 +40,11 @@
|
||||
"@nuxt/vite-builder": "workspace:*",
|
||||
"@nuxt/webpack-builder": "workspace:*",
|
||||
"@types/node": "22.10.5",
|
||||
"@unhead/dom": "1.11.14",
|
||||
"@unhead/schema": "1.11.14",
|
||||
"@unhead/shared": "1.11.14",
|
||||
"@unhead/ssr": "1.11.14",
|
||||
"@unhead/vue": "1.11.14",
|
||||
"@unhead/dom": "1.11.15",
|
||||
"@unhead/schema": "1.11.15",
|
||||
"@unhead/shared": "1.11.15",
|
||||
"@unhead/ssr": "1.11.15",
|
||||
"@unhead/vue": "1.11.15",
|
||||
"@vue/compiler-core": "3.5.13",
|
||||
"@vue/compiler-dom": "3.5.13",
|
||||
"@vue/shared": "3.5.13",
|
||||
@ -56,19 +56,19 @@
|
||||
"nuxt": "workspace:*",
|
||||
"ohash": "1.1.4",
|
||||
"postcss": "8.4.49",
|
||||
"rollup": "4.30.0",
|
||||
"rollup": "4.30.1",
|
||||
"send": ">=1.1.0",
|
||||
"typescript": "5.7.2",
|
||||
"typescript": "5.7.3",
|
||||
"ufo": "1.5.4",
|
||||
"unbuild": "3.2.0",
|
||||
"unhead": "1.11.14",
|
||||
"unhead": "1.11.15",
|
||||
"unimport": "3.14.5",
|
||||
"vite": "6.0.7",
|
||||
"vue": "3.5.13"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@arethetypeswrong/cli": "0.17.2",
|
||||
"@nuxt/eslint-config": "0.7.4",
|
||||
"@nuxt/eslint-config": "0.7.5",
|
||||
"@nuxt/kit": "workspace:*",
|
||||
"@nuxt/rspack-builder": "workspace:*",
|
||||
"@nuxt/test-utils": "3.15.1",
|
||||
@ -76,8 +76,8 @@
|
||||
"@testing-library/vue": "8.1.0",
|
||||
"@types/node": "22.10.5",
|
||||
"@types/semver": "7.5.8",
|
||||
"@unhead/schema": "1.11.14",
|
||||
"@unhead/vue": "1.11.14",
|
||||
"@unhead/schema": "1.11.15",
|
||||
"@unhead/vue": "1.11.15",
|
||||
"@vitest/coverage-v8": "2.1.8",
|
||||
"@vue/test-utils": "2.4.6",
|
||||
"autoprefixer": "10.4.20",
|
||||
@ -90,27 +90,27 @@
|
||||
"eslint": "9.17.0",
|
||||
"eslint-plugin-no-only-tests": "3.3.0",
|
||||
"eslint-plugin-perfectionist": "4.6.0",
|
||||
"eslint-typegen": "0.3.2",
|
||||
"eslint-typegen": "1.0.0",
|
||||
"h3": "npm:h3-nightly@2.0.0-1718872656.6765a6e",
|
||||
"happy-dom": "16.3.0",
|
||||
"happy-dom": "16.5.3",
|
||||
"installed-check": "9.3.0",
|
||||
"jiti": "2.4.2",
|
||||
"knip": "5.41.1",
|
||||
"knip": "5.42.0",
|
||||
"markdownlint-cli": "0.43.0",
|
||||
"memfs": "4.15.3",
|
||||
"memfs": "4.17.0",
|
||||
"nitro": "npm:nitro-nightly@3.0.0-beta-28796231.359af68d",
|
||||
"nuxi": "3.18.2",
|
||||
"nuxt": "workspace:*",
|
||||
"nuxt-content-twoslash": "0.1.2",
|
||||
"ofetch": "1.4.1",
|
||||
"pathe": "2.0.0",
|
||||
"pathe": "2.0.1",
|
||||
"playwright-core": "1.49.1",
|
||||
"semver": "7.6.3",
|
||||
"sherif": "1.1.1",
|
||||
"std-env": "3.8.0",
|
||||
"tinyexec": "0.3.2",
|
||||
"tinyglobby": "0.2.10",
|
||||
"typescript": "5.7.2",
|
||||
"typescript": "5.7.3",
|
||||
"ufo": "1.5.4",
|
||||
"vitest": "2.1.8",
|
||||
"vitest-environment-nuxt": "1.0.1",
|
||||
|
@ -39,7 +39,7 @@
|
||||
"klona": "^2.0.6",
|
||||
"mlly": "^1.7.3",
|
||||
"ohash": "^1.1.4",
|
||||
"pathe": "^2.0.0",
|
||||
"pathe": "^2.0.1",
|
||||
"pkg-types": "^1.3.0",
|
||||
"scule": "^1.3.0",
|
||||
"semver": "^7.6.3",
|
||||
|
@ -291,6 +291,10 @@ export async function _generateTypes (nuxt: Nuxt) {
|
||||
}))
|
||||
}
|
||||
|
||||
// Ensure `#build` is placed at the end of the paths object.
|
||||
// https://github.com/nuxt/nuxt/issues/30325
|
||||
sortTsPaths(tsConfig.compilerOptions.paths)
|
||||
|
||||
tsConfig.include = [...new Set(tsConfig.include.map(p => isAbsolute(p) ? relativeWithDot(nuxt.options.buildDir, p) : p))]
|
||||
tsConfig.exclude = [...new Set(tsConfig.exclude!.map(p => isAbsolute(p) ? relativeWithDot(nuxt.options.buildDir, p) : p))]
|
||||
|
||||
@ -330,6 +334,17 @@ export async function writeTypes (nuxt: Nuxt) {
|
||||
await writeFile()
|
||||
}
|
||||
|
||||
function sortTsPaths (paths: Record<string, string[]>) {
|
||||
for (const pathKey in paths) {
|
||||
if (pathKey.startsWith('#build')) {
|
||||
const pathValue = paths[pathKey]!
|
||||
// Delete & Reassign to ensure key is inserted at the end of object.
|
||||
delete paths[pathKey]
|
||||
paths[pathKey] = pathValue
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function renderAttrs (obj: Record<string, string>) {
|
||||
const attrs: string[] = []
|
||||
for (const key in obj) {
|
||||
|
@ -59,4 +59,42 @@ describe('tsConfig generation', () => {
|
||||
]
|
||||
`)
|
||||
})
|
||||
|
||||
it('should add #build after #components to paths', async () => {
|
||||
const { tsConfig } = await _generateTypes(mockNuxtWithOptions({
|
||||
alias: {
|
||||
'~': '/my-app',
|
||||
'@': '/my-app',
|
||||
'some-custom-alias': '/my-app/some-alias',
|
||||
'#build': './build-dir',
|
||||
'#build/*': './build-dir/*',
|
||||
'#imports': './imports',
|
||||
'#components': './components',
|
||||
},
|
||||
}))
|
||||
|
||||
expect(tsConfig.compilerOptions?.paths).toMatchObject({
|
||||
'~': [
|
||||
'..',
|
||||
],
|
||||
'some-custom-alias': [
|
||||
'../some-alias',
|
||||
],
|
||||
'@': [
|
||||
'..',
|
||||
],
|
||||
'#imports': [
|
||||
'./imports',
|
||||
],
|
||||
'#components': [
|
||||
'./components',
|
||||
],
|
||||
'#build': [
|
||||
'./build-dir',
|
||||
],
|
||||
'#build/*': [
|
||||
'./build-dir/*',
|
||||
],
|
||||
})
|
||||
})
|
||||
})
|
||||
|
@ -1,2 +1,2 @@
|
||||
#!/usr/bin/env node
|
||||
import 'nuxi/cli'
|
||||
import '@nuxt/cli/cli'
|
||||
|
@ -22,7 +22,7 @@ export default defineBuildConfig({
|
||||
},
|
||||
},
|
||||
dependencies: [
|
||||
'nuxi',
|
||||
'@nuxt/cli',
|
||||
'vue-router',
|
||||
'ofetch',
|
||||
],
|
||||
|
@ -64,16 +64,17 @@
|
||||
"test:attw": "attw --pack"
|
||||
},
|
||||
"dependencies": {
|
||||
"@nuxt/cli": "^3.20.0",
|
||||
"@nuxt/devalue": "^2.0.2",
|
||||
"@nuxt/devtools": "^1.7.0",
|
||||
"@nuxt/kit": "workspace:*",
|
||||
"@nuxt/schema": "workspace:*",
|
||||
"@nuxt/telemetry": "^2.6.4",
|
||||
"@nuxt/vite-builder": "workspace:*",
|
||||
"@unhead/dom": "^1.11.14",
|
||||
"@unhead/shared": "^1.11.14",
|
||||
"@unhead/ssr": "^1.11.14",
|
||||
"@unhead/vue": "^1.11.14",
|
||||
"@unhead/dom": "^1.11.15",
|
||||
"@unhead/shared": "^1.11.15",
|
||||
"@unhead/ssr": "^1.11.15",
|
||||
"@unhead/vue": "^1.11.15",
|
||||
"@vue/shared": "^3.5.13",
|
||||
"acorn": "8.14.0",
|
||||
"c12": "^2.0.1",
|
||||
@ -100,11 +101,10 @@
|
||||
"mlly": "^1.7.3",
|
||||
"nanotar": "^0.1.1",
|
||||
"nitro": "npm:nitro-nightly@3.0.0-beta-28796231.359af68d",
|
||||
"nuxi": "^3.18.2",
|
||||
"nypm": "^0.4.1",
|
||||
"ofetch": "^1.4.1",
|
||||
"ohash": "^1.1.4",
|
||||
"pathe": "^2.0.0",
|
||||
"pathe": "^2.0.1",
|
||||
"perfect-debounce": "^1.0.0",
|
||||
"pkg-types": "^1.3.0",
|
||||
"radix3": "^1.1.2",
|
||||
@ -118,7 +118,7 @@
|
||||
"uncrypto": "^0.1.3",
|
||||
"unctx": "^2.4.1",
|
||||
"unenv": "^1.10.0",
|
||||
"unhead": "^1.11.14",
|
||||
"unhead": "^1.11.15",
|
||||
"unimport": "^3.14.5",
|
||||
"unplugin": "^2.1.2",
|
||||
"unplugin-vue-router": "^0.10.9",
|
||||
|
@ -53,7 +53,7 @@ export function installNuxtModule (name: string, options?: EnsurePackageInstalle
|
||||
installPrompts.add(name)
|
||||
const nuxt = useNuxt()
|
||||
return promptToInstall(name, async () => {
|
||||
const { runCommand } = await import('nuxi')
|
||||
const { runCommand } = await import('@nuxt/cli')
|
||||
await runCommand('module', ['add', name, '--cwd', nuxt.options.rootDir])
|
||||
}, { rootDir: nuxt.options.rootDir, searchPaths: nuxt.options.modulesDir, ...options })
|
||||
}
|
||||
|
@ -53,12 +53,30 @@ export function withLocations<T> (node: T): WithLocations<T> {
|
||||
return node as WithLocations<T>
|
||||
}
|
||||
|
||||
/**
|
||||
* A function to check whether scope A is a child of scope B.
|
||||
* @example
|
||||
* ```ts
|
||||
* isChildScope('0-1-2', '0-1') // true
|
||||
* isChildScope('0-1', '0-1') // false
|
||||
* ```
|
||||
*
|
||||
* @param a the child scope
|
||||
* @param b the parent scope
|
||||
* @returns true if scope A is a child of scope B, false otherwise (also when they are the same)
|
||||
*/
|
||||
function isChildScope (a: string, b: string) {
|
||||
return a.startsWith(b) && a.length > b.length
|
||||
}
|
||||
|
||||
abstract class BaseNode<T extends Node = Node> {
|
||||
abstract type: string
|
||||
readonly scope: string
|
||||
node: WithLocations<T>
|
||||
|
||||
constructor (node: WithLocations<T>) {
|
||||
constructor (node: WithLocations<T>, scope: string) {
|
||||
this.node = node
|
||||
this.scope = scope
|
||||
}
|
||||
|
||||
/**
|
||||
@ -72,6 +90,14 @@ abstract class BaseNode<T extends Node = Node> {
|
||||
* For instance, for a function parameter, this would be the end of the function declaration.
|
||||
*/
|
||||
abstract get end (): number
|
||||
|
||||
/**
|
||||
* Check if the node is defined under a specific scope.
|
||||
* @param scope
|
||||
*/
|
||||
isUnderScope (scope: string) {
|
||||
return isChildScope(this.scope, scope)
|
||||
}
|
||||
}
|
||||
|
||||
class IdentifierNode extends BaseNode<Identifier> {
|
||||
@ -90,8 +116,8 @@ class FunctionParamNode extends BaseNode {
|
||||
type = 'FunctionParam' as const
|
||||
fnNode: WithLocations<FunctionDeclaration | FunctionExpression | ArrowFunctionExpression>
|
||||
|
||||
constructor (node: WithLocations<Node>, fnNode: WithLocations<FunctionDeclaration | FunctionExpression | ArrowFunctionExpression>) {
|
||||
super(node)
|
||||
constructor (node: WithLocations<Node>, scope: string, fnNode: WithLocations<FunctionDeclaration | FunctionExpression | ArrowFunctionExpression>) {
|
||||
super(node, scope)
|
||||
this.fnNode = fnNode
|
||||
}
|
||||
|
||||
@ -120,8 +146,8 @@ class VariableNode extends BaseNode<Identifier> {
|
||||
type = 'Variable' as const
|
||||
variableNode: WithLocations<VariableDeclaration>
|
||||
|
||||
constructor (node: WithLocations<Identifier>, variableNode: WithLocations<VariableDeclaration>) {
|
||||
super(node)
|
||||
constructor (node: WithLocations<Identifier>, scope: string, variableNode: WithLocations<VariableDeclaration>) {
|
||||
super(node, scope)
|
||||
this.variableNode = variableNode
|
||||
}
|
||||
|
||||
@ -138,8 +164,8 @@ class ImportNode extends BaseNode<ImportSpecifier | ImportDefaultSpecifier | Imp
|
||||
type = 'Import' as const
|
||||
importNode: WithLocations<Node>
|
||||
|
||||
constructor (node: WithLocations<ImportSpecifier | ImportDefaultSpecifier | ImportNamespaceSpecifier>, importNode: WithLocations<Node>) {
|
||||
super(node)
|
||||
constructor (node: WithLocations<ImportSpecifier | ImportDefaultSpecifier | ImportNamespaceSpecifier>, scope: string, importNode: WithLocations<Node>) {
|
||||
super(node, scope)
|
||||
this.importNode = importNode
|
||||
}
|
||||
|
||||
@ -156,8 +182,8 @@ class CatchParamNode extends BaseNode {
|
||||
type = 'CatchParam' as const
|
||||
catchNode: WithLocations<CatchClause>
|
||||
|
||||
constructor (node: WithLocations<Node>, catchNode: WithLocations<CatchClause>) {
|
||||
super(node)
|
||||
constructor (node: WithLocations<Node>, scope: string, catchNode: WithLocations<CatchClause>) {
|
||||
super(node, scope)
|
||||
this.catchNode = catchNode
|
||||
}
|
||||
|
||||
@ -264,7 +290,7 @@ export class ScopeTracker {
|
||||
|
||||
const identifiers = getPatternIdentifiers(param)
|
||||
for (const identifier of identifiers) {
|
||||
this.declareIdentifier(identifier.name, new FunctionParamNode(identifier, fn))
|
||||
this.declareIdentifier(identifier.name, new FunctionParamNode(identifier, this.scopeIndexKey, fn))
|
||||
}
|
||||
}
|
||||
|
||||
@ -276,10 +302,10 @@ export class ScopeTracker {
|
||||
this.declareIdentifier(
|
||||
identifier.name,
|
||||
parent.type === 'VariableDeclaration'
|
||||
? new VariableNode(identifier, parent)
|
||||
? new VariableNode(identifier, this.scopeIndexKey, parent)
|
||||
: parent.type === 'CatchClause'
|
||||
? new CatchParamNode(identifier, parent)
|
||||
: new FunctionParamNode(identifier, parent),
|
||||
? new CatchParamNode(identifier, this.scopeIndexKey, parent)
|
||||
: new FunctionParamNode(identifier, this.scopeIndexKey, parent),
|
||||
)
|
||||
}
|
||||
}
|
||||
@ -295,7 +321,7 @@ export class ScopeTracker {
|
||||
case 'FunctionDeclaration':
|
||||
// declare function name for named functions, skip for `export default`
|
||||
if (node.id?.name) {
|
||||
this.declareIdentifier(node.id.name, new FunctionNode(node))
|
||||
this.declareIdentifier(node.id.name, new FunctionNode(node, this.scopeIndexKey))
|
||||
}
|
||||
this.pushScope()
|
||||
for (const param of node.params) {
|
||||
@ -309,7 +335,7 @@ export class ScopeTracker {
|
||||
this.pushScope()
|
||||
// can be undefined, for example in class method definitions
|
||||
if (node.id?.name) {
|
||||
this.declareIdentifier(node.id.name, new FunctionNode(node))
|
||||
this.declareIdentifier(node.id.name, new FunctionNode(node, this.scopeIndexKey))
|
||||
}
|
||||
|
||||
this.pushScope()
|
||||
@ -333,7 +359,7 @@ export class ScopeTracker {
|
||||
case 'ClassDeclaration':
|
||||
// declare class name for named classes, skip for `export default`
|
||||
if (node.id?.name) {
|
||||
this.declareIdentifier(node.id.name, new IdentifierNode(withLocations(node.id)))
|
||||
this.declareIdentifier(node.id.name, new IdentifierNode(withLocations(node.id), this.scopeIndexKey))
|
||||
}
|
||||
break
|
||||
|
||||
@ -342,13 +368,13 @@ export class ScopeTracker {
|
||||
// e.g. const MyClass = class InternalClassName { // InternalClassName is only available within the class body
|
||||
this.pushScope()
|
||||
if (node.id?.name) {
|
||||
this.declareIdentifier(node.id.name, new IdentifierNode(withLocations(node.id)))
|
||||
this.declareIdentifier(node.id.name, new IdentifierNode(withLocations(node.id), this.scopeIndexKey))
|
||||
}
|
||||
break
|
||||
|
||||
case 'ImportDeclaration':
|
||||
for (const specifier of node.specifiers) {
|
||||
this.declareIdentifier(specifier.local.name, new ImportNode(withLocations(specifier), node))
|
||||
this.declareIdentifier(specifier.local.name, new ImportNode(withLocations(specifier), this.scopeIndexKey, node))
|
||||
}
|
||||
break
|
||||
|
||||
@ -429,6 +455,26 @@ export class ScopeTracker {
|
||||
return null
|
||||
}
|
||||
|
||||
getCurrentScope () {
|
||||
return this.scopeIndexKey
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if the current scope is a child of a specific scope.
|
||||
* @example
|
||||
* ```ts
|
||||
* // current scope is 0-1
|
||||
* isCurrentScopeUnder('0') // true
|
||||
* isCurrentScopeUnder('0-1') // false
|
||||
* ```
|
||||
*
|
||||
* @param scope the parent scope
|
||||
* @returns `true` if the current scope is a child of the specified scope, `false` otherwise (also when they are the same)
|
||||
*/
|
||||
isCurrentScopeUnder (scope: string) {
|
||||
return isChildScope(this.scopeIndexKey, scope)
|
||||
}
|
||||
|
||||
/**
|
||||
* Freezes the scope tracker, preventing further declarations.
|
||||
* It also resets the scope index stack to its initial state, so that the scope tracker can be reused.
|
||||
|
@ -228,6 +228,8 @@ export const PageMetaPlugin = (options: PageMetaPluginOptions = {}) => createUnp
|
||||
|
||||
if (!meta) { return }
|
||||
|
||||
const definePageMetaScope = scopeTracker.getCurrentScope()
|
||||
|
||||
walk(meta, {
|
||||
scopeTracker,
|
||||
enter (node, parent) {
|
||||
@ -236,10 +238,24 @@ export const PageMetaPlugin = (options: PageMetaPluginOptions = {}) => createUnp
|
||||
|| node.type !== 'Identifier' // checking for `node.type` to narrow down the type
|
||||
) { return }
|
||||
|
||||
const declaration = scopeTracker.getDeclaration(node.name)
|
||||
if (declaration) {
|
||||
// check if the declaration was made inside `definePageMeta` and if so, do not process it
|
||||
// (ensures that we don't hoist local variables in inline middleware, for example)
|
||||
if (
|
||||
declaration.isUnderScope(definePageMetaScope)
|
||||
// ensures that we compare the correct declaration to the reference
|
||||
// (when in the same scope, the declaration must come before the reference, otherwise it must be in a parent scope)
|
||||
&& (scopeTracker.isCurrentScopeUnder(declaration.scope) || declaration.start < node.start)
|
||||
) {
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
if (isStaticIdentifier(node.name)) {
|
||||
addImport(node.name)
|
||||
} else {
|
||||
processDeclaration(scopeTracker.getDeclaration(node.name))
|
||||
} else if (declaration) {
|
||||
processDeclaration(declaration)
|
||||
}
|
||||
},
|
||||
})
|
||||
|
@ -393,6 +393,188 @@ definePageMeta({
|
||||
`)
|
||||
})
|
||||
|
||||
it('should not import static identifiers when shadowed in the same scope', () => {
|
||||
const sfc = `
|
||||
<script setup lang="ts">
|
||||
import { useState } from '#app/composables/state'
|
||||
|
||||
definePageMeta({
|
||||
middleware: () => {
|
||||
const useState = (key) => ({ value: { isLoggedIn: false } })
|
||||
const auth = useState('auth')
|
||||
if (!auth.value.isLoggedIn) {
|
||||
return navigateTo('/login')
|
||||
}
|
||||
},
|
||||
})
|
||||
</script>
|
||||
`
|
||||
const res = compileScript(parse(sfc).descriptor, { id: 'component.vue' })
|
||||
expect(transformPlugin.transform.call({
|
||||
parse: (code: string, opts: any = {}) => Parser.parse(code, {
|
||||
sourceType: 'module',
|
||||
ecmaVersion: 'latest',
|
||||
locations: true,
|
||||
...opts,
|
||||
}),
|
||||
}, res.content, 'component.vue?macro=true')?.code).toMatchInlineSnapshot(`
|
||||
"const __nuxt_page_meta = {
|
||||
middleware: () => {
|
||||
const useState = (key) => ({ value: { isLoggedIn: false } })
|
||||
const auth = useState('auth')
|
||||
if (!auth.value.isLoggedIn) {
|
||||
return navigateTo('/login')
|
||||
}
|
||||
},
|
||||
}
|
||||
export default __nuxt_page_meta"
|
||||
`)
|
||||
})
|
||||
|
||||
it('should not import static identifiers when shadowed in parent scope', () => {
|
||||
const sfc = `
|
||||
<script setup lang="ts">
|
||||
import { useState } from '#app/composables/state'
|
||||
|
||||
definePageMeta({
|
||||
middleware: () => {
|
||||
function isLoggedIn() {
|
||||
const auth = useState('auth')
|
||||
return auth.value.isLoggedIn
|
||||
}
|
||||
|
||||
const useState = (key) => ({ value: { isLoggedIn: false } })
|
||||
if (!isLoggedIn()) {
|
||||
return navigateTo('/login')
|
||||
}
|
||||
},
|
||||
})
|
||||
</script>
|
||||
`
|
||||
const res = compileScript(parse(sfc).descriptor, { id: 'component.vue' })
|
||||
expect(transformPlugin.transform.call({
|
||||
parse: (code: string, opts: any = {}) => Parser.parse(code, {
|
||||
sourceType: 'module',
|
||||
ecmaVersion: 'latest',
|
||||
locations: true,
|
||||
...opts,
|
||||
}),
|
||||
}, res.content, 'component.vue?macro=true')?.code).toMatchInlineSnapshot(`
|
||||
"const __nuxt_page_meta = {
|
||||
middleware: () => {
|
||||
function isLoggedIn() {
|
||||
const auth = useState('auth')
|
||||
return auth.value.isLoggedIn
|
||||
}
|
||||
|
||||
const useState = (key) => ({ value: { isLoggedIn: false } })
|
||||
if (!isLoggedIn()) {
|
||||
return navigateTo('/login')
|
||||
}
|
||||
},
|
||||
}
|
||||
export default __nuxt_page_meta"
|
||||
`)
|
||||
})
|
||||
|
||||
it('should import static identifiers when a shadowed and a non-shadowed one is used', () => {
|
||||
const sfc = `
|
||||
<script setup lang="ts">
|
||||
import { useState } from '#app/composables/state'
|
||||
|
||||
definePageMeta({
|
||||
middleware: [
|
||||
() => {
|
||||
const useState = (key) => ({ value: { isLoggedIn: false } })
|
||||
const auth = useState('auth')
|
||||
if (!auth.value.isLoggedIn) {
|
||||
return navigateTo('/login')
|
||||
}
|
||||
},
|
||||
() => {
|
||||
const auth = useState('auth')
|
||||
if (!auth.value.isLoggedIn) {
|
||||
return navigateTo('/login')
|
||||
}
|
||||
}
|
||||
]
|
||||
})
|
||||
</script>
|
||||
`
|
||||
const res = compileScript(parse(sfc).descriptor, { id: 'component.vue' })
|
||||
expect(transformPlugin.transform.call({
|
||||
parse: (code: string, opts: any = {}) => Parser.parse(code, {
|
||||
sourceType: 'module',
|
||||
ecmaVersion: 'latest',
|
||||
locations: true,
|
||||
...opts,
|
||||
}),
|
||||
}, res.content, 'component.vue?macro=true')?.code).toMatchInlineSnapshot(`
|
||||
"import { useState } from '#app/composables/state'
|
||||
|
||||
const __nuxt_page_meta = {
|
||||
middleware: [
|
||||
() => {
|
||||
const useState = (key) => ({ value: { isLoggedIn: false } })
|
||||
const auth = useState('auth')
|
||||
if (!auth.value.isLoggedIn) {
|
||||
return navigateTo('/login')
|
||||
}
|
||||
},
|
||||
() => {
|
||||
const auth = useState('auth')
|
||||
if (!auth.value.isLoggedIn) {
|
||||
return navigateTo('/login')
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
export default __nuxt_page_meta"
|
||||
`)
|
||||
})
|
||||
|
||||
it('should import static identifiers when a shadowed and a non-shadowed one is used in the same scope', () => {
|
||||
const sfc = `
|
||||
<script setup lang="ts">
|
||||
import { useState } from '#app/composables/state'
|
||||
|
||||
definePageMeta({
|
||||
middleware: () => {
|
||||
const auth1 = useState('auth')
|
||||
const useState = (key) => ({ value: { isLoggedIn: false } })
|
||||
const auth2 = useState('auth')
|
||||
if (!auth1.value.isLoggedIn || !auth2.value.isLoggedIn) {
|
||||
return navigateTo('/login')
|
||||
}
|
||||
},
|
||||
})
|
||||
</script>
|
||||
`
|
||||
const res = compileScript(parse(sfc).descriptor, { id: 'component.vue' })
|
||||
expect(transformPlugin.transform.call({
|
||||
parse: (code: string, opts: any = {}) => Parser.parse(code, {
|
||||
sourceType: 'module',
|
||||
ecmaVersion: 'latest',
|
||||
locations: true,
|
||||
...opts,
|
||||
}),
|
||||
}, res.content, 'component.vue?macro=true')?.code).toMatchInlineSnapshot(`
|
||||
"import { useState } from '#app/composables/state'
|
||||
|
||||
const __nuxt_page_meta = {
|
||||
middleware: () => {
|
||||
const auth1 = useState('auth')
|
||||
const useState = (key) => ({ value: { isLoggedIn: false } })
|
||||
const auth2 = useState('auth')
|
||||
if (!auth1.value.isLoggedIn || !auth2.value.isLoggedIn) {
|
||||
return navigateTo('/login')
|
||||
}
|
||||
},
|
||||
}
|
||||
export default __nuxt_page_meta"
|
||||
`)
|
||||
})
|
||||
|
||||
it('should work with esbuild.keepNames = true', async () => {
|
||||
const sfc = `
|
||||
<script setup lang="ts">
|
||||
@ -516,7 +698,12 @@ definePageMeta({
|
||||
test () {}
|
||||
}
|
||||
|
||||
console.log(hoisted.value)
|
||||
const someFunction = () => {
|
||||
const someValue = 'someValue'
|
||||
console.log(someValue)
|
||||
}
|
||||
|
||||
console.log(hoisted.value, val)
|
||||
},
|
||||
],
|
||||
validate: (route) => {
|
||||
@ -564,7 +751,12 @@ const hoisted = ref('hoisted')
|
||||
test () {}
|
||||
}
|
||||
|
||||
console.log(hoisted.value)
|
||||
const someFunction = () => {
|
||||
const someValue = 'someValue'
|
||||
console.log(someValue)
|
||||
}
|
||||
|
||||
console.log(hoisted.value, val)
|
||||
},
|
||||
],
|
||||
validate: (route) => {
|
||||
|
@ -1,4 +1,4 @@
|
||||
import { describe, expect, it } from 'vitest'
|
||||
import { assert, describe, expect, it } from 'vitest'
|
||||
import { getUndeclaredIdentifiersInFunction, parseAndWalk } from '../src/core/utils/parse'
|
||||
import { TestScopeTracker } from './fixture/scope-tracker'
|
||||
|
||||
@ -667,4 +667,87 @@ describe('parsing', () => {
|
||||
|
||||
expect(processedFunctions).toBe(5)
|
||||
})
|
||||
|
||||
it ('should correctly compare identifiers defined in different scopes', () => {
|
||||
const code = `
|
||||
// ""
|
||||
const a = 1
|
||||
|
||||
// ""
|
||||
const func = () => {
|
||||
// "0-0"
|
||||
const b = 2
|
||||
|
||||
// "0-0"
|
||||
function foo() {
|
||||
// "0-0-0-0"
|
||||
const c = 3
|
||||
}
|
||||
}
|
||||
|
||||
// ""
|
||||
const func2 = () => {
|
||||
// "1-0"
|
||||
const d = 2
|
||||
|
||||
// "1-0"
|
||||
function bar() {
|
||||
// "1-0-0-0"
|
||||
const e = 3
|
||||
}
|
||||
}
|
||||
|
||||
// ""
|
||||
const f = 4
|
||||
`
|
||||
|
||||
const scopeTracker = new TestScopeTracker({
|
||||
keepExitedScopes: true,
|
||||
})
|
||||
|
||||
parseAndWalk(code, filename, {
|
||||
scopeTracker,
|
||||
})
|
||||
|
||||
const a = scopeTracker.getDeclarationFromScope('a', '')
|
||||
const func = scopeTracker.getDeclarationFromScope('func', '')
|
||||
const foo = scopeTracker.getDeclarationFromScope('foo', '0-0')
|
||||
const b = scopeTracker.getDeclarationFromScope('b', '0-0')
|
||||
const c = scopeTracker.getDeclarationFromScope('c', '0-0-0-0')
|
||||
const func2 = scopeTracker.getDeclarationFromScope('func2', '')
|
||||
const bar = scopeTracker.getDeclarationFromScope('bar', '1-0')
|
||||
const d = scopeTracker.getDeclarationFromScope('d', '1-0')
|
||||
const e = scopeTracker.getDeclarationFromScope('e', '1-0-0-0')
|
||||
const f = scopeTracker.getDeclarationFromScope('f', '')
|
||||
|
||||
assert(a && func && foo && b && c && func2 && bar && d && e && f, 'All declarations should be found')
|
||||
|
||||
// identifiers in the same scope should be equal
|
||||
expect(f.isUnderScope(a.scope)).toBe(false)
|
||||
expect(func.isUnderScope(a.scope)).toBe(false)
|
||||
expect(d.isUnderScope(bar.scope)).toBe(false)
|
||||
|
||||
// identifiers in deeper scopes should be under the scope of the parent scope
|
||||
expect(b.isUnderScope(a.scope)).toBe(true)
|
||||
expect(b.isUnderScope(func.scope)).toBe(true)
|
||||
expect(c.isUnderScope(a.scope)).toBe(true)
|
||||
expect(c.isUnderScope(b.scope)).toBe(true)
|
||||
expect(d.isUnderScope(a.scope)).toBe(true)
|
||||
expect(d.isUnderScope(func2.scope)).toBe(true)
|
||||
expect(e.isUnderScope(a.scope)).toBe(true)
|
||||
expect(e.isUnderScope(d.scope)).toBe(true)
|
||||
|
||||
// identifiers in parent scope should not be under the scope of the children
|
||||
expect(a.isUnderScope(b.scope)).toBe(false)
|
||||
expect(a.isUnderScope(c.scope)).toBe(false)
|
||||
expect(a.isUnderScope(d.scope)).toBe(false)
|
||||
expect(a.isUnderScope(e.scope)).toBe(false)
|
||||
expect(b.isUnderScope(c.scope)).toBe(false)
|
||||
|
||||
// identifiers in parallel scopes should not influence each other
|
||||
expect(d.isUnderScope(b.scope)).toBe(false)
|
||||
expect(e.isUnderScope(b.scope)).toBe(false)
|
||||
expect(b.isUnderScope(d.scope)).toBe(false)
|
||||
expect(c.isUnderScope(e.scope)).toBe(false)
|
||||
})
|
||||
})
|
||||
|
@ -47,9 +47,9 @@
|
||||
"jiti": "^2.4.2",
|
||||
"knitwork": "^1.2.0",
|
||||
"magic-string": "^0.30.17",
|
||||
"memfs": "^4.15.3",
|
||||
"memfs": "^4.17.0",
|
||||
"ohash": "^1.1.4",
|
||||
"pathe": "^2.0.0",
|
||||
"pathe": "^2.0.1",
|
||||
"pify": "^6.1.0",
|
||||
"postcss": "^8.4.49",
|
||||
"postcss-import": "^16.1.0",
|
||||
@ -75,7 +75,7 @@
|
||||
"@types/pify": "5.0.4",
|
||||
"@types/webpack-bundle-analyzer": "4.7.0",
|
||||
"@types/webpack-hot-middleware": "2.25.9",
|
||||
"rollup": "4.30.0",
|
||||
"rollup": "4.30.1",
|
||||
"unbuild": "3.2.0",
|
||||
"vue": "3.5.13"
|
||||
},
|
||||
|
@ -37,7 +37,7 @@
|
||||
},
|
||||
"devDependencies": {
|
||||
"@types/pug": "2.0.10",
|
||||
"@unhead/schema": "1.11.14",
|
||||
"@unhead/schema": "1.11.15",
|
||||
"@vitejs/plugin-vue": "5.2.1",
|
||||
"@vitejs/plugin-vue-jsx": "4.1.1",
|
||||
"@vue/compiler-core": "3.5.13",
|
||||
@ -70,7 +70,7 @@
|
||||
"dependencies": {
|
||||
"consola": "^3.3.3",
|
||||
"defu": "^6.1.4",
|
||||
"pathe": "^2.0.0",
|
||||
"pathe": "^2.0.1",
|
||||
"std-env": "^3.8.0"
|
||||
},
|
||||
"engines": {
|
||||
|
@ -17,19 +17,19 @@
|
||||
"prerender": "pnpm build && jiti ./lib/prerender"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@unocss/reset": "0.65.3",
|
||||
"@unocss/reset": "65.4.0",
|
||||
"beasties": "0.2.0",
|
||||
"html-validate": "9.1.1",
|
||||
"html-validate": "9.1.3",
|
||||
"htmlnano": "2.1.1",
|
||||
"jiti": "2.4.2",
|
||||
"knitwork": "1.2.0",
|
||||
"pathe": "2.0.0",
|
||||
"pathe": "2.0.1",
|
||||
"prettier": "3.4.2",
|
||||
"scule": "1.3.0",
|
||||
"svgo": "3.3.2",
|
||||
"tinyexec": "0.3.2",
|
||||
"tinyglobby": "0.2.10",
|
||||
"unocss": "0.65.3",
|
||||
"unocss": "65.4.0",
|
||||
"vite": "6.0.7"
|
||||
},
|
||||
"engines": {
|
||||
|
@ -26,7 +26,7 @@
|
||||
},
|
||||
"devDependencies": {
|
||||
"@nuxt/schema": "workspace:*",
|
||||
"rollup": "4.30.0",
|
||||
"rollup": "4.30.1",
|
||||
"unbuild": "3.2.0",
|
||||
"vue": "3.5.13"
|
||||
},
|
||||
@ -48,7 +48,7 @@
|
||||
"knitwork": "^1.2.0",
|
||||
"magic-string": "^0.30.17",
|
||||
"mlly": "^1.7.3",
|
||||
"pathe": "^2.0.0",
|
||||
"pathe": "^2.0.1",
|
||||
"pkg-types": "^1.3.0",
|
||||
"postcss": "^8.4.49",
|
||||
"rollup-plugin-visualizer": "^5.13.1",
|
||||
|
@ -46,10 +46,10 @@
|
||||
"jiti": "^2.4.2",
|
||||
"knitwork": "^1.2.0",
|
||||
"magic-string": "^0.30.17",
|
||||
"memfs": "^4.15.3",
|
||||
"memfs": "^4.17.0",
|
||||
"mini-css-extract-plugin": "^2.9.2",
|
||||
"ohash": "^1.1.4",
|
||||
"pathe": "^2.0.0",
|
||||
"pathe": "^2.0.1",
|
||||
"pify": "^6.1.0",
|
||||
"postcss": "^8.4.49",
|
||||
"postcss-import": "^16.1.0",
|
||||
@ -77,7 +77,7 @@
|
||||
"@types/pify": "5.0.4",
|
||||
"@types/webpack-bundle-analyzer": "4.7.0",
|
||||
"@types/webpack-hot-middleware": "2.25.9",
|
||||
"rollup": "4.30.0",
|
||||
"rollup": "4.30.1",
|
||||
"unbuild": "3.2.0",
|
||||
"vue": "3.5.13"
|
||||
},
|
||||
|
4175
pnpm-lock.yaml
4175
pnpm-lock.yaml
File diff suppressed because it is too large
Load Diff
@ -61,7 +61,7 @@ describe.skipIf(process.env.SKIP_BUNDLE_SIZE === 'true' || process.env.ECOSYSTEM
|
||||
expect.soft(roundToKilobytes(serverStats.totalBytes)).toMatchInlineSnapshot(`"210k"`)
|
||||
|
||||
const modules = await analyzeSizes(['node_modules/**/*'], serverDir)
|
||||
expect.soft(roundToKilobytes(modules.totalBytes)).toMatchInlineSnapshot(`"1396k"`)
|
||||
expect.soft(roundToKilobytes(modules.totalBytes)).toMatchInlineSnapshot(`"1398k"`)
|
||||
|
||||
const packages = modules.files
|
||||
.filter(m => m.endsWith('package.json'))
|
||||
@ -86,6 +86,7 @@ describe.skipIf(process.env.SKIP_BUNDLE_SIZE === 'true' || process.env.ECOSYSTEM
|
||||
"entities",
|
||||
"estree-walker",
|
||||
"hookable",
|
||||
"packrup",
|
||||
"source-map-js",
|
||||
"ufo",
|
||||
"unhead",
|
||||
@ -102,7 +103,7 @@ describe.skipIf(process.env.SKIP_BUNDLE_SIZE === 'true' || process.env.ECOSYSTEM
|
||||
expect.soft(roundToKilobytes(serverStats.totalBytes)).toMatchInlineSnapshot(`"561k"`)
|
||||
|
||||
const modules = await analyzeSizes(['node_modules/**/*'], serverDir)
|
||||
expect.soft(roundToKilobytes(modules.totalBytes)).toMatchInlineSnapshot(`"94.4k"`)
|
||||
expect.soft(roundToKilobytes(modules.totalBytes)).toMatchInlineSnapshot(`"96.4k"`)
|
||||
|
||||
const packages = modules.files
|
||||
.filter(m => m.endsWith('package.json'))
|
||||
@ -116,6 +117,7 @@ describe.skipIf(process.env.SKIP_BUNDLE_SIZE === 'true' || process.env.ECOSYSTEM
|
||||
"db0",
|
||||
"devalue",
|
||||
"hookable",
|
||||
"packrup",
|
||||
"unhead",
|
||||
]
|
||||
`)
|
||||
@ -128,7 +130,7 @@ describe.skipIf(process.env.SKIP_BUNDLE_SIZE === 'true' || process.env.ECOSYSTEM
|
||||
expect.soft(roundToKilobytes(serverStats.totalBytes)).toMatchInlineSnapshot(`"303k"`)
|
||||
|
||||
const modules = await analyzeSizes(['node_modules/**/*'], serverDir)
|
||||
expect.soft(roundToKilobytes(modules.totalBytes)).toMatchInlineSnapshot(`"1396k"`)
|
||||
expect.soft(roundToKilobytes(modules.totalBytes)).toMatchInlineSnapshot(`"1398k"`)
|
||||
|
||||
const packages = modules.files
|
||||
.filter(m => m.endsWith('package.json'))
|
||||
@ -153,6 +155,7 @@ describe.skipIf(process.env.SKIP_BUNDLE_SIZE === 'true' || process.env.ECOSYSTEM
|
||||
"entities",
|
||||
"estree-walker",
|
||||
"hookable",
|
||||
"packrup",
|
||||
"source-map-js",
|
||||
"ufo",
|
||||
"unhead",
|
||||
|
Loading…
Reference in New Issue
Block a user