@@ -898,10 +898,10 @@ export function compileScript(
898
898
}
899
899
}
900
900
901
- // 1. process normal <script> first if it exists
902
- let scriptAst : Program | undefined
903
- if ( script ) {
904
- scriptAst = parse (
901
+ // 0. parse both <script> and <script setup> blocks
902
+ const scriptAst =
903
+ script &&
904
+ parse (
905
905
script . content ,
906
906
{
907
907
plugins,
@@ -910,7 +910,21 @@ export function compileScript(
910
910
scriptStartOffset !
911
911
)
912
912
913
- // walk import declarations first
913
+ const scriptSetupAst = parse (
914
+ scriptSetup . content ,
915
+ {
916
+ plugins : [
917
+ ...plugins ,
918
+ // allow top level await but only inside <script setup>
919
+ 'topLevelAwait'
920
+ ] ,
921
+ sourceType : 'module'
922
+ } ,
923
+ startOffset
924
+ )
925
+
926
+ // 1.1 walk import delcarations of <script>
927
+ if ( scriptAst ) {
914
928
for ( const node of scriptAst . body ) {
915
929
if ( node . type === 'ImportDeclaration' ) {
916
930
// record imports for dedupe
@@ -932,7 +946,88 @@ export function compileScript(
932
946
}
933
947
}
934
948
}
949
+ }
950
+
951
+ // 1.2 walk import declarations of <script setup>
952
+ for ( const node of scriptSetupAst . body ) {
953
+ if ( node . type === 'ImportDeclaration' ) {
954
+ // import declarations are moved to top
955
+ hoistNode ( node )
956
+
957
+ // dedupe imports
958
+ let removed = 0
959
+ const removeSpecifier = ( i : number ) => {
960
+ const removeLeft = i > removed
961
+ removed ++
962
+ const current = node . specifiers [ i ]
963
+ const next = node . specifiers [ i + 1 ]
964
+ s . remove (
965
+ removeLeft
966
+ ? node . specifiers [ i - 1 ] . end ! + startOffset
967
+ : current . start ! + startOffset ,
968
+ next && ! removeLeft
969
+ ? next . start ! + startOffset
970
+ : current . end ! + startOffset
971
+ )
972
+ }
973
+
974
+ for ( let i = 0 ; i < node . specifiers . length ; i ++ ) {
975
+ const specifier = node . specifiers [ i ]
976
+ const local = specifier . local . name
977
+ let imported =
978
+ specifier . type === 'ImportSpecifier' &&
979
+ specifier . imported . type === 'Identifier' &&
980
+ specifier . imported . name
981
+ if ( specifier . type === 'ImportNamespaceSpecifier' ) {
982
+ imported = '*'
983
+ }
984
+ const source = node . source . value
985
+ const existing = userImports [ local ]
986
+ if (
987
+ source === 'vue' &&
988
+ ( imported === DEFINE_PROPS ||
989
+ imported === DEFINE_EMITS ||
990
+ imported === DEFINE_EXPOSE )
991
+ ) {
992
+ warnOnce (
993
+ `\`${ imported } \` is a compiler macro and no longer needs to be imported.`
994
+ )
995
+ removeSpecifier ( i )
996
+ } else if ( existing ) {
997
+ if ( existing . source === source && existing . imported === imported ) {
998
+ // already imported in <script setup>, dedupe
999
+ removeSpecifier ( i )
1000
+ } else {
1001
+ error ( `different imports aliased to same local name.` , specifier )
1002
+ }
1003
+ } else {
1004
+ registerUserImport (
1005
+ source ,
1006
+ local ,
1007
+ imported ,
1008
+ node . importKind === 'type' ||
1009
+ ( specifier . type === 'ImportSpecifier' &&
1010
+ specifier . importKind === 'type' ) ,
1011
+ true ,
1012
+ ! options . inlineTemplate
1013
+ )
1014
+ }
1015
+ }
1016
+ if ( node . specifiers . length && removed === node . specifiers . length ) {
1017
+ s . remove ( node . start ! + startOffset , node . end ! + startOffset )
1018
+ }
1019
+ }
1020
+ }
1021
+
1022
+ // 1.3 resolve possible user import alias of `ref` and `reactive`
1023
+ const vueImportAliases : Record < string , string > = { }
1024
+ for ( const key in userImports ) {
1025
+ const { source, imported, local } = userImports [ key ]
1026
+ if ( source === 'vue' ) vueImportAliases [ imported ] = local
1027
+ }
935
1028
1029
+ // 2.1 process normal <script> body
1030
+ if ( script && scriptAst ) {
936
1031
for ( const node of scriptAst . body ) {
937
1032
if ( node . type === 'ExportDefaultDeclaration' ) {
938
1033
// export default
@@ -1011,7 +1106,7 @@ export function compileScript(
1011
1106
}
1012
1107
}
1013
1108
if ( node . declaration ) {
1014
- walkDeclaration ( node . declaration , scriptBindings , userImports )
1109
+ walkDeclaration ( node . declaration , scriptBindings , vueImportAliases )
1015
1110
}
1016
1111
} else if (
1017
1112
( node . type === 'VariableDeclaration' ||
@@ -1020,7 +1115,7 @@ export function compileScript(
1020
1115
node . type === 'TSEnumDeclaration' ) &&
1021
1116
! node . declare
1022
1117
) {
1023
- walkDeclaration ( node , scriptBindings , userImports )
1118
+ walkDeclaration ( node , scriptBindings , vueImportAliases )
1024
1119
}
1025
1120
}
1026
1121
@@ -1049,94 +1144,8 @@ export function compileScript(
1049
1144
}
1050
1145
}
1051
1146
1052
- // 2. parse <script setup> and walk over top level statements
1053
- const scriptSetupAst = parse (
1054
- scriptSetup . content ,
1055
- {
1056
- plugins : [
1057
- ...plugins ,
1058
- // allow top level await but only inside <script setup>
1059
- 'topLevelAwait'
1060
- ] ,
1061
- sourceType : 'module'
1062
- } ,
1063
- startOffset
1064
- )
1065
-
1147
+ // 2.2 process <script setup> body
1066
1148
for ( const node of scriptSetupAst . body ) {
1067
- if ( node . type === 'ImportDeclaration' ) {
1068
- // import declarations are moved to top
1069
- hoistNode ( node )
1070
-
1071
- // dedupe imports
1072
- let removed = 0
1073
- const removeSpecifier = ( i : number ) => {
1074
- const removeLeft = i > removed
1075
- removed ++
1076
- const current = node . specifiers [ i ]
1077
- const next = node . specifiers [ i + 1 ]
1078
- s . remove (
1079
- removeLeft
1080
- ? node . specifiers [ i - 1 ] . end ! + startOffset
1081
- : current . start ! + startOffset ,
1082
- next && ! removeLeft
1083
- ? next . start ! + startOffset
1084
- : current . end ! + startOffset
1085
- )
1086
- }
1087
-
1088
- for ( let i = 0 ; i < node . specifiers . length ; i ++ ) {
1089
- const specifier = node . specifiers [ i ]
1090
- const local = specifier . local . name
1091
- let imported =
1092
- specifier . type === 'ImportSpecifier' &&
1093
- specifier . imported . type === 'Identifier' &&
1094
- specifier . imported . name
1095
- if ( specifier . type === 'ImportNamespaceSpecifier' ) {
1096
- imported = '*'
1097
- }
1098
- const source = node . source . value
1099
- const existing = userImports [ local ]
1100
- if (
1101
- source === 'vue' &&
1102
- ( imported === DEFINE_PROPS ||
1103
- imported === DEFINE_EMITS ||
1104
- imported === DEFINE_EXPOSE )
1105
- ) {
1106
- warnOnce (
1107
- `\`${ imported } \` is a compiler macro and no longer needs to be imported.`
1108
- )
1109
- removeSpecifier ( i )
1110
- } else if ( existing ) {
1111
- if ( existing . source === source && existing . imported === imported ) {
1112
- // already imported in <script setup>, dedupe
1113
- removeSpecifier ( i )
1114
- } else {
1115
- error ( `different imports aliased to same local name.` , specifier )
1116
- }
1117
- } else {
1118
- registerUserImport (
1119
- source ,
1120
- local ,
1121
- imported ,
1122
- node . importKind === 'type' ||
1123
- ( specifier . type === 'ImportSpecifier' &&
1124
- specifier . importKind === 'type' ) ,
1125
- true ,
1126
- ! options . inlineTemplate
1127
- )
1128
- }
1129
- }
1130
- if ( node . specifiers . length && removed === node . specifiers . length ) {
1131
- s . remove ( node . start ! + startOffset , node . end ! + startOffset )
1132
- }
1133
- }
1134
- }
1135
-
1136
- for ( const node of scriptSetupAst . body ) {
1137
- // already processed
1138
- if ( node . type === 'ImportDeclaration' ) continue
1139
-
1140
1149
// (Dropped) `ref: x` bindings
1141
1150
// TODO remove when out of experimental
1142
1151
if (
@@ -1210,7 +1219,7 @@ export function compileScript(
1210
1219
node . type === 'ClassDeclaration' ) &&
1211
1220
! node . declare
1212
1221
) {
1213
- walkDeclaration ( node , setupBindings , userImports )
1222
+ walkDeclaration ( node , setupBindings , vueImportAliases )
1214
1223
}
1215
1224
1216
1225
// walk statements & named exports / variable declarations for top level
@@ -1665,17 +1674,8 @@ function registerBinding(
1665
1674
function walkDeclaration (
1666
1675
node : Declaration ,
1667
1676
bindings : Record < string , BindingTypes > ,
1668
- userImports : Record < string , ImportBinding >
1677
+ userImportAliases : Record < string , string >
1669
1678
) {
1670
- function getUserBinding ( name : string ) {
1671
- const binding = Object . values ( userImports ) . find (
1672
- binding => binding . source === 'vue' && binding . imported === name
1673
- )
1674
- if ( binding ) return binding . local
1675
- else if ( ! userImports [ name ] ) return name
1676
- return undefined
1677
- }
1678
-
1679
1679
if ( node . type === 'VariableDeclaration' ) {
1680
1680
const isConst = node . kind === 'const'
1681
1681
// export const foo = ...
@@ -1689,7 +1689,7 @@ function walkDeclaration(
1689
1689
)
1690
1690
if ( id . type === 'Identifier' ) {
1691
1691
let bindingType
1692
- const userReactiveBinding = getUserBinding ( 'reactive' )
1692
+ const userReactiveBinding = userImportAliases [ 'reactive' ]
1693
1693
if ( isCallOf ( init , userReactiveBinding ) ) {
1694
1694
// treat reactive() calls as let since it's meant to be mutable
1695
1695
bindingType = isConst
@@ -1705,7 +1705,7 @@ function walkDeclaration(
1705
1705
? BindingTypes . SETUP_REACTIVE_CONST
1706
1706
: BindingTypes . SETUP_CONST
1707
1707
} else if ( isConst ) {
1708
- if ( isCallOf ( init , getUserBinding ( 'ref' ) ) ) {
1708
+ if ( isCallOf ( init , userImportAliases [ 'ref' ] ) ) {
1709
1709
bindingType = BindingTypes . SETUP_REF
1710
1710
} else {
1711
1711
bindingType = BindingTypes . SETUP_MAYBE_REF
0 commit comments