diff --git a/packages/compiler-sfc/__tests__/compileScript.spec.ts b/packages/compiler-sfc/__tests__/compileScript.spec.ts index 5c03ac17fdb..96160e272d5 100644 --- a/packages/compiler-sfc/__tests__/compileScript.spec.ts +++ b/packages/compiler-sfc/__tests__/compileScript.spec.ts @@ -391,6 +391,19 @@ defineExpose({ foo: 123 }) bar: BindingTypes.SETUP_REACTIVE_CONST }) }) + + test('aliased usage before import site', () => { + const { bindings } = compile(` + + `) + expect(bindings).toStrictEqual({ + bar: BindingTypes.SETUP_REACTIVE_CONST, + x: BindingTypes.SETUP_CONST + }) + }) }) }) diff --git a/packages/compiler-sfc/src/compileScript.ts b/packages/compiler-sfc/src/compileScript.ts index adc22ab6820..ccf670725ec 100644 --- a/packages/compiler-sfc/src/compileScript.ts +++ b/packages/compiler-sfc/src/compileScript.ts @@ -354,6 +354,25 @@ export function compileScript( ) } + function hoistNode(node: Statement) { + const start = node.start! + startOffset + let end = node.end! + startOffset + // locate comment + if (node.trailingComments && node.trailingComments.length > 0) { + const lastCommentNode = + node.trailingComments[node.trailingComments.length - 1] + end = lastCommentNode.end + startOffset + } + // locate the end of whitespace between this statement and the next + while (end <= source.length) { + if (!/\s/.test(source.charAt(end))) { + break + } + end++ + } + s.move(start, end, 0) + } + function registerUserImport( source: string, local: string, @@ -891,6 +910,7 @@ export function compileScript( scriptStartOffset! ) + // walk import declarations first for (const node of scriptAst.body) { if (node.type === 'ImportDeclaration') { // record imports for dedupe @@ -910,7 +930,11 @@ export function compileScript( !options.inlineTemplate ) } - } else if (node.type === 'ExportDefaultDeclaration') { + } + } + + for (const node of scriptAst.body) { + if (node.type === 'ExportDefaultDeclaration') { // export default defaultExport = node @@ -1040,39 +1064,9 @@ export function compileScript( ) for (const node of scriptSetupAst.body) { - const start = node.start! + startOffset - let end = node.end! + startOffset - // locate comment - if (node.trailingComments && node.trailingComments.length > 0) { - const lastCommentNode = - node.trailingComments[node.trailingComments.length - 1] - end = lastCommentNode.end + startOffset - } - // locate the end of whitespace between this statement and the next - while (end <= source.length) { - if (!/\s/.test(source.charAt(end))) { - break - } - end++ - } - - // (Dropped) `ref: x` bindings - if ( - node.type === 'LabeledStatement' && - node.label.name === 'ref' && - node.body.type === 'ExpressionStatement' - ) { - error( - `ref sugar using the label syntax was an experimental proposal and ` + - `has been dropped based on community feedback. Please check out ` + - `the new proposal at https://github.com/vuejs/rfcs/discussions/369`, - node - ) - } - if (node.type === 'ImportDeclaration') { // import declarations are moved to top - s.move(start, end, 0) + hoistNode(node) // dedupe imports let removed = 0 @@ -1137,6 +1131,26 @@ export function compileScript( s.remove(node.start! + startOffset, node.end! + startOffset) } } + } + + for (const node of scriptSetupAst.body) { + // already processed + if (node.type === 'ImportDeclaration') continue + + // (Dropped) `ref: x` bindings + // TODO remove when out of experimental + if ( + node.type === 'LabeledStatement' && + node.label.name === 'ref' && + node.body.type === 'ExpressionStatement' + ) { + error( + `ref sugar using the label syntax was an experimental proposal and ` + + `has been dropped based on community feedback. Please check out ` + + `the new proposal at https://github.com/vuejs/rfcs/discussions/369`, + node + ) + } if (node.type === 'ExpressionStatement') { // process `defineProps` and `defineEmit(s)` calls @@ -1268,7 +1282,7 @@ export function compileScript( (node.type === 'VariableDeclaration' && node.declare) ) { recordType(node, declaredTypes) - s.move(start, end, 0) + hoistNode(node) } } }