소스 검색

refactor: rewrite defineVaporAsyncComponent import to defineAsyncComponent in SSR + vapor mode (#14016)

This limits the defineVaporAsyncComponent only used in the `.vue` file in SSR
revert https://github.com/vuejs/core/pull/13383/commits/897404317ef749c12d59f9731d0e8d94b013c6ac
edison 6 달 전
부모
커밋
1cc0553905

+ 38 - 0
packages/compiler-sfc/__tests__/compileScript.spec.ts

@@ -1711,3 +1711,41 @@ describe('compileScript', () => {
     expect(scriptAst).not.toBeDefined()
   })
 })
+
+describe('vapor mode + ssr', () => {
+  test('rewrite defineVaporAsyncComponent import', () => {
+    const { content } = compile(
+      `
+        <script setup vapor>
+        import { defineVaporAsyncComponent } from 'vue'
+        </script>
+      `,
+      {
+        templateOptions: {
+          ssr: true,
+        },
+      },
+    )
+    expect(content).toContain(
+      `import { defineAsyncComponent as defineVaporAsyncComponent } from 'vue'`,
+    )
+  })
+
+  test('rewrite defineVaporAsyncComponent import with local name', () => {
+    const { content } = compile(
+      `
+        <script setup vapor>
+        import { defineVaporAsyncComponent as def } from 'vue'
+        </script>
+      `,
+      {
+        templateOptions: {
+          ssr: true,
+        },
+      },
+    )
+    expect(content).toContain(
+      `import { defineAsyncComponent as def } from 'vue'`,
+    )
+  })
+})

+ 17 - 0
packages/compiler-sfc/src/compileScript.ts

