compileScript.ts 42 KB

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