build: add runtime-with-vapor format + fix sfc playground for vapor mode

This commit is contained in:
Evan You 2024-12-08 15:18:16 +08:00
parent 9a8645d0c5
commit 7e8edcd9cd
No known key found for this signature in database
GPG Key ID: 00E9AB7A6704CE0A
17 changed files with 74 additions and 86 deletions

View File

@ -4,21 +4,21 @@
"packageManager": "pnpm@9.12.3", "packageManager": "pnpm@9.12.3",
"type": "module", "type": "module",
"scripts": { "scripts": {
"dev": "node scripts/dev.js vue runtime-vapor", "dev": "node scripts/dev.js",
"build": "node scripts/build.js", "build": "node scripts/build.js",
"build-dts": "tsc -p tsconfig.build.json --noCheck && rollup -c rollup.dts.config.js", "build-dts": "tsc -p tsconfig.build.json --noCheck && rollup -c rollup.dts.config.js",
"clean": "rimraf --glob packages/*/dist temp .eslintcache", "clean": "rimraf --glob packages/*/dist temp .eslintcache",
"size": "run-s \"size-*\" && node scripts/usage-size.js", "size": "run-s \"size-*\" && node scripts/usage-size.js",
"size-global": "node scripts/build.js vue runtime-vapor runtime-dom runtime-vapor compiler-dom -f global -p --size", "size-global": "node scripts/build.js vue runtime-dom compiler-dom -f global -p --size",
"size-esm-runtime": "node scripts/build.js vue -f esm-bundler-runtime", "size-esm-runtime": "node scripts/build.js vue -f esm-bundler-runtime",
"size-esm": "node scripts/build.js runtime-shared runtime-dom runtime-vapor runtime-core reactivity shared runtime-vapor -f esm-bundler", "size-esm": "node scripts/build.js runtime-shared runtime-dom runtime-core reactivity shared runtime-vapor -f esm-bundler",
"check": "tsc --incremental --noEmit", "check": "tsc --incremental --noEmit",
"lint": "eslint --cache .", "lint": "eslint --cache .",
"format": "prettier --write --cache .", "format": "prettier --write --cache .",
"format-check": "prettier --check --cache .", "format-check": "prettier --check --cache .",
"test": "vitest", "test": "vitest",
"test-unit": "vitest --project unit", "test-unit": "vitest --project unit",
"test-e2e": "node scripts/build.js vue runtime-vapor -f global -d && vitest --project e2e", "test-e2e": "node scripts/build.js vue -f global -d && vitest --project e2e",
"test-dts": "run-s build-dts test-dts-only", "test-dts": "run-s build-dts test-dts-only",
"test-dts-only": "tsc -p packages-private/dts-built-test/tsconfig.json && tsc -p ./packages-private/dts-test/tsconfig.test.json", "test-dts-only": "tsc -p packages-private/dts-built-test/tsconfig.json && tsc -p ./packages-private/dts-test/tsconfig.test.json",
"test-coverage": "vitest run --project unit --coverage", "test-coverage": "vitest run --project unit --coverage",
@ -33,15 +33,13 @@
"dev-compiler": "run-p \"dev template-explorer\" serve open", "dev-compiler": "run-p \"dev template-explorer\" serve open",
"dev-sfc": "run-s dev-prepare-cjs dev-sfc-run", "dev-sfc": "run-s dev-prepare-cjs dev-sfc-run",
"dev-sfc-serve": "vite packages-private/sfc-playground", "dev-sfc-serve": "vite packages-private/sfc-playground",
"dev-sfc-run": "run-p \"dev compiler-sfc -f esm-browser\" \"dev vue -if esm-bundler-runtime\" \"dev vue -ipf esm-browser-runtime\" \"dev server-renderer -if esm-bundler\" dev-sfc-serve", "dev-sfc-run": "run-p \"dev compiler-sfc -f esm-browser\" \"dev vue -if vapor\" \"dev vue -ipf vapor\" \"dev server-renderer -if esm-bundler\" dev-sfc-serve",
"dev-vapor": "pnpm -C playground run dev", "dev-vapor": "pnpm -C playground run dev",
"serve": "serve", "serve": "serve",
"open": "open http://localhost:3000/packages-private/template-explorer/local.html", "open": "open http://localhost:3000/packages-private/template-explorer/local.html",
"build-sfc-playground": "run-s build-all-cjs build-runtime-esm build-browser-esm build-ssr-esm build-sfc-playground-self", "build-sfc-playground": "run-s build-all-cjs build-all-esm build-sfc-playground-self",
"build-all-cjs": "node scripts/build.js vue runtime compiler reactivity shared -af cjs", "build-all-cjs": "node scripts/build.js vue runtime compiler reactivity shared -af cjs",
"build-runtime-esm": "node scripts/build.js runtime reactivity shared -af esm-bundler && node scripts/build.js vue -f esm-bundler-runtime && node scripts/build.js vue -f esm-browser-runtime", "build-all-esm": "node scripts/build.js runtime reactivity shared -af esm-bundler && node scripts/build.js vue -f esm-bundler,esm-browser,esm-bundler-runtime,esm-browser-runtime && node scripts/build.js compiler-sfc server-renderer -f esm-browser",
"build-browser-esm": "node scripts/build.js runtime reactivity shared -af esm-bundler && node scripts/build.js vue -f esm-bundler && node scripts/build.js vue -f esm-browser",
"build-ssr-esm": "node scripts/build.js compiler-sfc server-renderer runtime-vapor -f esm-browser",
"build-sfc-playground-self": "cd packages-private/sfc-playground && npm run build", "build-sfc-playground-self": "cd packages-private/sfc-playground && npm run build",
"preinstall": "npx only-allow pnpm", "preinstall": "npx only-allow pnpm",
"postinstall": "simple-git-hooks" "postinstall": "simple-git-hooks"

View File

@ -5,10 +5,8 @@ import {
type SFCOptions, type SFCOptions,
useStore, useStore,
useVueImportMap, useVueImportMap,
mergeImportMap,
File, File,
StoreState, StoreState,
ImportMap,
} from '@vue/repl' } from '@vue/repl'
import Monaco from '@vue/repl/monaco-editor' import Monaco from '@vue/repl/monaco-editor'
import { ref, watchEffect, onMounted, computed, watch } from 'vue' import { ref, watchEffect, onMounted, computed, watch } from 'vue'
@ -31,36 +29,26 @@ const initAutoSave: boolean = JSON.parse(
) )
const autoSave = ref(initAutoSave) const autoSave = ref(initAutoSave)
const { const { vueVersion, productionMode, importMap } = useVueImportMap({
vueVersion, runtimeDev: () => {
productionMode, return import.meta.env.PROD
importMap: vueImportMap, ? useVaporMode.value
} = useVueImportMap({ ? `${location.origin}/vue.runtime-with-vapor.esm-browser.js`
runtimeDev: import.meta.env.PROD : `${location.origin}/vue.runtime.esm-browser.js`
? `${location.origin}/vue.runtime.esm-browser.js` : `${location.origin}/src/vue-dev-proxy`
: `${location.origin}/src/vue-dev-proxy`, },
runtimeProd: import.meta.env.PROD runtimeProd: () => {
? `${location.origin}/vue.runtime.esm-browser.prod.js` return import.meta.env.PROD
: `${location.origin}/src/vue-dev-proxy-prod`, ? useVaporMode.value
? `${location.origin}/vue.runtime-with-vapor.esm-browser.prod.js`
: `${location.origin}/vue.runtime.esm-browser.prod.js`
: `${location.origin}/src/vue-dev-proxy-prod`
},
serverRenderer: import.meta.env.PROD serverRenderer: import.meta.env.PROD
? `${location.origin}/server-renderer.esm-browser.js` ? `${location.origin}/server-renderer.esm-browser.js`
: `${location.origin}/src/vue-server-renderer-dev-proxy`, : `${location.origin}/src/vue-server-renderer-dev-proxy`,
}) })
const importMap = computed(() => {
const vapor = import.meta.env.PROD
? `${location.origin}/vue-vapor.esm-browser.js`
: `${location.origin}/src/vue-vapor-dev-proxy`
const vaporImportMap: ImportMap = {
imports: {
'vue/vapor': vapor,
},
}
return mergeImportMap(vueImportMap.value, vaporImportMap)
})
let hash = location.hash.slice(1) let hash = location.hash.slice(1)
if (hash.startsWith('__DEV__')) { if (hash.startsWith('__DEV__')) {
hash = hash.slice(7) hash = hash.slice(7)
@ -126,7 +114,7 @@ watch(
files.value['src/index.html'] = new File( files.value['src/index.html'] = new File(
'src/index.html', 'src/index.html',
`<script type="module"> `<script type="module">
import { createVaporApp } from 'vue/vapor' import { createVaporApp } from 'vue'
import App from './App.vue' import App from './App.vue'
createVaporApp(App).mount('#app')` + createVaporApp(App).mount('#app')` +
'<' + '<' +

View File

@ -1,2 +1,2 @@
// serve vue to the iframe sandbox during dev. // serve vue to the iframe sandbox during dev.
export * from 'vue/dist/vue.runtime.esm-browser.prod.js' export * from 'vue/dist/vue.runtime-with-vapor.esm-browser.prod.js'

View File

@ -1,2 +1,2 @@
// serve vue to the iframe sandbox during dev. // serve vue to the iframe sandbox during dev.
export * from 'vue' export * from 'vue/dist/vue.runtime-with-vapor.esm-browser.js'

View File

@ -1,2 +0,0 @@
// serve vue/vapor to the iframe sandbox during dev.
export * from 'vue/vapor'

View File

@ -53,8 +53,9 @@ function copyVuePlugin(): Plugin {
copyFile(`vue/dist/vue.esm-browser.prod.js`) copyFile(`vue/dist/vue.esm-browser.prod.js`)
copyFile(`vue/dist/vue.runtime.esm-browser.js`) copyFile(`vue/dist/vue.runtime.esm-browser.js`)
copyFile(`vue/dist/vue.runtime.esm-browser.prod.js`) copyFile(`vue/dist/vue.runtime.esm-browser.prod.js`)
copyFile(`vue/dist/vue.runtime-with-vapor.esm-browser.js`)
copyFile(`vue/dist/vue.runtime-with-vapor.esm-browser.prod.js`)
copyFile(`server-renderer/dist/server-renderer.esm-browser.js`) copyFile(`server-renderer/dist/server-renderer.esm-browser.js`)
copyFile(`vue-vapor/dist/vue-vapor.esm-browser.js`)
}, },
} }
} }