@@ -388,6 +388,23 @@ export function compileScript(
         const local = specifier.local.name
         const imported = getImportedName(specifier)
         const source = node.source.value
+
+        // rewrite defineVaporAsyncComponent import to defineAsyncComponent
+        // in SSR + Vapor mode
+        if (
+          vapor &&
+          ssr &&
+          specifier.type === 'ImportSpecifier' &&
+          source === 'vue' &&
+          imported === 'defineVaporAsyncComponent'
+        ) {
+          ctx.s.overwrite(
+            specifier.start! + startOffset,
+            specifier.end! + startOffset,
+            `defineAsyncComponent as ${local}`,
+          )
+        }
+
         const existing = ctx.userImports[local]
         if (source === 'vue' && MACROS.includes(imported)) {
           if (local === imported) {

+ 1 - 1
packages/vue/src/index-with-vapor.ts

@@ -1,3 +1,3 @@
 // for type generation only
-export * from './indexBase'
+export * from './index'
 export * from '@vue/runtime-vapor'

+ 107 - 2
packages/vue/src/index.ts

@@ -1,2 +1,107 @@
-export * from './indexBase'
-export * from './vaporAliases'
+// This entry is the "full-build" that includes both the runtime
+// and the compiler, and supports on-the-fly compilation of the template option.
+import { initDev } from './dev'
+import {
+  type CompilerError,
+  type CompilerOptions,
+  compile,
+} from '@vue/compiler-dom'
+import {
+  type RenderFunction,
+  registerRuntimeCompiler,
+  warn,
+} from '@vue/runtime-dom'
+import * as runtimeDom from '@vue/runtime-dom'
+import {
+  NOOP,
+  extend,
+  genCacheKey,
+  generateCodeFrame,
+  isString,
+} from '@vue/shared'
+import type { InternalRenderFunction } from 'packages/runtime-core/src/component'
+
+if (__DEV__) {
+  initDev()
+}
+
+const compileCache: Record<string, RenderFunction> = Object.create(null)
+
+function compileToFunction(
+  template: string | HTMLElement,
+  options?: CompilerOptions,
+): RenderFunction {
+  if (!isString(template)) {
+    if (template.nodeType) {
+      template = template.innerHTML
+    } else {
+      __DEV__ && warn(`invalid template option: `, template)
+      return NOOP
+    }
+  }
+
+  const key = genCacheKey(template, options)
+  const cached = compileCache[key]
+  if (cached) {
+    return cached
+  }
+
+  if (template[0] === '#') {
+    const el = document.querySelector(template)
+    if (__DEV__ && !el) {
+      warn(`Template element not found or is empty: ${template}`)
+    }
+    // __UNSAFE__
+    // Reason: potential execution of JS expressions in in-DOM template.
+    // The user must make sure the in-DOM template is trusted. If it's rendered
+    // by the server, the template should not contain any user data.
+    template = el ? el.innerHTML : ``
+  }
+
+  const opts = extend(
+    {
+      hoistStatic: true,
+      onError: __DEV__ ? onError : undefined,
+      onWarn: __DEV__ ? e => onError(e, true) : NOOP,
+    } as CompilerOptions,
+    options,
+  )
+
+  if (!opts.isCustomElement && typeof customElements !== 'undefined') {
+    opts.isCustomElement = tag => !!customElements.get(tag)
+  }
+
+  const { code } = compile(template, opts)
+
+  function onError(err: CompilerError, asWarning = false) {
+    const message = asWarning
+      ? err.message
+      : `Template compilation error: ${err.message}`
+    const codeFrame =
+      err.loc &&
+      generateCodeFrame(
+        template as string,
+        err.loc.start.offset,
+        err.loc.end.offset,
+      )
+    warn(codeFrame ? `${message}\n${codeFrame}` : message)
+  }
+
+  // The wildcard import results in a huge object with every export
+  // with keys that cannot be mangled, and can be quite heavy size-wise.
+  // In the global build we know `Vue` is available globally so we can avoid
+  // the wildcard object.
+  const render = (
+    __GLOBAL__ ? new Function(code)() : new Function('Vue', code)(runtimeDom)
+  ) as RenderFunction
+
+  // mark the function as runtime compiled
+  ;(render as InternalRenderFunction)._rc = true
+
+  return (compileCache[key] = render)
+}
+
+registerRuntimeCompiler(compileToFunction)
+
+export { compileToFunction as compile }
+export * from '@vue/runtime-dom'

+ 0 - 107
packages/vue/src/indexBase.ts

@@ -1,107 +0,0 @@
-// This entry is the "full-build" that includes both the runtime
-// and the compiler, and supports on-the-fly compilation of the template option.
-import { initDev } from './dev'
-import {
-  type CompilerError,
-  type CompilerOptions,
-  compile,
-} from '@vue/compiler-dom'
-import {
-  type RenderFunction,
-  registerRuntimeCompiler,
-  warn,
-} from '@vue/runtime-dom'
-import * as runtimeDom from '@vue/runtime-dom'
-import {
-  NOOP,
-  extend,
-  genCacheKey,
-  generateCodeFrame,
-  isString,
-} from '@vue/shared'
-import type { InternalRenderFunction } from 'packages/runtime-core/src/component'
-
-if (__DEV__) {
-  initDev()
-}
-
-const compileCache: Record<string, RenderFunction> = Object.create(null)
-
-function compileToFunction(
-  template: string | HTMLElement,
-  options?: CompilerOptions,
-): RenderFunction {
-  if (!isString(template)) {
-    if (template.nodeType) {
-      template = template.innerHTML
-    } else {
-      __DEV__ && warn(`invalid template option: `, template)
-      return NOOP
-    }
-  }
-
-  const key = genCacheKey(template, options)
-  const cached = compileCache[key]
-  if (cached) {
-    return cached
-  }
-
-  if (template[0] === '#') {
-    const el = document.querySelector(template)
-    if (__DEV__ && !el) {
-      warn(`Template element not found or is empty: ${template}`)
-    }
-    // __UNSAFE__
-    // Reason: potential execution of JS expressions in in-DOM template.
-    // The user must make sure the in-DOM template is trusted. If it's rendered
-    // by the server, the template should not contain any user data.
-    template = el ? el.innerHTML : ``
-  }
-
-  const opts = extend(
-    {
-      hoistStatic: true,
-      onError: __DEV__ ? onError : undefined,
-      onWarn: __DEV__ ? e => onError(e, true) : NOOP,
-    } as CompilerOptions,
-    options,
-  )
-
-  if (!opts.isCustomElement && typeof customElements !== 'undefined') {
-    opts.isCustomElement = tag => !!customElements.get(tag)
-  }
-
-  const { code } = compile(template, opts)
-
-  function onError(err: CompilerError, asWarning = false) {
-    const message = asWarning
-      ? err.message
-      : `Template compilation error: ${err.message}`
-    const codeFrame =
-      err.loc &&
-      generateCodeFrame(
-        template as string,
-        err.loc.start.offset,
-        err.loc.end.offset,
-      )
-    warn(codeFrame ? `${message}\n${codeFrame}` : message)
-  }
-
-  // The wildcard import results in a huge object with every export
-  // with keys that cannot be mangled, and can be quite heavy size-wise.
-  // In the global build we know `Vue` is available globally so we can avoid
-  // the wildcard object.
-  const render = (
-    __GLOBAL__ ? new Function(code)() : new Function('Vue', code)(runtimeDom)
-  ) as RenderFunction
-
-  // mark the function as runtime compiled
-  ;(render as InternalRenderFunction)._rc = true
-
-  return (compileCache[key] = render)
-}
-
-registerRuntimeCompiler(compileToFunction)
-
-export { compileToFunction as compile }
-export * from '@vue/runtime-dom'

+ 1 - 1
packages/vue/src/runtime-with-vapor.ts

@@ -1,2 +1,2 @@
-export * from './runtimeBase'
+export * from './runtime'
 export * from '@vue/runtime-vapor'

+ 27 - 2
packages/vue/src/runtime.ts

@@ -1,2 +1,27 @@
-export * from './runtimeBase'
-export * from './vaporAliases'
+// This entry exports the runtime only, and is built as
+// `dist/vue.esm-bundler.js` which is used by default for bundlers.
+import { NOOP } from '@vue/shared'
+import { initDev } from './dev'
+import { type RenderFunction, warn } from '@vue/runtime-dom'
+
+if (__DEV__) {
+  initDev()
+}
+
+export * from '@vue/runtime-dom'
+
+export const compile = (_template: string): RenderFunction => {
+  if (__DEV__) {
+    warn(
+      `Runtime compilation is not supported in this build of Vue.` +
+        (__ESM_BUNDLER__
+          ? ` Configure your bundler to alias "vue" to "vue/dist/vue.esm-bundler.js".`
+          : __ESM_BROWSER__
+            ? ` Use "vue.esm-browser.js" instead.`
+            : __GLOBAL__
+              ? ` Use "vue.global.js" instead.`
+              : ``) /* should not happen */,
+    )
+  }
+  return NOOP
+}

+ 0 - 27
packages/vue/src/runtimeBase.ts

@@ -1,27 +0,0 @@
-// This entry exports the runtime only, and is built as
-// `dist/vue.esm-bundler.js` which is used by default for bundlers.
-import { NOOP } from '@vue/shared'
-import { initDev } from './dev'
-import { type RenderFunction, warn } from '@vue/runtime-dom'
-
-if (__DEV__) {
-  initDev()
-}
-
-export * from '@vue/runtime-dom'
-
-export const compile = (_template: string): RenderFunction => {
-  if (__DEV__) {
-    warn(
-      `Runtime compilation is not supported in this build of Vue.` +
-        (__ESM_BUNDLER__
-          ? ` Configure your bundler to alias "vue" to "vue/dist/vue.esm-bundler.js".`
-          : __ESM_BROWSER__
-            ? ` Use "vue.esm-browser.js" instead.`
-            : __GLOBAL__
-              ? ` Use "vue.global.js" instead.`
-              : ``) /* should not happen */,
-    )
-  }
-  return NOOP
-}

+ 0 - 7
packages/vue/src/vaporAliases.ts

@@ -1,7 +0,0 @@
-// Vapor-only APIs do not exist in the standard build, yet SSR executes
-// the standard entry. We alias them to the core implementations so SSR
-// keeps working without the Vapor runtime.
-export {
-  defineAsyncComponent as defineVaporAsyncComponent,
-  defineComponent as defineVaporComponent,
-} from '@vue/runtime-core'