compileScript.ts 41 KB

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