compileScript.ts 40 KB

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