compileScript.ts 40 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288128912901291129212931294129512961297129812991300
  1. import {
  2. BindingTypes,
  3. UNREF,
  4. isFunctionType,
  5. walkIdentifiers
  6. } from '@vue/compiler-dom'
  7. import { DEFAULT_FILENAME, SFCDescriptor, SFCScriptBlock } from './parse'
  8. import { ParserPlugin } from '@babel/parser'
  9. import { generateCodeFrame } from '@vue/shared'
  10. import {
  11. Node,
  12. Declaration,
  13. ObjectPattern,
  14. ArrayPattern,
  15. Identifier,
  16. ExportSpecifier,
  17. Statement,
  18. CallExpression
  19. } from '@babel/types'
  20. import { walk } from 'estree-walker'
  21. import { RawSourceMap } from 'source-map-js'
  22. import {
  23. processNormalScript,
  24. normalScriptDefaultVar
  25. } from './script/normalScript'
  26. import { CSS_VARS_HELPER, genCssVarsCode } from './style/cssVars'
  27. import { compileTemplate, SFCTemplateCompileOptions } from './compileTemplate'
  28. import { warnOnce } from './warn'
  29. import { shouldTransform, transformAST } from '@vue/reactivity-transform'
  30. import { transformDestructuredProps } from './script/definePropsDestructure'
  31. import { ScriptCompileContext } from './script/context'
  32. import {
  33. processDefineProps,
  34. genRuntimeProps,
  35. DEFINE_PROPS,
  36. WITH_DEFAULTS
  37. } from './script/defineProps'
  38. import {
  39. processDefineEmits,
  40. genRuntimeEmits,
  41. DEFINE_EMITS
  42. } from './script/defineEmits'
  43. import { DEFINE_EXPOSE, processDefineExpose } from './script/defineExpose'
  44. import { DEFINE_OPTIONS, processDefineOptions } from './script/defineOptions'
  45. import { processDefineSlots } from './script/defineSlots'
  46. import { DEFINE_MODEL, processDefineModel } from './script/defineModel'
  47. import {
  48. isLiteralNode,
  49. unwrapTSNode,
  50. isCallOf,
  51. getImportedName
  52. } from './script/utils'
  53. import { analyzeScriptBindings } from './script/analyzeScriptBindings'
  54. import { isImportUsed } from './script/importUsageCheck'
  55. import { processAwait } from './script/topLevelAwait'
  56. export interface SFCScriptCompileOptions {
  57. /**
  58. * Scope ID for prefixing injected CSS variables.
  59. * This must be consistent with the `id` passed to `compileStyle`.
  60. */
  61. id: string
  62. /**
  63. * Production mode. Used to determine whether to generate hashed CSS variables
  64. */
  65. isProd?: boolean
  66. /**
  67. * Enable/disable source map. Defaults to true.
  68. */
  69. sourceMap?: boolean
  70. /**
  71. * https://babeljs.io/docs/en/babel-parser#plugins
  72. */
  73. babelParserPlugins?: ParserPlugin[]
  74. /**
  75. * A list of files to parse for global types to be made available for type
  76. * resolving in SFC macros. The list must be fully resolved file system paths.
  77. */
  78. globalTypeFiles?: string[]
  79. /**
  80. * Compile the template and inline the resulting render function
  81. * directly inside setup().
  82. * - Only affects `<script setup>`
  83. * - This should only be used in production because it prevents the template
  84. * from being hot-reloaded separately from component state.
  85. */
  86. inlineTemplate?: boolean
  87. /**
  88. * Generate the final component as a variable instead of default export.
  89. * This is useful in e.g. @vitejs/plugin-vue where the script needs to be
  90. * placed inside the main module.
  91. */
  92. genDefaultAs?: string
  93. /**
  94. * Options for template compilation when inlining. Note these are options that
  95. * would normally be passed to `compiler-sfc`'s own `compileTemplate()`, not
  96. * options passed to `compiler-dom`.
  97. */
  98. templateOptions?: Partial<SFCTemplateCompileOptions>
  99. /**
  100. * Hoist <script setup> static constants.
  101. * - Only enables when one `<script setup>` exists.
  102. * @default true
  103. */
  104. hoistStatic?: boolean
  105. /**
  106. * (**Experimental**) Enable macro `defineModel`
  107. * @default false
  108. */
  109. defineModel?: boolean
  110. /**
  111. * (**Experimental**) Enable reactive destructure for `defineProps`
  112. * @default false
  113. */
  114. propsDestructure?: boolean
  115. /**
  116. * File system access methods to be used when resolving types
  117. * imported in SFC macros. Defaults to ts.sys in Node.js, can be overwritten
  118. * to use a virtual file system for use in browsers (e.g. in REPLs)
  119. */
  120. fs?: {
  121. fileExists(file: string): boolean
  122. readFile(file: string): string | undefined
  123. }
  124. /**
  125. * (Experimental) Enable syntax transform for using refs without `.value` and
  126. * using destructured props with reactivity
  127. * @deprecated the Reactivity Transform proposal has been dropped. This
  128. * feature will be removed from Vue core in 3.4. If you intend to continue
  129. * using it, disable this and switch to the [Vue Macros implementation](https://vue-macros.sxzz.moe/features/reactivity-transform.html).
  130. */
  131. reactivityTransform?: boolean
  132. }
  133. export interface ImportBinding {
  134. isType: boolean
  135. imported: string
  136. local: string
  137. source: string
  138. isFromSetup: boolean
  139. isUsedInTemplate: boolean
  140. }
  141. /**
  142. * Compile `<script setup>`
  143. * It requires the whole SFC descriptor because we need to handle and merge
  144. * normal `<script>` + `<script setup>` if both are present.
  145. */
  146. export function compileScript(
  147. sfc: SFCDescriptor,
  148. options: SFCScriptCompileOptions
  149. ): SFCScriptBlock {
  150. if (!options.id) {
  151. warnOnce(
  152. `compileScript now requires passing the \`id\` option.\n` +
  153. `Upgrade your vite or vue-loader version for compatibility with ` +
  154. `the latest experimental proposals.`
  155. )
  156. }
  157. const ctx = new ScriptCompileContext(sfc, options)
  158. const { script, scriptSetup, source, filename } = sfc
  159. const hoistStatic = options.hoistStatic !== false && !script
  160. const scopeId = options.id ? options.id.replace(/^data-v-/, '') : ''
  161. const scriptLang = script && script.lang
  162. const scriptSetupLang = scriptSetup && scriptSetup.lang
  163. // TODO remove in 3.4
  164. const enableReactivityTransform = !!options.reactivityTransform
  165. let refBindings: string[] | undefined
  166. if (!scriptSetup) {
  167. if (!script) {
  168. throw new Error(`[@vue/compiler-sfc] SFC contains no <script> tags.`)
  169. }
  170. // normal <script> only
  171. return processNormalScript(ctx, scopeId)
  172. }
  173. if (script && scriptLang !== scriptSetupLang) {
  174. throw new Error(
  175. `[@vue/compiler-sfc] <script> and <script setup> must have the same ` +
  176. `language type.`
  177. )
  178. }
  179. if (scriptSetupLang && !ctx.isJS && !ctx.isTS) {
  180. // do not process non js/ts script blocks
  181. return scriptSetup
  182. }
  183. // metadata that needs to be returned
  184. // const ctx.bindingMetadata: BindingMetadata = {}
  185. const scriptBindings: Record<string, BindingTypes> = Object.create(null)
  186. const setupBindings: Record<string, BindingTypes> = Object.create(null)
  187. let defaultExport: Node | undefined
  188. let hasAwait = false
  189. let hasInlinedSsrRenderFn = false
  190. // string offsets
  191. const startOffset = ctx.startOffset!
  192. const endOffset = ctx.endOffset!
  193. const scriptStartOffset = script && script.loc.start.offset
  194. const scriptEndOffset = script && script.loc.end.offset
  195. function hoistNode(node: Statement) {
  196. const start = node.start! + startOffset
  197. let end = node.end! + startOffset
  198. // locate comment
  199. if (node.trailingComments && node.trailingComments.length > 0) {
  200. const lastCommentNode =
  201. node.trailingComments[node.trailingComments.length - 1]
  202. end = lastCommentNode.end! + startOffset
  203. }
  204. // locate the end of whitespace between this statement and the next
  205. while (end <= source.length) {
  206. if (!/\s/.test(source.charAt(end))) {
  207. break
  208. }
  209. end++
  210. }
  211. ctx.s.move(start, end, 0)
  212. }
  213. function registerUserImport(
  214. source: string,
  215. local: string,
  216. imported: string,
  217. isType: boolean,
  218. isFromSetup: boolean,
  219. needTemplateUsageCheck: boolean
  220. ) {
  221. // template usage check is only needed in non-inline mode, so we can skip
  222. // the work if inlineTemplate is true.
  223. let isUsedInTemplate = needTemplateUsageCheck
  224. if (
  225. needTemplateUsageCheck &&
  226. ctx.isTS &&
  227. sfc.template &&
  228. !sfc.template.src &&
  229. !sfc.template.lang
  230. ) {
  231. isUsedInTemplate = isImportUsed(local, sfc)
  232. }
  233. ctx.userImports[local] = {
  234. isType,
  235. imported,
  236. local,
  237. source,
  238. isFromSetup,
  239. isUsedInTemplate
  240. }
  241. }
  242. function checkInvalidScopeReference(node: Node | undefined, method: string) {
  243. if (!node) return
  244. walkIdentifiers(node, id => {
  245. const binding = setupBindings[id.name]
  246. if (binding && binding !== BindingTypes.LITERAL_CONST) {
  247. ctx.error(
  248. `\`${method}()\` in <script setup> cannot reference locally ` +
  249. `declared variables because it will be hoisted outside of the ` +
  250. `setup() function. If your component options require initialization ` +
  251. `in the module scope, use a separate normal <script> to export ` +
  252. `the options instead.`,
  253. id
  254. )
  255. }
  256. })
  257. }
  258. const scriptAst = ctx.scriptAst
  259. const scriptSetupAst = ctx.scriptSetupAst!
  260. // 1.1 walk import declarations of <script>
  261. if (scriptAst) {
  262. for (const node of scriptAst.body) {
  263. if (node.type === 'ImportDeclaration') {
  264. // record imports for dedupe
  265. for (const specifier of node.specifiers) {
  266. const imported = getImportedName(specifier)
  267. registerUserImport(
  268. node.source.value,
  269. specifier.local.name,
  270. imported,
  271. node.importKind === 'type' ||
  272. (specifier.type === 'ImportSpecifier' &&
  273. specifier.importKind === 'type'),
  274. false,
  275. !options.inlineTemplate
  276. )
  277. }
  278. }
  279. }
  280. }
  281. // 1.2 walk import declarations of <script setup>
  282. for (const node of scriptSetupAst.body) {
  283. if (node.type === 'ImportDeclaration') {
  284. // import declarations are moved to top
  285. hoistNode(node)
  286. // dedupe imports
  287. let removed = 0
  288. const removeSpecifier = (i: number) => {
  289. const removeLeft = i > removed
  290. removed++
  291. const current = node.specifiers[i]
  292. const next = node.specifiers[i + 1]
  293. ctx.s.remove(
  294. removeLeft
  295. ? node.specifiers[i - 1].end! + startOffset
  296. : current.start! + startOffset,
  297. next && !removeLeft
  298. ? next.start! + startOffset
  299. : current.end! + startOffset
  300. )
  301. }
  302. for (let i = 0; i < node.specifiers.length; i++) {
  303. const specifier = node.specifiers[i]
  304. const local = specifier.local.name
  305. const imported = getImportedName(specifier)
  306. const source = node.source.value
  307. const existing = ctx.userImports[local]
  308. if (
  309. source === 'vue' &&
  310. (imported === DEFINE_PROPS ||
  311. imported === DEFINE_EMITS ||
  312. imported === DEFINE_EXPOSE)
  313. ) {
  314. warnOnce(
  315. `\`${imported}\` is a compiler macro and no longer needs to be imported.`
  316. )
  317. removeSpecifier(i)
  318. } else if (existing) {
  319. if (existing.source === source && existing.imported === imported) {
  320. // already imported in <script setup>, dedupe
  321. removeSpecifier(i)
  322. } else {
  323. ctx.error(
  324. `different imports aliased to same local name.`,
  325. specifier
  326. )
  327. }
  328. } else {
  329. registerUserImport(
  330. source,
  331. local,
  332. imported,
  333. node.importKind === 'type' ||
  334. (specifier.type === 'ImportSpecifier' &&
  335. specifier.importKind === 'type'),
  336. true,
  337. !options.inlineTemplate
  338. )
  339. }
  340. }
  341. if (node.specifiers.length && removed === node.specifiers.length) {
  342. ctx.s.remove(node.start! + startOffset, node.end! + startOffset)
  343. }
  344. }
  345. }
  346. // 1.3 resolve possible user import alias of `ref` and `reactive`
  347. const vueImportAliases: Record<string, string> = {}
  348. for (const key in ctx.userImports) {
  349. const { source, imported, local } = ctx.userImports[key]
  350. if (['vue', 'vue/vapor'].includes(source))
  351. vueImportAliases[imported] = local
  352. }
  353. // 2.1 process normal <script> body
  354. if (script && scriptAst) {
  355. for (const node of scriptAst.body) {
  356. if (node.type === 'ExportDefaultDeclaration') {
  357. // export default
  358. defaultExport = node
  359. // check if user has manually specified `name` or 'render` option in
  360. // export default
  361. // if has name, skip name inference
  362. // if has render and no template, generate return object instead of
  363. // empty render function (#4980)
  364. let optionProperties
  365. if (defaultExport.declaration.type === 'ObjectExpression') {
  366. optionProperties = defaultExport.declaration.properties
  367. } else if (
  368. defaultExport.declaration.type === 'CallExpression' &&
  369. defaultExport.declaration.arguments[0] &&
  370. defaultExport.declaration.arguments[0].type === 'ObjectExpression'
  371. ) {
  372. optionProperties = defaultExport.declaration.arguments[0].properties
  373. }
  374. if (optionProperties) {
  375. for (const p of optionProperties) {
  376. if (
  377. p.type === 'ObjectProperty' &&
  378. p.key.type === 'Identifier' &&
  379. p.key.name === 'name'
  380. ) {
  381. ctx.hasDefaultExportName = true
  382. }
  383. if (
  384. (p.type === 'ObjectMethod' || p.type === 'ObjectProperty') &&
  385. p.key.type === 'Identifier' &&
  386. p.key.name === 'render'
  387. ) {
  388. // TODO warn when we provide a better way to do it?
  389. ctx.hasDefaultExportRender = true
  390. }
  391. }
  392. }
  393. // export default { ... } --> const __default__ = { ... }
  394. const start = node.start! + scriptStartOffset!
  395. const end = node.declaration.start! + scriptStartOffset!
  396. ctx.s.overwrite(start, end, `const ${normalScriptDefaultVar} = `)
  397. } else if (node.type === 'ExportNamedDeclaration') {
  398. const defaultSpecifier = node.specifiers.find(
  399. s => s.exported.type === 'Identifier' && s.exported.name === 'default'
  400. ) as ExportSpecifier
  401. if (defaultSpecifier) {
  402. defaultExport = node
  403. // 1. remove specifier
  404. if (node.specifiers.length > 1) {
  405. ctx.s.remove(
  406. defaultSpecifier.start! + scriptStartOffset!,
  407. defaultSpecifier.end! + scriptStartOffset!
  408. )
  409. } else {
  410. ctx.s.remove(
  411. node.start! + scriptStartOffset!,
  412. node.end! + scriptStartOffset!
  413. )
  414. }
  415. if (node.source) {
  416. // export { x as default } from './x'
  417. // rewrite to `import { x as __default__ } from './x'` and
  418. // add to top
  419. ctx.s.prepend(
  420. `import { ${defaultSpecifier.local.name} as ${normalScriptDefaultVar} } from '${node.source.value}'\n`
  421. )
  422. } else {
  423. // export { x as default }
  424. // rewrite to `const __default__ = x` and move to end
  425. ctx.s.appendLeft(
  426. scriptEndOffset!,
  427. `\nconst ${normalScriptDefaultVar} = ${defaultSpecifier.local.name}\n`
  428. )
  429. }
  430. }
  431. if (node.declaration) {
  432. walkDeclaration(
  433. 'script',
  434. node.declaration,
  435. scriptBindings,
  436. vueImportAliases,
  437. hoistStatic
  438. )
  439. }
  440. } else if (
  441. (node.type === 'VariableDeclaration' ||
  442. node.type === 'FunctionDeclaration' ||
  443. node.type === 'ClassDeclaration' ||
  444. node.type === 'TSEnumDeclaration') &&
  445. !node.declare
  446. ) {
  447. walkDeclaration(
  448. 'script',
  449. node,
  450. scriptBindings,
  451. vueImportAliases,
  452. hoistStatic
  453. )
  454. }
  455. }
  456. // apply reactivity transform
  457. // TODO remove in 3.4
  458. if (enableReactivityTransform && shouldTransform(script.content)) {
  459. const { rootRefs, importedHelpers } = transformAST(
  460. scriptAst,
  461. ctx.s,
  462. scriptStartOffset!
  463. )
  464. refBindings = rootRefs
  465. for (const h of importedHelpers) {
  466. ctx.helperImports.add(h)
  467. }
  468. }
  469. // <script> after <script setup>
  470. // we need to move the block up so that `const __default__` is
  471. // declared before being used in the actual component definition
  472. if (scriptStartOffset! > startOffset) {
  473. // if content doesn't end with newline, add one
  474. if (!/\n$/.test(script.content.trim())) {
  475. ctx.s.appendLeft(scriptEndOffset!, `\n`)
  476. }
  477. ctx.s.move(scriptStartOffset!, scriptEndOffset!, 0)
  478. }
  479. }
  480. // 2.2 process <script setup> body
  481. for (const node of scriptSetupAst.body) {
  482. if (node.type === 'ExpressionStatement') {
  483. const expr = unwrapTSNode(node.expression)
  484. // process `defineProps` and `defineEmit(s)` calls
  485. if (
  486. processDefineProps(ctx, expr) ||
  487. processDefineEmits(ctx, expr) ||
  488. processDefineOptions(ctx, expr) ||
  489. processDefineSlots(ctx, expr)
  490. ) {
  491. ctx.s.remove(node.start! + startOffset, node.end! + startOffset)
  492. } else if (processDefineExpose(ctx, expr)) {
  493. // defineExpose({}) -> expose({})
  494. const callee = (expr as CallExpression).callee
  495. ctx.s.overwrite(
  496. callee.start! + startOffset,
  497. callee.end! + startOffset,
  498. '__expose'
  499. )
  500. } else {
  501. processDefineModel(ctx, expr)
  502. }
  503. }
  504. if (node.type === 'VariableDeclaration' && !node.declare) {
  505. const total = node.declarations.length
  506. let left = total
  507. let lastNonRemoved: number | undefined
  508. for (let i = 0; i < total; i++) {
  509. const decl = node.declarations[i]
  510. const init = decl.init && unwrapTSNode(decl.init)
  511. if (init) {
  512. if (processDefineOptions(ctx, init)) {
  513. ctx.error(
  514. `${DEFINE_OPTIONS}() has no returning value, it cannot be assigned.`,
  515. node
  516. )
  517. }
  518. // defineProps / defineEmits
  519. const isDefineProps = processDefineProps(ctx, init, decl.id)
  520. const isDefineEmits =
  521. !isDefineProps && processDefineEmits(ctx, init, decl.id)
  522. !isDefineEmits &&
  523. (processDefineSlots(ctx, init, decl.id) ||
  524. processDefineModel(ctx, init, decl.id))
  525. if (
  526. isDefineProps &&
  527. !ctx.propsDestructureRestId &&
  528. ctx.propsDestructureDecl
  529. ) {
  530. if (left === 1) {
  531. ctx.s.remove(node.start! + startOffset, node.end! + startOffset)
  532. } else {
  533. let start = decl.start! + startOffset
  534. let end = decl.end! + startOffset
  535. if (i === total - 1) {
  536. // last one, locate the end of the last one that is not removed
  537. // if we arrive at this branch, there must have been a
  538. // non-removed decl before us, so lastNonRemoved is non-null.
  539. start = node.declarations[lastNonRemoved!].end! + startOffset
  540. } else {
  541. // not the last one, locate the start of the next
  542. end = node.declarations[i + 1].start! + startOffset
  543. }
  544. ctx.s.remove(start, end)
  545. left--
  546. }
  547. } else if (isDefineEmits) {
  548. ctx.s.overwrite(
  549. startOffset + init.start!,
  550. startOffset + init.end!,
  551. '__emit'
  552. )
  553. } else {
  554. lastNonRemoved = i
  555. }
  556. }
  557. }
  558. }
  559. let isAllLiteral = false
  560. // walk declarations to record declared bindings
  561. if (
  562. (node.type === 'VariableDeclaration' ||
  563. node.type === 'FunctionDeclaration' ||
  564. node.type === 'ClassDeclaration' ||
  565. node.type === 'TSEnumDeclaration') &&
  566. !node.declare
  567. ) {
  568. isAllLiteral = walkDeclaration(
  569. 'scriptSetup',
  570. node,
  571. setupBindings,
  572. vueImportAliases,
  573. hoistStatic
  574. )
  575. }
  576. // hoist literal constants
  577. if (hoistStatic && isAllLiteral) {
  578. hoistNode(node)
  579. }
  580. // walk statements & named exports / variable declarations for top level
  581. // await
  582. if (
  583. (node.type === 'VariableDeclaration' && !node.declare) ||
  584. node.type.endsWith('Statement')
  585. ) {
  586. const scope: Statement[][] = [scriptSetupAst.body]
  587. walk(node, {
  588. enter(child: Node, parent: Node | undefined) {
  589. if (isFunctionType(child)) {
  590. this.skip()
  591. }
  592. if (child.type === 'BlockStatement') {
  593. scope.push(child.body)
  594. }
  595. if (child.type === 'AwaitExpression') {
  596. hasAwait = true
  597. // if the await expression is an expression statement and
  598. // - is in the root scope
  599. // - or is not the first statement in a nested block scope
  600. // then it needs a semicolon before the generated code.
  601. const currentScope = scope[scope.length - 1]
  602. const needsSemi = currentScope.some((n, i) => {
  603. return (
  604. (scope.length === 1 || i > 0) &&
  605. n.type === 'ExpressionStatement' &&
  606. n.start === child.start
  607. )
  608. })
  609. processAwait(
  610. ctx,
  611. child,
  612. needsSemi,
  613. parent!.type === 'ExpressionStatement'
  614. )
  615. }
  616. },
  617. exit(node: Node) {
  618. if (node.type === 'BlockStatement') scope.pop()
  619. }
  620. })
  621. }
  622. if (
  623. (node.type === 'ExportNamedDeclaration' && node.exportKind !== 'type') ||
  624. node.type === 'ExportAllDeclaration' ||
  625. node.type === 'ExportDefaultDeclaration'
  626. ) {
  627. ctx.error(
  628. `<script setup> cannot contain ES module exports. ` +
  629. `If you are using a previous version of <script setup>, please ` +
  630. `consult the updated RFC at https://github.com/vuejs/rfcs/pull/227.`,
  631. node
  632. )
  633. }
  634. if (ctx.isTS) {
  635. // move all Type declarations to outer scope
  636. if (
  637. node.type.startsWith('TS') ||
  638. (node.type === 'ExportNamedDeclaration' &&
  639. node.exportKind === 'type') ||
  640. (node.type === 'VariableDeclaration' && node.declare)
  641. ) {
  642. if (node.type !== 'TSEnumDeclaration') {
  643. hoistNode(node)
  644. }
  645. }
  646. }
  647. }
  648. // 3 props destructure transform
  649. if (ctx.propsDestructureDecl) {
  650. transformDestructuredProps(ctx, vueImportAliases)
  651. }
  652. // 4. Apply reactivity transform
  653. // TODO remove in 3.4
  654. if (
  655. enableReactivityTransform &&
  656. // normal <script> had ref bindings that maybe used in <script setup>
  657. (refBindings || shouldTransform(scriptSetup.content))
  658. ) {
  659. const { rootRefs, importedHelpers } = transformAST(
  660. scriptSetupAst,
  661. ctx.s,
  662. startOffset,
  663. refBindings
  664. )
  665. refBindings = refBindings ? [...refBindings, ...rootRefs] : rootRefs
  666. for (const h of importedHelpers) {
  667. ctx.helperImports.add(h)
  668. }
  669. }
  670. // 5. check macro args to make sure it doesn't reference setup scope
  671. // variables
  672. checkInvalidScopeReference(ctx.propsRuntimeDecl, DEFINE_PROPS)
  673. checkInvalidScopeReference(ctx.propsRuntimeDefaults, DEFINE_PROPS)
  674. checkInvalidScopeReference(ctx.propsDestructureDecl, DEFINE_PROPS)
  675. checkInvalidScopeReference(ctx.emitsRuntimeDecl, DEFINE_EMITS)
  676. checkInvalidScopeReference(ctx.optionsRuntimeDecl, DEFINE_OPTIONS)
  677. // 6. remove non-script content
  678. if (script) {
  679. if (startOffset < scriptStartOffset!) {
  680. // <script setup> before <script>
  681. ctx.s.remove(0, startOffset)
  682. ctx.s.remove(endOffset, scriptStartOffset!)
  683. ctx.s.remove(scriptEndOffset!, source.length)
  684. } else {
  685. // <script> before <script setup>
  686. ctx.s.remove(0, scriptStartOffset!)
  687. ctx.s.remove(scriptEndOffset!, startOffset)
  688. ctx.s.remove(endOffset, source.length)
  689. }
  690. } else {
  691. // only <script setup>
  692. ctx.s.remove(0, startOffset)
  693. ctx.s.remove(endOffset, source.length)
  694. }
  695. // 7. analyze binding metadata
  696. // `defineProps` & `defineModel` also register props bindings
  697. if (scriptAst) {
  698. Object.assign(ctx.bindingMetadata, analyzeScriptBindings(scriptAst.body))
  699. }
  700. for (const [key, { isType, imported, source }] of Object.entries(
  701. ctx.userImports
  702. )) {
  703. if (isType) continue
  704. ctx.bindingMetadata[key] =
  705. imported === '*' ||
  706. (imported === 'default' && source.endsWith('.vue')) ||
  707. ['vue', 'vue/vapor'].includes(source)
  708. ? BindingTypes.SETUP_CONST
  709. : BindingTypes.SETUP_MAYBE_REF
  710. }
  711. for (const key in scriptBindings) {
  712. ctx.bindingMetadata[key] = scriptBindings[key]
  713. }
  714. for (const key in setupBindings) {
  715. ctx.bindingMetadata[key] = setupBindings[key]
  716. }
  717. // known ref bindings
  718. if (refBindings) {
  719. for (const key of refBindings) {
  720. ctx.bindingMetadata[key] = BindingTypes.SETUP_REF
  721. }
  722. }
  723. // 8. inject `useCssVars` calls
  724. if (
  725. sfc.cssVars.length &&
  726. // no need to do this when targeting SSR
  727. !options.templateOptions?.ssr
  728. ) {
  729. ctx.helperImports.add(CSS_VARS_HELPER)
  730. ctx.helperImports.add('unref')
  731. ctx.s.prependLeft(
  732. startOffset,
  733. `\n${genCssVarsCode(
  734. sfc.cssVars,
  735. ctx.bindingMetadata,
  736. scopeId,
  737. !!options.isProd
  738. )}\n`
  739. )
  740. }
  741. // 9. finalize setup() argument signature
  742. let args = `__props`
  743. if (ctx.propsTypeDecl) {
  744. // mark as any and only cast on assignment
  745. // since the user defined complex types may be incompatible with the
  746. // inferred type from generated runtime declarations
  747. args += `: any`
  748. }
  749. // inject user assignment of props
  750. // we use a default __props so that template expressions referencing props
  751. // can use it directly
  752. if (ctx.propsDecl) {
  753. if (ctx.propsDestructureRestId) {
  754. ctx.s.overwrite(
  755. startOffset + ctx.propsCall!.start!,
  756. startOffset + ctx.propsCall!.end!,
  757. `${ctx.helper(`createPropsRestProxy`)}(__props, ${JSON.stringify(
  758. Object.keys(ctx.propsDestructuredBindings)
  759. )})`
  760. )
  761. ctx.s.overwrite(
  762. startOffset + ctx.propsDestructureDecl!.start!,
  763. startOffset + ctx.propsDestructureDecl!.end!,
  764. ctx.propsDestructureRestId
  765. )
  766. } else if (!ctx.propsDestructureDecl) {
  767. ctx.s.overwrite(
  768. startOffset + ctx.propsCall!.start!,
  769. startOffset + ctx.propsCall!.end!,
  770. '__props'
  771. )
  772. }
  773. }
  774. // inject temp variables for async context preservation
  775. if (hasAwait) {
  776. const any = ctx.isTS ? `: any` : ``
  777. ctx.s.prependLeft(startOffset, `\nlet __temp${any}, __restore${any}\n`)
  778. }
  779. const destructureElements =
  780. ctx.hasDefineExposeCall || !options.inlineTemplate
  781. ? [`expose: __expose`]
  782. : []
  783. if (ctx.emitDecl) {
  784. destructureElements.push(`emit: __emit`)
  785. }
  786. if (destructureElements.length) {
  787. args += `, { ${destructureElements.join(', ')} }`
  788. }
  789. // 10. generate return statement
  790. let returned
  791. if (
  792. !options.inlineTemplate ||
  793. (!sfc.template && ctx.hasDefaultExportRender)
  794. ) {
  795. // non-inline mode, or has manual render in normal <script>
  796. // return bindings from script and script setup
  797. const allBindings: Record<string, any> = {
  798. ...scriptBindings,
  799. ...setupBindings
  800. }
  801. for (const key in ctx.userImports) {
  802. if (
  803. !ctx.userImports[key].isType &&
  804. ctx.userImports[key].isUsedInTemplate
  805. ) {
  806. allBindings[key] = true
  807. }
  808. }
  809. returned = `{ `
  810. for (const key in allBindings) {
  811. if (
  812. allBindings[key] === true &&
  813. ['vue', 'vue/vapor'].includes(ctx.userImports[key].source) &&
  814. !ctx.userImports[key].source.endsWith('.vue')
  815. ) {
  816. // generate getter for import bindings
  817. // skip vue imports since we know they will never change
  818. returned += `get ${key}() { return ${key} }, `
  819. } else if (ctx.bindingMetadata[key] === BindingTypes.SETUP_LET) {
  820. // local let binding, also add setter
  821. const setArg = key === 'v' ? `_v` : `v`
  822. returned +=
  823. `get ${key}() { return ${key} }, ` +
  824. `set ${key}(${setArg}) { ${key} = ${setArg} }, `
  825. } else {
  826. returned += `${key}, `
  827. }
  828. }
  829. returned = returned.replace(/, $/, '') + ` }`
  830. } else {
  831. // inline mode
  832. if (sfc.template && !sfc.template.src) {
  833. if (options.templateOptions && options.templateOptions.ssr) {
  834. hasInlinedSsrRenderFn = true
  835. }
  836. // inline render function mode - we are going to compile the template and
  837. // inline it right here
  838. const { code, ast, preamble, tips, errors } = compileTemplate({
  839. filename,
  840. source: sfc.template.content,
  841. inMap: sfc.template.map,
  842. ...options.templateOptions,
  843. id: scopeId,
  844. scoped: sfc.styles.some(s => s.scoped),
  845. isProd: options.isProd,
  846. ssrCssVars: sfc.cssVars,
  847. compilerOptions: {
  848. ...(options.templateOptions &&
  849. options.templateOptions.compilerOptions),
  850. inline: true,
  851. isTS: ctx.isTS,
  852. bindingMetadata: ctx.bindingMetadata
  853. }
  854. })
  855. if (tips.length) {
  856. tips.forEach(warnOnce)
  857. }
  858. const err = errors[0]
  859. if (typeof err === 'string') {
  860. throw new Error(err)
  861. } else if (err) {
  862. if (err.loc) {
  863. err.message +=
  864. `\n\n` +
  865. sfc.filename +
  866. '\n' +
  867. generateCodeFrame(
  868. source,
  869. err.loc.start.offset,
  870. err.loc.end.offset
  871. ) +
  872. `\n`
  873. }
  874. throw err
  875. }
  876. if (preamble) {
  877. ctx.s.prepend(preamble)
  878. }
  879. // avoid duplicated unref import
  880. // as this may get injected by the render function preamble OR the
  881. // css vars codegen
  882. if (ast && ast.helpers.has(UNREF)) {
  883. ctx.helperImports.delete('unref')
  884. }
  885. returned = code
  886. } else {
  887. returned = `() => {}`
  888. }
  889. }
  890. if (!options.inlineTemplate && !__TEST__) {
  891. // in non-inline mode, the `__isScriptSetup: true` flag is used by
  892. // componentPublicInstance proxy to allow properties that start with $ or _
  893. ctx.s.appendRight(
  894. endOffset,
  895. `\nconst __returned__ = ${returned}\n` +
  896. `Object.defineProperty(__returned__, '__isScriptSetup', { enumerable: false, value: true })\n` +
  897. `return __returned__` +
  898. `\n}\n\n`
  899. )
  900. } else {
  901. ctx.s.appendRight(endOffset, `\nreturn ${returned}\n}\n\n`)
  902. }
  903. // 11. finalize default export
  904. const genDefaultAs = options.genDefaultAs
  905. ? `const ${options.genDefaultAs} =`
  906. : `export default`
  907. let runtimeOptions = ``
  908. if (!ctx.hasDefaultExportName && filename && filename !== DEFAULT_FILENAME) {
  909. const match = filename.match(/([^/\\]+)\.\w+$/)
  910. if (match) {
  911. runtimeOptions += `\n __name: '${match[1]}',`
  912. }
  913. }
  914. if (hasInlinedSsrRenderFn) {
  915. runtimeOptions += `\n __ssrInlineRender: true,`
  916. }
  917. const propsDecl = genRuntimeProps(ctx)
  918. if (propsDecl) runtimeOptions += `\n props: ${propsDecl},`
  919. const emitsDecl = genRuntimeEmits(ctx)
  920. if (emitsDecl) runtimeOptions += `\n emits: ${emitsDecl},`
  921. let definedOptions = ''
  922. if (ctx.optionsRuntimeDecl) {
  923. definedOptions = scriptSetup.content
  924. .slice(ctx.optionsRuntimeDecl.start!, ctx.optionsRuntimeDecl.end!)
  925. .trim()
  926. }
  927. // <script setup> components are closed by default. If the user did not
  928. // explicitly call `defineExpose`, call expose() with no args.
  929. const exposeCall =
  930. ctx.hasDefineExposeCall || options.inlineTemplate ? `` : ` __expose();\n`
  931. // wrap setup code with function.
  932. if (ctx.isTS) {
  933. // for TS, make sure the exported type is still valid type with
  934. // correct props information
  935. // we have to use object spread for types to be merged properly
  936. // user's TS setting should compile it down to proper targets
  937. // export default defineComponent({ ...__default__, ... })
  938. const def =
  939. (defaultExport ? `\n ...${normalScriptDefaultVar},` : ``) +
  940. (definedOptions ? `\n ...${definedOptions},` : '')
  941. ctx.s.prependLeft(
  942. startOffset,
  943. `\n${genDefaultAs} /*#__PURE__*/${ctx.helper(
  944. `defineComponent`
  945. )}({${def}${runtimeOptions}\n ${
  946. hasAwait ? `async ` : ``
  947. }setup(${args}) {\n${exposeCall}`
  948. )
  949. ctx.s.appendRight(endOffset, `})`)
  950. } else {
  951. if (defaultExport || definedOptions) {
  952. // without TS, can't rely on rest spread, so we use Object.assign
  953. // export default Object.assign(__default__, { ... })
  954. ctx.s.prependLeft(
  955. startOffset,
  956. `\n${genDefaultAs} /*#__PURE__*/Object.assign(${
  957. defaultExport ? `${normalScriptDefaultVar}, ` : ''
  958. }${definedOptions ? `${definedOptions}, ` : ''}{${runtimeOptions}\n ` +
  959. `${hasAwait ? `async ` : ``}setup(${args}) {\n${exposeCall}`
  960. )
  961. ctx.s.appendRight(endOffset, `})`)
  962. } else {
  963. ctx.s.prependLeft(
  964. startOffset,
  965. `\n${genDefaultAs} {${runtimeOptions}\n ` +
  966. `${hasAwait ? `async ` : ``}setup(${args}) {\n${exposeCall}`
  967. )
  968. ctx.s.appendRight(endOffset, `}`)
  969. }
  970. }
  971. // 12. finalize Vue helper imports
  972. if (ctx.helperImports.size > 0) {
  973. ctx.s.prepend(
  974. `import { ${[...ctx.helperImports]
  975. .map(h => `${h} as _${h}`)
  976. .join(', ')} } from 'vue'\n`
  977. )
  978. }
  979. ctx.s.trim()
  980. return {
  981. ...scriptSetup,
  982. bindings: ctx.bindingMetadata,
  983. imports: ctx.userImports,
  984. content: ctx.s.toString(),
  985. map:
  986. options.sourceMap !== false
  987. ? (ctx.s.generateMap({
  988. source: filename,
  989. hires: true,
  990. includeContent: true
  991. }) as unknown as RawSourceMap)
  992. : undefined,
  993. scriptAst: scriptAst?.body,
  994. scriptSetupAst: scriptSetupAst?.body,
  995. deps: ctx.deps ? [...ctx.deps] : undefined
  996. }
  997. }
  998. function registerBinding(
  999. bindings: Record<string, BindingTypes>,
  1000. node: Identifier,
  1001. type: BindingTypes
  1002. ) {
  1003. bindings[node.name] = type
  1004. }
  1005. function walkDeclaration(
  1006. from: 'script' | 'scriptSetup',
  1007. node: Declaration,
  1008. bindings: Record<string, BindingTypes>,
  1009. userImportAliases: Record<string, string>,
  1010. hoistStatic: boolean
  1011. ): boolean {
  1012. let isAllLiteral = false
  1013. if (node.type === 'VariableDeclaration') {
  1014. const isConst = node.kind === 'const'
  1015. isAllLiteral =
  1016. isConst &&
  1017. node.declarations.every(
  1018. decl => decl.id.type === 'Identifier' && isStaticNode(decl.init!)
  1019. )
  1020. // export const foo = ...
  1021. for (const { id, init: _init } of node.declarations) {
  1022. const init = _init && unwrapTSNode(_init)
  1023. const isDefineCall = !!(
  1024. isConst &&
  1025. isCallOf(
  1026. init,
  1027. c => c === DEFINE_PROPS || c === DEFINE_EMITS || c === WITH_DEFAULTS
  1028. )
  1029. )
  1030. if (id.type === 'Identifier') {
  1031. let bindingType
  1032. const userReactiveBinding = userImportAliases['reactive']
  1033. if (
  1034. (hoistStatic || from === 'script') &&
  1035. (isAllLiteral || (isConst && isStaticNode(init!)))
  1036. ) {
  1037. bindingType = BindingTypes.LITERAL_CONST
  1038. } else if (isCallOf(init, userReactiveBinding)) {
  1039. // treat reactive() calls as let since it's meant to be mutable
  1040. bindingType = isConst
  1041. ? BindingTypes.SETUP_REACTIVE_CONST
  1042. : BindingTypes.SETUP_LET
  1043. } else if (
  1044. // if a declaration is a const literal, we can mark it so that
  1045. // the generated render fn code doesn't need to unref() it
  1046. isDefineCall ||
  1047. (isConst && canNeverBeRef(init!, userReactiveBinding))
  1048. ) {
  1049. bindingType = isCallOf(init, DEFINE_PROPS)
  1050. ? BindingTypes.SETUP_REACTIVE_CONST
  1051. : BindingTypes.SETUP_CONST
  1052. } else if (isConst) {
  1053. if (
  1054. isCallOf(
  1055. init,
  1056. m =>
  1057. m === userImportAliases['ref'] ||
  1058. m === userImportAliases['computed'] ||
  1059. m === userImportAliases['shallowRef'] ||
  1060. m === userImportAliases['customRef'] ||
  1061. m === userImportAliases['toRef'] ||
  1062. m === DEFINE_MODEL
  1063. )
  1064. ) {
  1065. bindingType = BindingTypes.SETUP_REF
  1066. } else {
  1067. bindingType = BindingTypes.SETUP_MAYBE_REF
  1068. }
  1069. } else {
  1070. bindingType = BindingTypes.SETUP_LET
  1071. }
  1072. registerBinding(bindings, id, bindingType)
  1073. } else {
  1074. if (isCallOf(init, DEFINE_PROPS)) {
  1075. continue
  1076. }
  1077. if (id.type === 'ObjectPattern') {
  1078. walkObjectPattern(id, bindings, isConst, isDefineCall)
  1079. } else if (id.type === 'ArrayPattern') {
  1080. walkArrayPattern(id, bindings, isConst, isDefineCall)
  1081. }
  1082. }
  1083. }
  1084. } else if (node.type === 'TSEnumDeclaration') {
  1085. isAllLiteral = node.members.every(
  1086. member => !member.initializer || isStaticNode(member.initializer)
  1087. )
  1088. bindings[node.id!.name] = isAllLiteral
  1089. ? BindingTypes.LITERAL_CONST
  1090. : BindingTypes.SETUP_CONST
  1091. } else if (
  1092. node.type === 'FunctionDeclaration' ||
  1093. node.type === 'ClassDeclaration'
  1094. ) {
  1095. // export function foo() {} / export class Foo {}
  1096. // export declarations must be named.
  1097. bindings[node.id!.name] = BindingTypes.SETUP_CONST
  1098. }
  1099. return isAllLiteral
  1100. }
  1101. function walkObjectPattern(
  1102. node: ObjectPattern,
  1103. bindings: Record<string, BindingTypes>,
  1104. isConst: boolean,
  1105. isDefineCall = false
  1106. ) {
  1107. for (const p of node.properties) {
  1108. if (p.type === 'ObjectProperty') {
  1109. if (p.key.type === 'Identifier' && p.key === p.value) {
  1110. // shorthand: const { x } = ...
  1111. const type = isDefineCall
  1112. ? BindingTypes.SETUP_CONST
  1113. : isConst
  1114. ? BindingTypes.SETUP_MAYBE_REF
  1115. : BindingTypes.SETUP_LET
  1116. registerBinding(bindings, p.key, type)
  1117. } else {
  1118. walkPattern(p.value, bindings, isConst, isDefineCall)
  1119. }
  1120. } else {
  1121. // ...rest
  1122. // argument can only be identifier when destructuring
  1123. const type = isConst ? BindingTypes.SETUP_CONST : BindingTypes.SETUP_LET
  1124. registerBinding(bindings, p.argument as Identifier, type)
  1125. }
  1126. }
  1127. }
  1128. function walkArrayPattern(
  1129. node: ArrayPattern,
  1130. bindings: Record<string, BindingTypes>,
  1131. isConst: boolean,
  1132. isDefineCall = false
  1133. ) {
  1134. for (const e of node.elements) {
  1135. e && walkPattern(e, bindings, isConst, isDefineCall)
  1136. }
  1137. }
  1138. function walkPattern(
  1139. node: Node,
  1140. bindings: Record<string, BindingTypes>,
  1141. isConst: boolean,
  1142. isDefineCall = false
  1143. ) {
  1144. if (node.type === 'Identifier') {
  1145. const type = isDefineCall
  1146. ? BindingTypes.SETUP_CONST
  1147. : isConst
  1148. ? BindingTypes.SETUP_MAYBE_REF
  1149. : BindingTypes.SETUP_LET
  1150. registerBinding(bindings, node, type)
  1151. } else if (node.type === 'RestElement') {
  1152. // argument can only be identifier when destructuring
  1153. const type = isConst ? BindingTypes.SETUP_CONST : BindingTypes.SETUP_LET
  1154. registerBinding(bindings, node.argument as Identifier, type)
  1155. } else if (node.type === 'ObjectPattern') {
  1156. walkObjectPattern(node, bindings, isConst)
  1157. } else if (node.type === 'ArrayPattern') {
  1158. walkArrayPattern(node, bindings, isConst)
  1159. } else if (node.type === 'AssignmentPattern') {
  1160. if (node.left.type === 'Identifier') {
  1161. const type = isDefineCall
  1162. ? BindingTypes.SETUP_CONST
  1163. : isConst
  1164. ? BindingTypes.SETUP_MAYBE_REF
  1165. : BindingTypes.SETUP_LET
  1166. registerBinding(bindings, node.left, type)
  1167. } else {
  1168. walkPattern(node.left, bindings, isConst)
  1169. }
  1170. }
  1171. }
  1172. function canNeverBeRef(node: Node, userReactiveImport?: string): boolean {
  1173. if (isCallOf(node, userReactiveImport)) {
  1174. return true
  1175. }
  1176. switch (node.type) {
  1177. case 'UnaryExpression':
  1178. case 'BinaryExpression':
  1179. case 'ArrayExpression':
  1180. case 'ObjectExpression':
  1181. case 'FunctionExpression':
  1182. case 'ArrowFunctionExpression':
  1183. case 'UpdateExpression':
  1184. case 'ClassExpression':
  1185. case 'TaggedTemplateExpression':
  1186. return true
  1187. case 'SequenceExpression':
  1188. return canNeverBeRef(
  1189. node.expressions[node.expressions.length - 1],
  1190. userReactiveImport
  1191. )
  1192. default:
  1193. if (isLiteralNode(node)) {
  1194. return true
  1195. }
  1196. return false
  1197. }
  1198. }
  1199. function isStaticNode(node: Node): boolean {
  1200. node = unwrapTSNode(node)
  1201. switch (node.type) {
  1202. case 'UnaryExpression': // void 0, !true
  1203. return isStaticNode(node.argument)
  1204. case 'LogicalExpression': // 1 > 2
  1205. case 'BinaryExpression': // 1 + 2
  1206. return isStaticNode(node.left) && isStaticNode(node.right)
  1207. case 'ConditionalExpression': {
  1208. // 1 ? 2 : 3
  1209. return (
  1210. isStaticNode(node.test) &&
  1211. isStaticNode(node.consequent) &&
  1212. isStaticNode(node.alternate)
  1213. )
  1214. }
  1215. case 'SequenceExpression': // (1, 2)
  1216. case 'TemplateLiteral': // `foo${1}`
  1217. return node.expressions.every(expr => isStaticNode(expr))
  1218. case 'ParenthesizedExpression': // (1)
  1219. return isStaticNode(node.expression)
  1220. case 'StringLiteral':
  1221. case 'NumericLiteral':
  1222. case 'BooleanLiteral':
  1223. case 'NullLiteral':
  1224. case 'BigIntLiteral':
  1225. return true
  1226. }
  1227. return false
  1228. }