View File

@ -333,11 +333,6 @@ export interface CodegenOptions extends SharedTransformCodegenOptions {
* @default 'vue' * @default 'vue'
*/ */
runtimeModuleName?: string runtimeModuleName?: string
/**
* Customize where to import runtime helpers from.
* @default 'vue/vapor'
*/
vaporRuntimeModuleName?: string
/** /**
* Customize where to import ssr runtime helpers from/** * Customize where to import ssr runtime helpers from/**
* @default 'vue/server-renderer' * @default 'vue/server-renderer'

View File

@ -379,8 +379,7 @@ export function compileScript(
const vueImportAliases: Record<string, string> = {} const vueImportAliases: Record<string, string> = {}
for (const key in ctx.userImports) { for (const key in ctx.userImports) {
const { source, imported, local } = ctx.userImports[key] const { source, imported, local } = ctx.userImports[key]
if (['vue', 'vue/vapor'].includes(source)) if (source === 'vue') vueImportAliases[imported] = local
vueImportAliases[imported] = local
} }
// 2.1 process normal <script> body // 2.1 process normal <script> body
@ -736,7 +735,7 @@ export function compileScript(
ctx.bindingMetadata[key] = ctx.bindingMetadata[key] =
imported === '*' || imported === '*' ||
(imported === 'default' && source.endsWith('.vue')) || (imported === 'default' && source.endsWith('.vue')) ||
['vue', 'vue/vapor'].includes(source) source === 'vue'
? BindingTypes.SETUP_CONST ? BindingTypes.SETUP_CONST
: BindingTypes.SETUP_MAYBE_REF : BindingTypes.SETUP_MAYBE_REF
} }
@ -847,7 +846,7 @@ export function compileScript(
for (const key in allBindings) { for (const key in allBindings) {
if ( if (
allBindings[key] === true && allBindings[key] === true &&
!['vue', 'vue/vapor'].includes(ctx.userImports[key].source) && ctx.userImports[key].source !== 'vue' &&
!ctx.userImports[key].source.endsWith('.vue') !ctx.userImports[key].source.endsWith('.vue')
) { ) {
// generate getter for import bindings // generate getter for import bindings
@ -989,8 +988,7 @@ export function compileScript(
ctx.s.prependLeft( ctx.s.prependLeft(
startOffset, startOffset,
`\n${genDefaultAs} /*@__PURE__*/${ctx.helper( `\n${genDefaultAs} /*@__PURE__*/${ctx.helper(
`defineComponent`, vapor ? `defineVaporComponent` : `defineComponent`,
vapor,
)}({${def}${runtimeOptions}\n ${ )}({${def}${runtimeOptions}\n ${
hasAwait ? `async ` : `` hasAwait ? `async ` : ``
}setup(${args}) {\n${exposeCall}`, }setup(${args}) {\n${exposeCall}`,
@ -1031,13 +1029,6 @@ export function compileScript(
.join(', ')} } from ${importSrc}\n`, .join(', ')} } from ${importSrc}\n`,
) )
} }
if (ctx.vaporHelperImports.size > 0) {
ctx.s.prepend(
`import { ${[...ctx.vaporHelperImports]
.map(h => `${h} as _${h}`)
.join(', ')} } from 'vue/vapor'\n`,
)
}
return { return {
...scriptSetup, ...scriptSetup,

View File

@ -64,9 +64,8 @@ export class ScriptCompileContext {
// codegen // codegen
bindingMetadata: BindingMetadata = {} bindingMetadata: BindingMetadata = {}
helperImports: Set<string> = new Set() helperImports: Set<string> = new Set()
vaporHelperImports: Set<string> = new Set() helper(key: string): string {
helper(key: string, vapor?: boolean): string { this.helperImports.add(key)
;(vapor ? this.vaporHelperImports : this.helperImports).add(key)
return `_${key}` return `_${key}`
} }

View File

@ -76,7 +76,6 @@ export class CodegenContext {
scopeId: null, scopeId: null,
runtimeGlobalName: `Vue`, runtimeGlobalName: `Vue`,
runtimeModuleName: `vue`, runtimeModuleName: `vue`,
vaporRuntimeModuleName: 'vue/vapor',
ssrRuntimeModuleName: 'vue/server-renderer', ssrRuntimeModuleName: 'vue/server-renderer',
ssr: false, ssr: false,
isTS: false, isTS: false,
@ -176,7 +175,7 @@ function genHelperImports({ helpers, vaporHelpers, options }: CodegenContext) {
if (vaporHelpers.size) { if (vaporHelpers.size) {
imports += `import { ${[...vaporHelpers] imports += `import { ${[...vaporHelpers]
.map(h => `${h} as _${h}`) .map(h => `${h} as _${h}`)
.join(', ')} } from '${options.vaporRuntimeModuleName}';\n` .join(', ')} } from 'vue';\n`
} }
return imports return imports
} }

View File

@ -14,10 +14,7 @@
"buildOptions": { "buildOptions": {
"name": "VueRuntimeVapor", "name": "VueRuntimeVapor",
"formats": [ "formats": [
"esm-bundler", "esm-bundler"
"esm-browser",
"cjs",
"global"
] ]
}, },
"repository": { "repository": {

View File

@ -1,7 +1,7 @@
import type { VaporComponent } from './component' import type { VaporComponent } from './component'
/*! #__NO_SIDE_EFFECTS__ */ /*! #__NO_SIDE_EFFECTS__ */
export function defineComponent(comp: VaporComponent): VaporComponent { export function defineVaporComponent(comp: VaporComponent): VaporComponent {
// TODO type inference // TODO type inference
return comp return comp
} }

View File

@ -1,10 +1,11 @@
// public APIs
export { createVaporApp } from './apiCreateApp'
export { defineVaporComponent } from './apiDefineComponent'
// compiler-use only
export { createComponent, createComponentWithFallback } from './component' export { createComponent, createComponentWithFallback } from './component'
export { renderEffect } from './renderEffect' export { renderEffect } from './renderEffect'
export { createVaporApp } from './apiCreateApp'
export { defineComponent } from './apiDefineComponent'
export { createSlot } from './componentSlots' export { createSlot } from './componentSlots'
// DOM
export { template, children, next } from './dom/template' export { template, children, next } from './dom/template'
export { insert, prepend, remove, createTextNode } from './dom/node' export { insert, prepend, remove, createTextNode } from './dom/node'
export { setStyle } from './dom/style' export { setStyle } from './dom/style'
@ -20,6 +21,3 @@ export {
} from './dom/prop' } from './dom/prop'
export { on, delegate, delegateEvents, setDynamicEvents } from './dom/event' export { on, delegate, delegateEvents, setDynamicEvents } from './dom/event'
export { setRef } from './dom/templateRef' export { setRef } from './dom/templateRef'
// re-exports
export { resolveComponent } from '@vue/runtime-dom'

View File

@ -89,7 +89,8 @@
"global", "global",
"global-runtime", "global-runtime",
"esm-browser", "esm-browser",
"esm-browser-runtime" "esm-browser-runtime",
"vapor"
] ]
}, },
"repository": { "repository": {

View File

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

View File

@ -21,7 +21,7 @@ import { minify as minifySwc } from '@swc/core'
* @template {keyof T} K * @template {keyof T} K
* @typedef { Omit<T, K> & Required<Pick<T, K>> } MarkRequired * @typedef { Omit<T, K> & Required<Pick<T, K>> } MarkRequired
*/ */
/** @typedef {'cjs' | 'esm-bundler' | 'global' | 'global-runtime' | 'esm-browser' | 'esm-bundler-runtime' | 'esm-browser-runtime'} PackageFormat */ /** @typedef {'cjs' | 'esm-bundler' | 'global' | 'global-runtime' | 'esm-browser' | 'esm-bundler-runtime' | 'esm-browser-runtime' | 'vapor'} PackageFormat */
/** @typedef {MarkRequired<import('rollup').OutputOptions, 'file' | 'format'>} OutputOptions */ /** @typedef {MarkRequired<import('rollup').OutputOptions, 'file' | 'format'>} OutputOptions */
if (!process.env.TARGET) { if (!process.env.TARGET) {
@ -85,6 +85,12 @@ const outputConfigs = {
file: resolve(`dist/${name}.runtime.global.js`), file: resolve(`dist/${name}.runtime.global.js`),
format: 'iife', format: 'iife',
}, },
// The vapor format is a esm-browser + runtime only build that is meant for
// the SFC playground only.
vapor: {
file: resolve(`dist/${name}.runtime-with-vapor.esm-browser.js`),
format: 'es',
},
} }
/** @type {ReadonlyArray<PackageFormat>} */ /** @type {ReadonlyArray<PackageFormat>} */
@ -107,7 +113,7 @@ if (process.env.NODE_ENV === 'production') {
if (format === 'cjs') { if (format === 'cjs') {
packageConfigs.push(createProductionConfig(format)) packageConfigs.push(createProductionConfig(format))
} }
if (/^(global|esm-browser)(-runtime)?/.test(format)) { if (format === 'vapor' || /^(global|esm-browser)(-runtime)?/.test(format)) {
packageConfigs.push(createMinifiedConfig(format)) packageConfigs.push(createMinifiedConfig(format))
} }
}) })
@ -131,7 +137,7 @@ function createConfig(format, output, plugins = []) {
const isProductionBuild = const isProductionBuild =
process.env.__DEV__ === 'false' || /\.prod\.js$/.test(output.file) process.env.__DEV__ === 'false' || /\.prod\.js$/.test(output.file)
const isBundlerESMBuild = /esm-bundler/.test(format) const isBundlerESMBuild = /esm-bundler/.test(format)
const isBrowserESMBuild = /esm-browser/.test(format) const isBrowserESMBuild = /esm-browser/.test(format) || format === 'vapor'
const isServerRenderer = name === 'server-renderer' const isServerRenderer = name === 'server-renderer'
const isCJSBuild = format === 'cjs' const isCJSBuild = format === 'cjs'
const isGlobalBuild = /global/.test(format) const isGlobalBuild = /global/.test(format)
@ -159,7 +165,12 @@ function createConfig(format, output, plugins = []) {
output.name = packageOptions.name output.name = packageOptions.name
} }
let entryFile = /\bruntime\b/.test(format) ? `runtime.ts` : `index.ts` let entryFile =
format === 'vapor'
? 'runtime-with-vapor.ts'
: /\bruntime\b/.test(format)
? `runtime.ts`
: `index.ts`
// the compat build needs both default AND named exports. This will cause // the compat build needs both default AND named exports. This will cause
// Rollup to complain for non-ESM targets, so we use separate entries for // Rollup to complain for non-ESM targets, so we use separate entries for

View File

@ -49,9 +49,12 @@ const outputFormat = format.startsWith('global')
? 'cjs' ? 'cjs'
: 'esm' : 'esm'
const postfix = format.endsWith('-runtime') const postfix =
? `runtime.${format.replace(/-runtime$/, '')}` format === 'vapor'
: format ? 'runtime-with-vapor.esm-browser'
: format.endsWith('-runtime')
? `runtime.${format.replace(/-runtime$/, '')}`
: format
const privatePackages = fs.readdirSync('packages-private') const privatePackages = fs.readdirSync('packages-private')
@ -127,9 +130,16 @@ for (const target of targets) {
plugins.push(polyfillNode()) plugins.push(polyfillNode())
} }
const entry =
format === 'vapor'
? 'runtime-with-vapor.ts'
: format.endsWith('-runtime')
? 'runtime.ts'
: 'index.ts'
esbuild esbuild
.context({ .context({
entryPoints: [resolve(__dirname, `${pkgBasePath}/src/index.ts`)], entryPoints: [resolve(__dirname, `${pkgBasePath}/src/${entry}`)],
outfile, outfile,
bundle: true, bundle: true,
external, external,