]> git.ipfire.org Git - thirdparty/vuejs/core.git/commitdiff
feat(compiler-sfc): support generating variable instead of default export in compileS...
authorEvan You <yyx990803@gmail.com>
Thu, 30 Mar 2023 08:58:41 +0000 (16:58 +0800)
committerEvan You <yyx990803@gmail.com>
Thu, 30 Mar 2023 08:58:41 +0000 (16:58 +0800)
packages/compiler-sfc/__tests__/__snapshots__/compileScript.spec.ts.snap
packages/compiler-sfc/__tests__/compileScript.spec.ts
packages/compiler-sfc/src/compileScript.ts
packages/compiler-sfc/src/cssVars.ts
packages/compiler-sfc/src/index.ts
packages/compiler-sfc/src/parse.ts

index 2d6df5607a3c1443794c57add82b87301ed58637..e78522ee3af165fa1ab9d997c87986b8173ca406 100644 (file)
@@ -1963,3 +1963,102 @@ return { props }
 
 })"
 `;
+
+exports[`SFC genDefaultAs > <script setup> only 1`] = `
+"const a = 1
+      
+const _sfc_ = {
+  setup(__props, { expose: __expose }) {
+  __expose();
+
+      
+return { a }
+}
+
+}"
+`;
+
+exports[`SFC genDefaultAs > <script setup> only w/ ts 1`] = `
+"import { defineComponent as _defineComponent } from 'vue'
+const a = 1
+      
+const _sfc_ = /*#__PURE__*/_defineComponent({
+  setup(__props, { expose: __expose }) {
+  __expose();
+
+      
+return { a }
+}
+
+})"
+`;
+
+exports[`SFC genDefaultAs > <script> + <script setup> 1`] = `
+"const __default__ = {}
+      
+const _sfc_ = /*#__PURE__*/Object.assign(__default__, {
+  setup(__props, { expose: __expose }) {
+  __expose();
+
+      const a = 1
+      
+return { a }
+}
+
+})"
+`;
+
+exports[`SFC genDefaultAs > <script> + <script setup> 2`] = `
+"const __default__ = {}
+      
+const _sfc_ = /*#__PURE__*/Object.assign(__default__, {
+  setup(__props, { expose: __expose }) {
+  __expose();
+
+      const a = 1
+      
+return { a }
+}
+
+})"
+`;
+
+exports[`SFC genDefaultAs > <script> + <script setup> w/ ts 1`] = `
+"import { defineComponent as _defineComponent } from 'vue'
+
+      const __default__ = {}
+      
+const _sfc_ = /*#__PURE__*/_defineComponent({
+  ...__default__,
+  setup(__props, { expose: __expose }) {
+  __expose();
+
+      const a = 1
+      
+return { a }
+}
+
+})"
+`;
+
+exports[`SFC genDefaultAs > normal <script> only 1`] = `
+"
+      const _sfc_ = {}
+      "
+`;
+
+exports[`SFC genDefaultAs > normal <script> w/ cssVars 1`] = `
+"
+      const _sfc_ = {}
+      
+import { useCssVars as _useCssVars } from 'vue'
+const __injectCSSVars__ = () => {
+_useCssVars(_ctx => ({
+  \\"xxxxxxxx-x\\": (_ctx.x)
+}))}
+const __setup__ = _sfc_.setup
+_sfc_.setup = __setup__
+  ? (props, ctx) => { __injectCSSVars__();return __setup__(props, ctx) }
+  : __injectCSSVars__
+"
+`;
index f39d5aec1e01dbb450aa2c6d4c8f1e0076989851..11d1bc02f9cc58cbbfb90d83ee4180b8d6712e1a 100644 (file)
@@ -2190,3 +2190,122 @@ describe('SFC analyze <script> bindings', () => {
     })
   })
 })
+
+describe('SFC genDefaultAs', () => {
+  test('normal <script> only', () => {
+    const { content } = compile(
+      `<script>
+      export default {}
+      </script>`,
+      {
+        genDefaultAs: '_sfc_'
+      }
+    )
+    expect(content).not.toMatch('export default')
+    expect(content).toMatch(`const _sfc_ = {}`)
+    assertCode(content)
+  })
+
+  test('normal <script> w/ cssVars', () => {
+    const { content } = compile(
+      `<script>
+      export default {}
+      </script>
+      <style>
+      .foo { color: v-bind(x) }
+      </style>`,
+      {
+        genDefaultAs: '_sfc_'
+      }
+    )
+    expect(content).not.toMatch('export default')
+    expect(content).not.toMatch('__default__')
+    expect(content).toMatch(`const _sfc_ = {}`)
+    assertCode(content)
+  })
+
+  test('<script> + <script setup>', () => {
+    const { content } = compile(
+      `<script>
+      export default {}
+      </script>
+      <script setup>
+      const a = 1
+      </script>`,
+      {
+        genDefaultAs: '_sfc_'
+      }
+    )
+    expect(content).not.toMatch('export default')
+    expect(content).toMatch(
+      `const _sfc_ = /*#__PURE__*/Object.assign(__default__`
+    )
+    assertCode(content)
+  })
+
+  test('<script> + <script setup>', () => {
+    const { content } = compile(
+      `<script>
+      export default {}
+      </script>
+      <script setup>
+      const a = 1
+      </script>`,
+      {
+        genDefaultAs: '_sfc_'
+      }
+    )
+    expect(content).not.toMatch('export default')
+    expect(content).toMatch(
+      `const _sfc_ = /*#__PURE__*/Object.assign(__default__`
+    )
+    assertCode(content)
+  })
+
+  test('<script setup> only', () => {
+    const { content } = compile(
+      `<script setup>
+      const a = 1
+      </script>`,
+      {
+        genDefaultAs: '_sfc_'
+      }
+    )
+    expect(content).not.toMatch('export default')
+    expect(content).toMatch(`const _sfc_ = {\n  setup`)
+    assertCode(content)
+  })
+
+  test('<script setup> only w/ ts', () => {
+    const { content } = compile(
+      `<script setup lang="ts">
+      const a = 1
+      </script>`,
+      {
+        genDefaultAs: '_sfc_'
+      }
+    )
+    expect(content).not.toMatch('export default')
+    expect(content).toMatch(`const _sfc_ = /*#__PURE__*/_defineComponent(`)
+    assertCode(content)
+  })
+
+  test('<script> + <script setup> w/ ts', () => {
+    const { content } = compile(
+      `<script lang="ts">
+      export default {}
+      </script>
+      <script setup lang="ts">
+      const a = 1
+      </script>`,
+      {
+        genDefaultAs: '_sfc_'
+      }
+    )
+    expect(content).not.toMatch('export default')
+    expect(content).toMatch(
+      `const _sfc_ = /*#__PURE__*/_defineComponent({\n  ...__default__`
+    )
+    assertCode(content)
+  })
+})
index 61b4b7c0ee1f50be9586b993c5f16b251a73ee03..9a5e19c84d41d64ed269135618fb08087fc9b13c 100644 (file)
@@ -69,9 +69,6 @@ const DEFINE_EXPOSE = 'defineExpose'
 const WITH_DEFAULTS = 'withDefaults'
 const DEFINE_OPTIONS = 'defineOptions'
 
-// constants
-const DEFAULT_VAR = `__default__`
-
 const isBuiltInDir = makeMap(
   `once,memo,if,for,else,else-if,slot,text,html,on,bind,model,show,cloak,is`
 )
@@ -110,6 +107,12 @@ export interface SFCScriptCompileOptions {
    * from being hot-reloaded separately from component state.
    */
   inlineTemplate?: boolean
+  /**
+   * Generate the final component as a variable instead of default export.
+   * This is useful in e.g. @vitejs/plugin-vue where the script needs to be
+   * placed inside the main module.
+   */
+  genDefaultAs?: string
   /**
    * Options for template compilation when inlining. Note these are options that
    * would normally be passed to `compiler-sfc`'s own `compileTemplate()`, not
@@ -178,6 +181,10 @@ export function compileScript(
   const cssVars = sfc.cssVars
   const scriptLang = script && script.lang
   const scriptSetupLang = scriptSetup && scriptSetup.lang
+  const genDefaultAs = options.genDefaultAs
+    ? `const ${options.genDefaultAs} =`
+    : `export default`
+  const normalScriptDefaultVar = `__default__`
   const isJS =
     scriptLang === 'js' ||
     scriptLang === 'jsx' ||
@@ -216,6 +223,7 @@ export function compileScript(
       // do not process non js/ts script blocks
       return script
     }
+    // normal <script> only
     try {
       let content = script.content
       let map = script.map
@@ -247,17 +255,23 @@ export function compileScript(
           }) as unknown as RawSourceMap
         }
       }
-      if (cssVars.length) {
+      if (cssVars.length || options.genDefaultAs) {
+        const defaultVar = options.genDefaultAs || normalScriptDefaultVar
         const s = new MagicString(content)
-        rewriteDefaultAST(scriptAst.body, s, DEFAULT_VAR)
+        rewriteDefaultAST(scriptAst.body, s, defaultVar)
         content = s.toString()
-        content += genNormalScriptCssVarsCode(
-          cssVars,
-          bindings,
-          scopeId,
-          isProd
-        )
-        content += `\nexport default ${DEFAULT_VAR}`
+        if (cssVars.length) {
+          content += genNormalScriptCssVarsCode(
+            cssVars,
+            bindings,
+            scopeId,
+            isProd,
+            defaultVar
+          )
+        }
+        if (!options.genDefaultAs) {
+          content += `\nexport default ${defaultVar}`
+        }
       }
       return {
         ...script,
@@ -1189,7 +1203,7 @@ export function compileScript(
         // export default { ... } --> const __default__ = { ... }
         const start = node.start! + scriptStartOffset!
         const end = node.declaration.start! + scriptStartOffset!
-        s.overwrite(start, end, `const ${DEFAULT_VAR} = `)
+        s.overwrite(start, end, `const ${normalScriptDefaultVar} = `)
       } else if (node.type === 'ExportNamedDeclaration') {
         const defaultSpecifier = node.specifiers.find(
           s => s.exported.type === 'Identifier' && s.exported.name === 'default'
@@ -1213,14 +1227,14 @@ export function compileScript(
             // rewrite to `import { x as __default__ } from './x'` and
             // add to top
             s.prepend(
-              `import { ${defaultSpecifier.local.name} as ${DEFAULT_VAR} } from '${node.source.value}'\n`
+              `import { ${defaultSpecifier.local.name} as ${normalScriptDefaultVar} } from '${node.source.value}'\n`
             )
           } else {
             // export { x as default }
             // rewrite to `const __default__ = x` and move to end
             s.appendLeft(
               scriptEndOffset!,
-              `\nconst ${DEFAULT_VAR} = ${defaultSpecifier.local.name}\n`
+              `\nconst ${normalScriptDefaultVar} = ${defaultSpecifier.local.name}\n`
             )
           }
         }
@@ -1793,11 +1807,11 @@ export function compileScript(
     // user's TS setting should compile it down to proper targets
     // export default defineComponent({ ...__default__, ... })
     const def =
-      (defaultExport ? `\n  ...${DEFAULT_VAR},` : ``) +
+      (defaultExport ? `\n  ...${normalScriptDefaultVar},` : ``) +
       (definedOptions ? `\n  ...${definedOptions},` : '')
     s.prependLeft(
       startOffset,
-      `\nexport default /*#__PURE__*/${helper(
+      `\n${genDefaultAs} /*#__PURE__*/${helper(
         `defineComponent`
       )}({${def}${runtimeOptions}\n  ${
         hasAwait ? `async ` : ``
@@ -1810,8 +1824,8 @@ export function compileScript(
       // export default Object.assign(__default__, { ... })
       s.prependLeft(
         startOffset,
-        `\nexport default /*#__PURE__*/Object.assign(${
-          defaultExport ? `${DEFAULT_VAR}, ` : ''
+        `\n${genDefaultAs} /*#__PURE__*/Object.assign(${
+          defaultExport ? `${normalScriptDefaultVar}, ` : ''
         }${definedOptions ? `${definedOptions}, ` : ''}{${runtimeOptions}\n  ` +
           `${hasAwait ? `async ` : ``}setup(${args}) {\n${exposeCall}`
       )
@@ -1819,7 +1833,7 @@ export function compileScript(
     } else {
       s.prependLeft(
         startOffset,
-        `\nexport default {${runtimeOptions}\n  ` +
+        `\n${genDefaultAs} {${runtimeOptions}\n  ` +
           `${hasAwait ? `async ` : ``}setup(${args}) {\n${exposeCall}`
       )
       s.appendRight(endOffset, `}`)
@@ -1839,7 +1853,6 @@ export function compileScript(
 
   return {
     ...scriptSetup,
-    s,
     bindings: bindingMetadata,
     imports: userImports,
     content: s.toString(),
index c7c04f83d729993a4f141f48a60a873ed1233aba..411e48cb76a80f850daea162b5e17cbfbd803777 100644 (file)
@@ -184,7 +184,8 @@ export function genNormalScriptCssVarsCode(
   cssVars: string[],
   bindings: BindingMetadata,
   id: string,
-  isProd: boolean
+  isProd: boolean,
+  defaultVar: string
 ): string {
   return (
     `\nimport { ${CSS_VARS_HELPER} as _${CSS_VARS_HELPER} } from 'vue'\n` +
@@ -194,8 +195,8 @@ export function genNormalScriptCssVarsCode(
       id,
       isProd
     )}}\n` +
-    `const __setup__ = __default__.setup\n` +
-    `__default__.setup = __setup__\n` +
+    `const __setup__ = ${defaultVar}.setup\n` +
+    `${defaultVar}.setup = __setup__\n` +
     `  ? (props, ctx) => { __injectCSSVars__();return __setup__(props, ctx) }\n` +
     `  : __injectCSSVars__\n`
   )
index e42678de4bba1cede5954ec67ee64a0120691f3f..c73276d9c919318ddf70c5e4669f70081be14304 100644 (file)
@@ -1,3 +1,5 @@
+export const version = __VERSION__
+
 // API
 export { parse } from './parse'
 export { compileTemplate } from './compileTemplate'
index b36d133eb101b175de53310c56e48c0d4837a814..79065fc667eb2d1821d058530b276d7e8c6bdac1 100644 (file)
@@ -12,7 +12,6 @@ import { TemplateCompiler } from './compileTemplate'
 import { parseCssVars } from './cssVars'
 import { createCache } from './cache'
 import { hmrShouldReload, ImportBinding } from './compileScript'
-import MagicString from 'magic-string'
 
 export const DEFAULT_FILENAME = 'anonymous.vue'
 
@@ -42,7 +41,6 @@ export interface SFCTemplateBlock extends SFCBlock {
 
 export interface SFCScriptBlock extends SFCBlock {
   type: 'script'
-  s: MagicString
   setup?: string | boolean
   bindings?: BindingMetadata
   imports?: Record<string, ImportBinding>