-
+
diff --git a/playground/src/bench/data.ts b/benchmark/client/data.ts
similarity index 100%
rename from playground/src/bench/data.ts
rename to benchmark/client/data.ts
diff --git a/benchmark/client/index.html b/benchmark/client/index.html
new file mode 100644
index 000000000..22524c4b8
--- /dev/null
+++ b/benchmark/client/index.html
@@ -0,0 +1,17 @@
+
+
+
+
+
+
Vue Vapor Benchmark
+
+
+
+
+
+
+
diff --git a/benchmark/client/index.ts b/benchmark/client/index.ts
new file mode 100644
index 000000000..a12f727a1
--- /dev/null
+++ b/benchmark/client/index.ts
@@ -0,0 +1,5 @@
+if (import.meta.env.IS_VAPOR) {
+ import('./vapor')
+} else {
+ import('./vdom')
+}
diff --git a/benchmark/client/profiling.ts b/benchmark/client/profiling.ts
new file mode 100644
index 000000000..d4de3f517
--- /dev/null
+++ b/benchmark/client/profiling.ts
@@ -0,0 +1,79 @@
+/* eslint-disable no-console */
+/* eslint-disable no-restricted-syntax */
+/* eslint-disable no-restricted-globals */
+
+declare module globalThis {
+ let doProfile: boolean
+ let recordTime: boolean
+ let times: Record
+}
+
+globalThis.recordTime = true
+globalThis.doProfile = false
+
+export const defer = () => new Promise(r => requestIdleCallback(r))
+
+const times: Record = (globalThis.times = {})
+
+export function wrap(
+ id: string,
+ fn: (...args: any[]) => any,
+): (...args: any[]) => Promise {
+ return async (...args) => {
+ if (!globalThis.recordTime) {
+ return fn(...args)
+ }
+
+ document.body.classList.remove('done')
+
+ const { doProfile } = globalThis
+ await defer()
+
+ doProfile && console.profile(id)
+ const start = performance.now()
+ fn(...args)
+
+ await defer()
+ const time = performance.now() - start
+ const prevTimes = times[id] || (times[id] = [])
+ prevTimes.push(time)
+
+ const { min, max, median, mean, std } = compute(prevTimes)
+ const msg =
+ `${id}: min: ${min} / ` +
+ `max: ${max} / ` +
+ `median: ${median}ms / ` +
+ `mean: ${mean}ms / ` +
+ `time: ${time.toFixed(2)}ms / ` +
+ `std: ${std} ` +
+ `over ${prevTimes.length} runs`
+ doProfile && console.profileEnd(id)
+ console.log(msg)
+ const timeEl = document.getElementById('time')!
+ timeEl.textContent = msg
+
+ document.body.classList.add('done')
+ }
+}
+
+function compute(array: number[]) {
+ const n = array.length
+ const max = Math.max(...array)
+ const min = Math.min(...array)
+ const mean = array.reduce((a, b) => a + b) / n
+ const std = Math.sqrt(
+ array.map(x => Math.pow(x - mean, 2)).reduce((a, b) => a + b) / n,
+ )
+ const median = array.slice().sort((a, b) => a - b)[Math.floor(n / 2)]
+ return {
+ max: round(max),
+ min: round(min),
+ mean: round(mean),
+ std: round(std),
+ median: round(median),
+ }
+}
+
+function round(n: number) {
+ return +n.toFixed(2)
+}
diff --git a/benchmark/client/vapor.ts b/benchmark/client/vapor.ts
new file mode 100644
index 000000000..68071a84c
--- /dev/null
+++ b/benchmark/client/vapor.ts
@@ -0,0 +1,4 @@
+import { createVaporApp } from '@vue/vapor'
+import App from './App.vue'
+
+createVaporApp(App as any).mount('#app')
diff --git a/benchmark/client/vdom.ts b/benchmark/client/vdom.ts
new file mode 100644
index 000000000..01433bca2
--- /dev/null
+++ b/benchmark/client/vdom.ts
@@ -0,0 +1,4 @@
+import { createApp } from 'vue'
+import App from './App.vue'
+
+createApp(App).mount('#app')
diff --git a/benchmark/index.js b/benchmark/index.js
new file mode 100644
index 000000000..d5ca30893
--- /dev/null
+++ b/benchmark/index.js
@@ -0,0 +1,340 @@
+// @ts-check
+import path from 'node:path'
+import { parseArgs } from 'node:util'
+import { mkdir, rm, writeFile } from 'node:fs/promises'
+import Vue from '@vitejs/plugin-vue'
+import { build } from 'vite'
+import connect from 'connect'
+import sirv from 'sirv'
+import { launch } from 'puppeteer'
+import colors from 'picocolors'
+import { exec, getSha } from '../scripts/utils.js'
+
+// Thanks to https://github.com/krausest/js-framework-benchmark (Apache-2.0 license)
+const {
+ values: {
+ skipLib,
+ skipApp,
+ skipBench,
+ vdom,
+ noVapor,
+ port: portStr,
+ count: countStr,
+ noHeadless,
+ },
+} = parseArgs({
+ allowNegative: true,
+ allowPositionals: true,
+ options: {
+ skipLib: {
+ type: 'boolean',
+ short: 'v',
+ },
+ skipApp: {
+ type: 'boolean',
+ short: 'a',
+ },
+ skipBench: {
+ type: 'boolean',
+ short: 'b',
+ },
+ noVapor: {
+ type: 'boolean',
+ },
+ vdom: {
+ type: 'boolean',
+ short: 'v',
+ },
+ port: {
+ type: 'string',
+ short: 'p',
+ default: '8193',
+ },
+ count: {
+ type: 'string',
+ short: 'c',
+ default: '50',
+ },
+ noHeadless: {
+ type: 'boolean',
+ },
+ },
+})
+
+const port = +(/** @type {string}*/ (portStr))
+const count = +(/** @type {string}*/ (countStr))
+const sha = await getSha(true)
+
+if (!skipLib) {
+ await buildLib()
+}
+if (!skipApp) {
+ await rm('client/dist', { recursive: true }).catch(() => {})
+ vdom && (await buildApp(false))
+ !noVapor && (await buildApp(true))
+}
+const server = startServer()
+
+if (!skipBench) {
+ await benchmark()
+ server.close()
+}
+
+async function buildLib() {
+ console.info(colors.blue('Building lib...'))
+
+ const options = {
+ cwd: path.resolve(import.meta.dirname, '..'),
+ stdio: 'inherit',
+ }
+ const [{ ok }, { ok: ok2 }, { ok: ok3 }, { ok: ok4 }] = await Promise.all([
+ exec(
+ 'pnpm',
+ 'run --silent build shared compiler-core compiler-dom compiler-vapor -pf cjs'.split(
+ ' ',
+ ),
+ options,
+ ),
+ exec(
+ 'pnpm',
+ 'run --silent build compiler-sfc compiler-ssr -f cjs'.split(' '),
+ options,
+ ),
+ exec(
+ 'pnpm',
+ 'run --silent build vue-vapor -pf esm-browser'.split(' '),
+ options,
+ ),
+ exec(
+ 'pnpm',
+ 'run --silent build vue -pf esm-browser-runtime'.split(' '),
+ options,
+ ),
+ ])
+
+ if (!ok || !ok2 || !ok3 || !ok4) {
+ console.error('Failed to build')
+ process.exit(1)
+ }
+}
+
+/** @param {boolean} isVapor */
+async function buildApp(isVapor) {
+ console.info(
+ colors.blue(`\nBuilding ${isVapor ? 'Vapor' : 'Virtual DOM'} app...\n`),
+ )
+
+ process.env.NODE_ENV = 'production'
+ const CompilerSFC = await import(
+ '../packages/compiler-sfc/dist/compiler-sfc.cjs.js'
+ )
+ /** @type {any} */
+ const TemplateCompiler = await import(
+ isVapor
+ ? '../packages/compiler-vapor/dist/compiler-vapor.cjs.prod.js'
+ : '../packages/compiler-dom/dist/compiler-dom.cjs.prod.js'
+ )
+ const runtimePath = path.resolve(
+ import.meta.dirname,
+ isVapor
+ ? '../packages/vue-vapor/dist/vue-vapor.esm-browser.prod.js'
+ : '../packages/vue/dist/vue.runtime.esm-browser.prod.js',
+ )
+
+ const mode = isVapor ? 'vapor' : 'vdom'
+ await build({
+ root: './client',
+ base: `/${mode}`,
+ define: {
+ 'import.meta.env.IS_VAPOR': String(isVapor),
+ },
+ build: {
+ minify: 'terser',
+ outDir: path.resolve('./client/dist', mode),
+ rollupOptions: {
+ onwarn(log, handler) {
+ if (log.code === 'INVALID_ANNOTATION') return
+ handler(log)
+ },
+ },
+ },
+ resolve: {
+ alias: {
+ '@vue/vapor': runtimePath,
+ 'vue/vapor': runtimePath,
+ vue: runtimePath,
+ },
+ },
+ clearScreen: false,
+ plugins: [
+ Vue({
+ compiler: CompilerSFC,
+ template: { compiler: TemplateCompiler },
+ }),
+ ],
+ })
+}
+
+function startServer() {
+ const server = connect().use(sirv('./client/dist')).listen(port)
+ console.info(`\n\nServer started at`, colors.blue(`http://localhost:${port}`))
+ process.on('SIGTERM', () => server.close())
+ return server
+}
+
+async function benchmark() {
+ console.info(colors.blue(`\nStarting benchmark...`))
+
+ const browser = await initBrowser()
+
+ await mkdir('results', { recursive: true }).catch(() => {})
+ if (!noVapor) {
+ await doBench(browser, true)
+ }
+ if (vdom) {
+ await doBench(browser, false)
+ }
+
+ await browser.close()
+}
+
+/**
+ *
+ * @param {import('puppeteer').Browser} browser
+ * @param {boolean} isVapor
+ */
+async function doBench(browser, isVapor) {
+ const mode = isVapor ? 'vapor' : 'vdom'
+ console.info('\n\nmode:', mode)
+
+ const page = await browser.newPage()
+ await page.goto(`http://localhost:${port}/${mode}`, {
+ waitUntil: 'networkidle0',
+ })
+
+ await forceGC()
+ const t = performance.now()
+
+ for (let i = 0; i < count; i++) {
+ await clickButton('run') // test: create rows
+ await clickButton('update') // partial update
+ await clickButton('swaprows') // swap rows
+ await select() // test: select row, remove row
+ await clickButton('clear') // clear rows
+
+ await withoutRecord(() => clickButton('run'))
+ await clickButton('add') // append rows to large table
+
+ await withoutRecord(() => clickButton('clear'))
+ await clickButton('runLots') // create many rows
+ await withoutRecord(() => clickButton('clear'))
+
+ // TODO replace all rows
+ }
+
+ console.info(
+ 'Total time:',
+ colors.cyan(((performance.now() - t) / 1000).toFixed(2)),
+ 's',
+ )
+ const times = await getTimes()
+ const result =
+ /** @type {Record} */
+ Object.fromEntries(Object.entries(times).map(([k, v]) => [k, compute(v)]))
+
+ console.table(result)
+ await writeFile(
+ `results/benchmark-${sha}-${mode}.json`,
+ JSON.stringify(result, undefined, 2),
+ )
+ await page.close()
+ return result
+
+ function getTimes() {
+ return page.evaluate(() => /** @type {any} */ (globalThis).times)
+ }
+
+ async function forceGC() {
+ await page.evaluate(
+ `window.gc({type:'major',execution:'sync',flavor:'last-resort'})`,
+ )
+ }
+
+ /** @param {() => any} fn */
+ async function withoutRecord(fn) {
+ await page.evaluate(() => (globalThis.recordTime = false))
+ await fn()
+ await page.evaluate(() => (globalThis.recordTime = true))
+ }
+
+ /** @param {string} id */
+ async function clickButton(id) {
+ await page.click(`#${id}`)
+ await wait()
+ }
+
+ async function select() {
+ for (let i = 1; i <= 10; i++) {
+ await page.click(`tbody > tr:nth-child(2) > td:nth-child(2) > a`)
+ await page.waitForSelector(`tbody > tr:nth-child(2).danger`)
+ await page.click(`tbody > tr:nth-child(2) > td:nth-child(3) > button`)
+ await wait()
+ }
+ }
+
+ async function wait() {
+ await page.waitForSelector('.done')
+ }
+}
+
+async function initBrowser() {
+ const disableFeatures = [
+ 'Translate', // avoid translation popups
+ 'PrivacySandboxSettings4', // avoid privacy popup
+ 'IPH_SidePanelGenericMenuFeature', // bookmark popup see https://github.com/krausest/js-framework-benchmark/issues/1688
+ ]
+
+ const args = [
+ '--js-flags=--expose-gc', // needed for gc() function
+ '--no-default-browser-check',
+ '--disable-sync',
+ '--no-first-run',
+ '--ash-no-nudges',
+ '--disable-extensions',
+ `--disable-features=${disableFeatures.join(',')}`,
+ ]
+
+ const headless = !noHeadless
+ console.info('headless:', headless)
+ const browser = await launch({
+ headless: headless,
+ args,
+ })
+ console.log('browser version:', colors.blue(await browser.version()))
+
+ return browser
+}
+
+/** @param {number[]} array */
+function compute(array) {
+ const n = array.length
+ const max = Math.max(...array)
+ const min = Math.min(...array)
+ const mean = array.reduce((a, b) => a + b) / n
+ const std = Math.sqrt(
+ array.map(x => Math.pow(x - mean, 2)).reduce((a, b) => a + b) / n,
+ )
+ const median = array.slice().sort((a, b) => a - b)[Math.floor(n / 2)]
+ return {
+ max: round(max),
+ min: round(min),
+ mean: round(mean),
+ std: round(std),
+ median: round(median),
+ }
+}
+
+/** @param {number} n */
+function round(n) {
+ return +n.toFixed(2)
+}
diff --git a/benchmark/package.json b/benchmark/package.json
new file mode 100644
index 000000000..f6183b1ea
--- /dev/null
+++ b/benchmark/package.json
@@ -0,0 +1,19 @@
+{
+ "name": "benchmark",
+ "version": "0.0.0",
+ "author": "三咲智子 Kevin Deng ",
+ "license": "MIT",
+ "type": "module",
+ "scripts": {
+ "start": "node index.js"
+ },
+ "dependencies": {
+ "@vitejs/plugin-vue": "npm:@vue-vapor/vite-plugin-vue@0.0.0-alpha.6",
+ "connect": "^3.7.0",
+ "sirv": "^2.0.4",
+ "vite": "^5.0.12"
+ },
+ "devDependencies": {
+ "@types/connect": "^3.4.38"
+ }
+}
diff --git a/benchmark/tsconfig.json b/benchmark/tsconfig.json
new file mode 100644
index 000000000..0a2e412b2
--- /dev/null
+++ b/benchmark/tsconfig.json
@@ -0,0 +1,26 @@
+{
+ "compilerOptions": {
+ "target": "esnext",
+ "lib": ["es2022", "dom"],
+ "allowJs": true,
+ "moduleDetection": "force",
+ "module": "preserve",
+ "moduleResolution": "bundler",
+ "resolveJsonModule": true,
+ "types": ["node", "vite/client"],
+ "strict": true,
+ "noUnusedLocals": true,
+ "declaration": true,
+ "esModuleInterop": true,
+ "isolatedModules": true,
+ "verbatimModuleSyntax": true,
+ "skipLibCheck": true,
+ "noEmit": true,
+ "paths": {
+ "vue": ["../packages/vue/src"],
+ "@vue/vapor": ["../packages/vue-vapor/src"],
+ "@vue/*": ["../packages/*/src"]
+ }
+ },
+ "include": ["**/*"]
+}
diff --git a/eslint.config.js b/eslint.config.js
index f744b00c9..2717af4bb 100644
--- a/eslint.config.js
+++ b/eslint.config.js
@@ -146,6 +146,7 @@ export default tseslint.config(
'eslint.config.js',
'rollup*.config.js',
'scripts/**',
+ 'benchmark/*',
'./*.{js,ts}',
'packages/*/*.js',
'packages/vue/*/*.js',
diff --git a/playground/setup/dev.js b/playground/setup/dev.js
index 8b2f7bdaa..a6ba15274 100644
--- a/playground/setup/dev.js
+++ b/playground/setup/dev.js
@@ -1,10 +1,8 @@
// @ts-check
import path from 'node:path'
-import { fileURLToPath } from 'node:url'
-const dirname = path.dirname(fileURLToPath(new URL(import.meta.url)))
const resolve = (/** @type {string} */ p) =>
- path.resolve(dirname, '../../packages', p)
+ path.resolve(import.meta.dirname, '../../packages', p)
/**
* @param {Object} [env]
diff --git a/playground/src/bench/profiling.ts b/playground/src/bench/profiling.ts
deleted file mode 100644
index 256b0bd1c..000000000
--- a/playground/src/bench/profiling.ts
+++ /dev/null
@@ -1,60 +0,0 @@
-// @ts-expect-error
-globalThis.doProfile = false
-// const defer = nextTick
-const ric =
- typeof requestIdleCallback === 'undefined' ? setTimeout : requestIdleCallback
-export const defer = () => new Promise(r => ric(r))
-
-const times: Record = {}
-
-export const wrap = (
- id: string,
- fn: (...args: any[]) => any,
-): ((...args: any[]) => Promise) => {
- if (import.meta.env.PROD) return fn
- return async (...args) => {
- const btns = Array.from(
- document.querySelectorAll('#control button'),
- )
- for (const node of btns) {
- node.disabled = true
- }
- const doProfile = (globalThis as any).doProfile
- await defer()
-
- doProfile && console.profile(id)
- const start = performance.now()
- fn(...args)
- await defer()
- const time = performance.now() - start
- const prevTimes = times[id] || (times[id] = [])
- prevTimes.push(time)
- const median = prevTimes.slice().sort((a, b) => a - b)[
- Math.floor(prevTimes.length / 2)
- ]
- const mean = prevTimes.reduce((a, b) => a + b, 0) / prevTimes.length
- const msg =
- `${id}: min: ${Math.min(...prevTimes).toFixed(2)} / ` +
- `max: ${Math.max(...prevTimes).toFixed(2)} / ` +
- `median: ${median.toFixed(2)}ms / ` +
- `mean: ${mean.toFixed(2)}ms / ` +
- `time: ${time.toFixed(2)}ms / ` +
- `std: ${getStandardDeviation(prevTimes).toFixed(2)} ` +
- `over ${prevTimes.length} runs`
- doProfile && console.profileEnd(id)
- console.log(msg)
- document.getElementById('time')!.textContent = msg
-
- for (const node of btns) {
- node.disabled = false
- }
- }
-}
-
-function getStandardDeviation(array: number[]) {
- const n = array.length
- const mean = array.reduce((a, b) => a + b) / n
- return Math.sqrt(
- array.map(x => Math.pow(x - mean, 2)).reduce((a, b) => a + b) / n,
- )
-}
diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml
index de71a6eb4..51678bb6d 100644
--- a/pnpm-lock.yaml
+++ b/pnpm-lock.yaml
@@ -4,27 +4,6 @@ settings:
autoInstallPeers: true
excludeLinksFromLockfile: false
-catalogs:
- default:
- '@babel/parser':
- specifier: ^7.24.7
- version: 7.24.7
- '@babel/types':
- specifier: ^7.24.7
- version: 7.24.7
- estree-walker:
- specifier: ^2.0.2
- version: 2.0.2
- magic-string:
- specifier: ^0.30.10
- version: 0.30.10
- source-map-js:
- specifier: ^1.2.0
- version: 1.2.0
- vite:
- specifier: ^5.3.3
- version: 5.3.3
-
importers:
.:
@@ -177,6 +156,25 @@ importers:
specifier: ^1.6.0
version: 1.6.0(@types/node@20.14.13)(@vitest/ui@1.6.0)(jsdom@24.1.1)(sass@1.77.8)(terser@5.31.1)
+ benchmark:
+ dependencies:
+ '@vitejs/plugin-vue':
+ specifier: npm:@vue-vapor/vite-plugin-vue@0.0.0-alpha.6
+ version: '@vue-vapor/vite-plugin-vue@0.0.0-alpha.6(vite@5.3.3(@types/node@20.14.13)(sass@1.77.8)(terser@5.31.1))(vue@3.4.36(typescript@5.4.5))'
+ connect:
+ specifier: ^3.7.0
+ version: 3.7.0
+ sirv:
+ specifier: ^2.0.4
+ version: 2.0.4
+ vite:
+ specifier: ^5.0.12
+ version: 5.3.3(@types/node@20.14.13)(sass@1.77.8)(terser@5.31.1)
+ devDependencies:
+ '@types/connect':
+ specifier: ^3.4.38
+ version: 3.4.38
+
packages/compiler-core:
dependencies:
'@babel/parser':
@@ -1418,6 +1416,9 @@ packages:
'@tootallnate/quickjs-emscripten@0.23.0':
resolution: {integrity: sha512-C5Mc6rdnsaJDjO3UpGW/CQTHtCKaYlScZTly4JIu97Jxo/odCiH0ITnDXSJPTOrEKk/ycSZ0AOgTmkDtkOsvIA==}
+ '@types/connect@3.4.38':
+ resolution: {integrity: sha512-K6uROf1LD88uDQqJCktA4yzL1YYAK6NgfsI0v/mTgyPKWsX1CnJ0XPSDhViejru1GcRkLWb8RlzFYJRqGUbaug==}
+
'@types/estree@1.0.5':
resolution: {integrity: sha512-/kYRxGDLWzHOB7q+wtSUQlFrtcdUccpfy+X+9iMBpHK8QLLhx2wIPYuS5DYtR9Wa/YlZAbIovy7qVdB1Aq6Lyw==}
@@ -1566,13 +1567,49 @@ packages:
vite: ^5.0.0
vue: '*'
+ '@vue-vapor/vite-plugin-vue@0.0.0-alpha.6':
+ resolution: {integrity: sha512-V2aTQ7bkDXsoPvYIkTA54m3ypUXDIVpTFspn+ycuYcMfIY37cZ0ny6jm/afNY6k1DiaQ9JfAMBXAKzTBpu2B9A==}
+ engines: {node: ^18.0.0 || >=20.0.0}
+ peerDependencies:
+ vite: ^5.0.0
+ vue: '*'
+
+ '@vue/compiler-core@3.4.36':
+ resolution: {integrity: sha512-qBkndgpwFKdupmOPoiS10i7oFdN7a+4UNDlezD0GlQ1kuA1pNrscg9g12HnB5E8hrWSuEftRsbJhL1HI2zpJhg==}
+
+ '@vue/compiler-dom@3.4.36':
+ resolution: {integrity: sha512-eEIjy4GwwZTFon/Y+WO8tRRNGqylaRlA79T1RLhUpkOzJ7EtZkkb8MurNfkqY6x6Qiu0R7ESspEF7GkPR/4yYg==}
+
+ '@vue/compiler-sfc@3.4.36':
+ resolution: {integrity: sha512-rhuHu7qztt/rNH90dXPTzhB7hLQT2OC4s4GrPVqmzVgPY4XBlfWmcWzn4bIPEWNImt0CjO7kfHAf/1UXOtx3vw==}
+
+ '@vue/compiler-ssr@3.4.36':
+ resolution: {integrity: sha512-Wt1zyheF0zVvRJyhY74uxQbnkXV2Le/JPOrAxooR4rFYKC7cFr+cRqW6RU3cM/bsTy7sdZ83IDuy/gLPSfPGng==}
+
'@vue/consolidate@1.0.0':
resolution: {integrity: sha512-oTyUE+QHIzLw2PpV14GD/c7EohDyP64xCniWTcqcEmTd699eFqTIwOmtDYjcO1j3QgdXoJEoWv1/cCdLrRoOfg==}
engines: {node: '>= 0.12.0'}
+ '@vue/reactivity@3.4.36':
+ resolution: {integrity: sha512-wN1aoCwSoqrt1yt8wO0gc13QaC+Vk1o6AoSt584YHNnz6TGDhh1NCMUYgAnvp4HEIkLdGsaC1bvu/P+wpoDEXw==}
+
'@vue/repl@4.3.1':
resolution: {integrity: sha512-yzUuLhR+MqOGBDES+xbnm27SfPIEv7XKwhFWWpQhL7HUbXj77GVu+x50Q56JhCWWKTUJzk9MOvAn7bSgdvB5og==}
+ '@vue/runtime-core@3.4.36':
+ resolution: {integrity: sha512-9+TR14LAVEerZWLOm/N/sG2DVYhrH2bKgFrbH/FVt/Q8Jdw4OtdcGMRC6Tx8VAo0DA1eqAqrZaX0fbOaOxxZ4A==}
+
+ '@vue/runtime-dom@3.4.36':
+ resolution: {integrity: sha512-2Qe2fKkLxgZBVvHrG0QMNLL4bsx7Ae88pyXebY2WnQYABpOnGYvA+axMbcF9QwM4yxnsv+aELbC0eiNVns7mGw==}
+
+ '@vue/server-renderer@3.4.36':
+ resolution: {integrity: sha512-2XW90Rq8+Y7S1EIsAuubZVLm0gCU8HYb5mRAruFdwfC3XSOU5/YKePz29csFzsch8hXaY5UHh7ZMddmi1XTJEA==}
+ peerDependencies:
+ vue: 3.4.36
+
+ '@vue/shared@3.4.36':
+ resolution: {integrity: sha512-fdPLStwl1sDfYuUftBaUVn2pIrVFDASYerZSrlBvVBfylObPA1gtcWJHy5Ox8jLEJ524zBibss488Q3SZtU1uA==}
+
'@vueuse/core@10.9.0':
resolution: {integrity: sha512-/1vjTol8SXnx6xewDEKfS0Ra//ncg4Hb0DaZiwKf7drgfMsKFExQ+FnnENcN6efPen+1kIzhLQoGSy0eDUVOMg==}
@@ -1901,6 +1938,10 @@ packages:
confbox@0.1.7:
resolution: {integrity: sha512-uJcB/FKZtBMCJpK8MQji6bJHgu1tixKPxRLeGkNzBoOZzpnZUJm0jm2/sBDWcuBx1dYgxV4JU+g5hmNxCyAmdA==}
+ connect@3.7.0:
+ resolution: {integrity: sha512-ZqRXc+tZukToSNmh5C2iWMSoV3X1YUcPbqEM4DkEG5tNQXrQUZCNVGGv3IuicnkMtPfGf3Xtp8WCXs295iQ1pQ==}
+ engines: {node: '>= 0.10.0'}
+
constantinople@4.0.1:
resolution: {integrity: sha512-vCrqcSIq4//Gx74TXXCGnHpulY1dskqLTFGDmhrGxzeXL8lF8kvXv6mpNWlJj1uD4DW23D4ljAqbY4RRaaUZIw==}
@@ -2106,6 +2147,9 @@ packages:
eastasianwidth@0.2.0:
resolution: {integrity: sha512-I88TYZWc9XiYHRQ4/3c5rjjfgkjhLyW2luGIheGERbNQ6OY7yTybanSpDXZa8y7VUP9YmDcYa+eyq4ca7iLqWA==}
+ ee-first@1.1.1:
+ resolution: {integrity: sha512-WMwm9LhRUo+WUaRN+vRuETqG89IgZphVSNkdFgeb6sS/E4OrDIN7t48CAewSHXc6C8lefD8KKfr5vY61brQlow==}
+
electron-to-chromium@1.4.818:
resolution: {integrity: sha512-eGvIk2V0dGImV9gWLq8fDfTTsCAeMDwZqEPMr+jMInxZdnp9Us8UpovYpRCf9NQ7VOFgrN2doNSgvISbsbNpxA==}
@@ -2118,6 +2162,10 @@ packages:
emoji-regex@9.2.2:
resolution: {integrity: sha512-L18DaJsXSUk2+42pv8mLs5jJT2hqFkFE4j21wOmgbUqsZ2hL72NsUU785g9RXgo3s0ZNgVl42TiHp3ZtOv/Vyg==}
+ encodeurl@1.0.2:
+ resolution: {integrity: sha512-TPJXq8JqFaVYm2CWmPvnP2Iyo4ZSM7/QKcSmuMLDObfpH5fi7RUGmd/rTDf+rut/saiDiQEeVTNgAmJEdAOx0w==}
+ engines: {node: '>= 0.8'}
+
end-of-stream@1.4.4:
resolution: {integrity: sha512-+uw1inIHVPQoaVuHzRyXd21icM+cnt4CzD5rW+NC1wjOUSTOs+Te7FOv7AhN7vS9x/oIyhLP5PR1H+phQAHu5Q==}
@@ -2129,6 +2177,10 @@ packages:
resolution: {integrity: sha512-V0hjH4dGPh9Ao5p0MoRY6BVqtwCjhz6vI5LT8AJ55H+4g9/4vbHx1I54fS0XuclLhDHArPQCiMjDxjaL8fPxhw==}
engines: {node: '>=0.12'}
+ entities@5.0.0:
+ resolution: {integrity: sha512-BeJFvFRJddxobhvEdm5GqHzRV/X+ACeuw0/BuuxsCh1EUZcAIz8+kYmBp/LrQuloy6K1f3a0M7+IhmZ7QnkISA==}
+ engines: {node: '>=0.12'}
+
env-paths@2.2.1:
resolution: {integrity: sha512-+h1lkLKhZMTYjog1VEpJNG7NZJWcuc2DDk/qsqSTRRCOXiLjeQ1d1/udrUGhqMxUgAlwKNZ0cf2uqan5GLuS2A==}
engines: {node: '>=6'}
@@ -2174,6 +2226,9 @@ packages:
resolution: {integrity: sha512-ErCHMCae19vR8vQGe50xIsVomy19rg6gFu3+r3jkEO46suLMWBksvVyoGgQV+jOfl84ZSOSlmv6Gxa89PmTGmA==}
engines: {node: '>=6'}
+ escape-html@1.0.3:
+ resolution: {integrity: sha512-NiSupZ4OeuGwr68lGIeym/ksIZMJodUGOSCZ/FSnTxcrekbvqrgdUxlJOMpijaKZVjAJrWrGs/6Jy8OMuyj9ow==}
+
escape-string-regexp@1.0.5:
resolution: {integrity: sha512-vbRorB5FUQWvla16U8R/qgaFIya2qGzwDrNmCZuYKrbdSUMG6I1ZCGQRefkRVhuOkIGVne7BQ35DSfo1qvJqFg==}
engines: {node: '>=0.8.0'}
@@ -2316,6 +2371,10 @@ packages:
resolution: {integrity: sha512-YsGpe3WHLK8ZYi4tWDg2Jy3ebRz2rXowDxnld4bkQB00cc/1Zw9AWnC0i9ztDJitivtQvaI9KaLyKrc+hBW0yg==}
engines: {node: '>=8'}
+ finalhandler@1.1.2:
+ resolution: {integrity: sha512-aAWcW57uxVNrQZqFXjITpW3sIUQmHGG3qSb9mUah9MgMC4NeWhNOlNjXEYq3HjRAvL6arUviZGGJsBg6z0zsWA==}
+ engines: {node: '>= 0.8'}
+
find-up-simple@1.0.0:
resolution: {integrity: sha512-q7Us7kcjj2VMePAa02hDAF6d+MzsdsAWEwYyOpwUtlerRBkOEPBCRZrAV4XfcSN8fHAgaD0hP7miwoay6DCprw==}
engines: {node: '>=18'}
@@ -2974,6 +3033,10 @@ packages:
resolution: {integrity: sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg==}
engines: {node: '>=0.10.0'}
+ on-finished@2.3.0:
+ resolution: {integrity: sha512-ikqdkGAAyf/X/gPhXGvfgAytDZtDbr+bkNUJ0N9h5MI/dmdgCs3l6hoHrcUv41sRKew3jIwrp4qQDXiK99Utww==}
+ engines: {node: '>= 0.8'}
+
on-headers@1.0.2:
resolution: {integrity: sha512-pZAE+FJLoyITytdqK0U5s+FIpjN0JP3OzFi/u8Rx+EV5/W+JTWGXG8xFzevE7AjBfDqHv/8vL8qQsIhHnqRkrA==}
engines: {node: '>= 0.8'}
@@ -3038,6 +3101,10 @@ packages:
parse5@7.1.2:
resolution: {integrity: sha512-Czj1WaSVpaoj0wbhMzLmWD69anp2WH7FXMB9n1Sy8/ZFF9jolSQVMu1Ij5WIyGmcBmhk7EOndpO4mIpihVqAXw==}
+ parseurl@1.3.3:
+ resolution: {integrity: sha512-CiyeOxFT/JZyN5m0z9PfXw4SCBJ6Sygz1Dpl0wqjlhDEGGBP1GnsUVEL0p63hoG1fcj3fHynXi9NYO4nWOL+qQ==}
+ engines: {node: '>= 0.8'}
+
path-exists@4.0.0:
resolution: {integrity: sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==}
engines: {node: '>=8'}
@@ -3484,6 +3551,10 @@ packages:
stackback@0.0.2:
resolution: {integrity: sha512-1XMJE5fQo1jGH6Y/7ebnwPOBEkIEnT4QF32d5R1+VXdXveM0IBMJt8zfaxX1P3QhVwrYe+576+jkANtSS2mBbw==}
+ statuses@1.5.0:
+ resolution: {integrity: sha512-OpZ3zP+jT1PI7I8nemJX4AKmAX070ZkYPVWV/AaKTJl+tXCTGyVdC1a4SL8RUQYEwk/f34ZX8UTykN68FwrqAA==}
+ engines: {node: '>= 0.6'}
+
std-env@3.7.0:
resolution: {integrity: sha512-JPbdCEQLj1w5GilpiHAx3qJvFndqybBysA3qUOnznweH4QbNYUsW/ea8QzSrnh0vNsezMMw5bcVool8lM0gwzg==}
@@ -3699,6 +3770,10 @@ packages:
resolution: {integrity: sha512-gptHNQghINnc/vTGIk0SOFGFNXw7JVrlRUtConJRlvaw6DuX0wO5Jeko9sWrMBhh+PsYAZ7oXAiOnf/UKogyiw==}
engines: {node: '>= 10.0.0'}
+ unpipe@1.0.0:
+ resolution: {integrity: sha512-pjy2bYhSsufwWlKwPc+l3cN7+wuJlK6uz0YdJEOlQDbl6jo/YlPi4mb8agUkVC8BF7V8NuzeyPNqRksA3hztKQ==}
+ engines: {node: '>= 0.8'}
+
untildify@4.0.0:
resolution: {integrity: sha512-KK8xQ1mkzZeg9inewmFVDNkg3l5LUhoq9kN6iWYB/CC9YMG8HA+c1Q8HwDe6dEX7kErrEVNVBO3fWsVq5iDgtw==}
engines: {node: '>=8'}
@@ -3724,6 +3799,10 @@ packages:
util-deprecate@1.0.2:
resolution: {integrity: sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==}
+ utils-merge@1.0.1:
+ resolution: {integrity: sha512-pMZTvIkT1d+TFGvDOqodOclx0QWkkgi6Tdoa8gC8ffGAAqz9pzPTZWAybbsHHoED/ztMtkv/VoYTYyShUn81hA==}
+ engines: {node: '>= 0.4.0'}
+
validate-npm-package-license@3.0.4:
resolution: {integrity: sha512-DpKm2Ui/xN7/HQKCtpZxoRWBhZ9Z0kqtygG8XCgNQ8ZlDnxuQmWhj566j8fN4Cu3/JmbhsDo7fcAJq4s9h27Ew==}
@@ -3853,6 +3932,14 @@ packages:
'@vue/composition-api':
optional: true
+ vue@3.4.36:
+ resolution: {integrity: sha512-mIFvbLgjODfx3Iy1SrxOsiPpDb8Bo3EU+87ioimOZzZTOp15IEdAels70IjBOLO3ZFlLW5AhdwY4dWbXVQKYow==}
+ peerDependencies:
+ typescript: '*'
+ peerDependenciesMeta:
+ typescript:
+ optional: true
+
w3c-xmlserializer@5.0.0:
resolution: {integrity: sha512-o8qghlI8NZHU1lLPrpi2+Uq7abh4GGPpYANlalzWxyWteJOCsr/P+oPBA49TOLu5FTZO4d3F9MnWJfiMo4BkmA==}
engines: {node: '>=18'}
@@ -4644,6 +4731,10 @@ snapshots:
'@tootallnate/quickjs-emscripten@0.23.0': {}
+ '@types/connect@3.4.38':
+ dependencies:
+ '@types/node': 20.14.13
+
'@types/estree@1.0.5': {}
'@types/hash-sum@1.0.2': {}
@@ -4849,10 +4940,69 @@ snapshots:
vite: 5.2.9(@types/node@20.14.13)(sass@1.77.8)(terser@5.31.1)
vue: link:packages/vue
+ '@vue-vapor/vite-plugin-vue@0.0.0-alpha.6(vite@5.3.3(@types/node@20.14.13)(sass@1.77.8)(terser@5.31.1))(vue@3.4.36(typescript@5.4.5))':
+ dependencies:
+ vite: 5.3.3(@types/node@20.14.13)(sass@1.77.8)(terser@5.31.1)
+ vue: 3.4.36(typescript@5.4.5)
+
+ '@vue/compiler-core@3.4.36':
+ dependencies:
+ '@babel/parser': 7.24.7
+ '@vue/shared': 3.4.36
+ entities: 5.0.0
+ estree-walker: 2.0.2
+ source-map-js: 1.2.0
+
+ '@vue/compiler-dom@3.4.36':
+ dependencies:
+ '@vue/compiler-core': 3.4.36
+ '@vue/shared': 3.4.36
+
+ '@vue/compiler-sfc@3.4.36':
+ dependencies:
+ '@babel/parser': 7.24.7
+ '@vue/compiler-core': 3.4.36
+ '@vue/compiler-dom': 3.4.36
+ '@vue/compiler-ssr': 3.4.36
+ '@vue/shared': 3.4.36
+ estree-walker: 2.0.2
+ magic-string: 0.30.10
+ postcss: 8.4.40
+ source-map-js: 1.2.0
+
+ '@vue/compiler-ssr@3.4.36':
+ dependencies:
+ '@vue/compiler-dom': 3.4.36
+ '@vue/shared': 3.4.36
+
'@vue/consolidate@1.0.0': {}
+ '@vue/reactivity@3.4.36':
+ dependencies:
+ '@vue/shared': 3.4.36
+
'@vue/repl@4.3.1': {}
+ '@vue/runtime-core@3.4.36':
+ dependencies:
+ '@vue/reactivity': 3.4.36
+ '@vue/shared': 3.4.36
+
+ '@vue/runtime-dom@3.4.36':
+ dependencies:
+ '@vue/reactivity': 3.4.36
+ '@vue/runtime-core': 3.4.36
+ '@vue/shared': 3.4.36
+ csstype: 3.1.3
+
+ '@vue/server-renderer@3.4.36(vue@3.4.36(typescript@5.4.5))':
+ dependencies:
+ '@vue/compiler-ssr': 3.4.36
+ '@vue/shared': 3.4.36
+ vue: 3.4.36(typescript@5.4.5)
+
+ '@vue/shared@3.4.36': {}
+
'@vueuse/core@10.9.0(vue@packages+vue)':
dependencies:
'@types/web-bluetooth': 0.0.20
@@ -5201,6 +5351,15 @@ snapshots:
confbox@0.1.7: {}
+ connect@3.7.0:
+ dependencies:
+ debug: 2.6.9
+ finalhandler: 1.1.2
+ parseurl: 1.3.3
+ utils-merge: 1.0.1
+ transitivePeerDependencies:
+ - supports-color
+
constantinople@4.0.1:
dependencies:
'@babel/parser': 7.24.7
@@ -5394,6 +5553,8 @@ snapshots:
eastasianwidth@0.2.0: {}
+ ee-first@1.1.1: {}
+
electron-to-chromium@1.4.818: {}
emoji-regex@10.3.0: {}
@@ -5402,6 +5563,8 @@ snapshots:
emoji-regex@9.2.2: {}
+ encodeurl@1.0.2: {}
+
end-of-stream@1.4.4:
dependencies:
once: 1.4.0
@@ -5413,6 +5576,8 @@ snapshots:
entities@4.5.0: {}
+ entities@5.0.0: {}
+
env-paths@2.2.1: {}
error-ex@1.3.2:
@@ -5516,6 +5681,8 @@ snapshots:
escalade@3.1.2: {}
+ escape-html@1.0.3: {}
+
escape-string-regexp@1.0.5: {}
escape-string-regexp@4.0.0: {}
@@ -5725,6 +5892,18 @@ snapshots:
dependencies:
to-regex-range: 5.0.1
+ finalhandler@1.1.2:
+ dependencies:
+ debug: 2.6.9
+ encodeurl: 1.0.2
+ escape-html: 1.0.3
+ on-finished: 2.3.0
+ parseurl: 1.3.3
+ statuses: 1.5.0
+ unpipe: 1.0.0
+ transitivePeerDependencies:
+ - supports-color
+
find-up-simple@1.0.0: {}
find-up@5.0.0:
@@ -6362,6 +6541,10 @@ snapshots:
object-assign@4.1.1: {}
+ on-finished@2.3.0:
+ dependencies:
+ ee-first: 1.1.1
+
on-headers@1.0.2: {}
once@1.4.0:
@@ -6447,6 +6630,8 @@ snapshots:
dependencies:
entities: 4.5.0
+ parseurl@1.3.3: {}
+
path-exists@4.0.0: {}
path-is-absolute@1.0.1: {}
@@ -6976,6 +7161,8 @@ snapshots:
stackback@0.0.2: {}
+ statuses@1.5.0: {}
+
std-env@3.7.0: {}
streamx@2.18.0:
@@ -7171,6 +7358,8 @@ snapshots:
universalify@2.0.1: {}
+ unpipe@1.0.0: {}
+
untildify@4.0.0: {}
update-browserslist-db@1.1.0(browserslist@4.23.1):
@@ -7197,6 +7386,8 @@ snapshots:
util-deprecate@1.0.2: {}
+ utils-merge@1.0.1: {}
+
validate-npm-package-license@3.0.4:
dependencies:
spdx-correct: 3.2.0
@@ -7332,6 +7523,16 @@ snapshots:
dependencies:
vue: link:packages/vue
+ vue@3.4.36(typescript@5.4.5):
+ dependencies:
+ '@vue/compiler-dom': 3.4.36
+ '@vue/compiler-sfc': 3.4.36
+ '@vue/runtime-dom': 3.4.36
+ '@vue/server-renderer': 3.4.36(vue@3.4.36(typescript@5.4.5))
+ '@vue/shared': 3.4.36
+ optionalDependencies:
+ typescript: 5.4.5
+
w3c-xmlserializer@5.0.0:
dependencies:
xml-name-validator: 5.0.0
diff --git a/pnpm-workspace.yaml b/pnpm-workspace.yaml
index d79640584..c517e8536 100644
--- a/pnpm-workspace.yaml
+++ b/pnpm-workspace.yaml
@@ -1,6 +1,7 @@
packages:
- 'packages/*'
- playground
+ - benchmark
catalog:
'@babel/parser': ^7.24.7
diff --git a/scripts/inline-enums.js b/scripts/inline-enums.js
index b1baaa6c5..4e582e251 100644
--- a/scripts/inline-enums.js
+++ b/scripts/inline-enums.js
@@ -198,7 +198,9 @@ export function scanEnums() {
}
// 3. save cache
- if (!existsSync('temp')) mkdirSync('temp')
+ try {
+ mkdirSync('temp')
+ } catch {}
/** @type {EnumData} */
const enumData = {
diff --git a/scripts/release.js b/scripts/release.js
index dc1a5173e..1a3802819 100644
--- a/scripts/release.js
+++ b/scripts/release.js
@@ -6,7 +6,7 @@ import semver from 'semver'
import enquirer from 'enquirer'
import { createRequire } from 'node:module'
import { fileURLToPath } from 'node:url'
-import { exec } from './utils.js'
+import { exec, getSha } from './utils.js'
import { parseArgs } from 'node:util'
/**
@@ -445,15 +445,6 @@ async function isInSyncWithRemote() {
}
}
-/**
- * @param {boolean=} short
- */
-async function getSha(short) {
- return (
- await exec('git', ['rev-parse', ...(short ? ['--short'] : []), 'HEAD'])
- ).stdout
-}
-
async function getBranch() {
return (await exec('git', ['rev-parse', '--abbrev-ref', 'HEAD'])).stdout
}
diff --git a/scripts/utils.js b/scripts/utils.js
index d81680f2e..a789d6d30 100644
--- a/scripts/utils.js
+++ b/scripts/utils.js
@@ -3,17 +3,20 @@ import fs from 'node:fs'
import pico from 'picocolors'
import { createRequire } from 'node:module'
import { spawn } from 'node:child_process'
+import path from 'node:path'
const require = createRequire(import.meta.url)
+const packagesPath = path.resolve(import.meta.dirname, '../packages')
-export const targets = fs.readdirSync('packages').filter(f => {
+export const targets = fs.readdirSync(packagesPath).filter(f => {
+ const folder = path.resolve(packagesPath, f)
if (
- !fs.statSync(`packages/${f}`).isDirectory() ||
- !fs.existsSync(`packages/${f}/package.json`)
+ !fs.statSync(folder).isDirectory() ||
+ !fs.existsSync(`${folder}/package.json`)
) {
return false
}
- const pkg = require(`../packages/${f}/package.json`)
+ const pkg = require(`${folder}/package.json`)
if (pkg.private && !pkg.buildOptions) {
return false
}
@@ -61,6 +64,7 @@ export function fuzzyMatchTarget(partialTargets, includeAllMatching) {
* @param {string} command
* @param {ReadonlyArray} args
* @param {object} [options]
+ * @returns {Promise<{ ok: boolean, code: number | null, stderr: string, stdout: string }>}
*/
export async function exec(command, args, options) {
return new Promise((resolve, reject) => {
@@ -104,3 +108,12 @@ export async function exec(command, args, options) {
})
})
}
+
+/**
+ * @param {boolean=} short
+ */
+export async function getSha(short) {
+ return (
+ await exec('git', ['rev-parse', ...(short ? ['--short'] : []), 'HEAD'])
+ ).stdout
+}