From 623ba514ec0f5adc897db90c0f986b1b6905e014 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E4=B8=89=E5=92=B2=E6=99=BA=E5=AD=90=20Kevin=20Deng?= Date: Fri, 11 Aug 2023 17:30:04 +0800 Subject: [PATCH 01/53] chore: format code --- package.json | 6 +++--- packages/compiler-sfc/src/script/importUsageCheck.ts | 6 +++++- packages/dts-test/setupHelpers.test-d.ts | 7 ++++--- packages/reactivity/src/ref.ts | 5 ++++- packages/runtime-core/__tests__/apiOptions.spec.ts | 2 +- .../runtime-core/__tests__/rendererTemplateRef.spec.ts | 2 +- packages/runtime-core/src/components/Teleport.ts | 2 +- pnpm-lock.yaml | 10 +++++----- 8 files changed, 24 insertions(+), 16 deletions(-) diff --git a/package.json b/package.json index f14db16ddae..d514ccd9190 100644 --- a/package.json +++ b/package.json @@ -12,8 +12,8 @@ "size-baseline": "node scripts/build.js vue -f esm-bundler-runtime && node scripts/build.js runtime-dom runtime-core reactivity shared -f esm-bundler && cd packages/size-check && vite build && node brotli", "check": "tsc --incremental --noEmit", "lint": "eslint --cache --ext .ts packages/*/{src,__tests__}/**.ts", - "format": "prettier --write --cache --parser typescript \"**/*.[tj]s?(x)\"", - "format-check": "prettier --check --cache --parser typescript \"**/*.[tj]s?(x)\"", + "format": "prettier --write --cache \"**/*.[tj]s?(x)\"", + "format-check": "prettier --check --cache \"**/*.[tj]s?(x)\"", "test": "vitest", "test-unit": "vitest -c vitest.unit.config.ts", "test-e2e": "node scripts/build.js vue -f global -d && vitest -c vitest.e2e.config.ts", @@ -84,7 +84,7 @@ "marked": "^4.0.10", "minimist": "^1.2.0", "npm-run-all": "^4.1.5", - "prettier": "^2.7.1", + "prettier": "^3.0.1", "pug": "^3.0.1", "puppeteer": "~19.6.0", "rollup": "^3.26.0", diff --git a/packages/compiler-sfc/src/script/importUsageCheck.ts b/packages/compiler-sfc/src/script/importUsageCheck.ts index 7019dcf2312..f3c3932d829 100644 --- a/packages/compiler-sfc/src/script/importUsageCheck.ts +++ b/packages/compiler-sfc/src/script/importUsageCheck.ts @@ -63,7 +63,11 @@ function resolveTemplateUsageCheckString(sfc: SFCDescriptor) { )}` } } - if (prop.type === NodeTypes.ATTRIBUTE && prop.name === 'ref' && prop.value?.content) { + if ( + prop.type === NodeTypes.ATTRIBUTE && + prop.name === 'ref' && + prop.value?.content + ) { code += `,${prop.value.content}` } } diff --git a/packages/dts-test/setupHelpers.test-d.ts b/packages/dts-test/setupHelpers.test-d.ts index 934e6056d2d..feb4085dea0 100644 --- a/packages/dts-test/setupHelpers.test-d.ts +++ b/packages/dts-test/setupHelpers.test-d.ts @@ -100,7 +100,8 @@ describe('defineProps w/ union type declaration + withDefaults', () => { ) }) -describe('defineProps w/ generic type declaration + withDefaults', () => { const res = withDefaults( @@ -117,10 +118,10 @@ describe('defineProps w/ generic type declaration + withDefaults', [123, 33] as T[], - generic2: () => ({ x: 123 } as { x: T }), + generic2: () => ({ x: 123 }) as { x: T }, generic3: () => 'test' as TString, - generic4: () => ({ a: 'test' } as TA) + generic4: () => ({ a: 'test' }) as TA } ) diff --git a/packages/reactivity/src/ref.ts b/packages/reactivity/src/ref.ts index ef111fc1ff7..915f5760878 100644 --- a/packages/reactivity/src/ref.ts +++ b/packages/reactivity/src/ref.ts @@ -138,7 +138,10 @@ class RefImpl { public dep?: Dep = undefined public readonly __v_isRef = true - constructor(value: T, public readonly __v_isShallow: boolean) { + constructor( + value: T, + public readonly __v_isShallow: boolean + ) { this._rawValue = __v_isShallow ? value : toRaw(value) this._value = __v_isShallow ? value : toReactive(value) } diff --git a/packages/runtime-core/__tests__/apiOptions.spec.ts b/packages/runtime-core/__tests__/apiOptions.spec.ts index a172196d3f7..ca712e0d3ac 100644 --- a/packages/runtime-core/__tests__/apiOptions.spec.ts +++ b/packages/runtime-core/__tests__/apiOptions.spec.ts @@ -382,7 +382,7 @@ describe('api: options', () => { render() { return this[injectedKey] } - } as any) + }) as any const ChildA = defineChild(['a'], 'a') const ChildB = defineChild({ b: 'a' }) diff --git a/packages/runtime-core/__tests__/rendererTemplateRef.spec.ts b/packages/runtime-core/__tests__/rendererTemplateRef.spec.ts index 7d6279c5257..28d7a95eb0b 100644 --- a/packages/runtime-core/__tests__/rendererTemplateRef.spec.ts +++ b/packages/runtime-core/__tests__/rendererTemplateRef.spec.ts @@ -116,7 +116,7 @@ describe('api: template refs', () => { const toggle = ref(true) const Comp = defineComponent( - () => () => toggle.value ? h('div', { ref: fn }) : null + () => () => (toggle.value ? h('div', { ref: fn }) : null) ) render(h(Comp), root) expect(fn.mock.calls[0][0]).toBe(root.children[0]) diff --git a/packages/runtime-core/src/components/Teleport.ts b/packages/runtime-core/src/components/Teleport.ts index 4f7d16bc7d1..19ccbc5de27 100644 --- a/packages/runtime-core/src/components/Teleport.ts +++ b/packages/runtime-core/src/components/Teleport.ts @@ -400,7 +400,7 @@ function hydrateTeleport( // Force-casted public typing for h and TSX props inference export const Teleport = TeleportImpl as unknown as { __isTeleport: true - new(): { + new (): { $props: VNodeProps & TeleportProps $slots: { default(): VNode[] diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index a4a711ef39b..378cc5749d4 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -96,8 +96,8 @@ importers: specifier: ^4.1.5 version: 4.1.5 prettier: - specifier: ^2.7.1 - version: 2.8.8 + specifier: ^3.0.1 + version: 3.0.1 pug: specifier: ^3.0.1 version: 3.0.2 @@ -4621,9 +4621,9 @@ packages: engines: {node: '>= 0.8.0'} dev: true - /prettier@2.8.8: - resolution: {integrity: sha512-tdN8qQGvNjw4CHbY+XXk0JgCXn9QiF21a55rBe5LJAU+kDyC4WQn4+awm2Xfk2lQMk5fKup9XgzTZtGkjBdP9Q==} - engines: {node: '>=10.13.0'} + /prettier@3.0.1: + resolution: {integrity: sha512-fcOWSnnpCrovBsmFZIGIy9UqK2FaI7Hqax+DIO0A9UxeVoY4iweyaFjS5TavZN97Hfehph0nhsZnjlVKzEQSrQ==} + engines: {node: '>=14'} hasBin: true dev: true From bd08f057fc568f15ca19bfae1a4e506f2bc48ca8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E4=B8=89=E5=92=B2=E6=99=BA=E5=AD=90=20Kevin=20Deng?= Date: Mon, 14 Aug 2023 19:43:55 +0800 Subject: [PATCH 02/53] ci: enforce format check --- .github/workflows/ci.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index c52bbc06970..232c69b3b75 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -109,8 +109,8 @@ jobs: - name: Run eslint run: pnpm run lint - # - name: Run prettier - # run: pnpm run format-check + - name: Run prettier + run: pnpm run format-check - name: Run type declaration tests run: pnpm run test-dts From 1b564052994d6aefbb2c091d7dbff6f1e11cebbb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E4=B8=89=E5=92=B2=E6=99=BA=E5=AD=90=20Kevin=20Deng?= Date: Mon, 21 Aug 2023 11:33:51 +0800 Subject: [PATCH 03/53] ci: improved size report (#8992) --- .eslintrc.cjs | 7 +- .github/contributing.md | 2 - .github/workflows/ci.yml | 20 ---- .github/workflows/size-report.yml | 61 +++++++++++ package.json | 12 ++- packages/size-check/README.md | 3 - packages/size-check/brotli.js | 6 -- packages/size-check/package.json | 11 -- packages/size-check/src/index.ts | 6 -- packages/size-check/vite.config.js | 15 --- pnpm-lock.yaml | 162 ++++++++++++++++++++++++----- scripts/aliases.js | 7 +- scripts/build.js | 50 ++++++--- scripts/size-report.ts | 105 +++++++++++++++++++ scripts/usage-size.ts | 99 ++++++++++++++++++ tsconfig.build.json | 1 - 16 files changed, 443 insertions(+), 124 deletions(-) create mode 100644 .github/workflows/size-report.yml delete mode 100644 packages/size-check/README.md delete mode 100644 packages/size-check/brotli.js delete mode 100644 packages/size-check/package.json delete mode 100644 packages/size-check/src/index.ts delete mode 100644 packages/size-check/vite.config.js create mode 100644 scripts/size-report.ts create mode 100644 scripts/usage-size.ts diff --git a/.eslintrc.cjs b/.eslintrc.cjs index ec05a113113..04ecf049ca9 100644 --- a/.eslintrc.cjs +++ b/.eslintrc.cjs @@ -74,12 +74,7 @@ module.exports = { }, // Node scripts { - files: [ - 'scripts/**', - '*.{js,ts}', - 'packages/**/index.js', - 'packages/size-check/**' - ], + files: ['scripts/**', '*.{js,ts}', 'packages/**/index.js'], rules: { 'no-restricted-globals': 'off', 'no-restricted-syntax': 'off' diff --git a/.github/contributing.md b/.github/contributing.md index e728e4cc8eb..0c6771ca0b4 100644 --- a/.github/contributing.md +++ b/.github/contributing.md @@ -248,8 +248,6 @@ This repository employs a [monorepo](https://en.wikipedia.org/wiki/Monorepo) set - `template-explorer`: A development tool for debugging compiler output, continuously deployed at https://template-explorer.vuejs.org/. To run it locally, run [`nr dev-compiler`](#nr-dev-compiler). - - `size-check`: Used for checking built bundle sizes on CI. - ### Importing Packages The packages can import each other directly using their package names. Note that when importing a package, the name listed in its `package.json` should be used. Most of the time the `@vue/` prefix is needed: diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 232c69b3b75..8c08c9a935a 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -114,23 +114,3 @@ jobs: - name: Run type declaration tests run: pnpm run test-dts - - size: - runs-on: ubuntu-latest - if: github.event_name == 'push' || github.event.pull_request.head.repo.full_name != github.repository - env: - CI_JOB_NUMBER: 1 - steps: - - uses: actions/checkout@v3 - - - name: Install pnpm - uses: pnpm/action-setup@v2 - - - name: Set node version to 18 - uses: actions/setup-node@v3 - with: - node-version: 18 - cache: 'pnpm' - - - run: PUPPETEER_SKIP_DOWNLOAD=1 pnpm install - - run: pnpm run size diff --git a/.github/workflows/size-report.yml b/.github/workflows/size-report.yml new file mode 100644 index 00000000000..87c6865927b --- /dev/null +++ b/.github/workflows/size-report.yml @@ -0,0 +1,61 @@ +name: size report + +on: + pull_request: + branches: + - main + +permissions: + contents: read + pull-requests: write + +jobs: + size: + runs-on: ubuntu-latest + + steps: + - uses: actions/checkout@v3 + + - name: Install pnpm + uses: pnpm/action-setup@v2 + + - name: Set node version to LTS + uses: actions/setup-node@v3 + with: + node-version: lts/* + cache: pnpm + + - run: PUPPETEER_SKIP_DOWNLOAD=1 pnpm install + - run: pnpm run size + + - name: Download Previous Size Report + id: download-artifact + uses: dawidd6/action-download-artifact@v2 + with: + branch: main + name: size-report + path: temp/size-prev + if_no_artifact_found: warn + + - name: Upload Size Report + uses: actions/upload-artifact@v3 + with: + name: size-report + path: temp/size + + - name: Compare size + run: pnpm tsx scripts/size-report.ts > size.md + + - name: Read Size Markdown + id: size-markdown + uses: juliangruber/read-file-action@v1 + with: + path: ./size.md + + - name: Create Comment + uses: actions-cool/maintain-one-comment@v3 + with: + body: | + ${{steps.size-markdown.outputs.content}} + + body-include: '' diff --git a/package.json b/package.json index d514ccd9190..b4ee1b00082 100644 --- a/package.json +++ b/package.json @@ -7,9 +7,10 @@ "dev": "node scripts/dev.js", "build": "node scripts/build.js", "build-dts": "tsc -p tsconfig.build.json && rollup -c rollup.dts.config.js", - "size": "run-s size-global size-baseline", - "size-global": "node scripts/build.js vue runtime-dom -f global -p", - "size-baseline": "node scripts/build.js vue -f esm-bundler-runtime && node scripts/build.js runtime-dom runtime-core reactivity shared -f esm-bundler && cd packages/size-check && vite build && node brotli", + "size": "run-s \"size-*\" && tsx scripts/usage-size.ts", + "size-global": "node scripts/build.js vue runtime-dom -f global -p --size", + "size-esm-runtime": "node scripts/build.js vue -f esm-bundler-runtime", + "size-esm": "node scripts/build.js runtime-dom runtime-core reactivity shared -f esm-bundler", "check": "tsc --incremental --noEmit", "lint": "eslint --cache --ext .ts packages/*/{src,__tests__}/**.ts", "format": "prettier --write --cache \"**/*.[tj]s?(x)\"", @@ -81,10 +82,12 @@ "lint-staged": "^10.2.10", "lodash": "^4.17.15", "magic-string": "^0.30.0", + "markdown-table": "^3.0.3", "marked": "^4.0.10", "minimist": "^1.2.0", "npm-run-all": "^4.1.5", "prettier": "^3.0.1", + "pretty-bytes": "^6.1.1", "pug": "^3.0.1", "puppeteer": "~19.6.0", "rollup": "^3.26.0", @@ -94,9 +97,10 @@ "semver": "^7.3.2", "serve": "^12.0.0", "simple-git-hooks": "^2.8.1", - "terser": "^5.15.1", + "terser": "^5.19.2", "todomvc-app-css": "^2.3.0", "tslib": "^2.5.0", + "tsx": "^3.12.7", "typescript": "^5.1.6", "vite": "^4.3.0", "vitest": "^0.30.1" diff --git a/packages/size-check/README.md b/packages/size-check/README.md deleted file mode 100644 index 23cf1899eaf..00000000000 --- a/packages/size-check/README.md +++ /dev/null @@ -1,3 +0,0 @@ -# Size Check - -This package is private and is used for checking the baseline runtime size after tree-shaking (with only the bare minimal code required to render something to the screen). diff --git a/packages/size-check/brotli.js b/packages/size-check/brotli.js deleted file mode 100644 index f9dedac0b1c..00000000000 --- a/packages/size-check/brotli.js +++ /dev/null @@ -1,6 +0,0 @@ -const { brotliCompressSync } = require('zlib') - -const file = require('fs').readFileSync('dist/index.js') -const compressed = brotliCompressSync(file) -const compressedSize = (compressed.length / 1024).toFixed(2) + 'kb' -console.log(`brotli: ${compressedSize}`) diff --git a/packages/size-check/package.json b/packages/size-check/package.json deleted file mode 100644 index 1f9fba88594..00000000000 --- a/packages/size-check/package.json +++ /dev/null @@ -1,11 +0,0 @@ -{ - "name": "@vue/size-check", - "version": "3.3.4", - "private": true, - "scripts": { - "build": "vite build" - }, - "dependencies": { - "vue": "workspace:*" - } -} diff --git a/packages/size-check/src/index.ts b/packages/size-check/src/index.ts deleted file mode 100644 index ad3b68a5cc1..00000000000 --- a/packages/size-check/src/index.ts +++ /dev/null @@ -1,6 +0,0 @@ -import { h, createApp } from 'vue' - -// The bare minimum code required for rendering something to the screen -createApp({ - render: () => h('div', 'hello world!') -}).mount('#app') diff --git a/packages/size-check/vite.config.js b/packages/size-check/vite.config.js deleted file mode 100644 index 73721f95910..00000000000 --- a/packages/size-check/vite.config.js +++ /dev/null @@ -1,15 +0,0 @@ -export default { - define: { - __VUE_PROD_DEVTOOLS__: false, - __VUE_OPTIONS_API__: true - }, - build: { - rollupOptions: { - input: ['src/index.ts'], - output: { - entryFileNames: `[name].js` - } - }, - minify: 'terser' - } -} diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 378cc5749d4..7c6e2a198c5 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -86,6 +86,9 @@ importers: magic-string: specifier: ^0.30.0 version: 0.30.0 + markdown-table: + specifier: ^3.0.3 + version: 3.0.3 marked: specifier: ^4.0.10 version: 4.3.0 @@ -98,6 +101,9 @@ importers: prettier: specifier: ^3.0.1 version: 3.0.1 + pretty-bytes: + specifier: ^6.1.1 + version: 6.1.1 pug: specifier: ^3.0.1 version: 3.0.2 @@ -126,23 +132,26 @@ importers: specifier: ^2.8.1 version: 2.8.1 terser: - specifier: ^5.15.1 - version: 5.18.2 + specifier: ^5.19.2 + version: 5.19.2 todomvc-app-css: specifier: ^2.3.0 version: 2.4.2 tslib: specifier: ^2.5.0 version: 2.6.0 + tsx: + specifier: ^3.12.7 + version: 3.12.7 typescript: specifier: ^5.1.6 version: 5.1.6 vite: specifier: ^4.3.0 - version: 4.3.1(@types/node@16.18.38)(terser@5.18.2) + version: 4.3.1(@types/node@16.18.38)(terser@5.19.2) vitest: specifier: ^0.30.1 - version: 0.30.1(jsdom@21.1.0)(terser@5.18.2) + version: 0.30.1(jsdom@21.1.0)(terser@5.19.2) packages/compiler-core: dependencies: @@ -354,12 +363,6 @@ importers: packages/shared: {} - packages/size-check: - dependencies: - vue: - specifier: workspace:* - version: link:../vue - packages/template-explorer: dependencies: monaco-editor: @@ -422,11 +425,12 @@ packages: '@babel/highlight': 7.22.5 dev: true - /@babel/code-frame@7.22.5: - resolution: {integrity: sha512-Xmwn266vad+6DAqEB2A6V/CcZVp62BbwVmcOJc2RPuwih1kw02TjQvWVWlcKGbBPd+8/0V5DEkOcizRGYsspYQ==} + /@babel/code-frame@7.22.10: + resolution: {integrity: sha512-/KKIMG4UEL35WmI9OlvMhurwtytjvXoFcGNrOvyG9zIzA8YmPjVtIZUf7b05+TPO7G7/GEmLHDaoCgACHl9hhA==} engines: {node: '>=6.9.0'} dependencies: - '@babel/highlight': 7.22.5 + '@babel/highlight': 7.22.10 + chalk: 2.4.2 dev: true /@babel/compat-data@7.21.0: @@ -567,6 +571,15 @@ packages: - supports-color dev: true + /@babel/highlight@7.22.10: + resolution: {integrity: sha512-78aUtVcT7MUscr0K5mIEnkwxPE0MaxkR5RxRwuHaQ+JuU5AmTPhY+do2mdzVTnIJJpyBglql2pehuBIWHug+WQ==} + engines: {node: '>=6.9.0'} + dependencies: + '@babel/helper-validator-identifier': 7.22.5 + chalk: 2.4.2 + js-tokens: 4.0.0 + dev: true + /@babel/highlight@7.22.5: resolution: {integrity: sha512-BSKlD1hgnedS5XRnGOljZawtag7H1yPfQp0tdNJCHoH6AZ+Pcm9VvkrK59/Yy593Ypg0zMxH2BxD1VPYUQ7UIw==} engines: {node: '>=6.9.0'} @@ -587,7 +600,7 @@ packages: resolution: {integrity: sha512-8SegXApWe6VoNw0r9JHpSteLKTpTiLZ4rMlGIm9JQ18KiCtyQiAMEazujAHrUS5flrcqYZa75ukev3P6QmUwUw==} engines: {node: '>=6.9.0'} dependencies: - '@babel/code-frame': 7.18.6 + '@babel/code-frame': 7.22.10 '@babel/parser': 7.21.3 '@babel/types': 7.21.3 dev: true @@ -596,7 +609,7 @@ packages: resolution: {integrity: sha512-XLyopNeaTancVitYZe2MlUEvgKb6YVVPXzofHgqHijCImG33b/uTurMS488ht/Hbsb2XK3U2BnSTxKVNGV3nGQ==} engines: {node: '>=6.9.0'} dependencies: - '@babel/code-frame': 7.22.5 + '@babel/code-frame': 7.22.10 '@babel/generator': 7.21.3 '@babel/helper-environment-visitor': 7.18.9 '@babel/helper-function-name': 7.21.0 @@ -618,6 +631,27 @@ packages: '@babel/helper-validator-identifier': 7.19.1 to-fast-properties: 2.0.0 + /@esbuild-kit/cjs-loader@2.4.2: + resolution: {integrity: sha512-BDXFbYOJzT/NBEtp71cvsrGPwGAMGRB/349rwKuoxNSiKjPraNNnlK6MIIabViCjqZugu6j+xeMDlEkWdHHJSg==} + dependencies: + '@esbuild-kit/core-utils': 3.1.0 + get-tsconfig: 4.7.0 + dev: true + + /@esbuild-kit/core-utils@3.1.0: + resolution: {integrity: sha512-Uuk8RpCg/7fdHSceR1M6XbSZFSuMrxcePFuGgyvsBn+u339dk5OeL4jv2EojwTN2st/unJGsVm4qHWjWNmJ/tw==} + dependencies: + esbuild: 0.17.19 + source-map-support: 0.5.21 + dev: true + + /@esbuild-kit/esm-loader@2.5.5: + resolution: {integrity: sha512-Qwfvj/qoPbClxCRNuac1Du01r9gvNOT+pMYtJDapfB1eoGN1YlJ1BixLyL9WVENRx5RXgNLdfYdx/CuswlGhMw==} + dependencies: + '@esbuild-kit/core-utils': 3.1.0 + get-tsconfig: 4.7.0 + dev: true + /@esbuild/android-arm64@0.17.19: resolution: {integrity: sha512-KBMWvEZooR7+kzY0BtbTQn0OAYY7CsiydT63pVEaPtVYF0hXbUaOyZog37DKxK7NF3XacBJOpYT4adIJh+avxA==} engines: {node: '>=12'} @@ -1078,11 +1112,25 @@ packages: '@jridgewell/trace-mapping': 0.3.17 dev: true + /@jridgewell/gen-mapping@0.3.3: + resolution: {integrity: sha512-HLhSWOLRi875zjjMG/r+Nv0oCW8umGb0BgEhyX3dDX3egwZtB8PqLnjz3yedt8R5StBrzcg4aBpnh8UA9D1BoQ==} + engines: {node: '>=6.0.0'} + dependencies: + '@jridgewell/set-array': 1.1.2 + '@jridgewell/sourcemap-codec': 1.4.15 + '@jridgewell/trace-mapping': 0.3.19 + dev: true + /@jridgewell/resolve-uri@3.1.0: resolution: {integrity: sha512-F2msla3tad+Mfht5cJq7LSXcdudKTWCVYUgw6pLFOOHSTtZlj6SWNYAp+AhuqLmWdBO2X5hPrLcu8cVP8fy28w==} engines: {node: '>=6.0.0'} dev: true + /@jridgewell/resolve-uri@3.1.1: + resolution: {integrity: sha512-dSYZh7HhCDtCKm4QakX0xFpsRDqjjtZf/kjI/v3T3Nwt5r8/qz/M19F9ySyOqU94SXBmeG9ttTul+YnR4LOxFA==} + engines: {node: '>=6.0.0'} + dev: true + /@jridgewell/set-array@1.1.2: resolution: {integrity: sha512-xnkseuNADM0gt2bs+BvhO0p78Mk762YnZdsuzFV018NoG1Sj1SCQvpSqa7XUaTam5vAGasABV9qXASMKnFMwMw==} engines: {node: '>=6.0.0'} @@ -1091,13 +1139,17 @@ packages: /@jridgewell/source-map@0.3.5: resolution: {integrity: sha512-UTYAUj/wviwdsMfzoSJspJxbkH5o1snzwX0//0ENX1u/55kkZZkcTZP6u9bwKGkv+dkk9at4m1Cpt0uY80kcpQ==} dependencies: - '@jridgewell/gen-mapping': 0.3.2 - '@jridgewell/trace-mapping': 0.3.17 + '@jridgewell/gen-mapping': 0.3.3 + '@jridgewell/trace-mapping': 0.3.19 dev: true /@jridgewell/sourcemap-codec@1.4.14: resolution: {integrity: sha512-XPSJHWmi394fuUuzDnGz1wiKqWfo1yXecHQMRf2l6hztTO+nPru658AyDngaBe7isIxEkRsPR3FZh+s7iVa4Uw==} + /@jridgewell/sourcemap-codec@1.4.15: + resolution: {integrity: sha512-eF2rxCRulEKXHTRiDrDy6erMYWqNw4LPdQ8UQA4huuxaQsVeRPFl2oM8oDGxMFhJUWZf9McpLtJasDDZb/Bpeg==} + dev: true + /@jridgewell/trace-mapping@0.3.17: resolution: {integrity: sha512-MCNzAp77qzKca9+W/+I0+sEpaUnZoeasnghNeVc41VZCEKaCH73Vq3BZZ/SzWIgrqE4H4ceI+p+b6C0mHf9T4g==} dependencies: @@ -1105,6 +1157,13 @@ packages: '@jridgewell/sourcemap-codec': 1.4.14 dev: true + /@jridgewell/trace-mapping@0.3.19: + resolution: {integrity: sha512-kf37QtfW+Hwx/buWGMPcR60iF9ziHa6r/CZJIHbmcm4+0qrXiVdxegAH0F6yddEVQ7zdkjcGCgCzUu+BcbhQxw==} + dependencies: + '@jridgewell/resolve-uri': 3.1.1 + '@jridgewell/sourcemap-codec': 1.4.15 + dev: true + /@jspm/core@2.0.1: resolution: {integrity: sha512-Lg3PnLp0QXpxwLIAuuJboLeRaIhrgJjeuh797QADg3xz8wGLugQOS5DpsE8A6i6Adgzf+bacllkKZG3J0tGfDw==} dev: true @@ -1233,7 +1292,7 @@ packages: rollup: 3.26.2 serialize-javascript: 6.0.1 smob: 0.0.6 - terser: 5.18.2 + terser: 5.19.2 dev: true /@rollup/pluginutils@5.0.2(rollup@3.26.2): @@ -1464,7 +1523,7 @@ packages: istanbul-lib-source-maps: 4.0.1 istanbul-reports: 3.1.5 test-exclude: 6.0.0 - vitest: 0.30.1(jsdom@21.1.0)(terser@5.18.2) + vitest: 0.30.1(jsdom@21.1.0)(terser@5.19.2) transitivePeerDependencies: - supports-color dev: true @@ -1568,6 +1627,12 @@ packages: hasBin: true dev: true + /acorn@8.10.0: + resolution: {integrity: sha512-F0SAmZ8iUtS//m8DmCTA0jlh6TDKkHQyK6xc6V4KDTyZKA9dnvX9/3sRTVQrWm79glUAZbnmmNcdYwUIHWVybw==} + engines: {node: '>=0.4.0'} + hasBin: true + dev: true + /acorn@8.8.2: resolution: {integrity: sha512-xjIYgE8HBrkpd/sJqOGNspf8uHG+NOHGOw6a/Urj8taM2EXfdNAH2oFcPeIFfsv3+kz/mJrS5VuMqbNLjCa2vw==} engines: {node: '>=0.4.0'} @@ -3066,6 +3131,12 @@ packages: get-intrinsic: 1.2.0 dev: true + /get-tsconfig@4.7.0: + resolution: {integrity: sha512-pmjiZ7xtB8URYm74PlGJozDNyhvsVLUcpBa8DZBG3bWHwaHa9bPiRpiSfovw+fjhwONSCWKRyk+JQHEGZmMrzw==} + dependencies: + resolve-pkg-maps: 1.0.0 + dev: true + /git-raw-commits@2.0.11: resolution: {integrity: sha512-VnctFhw+xfj8Va1xtfEqCUD2XDrbAPSJx+hSrE5K7fGdjZruW7XV+QOrN7LF/RJyvspRiD2I0asWsxFp0ya26A==} engines: {node: '>=10'} @@ -3991,6 +4062,10 @@ packages: engines: {node: '>=8'} dev: true + /markdown-table@3.0.3: + resolution: {integrity: sha512-Z1NL3Tb1M9wH4XESsCDEksWoKTdlUafKc4pt0GRwjUyXaCFZ+dc3g2erqB6zm3szA2IUSi7VnPI+o/9jnxh9hw==} + dev: true + /marked@4.3.0: resolution: {integrity: sha512-PRsaiG84bK+AMvxziE/lCFss8juXjNaWzVbN5tXAm4XjeaS9NAHhop+PjQxz2A9h8Q4M/xGmzP8vqNwy6JeK0A==} engines: {node: '>= 12'} @@ -4414,7 +4489,7 @@ packages: resolution: {integrity: sha512-ayCKvm/phCGxOkYRSCM82iDwct8/EonSEgCSxWxD7ve6jHggsFl4fZVQBPRNgQoKiuV/odhFrGzQXZwbifC8Rg==} engines: {node: '>=8'} dependencies: - '@babel/code-frame': 7.22.5 + '@babel/code-frame': 7.22.10 error-ex: 1.3.2 json-parse-even-better-errors: 2.3.1 lines-and-columns: 1.2.4 @@ -4627,6 +4702,11 @@ packages: hasBin: true dev: true + /pretty-bytes@6.1.1: + resolution: {integrity: sha512-mQUvGU6aUFQ+rNvTIAcZuWGRT9a6f6Yrg9bHs4ImKF+HZCEK+plBvnAZYSIQztknZF2qnzNtr6F8s0+IuptdlQ==} + engines: {node: ^14.13.1 || >=16.0.0} + dev: true + /pretty-format@27.5.1: resolution: {integrity: sha512-Qb1gy5OrP5+zDf2Bvnzdl3jsTf1qXVMazbvCoKhtKqVs4/YK4ozX4gKQJJVyNe+cajNPn0KoC0MC3FUmaHWEmQ==} engines: {node: ^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0} @@ -4963,6 +5043,10 @@ packages: engines: {node: '>=4'} dev: true + /resolve-pkg-maps@1.0.0: + resolution: {integrity: sha512-seS2Tj26TBVOC2NIc2rOe2y2ZO7efxITtLZcGSOnHHNOQ7CkiUBfw0Iw2ck6xkIhPwLhKNLS8BO+hEpngQlqzw==} + dev: true + /resolve@1.22.1: resolution: {integrity: sha512-nBpuuYuY5jFsli/JIs1oldw6fOQCBioohqWZg/2hiaOybXOft4lonv85uDOKXdf8rhyK159cxU5cDcK/NKk8zw==} hasBin: true @@ -5007,7 +5091,7 @@ packages: rollup: 3.26.2 typescript: 5.1.6 optionalDependencies: - '@babel/code-frame': 7.22.5 + '@babel/code-frame': 7.22.10 dev: true /rollup-plugin-esbuild@5.0.0(esbuild@0.17.19)(rollup@3.26.2): @@ -5492,6 +5576,17 @@ packages: source-map-support: 0.5.21 dev: true + /terser@5.19.2: + resolution: {integrity: sha512-qC5+dmecKJA4cpYxRa5aVkKehYsQKc+AHeKl0Oe62aYjBL8ZA33tTljktDHJSaxxMnbI5ZYw+o/S2DxxLu8OfA==} + engines: {node: '>=10'} + hasBin: true + dependencies: + '@jridgewell/source-map': 0.3.5 + acorn: 8.10.0 + commander: 2.20.3 + source-map-support: 0.5.21 + dev: true + /test-exclude@6.0.0: resolution: {integrity: sha512-cAGWPIyOHU6zlmg88jwm7VRyXnMN7iV68OGAbYDk/Mh/xC/pzVPlQtY6ngoIH/5/tciuhGfvESU8GrHrcxD56w==} engines: {node: '>=8'} @@ -5609,6 +5704,17 @@ packages: typescript: 5.1.6 dev: true + /tsx@3.12.7: + resolution: {integrity: sha512-C2Ip+jPmqKd1GWVQDvz/Eyc6QJbGfE7NrR3fx5BpEHMZsEHoIxHL1j+lKdGobr8ovEyqeNkPLSKp6SCSOt7gmw==} + hasBin: true + dependencies: + '@esbuild-kit/cjs-loader': 2.4.2 + '@esbuild-kit/core-utils': 3.1.0 + '@esbuild-kit/esm-loader': 2.5.5 + optionalDependencies: + fsevents: 2.3.2 + dev: true + /type-check@0.3.2: resolution: {integrity: sha512-ZCmOJdvOWDBYJlzAoFkC+Q0+bUyEOS1ltgp1MGU03fqHG+dbi9tBFU2Rd9QKiDZFAYrhPh2JUf7rZRIuHRKtOg==} engines: {node: '>= 0.8.0'} @@ -5758,7 +5864,7 @@ packages: engines: {node: '>= 0.8'} dev: true - /vite-node@0.30.1(@types/node@16.18.38)(terser@5.18.2): + /vite-node@0.30.1(@types/node@16.18.38)(terser@5.19.2): resolution: {integrity: sha512-vTikpU/J7e6LU/8iM3dzBo8ZhEiKZEKRznEMm+mJh95XhWaPrJQraT/QsT2NWmuEf+zgAoMe64PKT7hfZ1Njmg==} engines: {node: '>=v14.18.0'} hasBin: true @@ -5768,7 +5874,7 @@ packages: mlly: 1.2.0 pathe: 1.1.0 picocolors: 1.0.0 - vite: 4.3.1(@types/node@16.18.38)(terser@5.18.2) + vite: 4.3.1(@types/node@16.18.38)(terser@5.19.2) transitivePeerDependencies: - '@types/node' - less @@ -5779,7 +5885,7 @@ packages: - terser dev: true - /vite@4.3.1(@types/node@16.18.38)(terser@5.18.2): + /vite@4.3.1(@types/node@16.18.38)(terser@5.19.2): resolution: {integrity: sha512-EPmfPLAI79Z/RofuMvkIS0Yr091T2ReUoXQqc5ppBX/sjFRhHKiPPF/R46cTdoci/XgeQpB23diiJxq5w30vdg==} engines: {node: ^14.18.0 || >=16.0.0} hasBin: true @@ -5808,7 +5914,7 @@ packages: esbuild: 0.17.19 postcss: 8.4.21 rollup: 3.26.2 - terser: 5.18.2 + terser: 5.19.2 optionalDependencies: fsevents: 2.3.2 dev: true @@ -5850,7 +5956,7 @@ packages: fsevents: 2.3.2 dev: true - /vitest@0.30.1(jsdom@21.1.0)(terser@5.18.2): + /vitest@0.30.1(jsdom@21.1.0)(terser@5.19.2): resolution: {integrity: sha512-y35WTrSTlTxfMLttgQk4rHcaDkbHQwDP++SNwPb+7H8yb13Q3cu2EixrtHzF27iZ8v0XCciSsLg00RkPAzB/aA==} engines: {node: '>=v14.18.0'} hasBin: true @@ -5905,8 +6011,8 @@ packages: strip-literal: 1.0.1 tinybench: 2.4.0 tinypool: 0.4.0 - vite: 4.3.1(@types/node@16.18.38)(terser@5.18.2) - vite-node: 0.30.1(@types/node@16.18.38)(terser@5.18.2) + vite: 4.3.1(@types/node@16.18.38)(terser@5.19.2) + vite-node: 0.30.1(@types/node@16.18.38)(terser@5.19.2) why-is-node-running: 2.2.2 transitivePeerDependencies: - less diff --git a/scripts/aliases.js b/scripts/aliases.js index 95e3016322c..34a7c643557 100644 --- a/scripts/aliases.js +++ b/scripts/aliases.js @@ -19,12 +19,7 @@ const entries = { '@vue/compat': resolveEntryForPkg('vue-compat') } -const nonSrcPackages = [ - 'sfc-playground', - 'size-check', - 'template-explorer', - 'dts-test' -] +const nonSrcPackages = ['sfc-playground', 'template-explorer', 'dts-test'] for (const dir of dirs) { const key = `@vue/${dir}` diff --git a/scripts/build.js b/scripts/build.js index 75a619046be..1f8af65017d 100644 --- a/scripts/build.js +++ b/scripts/build.js @@ -27,6 +27,7 @@ import { cpus } from 'node:os' import { createRequire } from 'node:module' import { targets as allTargets, fuzzyMatchTarget } from './utils.js' import { scanEnums } from './const-enum.js' +import prettyBytes from 'pretty-bytes' const require = createRequire(import.meta.url) const args = minimist(process.argv.slice(2)) @@ -38,18 +39,22 @@ const buildTypes = args.withTypes || args.t const sourceMap = args.sourcemap || args.s const isRelease = args.release const buildAllMatching = args.all || args.a +const writeSize = args.size const commit = execa.sync('git', ['rev-parse', 'HEAD']).stdout.slice(0, 7) +const sizeDir = path.resolve('temp/size') + run() async function run() { + if (writeSize) await fs.mkdir(sizeDir, { recursive: true }) const removeCache = scanEnums() try { const resolvedTargets = targets.length ? fuzzyMatchTarget(targets, buildAllMatching) : allTargets await buildAll(resolvedTargets) - checkAllSizes(resolvedTargets) + await checkAllSizes(resolvedTargets) if (buildTypes) { await execa( 'pnpm', @@ -129,39 +134,52 @@ async function build(target) { ) } -function checkAllSizes(targets) { +async function checkAllSizes(targets) { if (devOnly || (formats && !formats.includes('global'))) { return } console.log() for (const target of targets) { - checkSize(target) + await checkSize(target) } console.log() } -function checkSize(target) { +async function checkSize(target) { const pkgDir = path.resolve(`packages/${target}`) - checkFileSize(`${pkgDir}/dist/${target}.global.prod.js`) + await checkFileSize(`${pkgDir}/dist/${target}.global.prod.js`) if (!formats || formats.includes('global-runtime')) { - checkFileSize(`${pkgDir}/dist/${target}.runtime.global.prod.js`) + await checkFileSize(`${pkgDir}/dist/${target}.runtime.global.prod.js`) } } -function checkFileSize(filePath) { +async function checkFileSize(filePath) { if (!existsSync(filePath)) { return } - const file = readFileSync(filePath) - const minSize = (file.length / 1024).toFixed(2) + 'kb' + const file = await fs.readFile(filePath) + const fileName = path.basename(filePath) + const gzipped = gzipSync(file) - const gzippedSize = (gzipped.length / 1024).toFixed(2) + 'kb' - const compressed = brotliCompressSync(file) - // @ts-ignore - const compressedSize = (compressed.length / 1024).toFixed(2) + 'kb' + const brotli = brotliCompressSync(file) + console.log( - `${chalk.gray( - chalk.bold(path.basename(filePath)) - )} min:${minSize} / gzip:${gzippedSize} / brotli:${compressedSize}` + `${chalk.gray(chalk.bold(fileName))} min:${prettyBytes( + file.length + )} / gzip:${prettyBytes(gzipped.length)} / brotli:${prettyBytes( + brotli.length + )}` ) + + if (writeSize) + await fs.writeFile( + path.resolve(sizeDir, `${fileName}.json`), + JSON.stringify({ + file: fileName, + size: file.length, + gzip: gzipped.length, + brotli: brotli.length + }), + 'utf-8' + ) } diff --git a/scripts/size-report.ts b/scripts/size-report.ts new file mode 100644 index 00000000000..56e4491a19c --- /dev/null +++ b/scripts/size-report.ts @@ -0,0 +1,105 @@ +import path from 'node:path' +import { markdownTable } from 'markdown-table' +import prettyBytes from 'pretty-bytes' +import { readdir } from 'node:fs/promises' +import { existsSync } from 'node:fs' + +interface SizeResult { + size: number + gzip: number + brotli: number +} + +interface BundleResult extends SizeResult { + file: string +} + +type UsageResult = Record + +const currDir = path.resolve('temp/size') +const prevDir = path.resolve('temp/size-prev') +let output = '## Size Report\n\n' +const sizeHeaders = ['Size', 'Gzip', 'Brotli'] + +run() + +async function run() { + await renderFiles() + await renderUsages() + + process.stdout.write(output) +} + +async function renderFiles() { + const filterFiles = (files: string[]) => + files.filter(file => !file.startsWith('_')) + + const curr = filterFiles(await readdir(currDir)) + const prev = existsSync(prevDir) ? filterFiles(await readdir(prevDir)) : [] + const fileList = new Set([...curr, ...prev]) + + const rows: string[][] = [] + for (const file of fileList) { + const currPath = path.resolve(currDir, file) + const prevPath = path.resolve(prevDir, file) + + const curr = await importJSON(currPath) + const prev = await importJSON(prevPath) + const fileName = curr?.file || prev?.file || '' + + if (!curr) { + rows.push([`~~${fileName}~~`]) + } else + rows.push([ + fileName, + `${prettyBytes(curr.size)}${getDiff(curr.size, prev?.size)}`, + `${prettyBytes(curr.gzip)}${getDiff(curr.gzip, prev?.gzip)}`, + `${prettyBytes(curr.brotli)}${getDiff(curr.brotli, prev?.brotli)}` + ]) + } + + output += '### Bundles\n\n' + output += markdownTable([['File', ...sizeHeaders], ...rows]) + output += '\n\n' +} + +async function renderUsages() { + const curr = (await importJSON( + path.resolve(currDir, '_usages.json') + ))! + const prev = await importJSON( + path.resolve(prevDir, '_usages.json') + ) + output += '\n### Usages\n\n' + + const data = Object.values(curr) + .map(usage => { + const prevUsage = prev?.[usage.name] + const diffSize = getDiff(usage.size, prevUsage?.size) + const diffGzipped = getDiff(usage.gzip, prevUsage?.gzip) + const diffBrotli = getDiff(usage.brotli, prevUsage?.brotli) + + return [ + usage.name, + `${prettyBytes(usage.size)}${diffSize}`, + `${prettyBytes(usage.gzip)}${diffGzipped}`, + `${prettyBytes(usage.brotli)}${diffBrotli}` + ] + }) + .filter((usage): usage is string[] => !!usage) + + output += `${markdownTable([['Name', ...sizeHeaders], ...data])}\n\n` +} + +async function importJSON(path: string): Promise { + if (!existsSync(path)) return undefined + return (await import(path, { assert: { type: 'json' } })).default +} + +function getDiff(curr: number, prev?: number) { + if (prev === undefined) return '' + const diff = curr - prev + if (diff === 0) return '' + const sign = diff > 0 ? '+' : '' + return ` (**${sign}${prettyBytes(diff)}**)` +} diff --git a/scripts/usage-size.ts b/scripts/usage-size.ts new file mode 100644 index 00000000000..1a1013b7847 --- /dev/null +++ b/scripts/usage-size.ts @@ -0,0 +1,99 @@ +import { mkdir, writeFile } from 'fs/promises' +import path from 'node:path' +import { rollup } from 'rollup' +import nodeResolve from '@rollup/plugin-node-resolve' +import { minify } from 'terser' +import replace from '@rollup/plugin-replace' +import { brotliCompressSync, gzipSync } from 'node:zlib' + +const sizeDir = path.resolve('temp/size') +const entry = path.resolve('./packages/vue/dist/vue.runtime.esm-bundler.js') + +interface Preset { + name: string + imports: string[] +} + +const presets: Preset[] = [ + { name: 'createApp', imports: ['createApp'] }, + { name: 'createSSRApp', imports: ['createSSRApp'] }, + { name: 'defineCustomElement', imports: ['defineCustomElement'] }, + { + name: 'overall', + imports: [ + 'createApp', + 'ref', + 'watch', + 'Transition', + 'KeepAlive', + 'Suspense' + ] + } +] + +main() + +async function main() { + const tasks: ReturnType[] = [] + for (const preset of presets) { + tasks.push(generateBundle(preset)) + } + + const results = Object.fromEntries( + (await Promise.all(tasks)).map(r => [r.name, r]) + ) + + await mkdir(sizeDir, { recursive: true }) + await writeFile( + path.resolve(sizeDir, '_usages.json'), + JSON.stringify(results), + 'utf-8' + ) +} + +async function generateBundle(preset: Preset) { + const id = 'virtual:entry' + const content = `export { ${preset.imports.join(', ')} } from '${entry}'` + const result = await rollup({ + input: id, + plugins: [ + { + name: 'usage-size-plugin', + resolveId(_id) { + if (_id === id) return id + return null + }, + load(_id) { + if (_id === id) return content + } + }, + nodeResolve(), + replace({ + 'process.env.NODE_ENV': '"production"', + __VUE_PROD_DEVTOOLS__: 'false', + __VUE_OPTIONS_API__: 'true', + preventAssignment: true + }) + ] + }) + + const generated = await result.generate({}) + const bundled = generated.output[0].code + const minified = ( + await minify(bundled, { + module: true, + toplevel: true + }) + ).code! + + const size = minified.length + const gzip = gzipSync(minified).length + const brotli = brotliCompressSync(minified).length + + return { + name: preset.name, + size, + gzip, + brotli + } +} diff --git a/tsconfig.build.json b/tsconfig.build.json index 8b7749b858b..89aaa2278f4 100644 --- a/tsconfig.build.json +++ b/tsconfig.build.json @@ -9,7 +9,6 @@ "packages/runtime-test", "packages/template-explorer", "packages/sfc-playground", - "packages/size-check", "packages/dts-test" ] } From 61c3c8e86a47c2857195cc9115bf13e070c86764 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E4=B8=89=E5=92=B2=E6=99=BA=E5=AD=90=20Kevin=20Deng?= Date: Mon, 21 Aug 2023 11:44:43 +0800 Subject: [PATCH 04/53] ci: fix size report permissions --- .github/workflows/size-data.yml | 49 +++++++++++++++++++++++++ .github/workflows/size-report.yml | 61 ++++++++++++++++++++----------- 2 files changed, 89 insertions(+), 21 deletions(-) create mode 100644 .github/workflows/size-data.yml diff --git a/.github/workflows/size-data.yml b/.github/workflows/size-data.yml new file mode 100644 index 00000000000..647e029c578 --- /dev/null +++ b/.github/workflows/size-data.yml @@ -0,0 +1,49 @@ +name: size data + +on: + push: + branches: + - main + pull_request: + branches: + - main + +permissions: + contents: read + +jobs: + upload: + runs-on: ubuntu-latest + + steps: + - uses: actions/checkout@v3 + + - name: Install pnpm + uses: pnpm/action-setup@v2 + + - name: Set node version to LTS + uses: actions/setup-node@v3 + with: + node-version: lts/* + cache: pnpm + + - name: Install dependencies + run: PUPPETEER_SKIP_DOWNLOAD=1 pnpm install + + - run: pnpm run size + + - name: Upload Size Data + uses: actions/upload-artifact@v3 + with: + name: size-data + path: temp/size + + - name: Save PR number + if: ${{github.event_name == 'pull_request'}} + run: echo ${{ github.event.number }} > ./pr.txt + + - uses: actions/upload-artifact@v2 + if: ${{github.event_name == 'pull_request'}} + with: + name: pr-number + path: pr.txt diff --git a/.github/workflows/size-report.yml b/.github/workflows/size-report.yml index 87c6865927b..4d7e49e523b 100644 --- a/.github/workflows/size-report.yml +++ b/.github/workflows/size-report.yml @@ -1,18 +1,22 @@ name: size report on: - pull_request: - branches: - - main + workflow_run: + workflows: ['size data'] + types: + - completed permissions: contents: read pull-requests: write + issues: write jobs: - size: + size-report: runs-on: ubuntu-latest - + if: > + github.event.workflow_run.event == 'pull_request' && + github.event.workflow_run.conclusion == 'success' steps: - uses: actions/checkout@v3 @@ -25,37 +29,52 @@ jobs: node-version: lts/* cache: pnpm - - run: PUPPETEER_SKIP_DOWNLOAD=1 pnpm install - - run: pnpm run size + - name: Install dependencies + run: PUPPETEER_SKIP_DOWNLOAD=1 pnpm install - - name: Download Previous Size Report - id: download-artifact + - name: Download PR number uses: dawidd6/action-download-artifact@v2 with: - branch: main - name: size-report - path: temp/size-prev - if_no_artifact_found: warn + name: pr-number + run_id: ${{ github.event.workflow_run.id }} + + - name: Read PR Number + id: pr-number + uses: juliangruber/read-file-action@v1 + with: + path: ./pr.txt - - name: Upload Size Report - uses: actions/upload-artifact@v3 + - name: Download Size Data + uses: dawidd6/action-download-artifact@v2 with: - name: size-report + name: size-data + run_id: ${{ github.event.workflow_run.id }} path: temp/size + - name: Download Previous Size Data + uses: dawidd6/action-download-artifact@v2 + with: + branch: main + workflow: size-data.yml + name: size-data + path: temp/size-prev + if_no_artifact_found: warn + - name: Compare size - run: pnpm tsx scripts/size-report.ts > size.md + run: pnpm tsx scripts/size-report.ts > size-report.md - - name: Read Size Markdown - id: size-markdown + - name: Read Size Report + id: size-report uses: juliangruber/read-file-action@v1 with: - path: ./size.md + path: ./size-report.md - name: Create Comment uses: actions-cool/maintain-one-comment@v3 with: + token: ${{ secrets.GITHUB_TOKEN }} + number: ${{ steps.pr-number.outputs.content }} body: | - ${{steps.size-markdown.outputs.content}} + ${{ steps.size-report.outputs.content }} body-include: '' From e7d5a41758013966c3d9ce47c01f13da7d8c395a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E4=B8=89=E5=92=B2=E6=99=BA=E5=AD=90=20Kevin=20Deng?= Date: Mon, 21 Aug 2023 15:43:49 +0800 Subject: [PATCH 05/53] ci: fix get previous size data --- .github/workflows/size-report.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/.github/workflows/size-report.yml b/.github/workflows/size-report.yml index 4d7e49e523b..75c52f717b3 100644 --- a/.github/workflows/size-report.yml +++ b/.github/workflows/size-report.yml @@ -56,6 +56,7 @@ jobs: with: branch: main workflow: size-data.yml + event: push name: size-data path: temp/size-prev if_no_artifact_found: warn From 02c6924bcd29cb0ede9c3e049543b3c85a714346 Mon Sep 17 00:00:00 2001 From: Waleed Khaled Date: Tue, 22 Aug 2023 11:50:27 +0300 Subject: [PATCH 06/53] refactor(reactivity): encapsulate reactive handlers in class (#8586) reactive obj create has a huge positive impact (200% - 700%) get/set reactive obj props has a small negative impact (1% - 5%) --- packages/reactivity/src/baseHandlers.ts | 113 ++++++++++++------------ 1 file changed, 55 insertions(+), 58 deletions(-) diff --git a/packages/reactivity/src/baseHandlers.ts b/packages/reactivity/src/baseHandlers.ts index 45ecfa6d38a..259b44a1edc 100644 --- a/packages/reactivity/src/baseHandlers.ts +++ b/packages/reactivity/src/baseHandlers.ts @@ -26,7 +26,6 @@ import { hasChanged, isArray, isIntegerKey, - extend, makeMap } from '@vue/shared' import { isRef } from './ref' @@ -45,11 +44,6 @@ const builtInSymbols = new Set( .filter(isSymbol) ) -const get = /*#__PURE__*/ createGetter() -const shallowGet = /*#__PURE__*/ createGetter(false, true) -const readonlyGet = /*#__PURE__*/ createGetter(true) -const shallowReadonlyGet = /*#__PURE__*/ createGetter(true, true) - const arrayInstrumentations = /*#__PURE__*/ createArrayInstrumentations() function createArrayInstrumentations() { @@ -91,8 +85,15 @@ function hasOwnProperty(this: object, key: string) { return obj.hasOwnProperty(key) } -function createGetter(isReadonly = false, shallow = false) { - return function get(target: Target, key: string | symbol, receiver: object) { +class BaseReactiveHandler implements ProxyHandler { + constructor( + protected readonly _isReadonly = false, + protected readonly _shallow = false + ) {} + + get(target: Target, key: string | symbol, receiver: object) { + const isReadonly = this._isReadonly, + shallow = this._shallow if (key === ReactiveFlags.IS_REACTIVE) { return !isReadonly } else if (key === ReactiveFlags.IS_READONLY) { @@ -155,11 +156,12 @@ function createGetter(isReadonly = false, shallow = false) { } } -const set = /*#__PURE__*/ createSetter() -const shallowSet = /*#__PURE__*/ createSetter(true) +class MutableReactiveHandler extends BaseReactiveHandler { + constructor(shallow = false) { + super(false, shallow) + } -function createSetter(shallow = false) { - return function set( + set( target: object, key: string | symbol, value: unknown, @@ -169,7 +171,7 @@ function createSetter(shallow = false) { if (isReadonly(oldValue) && isRef(oldValue) && !isRef(value)) { return false } - if (!shallow) { + if (!this._shallow) { if (!isShallow(value) && !isReadonly(value)) { oldValue = toRaw(oldValue) value = toRaw(value) @@ -197,42 +199,40 @@ function createSetter(shallow = false) { } return result } -} -function deleteProperty(target: object, key: string | symbol): boolean { - const hadKey = hasOwn(target, key) - const oldValue = (target as any)[key] - const result = Reflect.deleteProperty(target, key) - if (result && hadKey) { - trigger(target, TriggerOpTypes.DELETE, key, undefined, oldValue) + deleteProperty(target: object, key: string | symbol): boolean { + const hadKey = hasOwn(target, key) + const oldValue = (target as any)[key] + const result = Reflect.deleteProperty(target, key) + if (result && hadKey) { + trigger(target, TriggerOpTypes.DELETE, key, undefined, oldValue) + } + return result } - return result -} -function has(target: object, key: string | symbol): boolean { - const result = Reflect.has(target, key) - if (!isSymbol(key) || !builtInSymbols.has(key)) { - track(target, TrackOpTypes.HAS, key) + has(target: object, key: string | symbol): boolean { + const result = Reflect.has(target, key) + if (!isSymbol(key) || !builtInSymbols.has(key)) { + track(target, TrackOpTypes.HAS, key) + } + return result + } + ownKeys(target: object): (string | symbol)[] { + track( + target, + TrackOpTypes.ITERATE, + isArray(target) ? 'length' : ITERATE_KEY + ) + return Reflect.ownKeys(target) } - return result -} - -function ownKeys(target: object): (string | symbol)[] { - track(target, TrackOpTypes.ITERATE, isArray(target) ? 'length' : ITERATE_KEY) - return Reflect.ownKeys(target) } -export const mutableHandlers: ProxyHandler = { - get, - set, - deleteProperty, - has, - ownKeys -} +class ReadonlyReactiveHandler extends BaseReactiveHandler { + constructor(shallow = false) { + super(true, shallow) + } -export const readonlyHandlers: ProxyHandler = { - get: readonlyGet, - set(target, key) { + set(target: object, key: string | symbol) { if (__DEV__) { warn( `Set operation on key "${String(key)}" failed: target is readonly.`, @@ -240,8 +240,9 @@ export const readonlyHandlers: ProxyHandler = { ) } return true - }, - deleteProperty(target, key) { + } + + deleteProperty(target: object, key: string | symbol) { if (__DEV__) { warn( `Delete operation on key "${String(key)}" failed: target is readonly.`, @@ -252,22 +253,18 @@ export const readonlyHandlers: ProxyHandler = { } } -export const shallowReactiveHandlers = /*#__PURE__*/ extend( - {}, - mutableHandlers, - { - get: shallowGet, - set: shallowSet - } +export const mutableHandlers: ProxyHandler = + /*#__PURE__*/ new MutableReactiveHandler() + +export const readonlyHandlers: ProxyHandler = + /*#__PURE__*/ new ReadonlyReactiveHandler() + +export const shallowReactiveHandlers = /*#__PURE__*/ new MutableReactiveHandler( + true ) // Props handlers are special in the sense that it should not unwrap top-level // refs (in order to allow refs to be explicitly passed down), but should // retain the reactivity of the normal readonly object. -export const shallowReadonlyHandlers = /*#__PURE__*/ extend( - {}, - readonlyHandlers, - { - get: shallowReadonlyGet - } -) +export const shallowReadonlyHandlers = + /*#__PURE__*/ new ReadonlyReactiveHandler(true) From 2ffe3d5b3e953b63d4743b1e2bc242d50916b545 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E4=B8=B6=E8=BF=9C=E6=96=B9?= Date: Tue, 22 Aug 2023 16:57:15 +0800 Subject: [PATCH 07/53] refactor: use symbol for private properties (#8681) --- .../src/components/BaseTransition.ts | 31 ++++++++++--------- .../runtime-dom/__tests__/patchClass.spec.ts | 6 ++-- .../runtime-dom/src/components/Transition.ts | 12 ++++--- .../src/components/TransitionGroup.ts | 23 ++++++++------ packages/runtime-dom/src/directives/vModel.ts | 28 +++++++++-------- packages/runtime-dom/src/directives/vShow.ts | 8 +++-- packages/runtime-dom/src/modules/class.ts | 4 +-- packages/runtime-dom/src/modules/events.ts | 6 ++-- packages/runtime-dom/src/modules/style.ts | 3 +- packages/vue/__tests__/svgNamespace.spec.ts | 3 +- 10 files changed, 70 insertions(+), 54 deletions(-) diff --git a/packages/runtime-core/src/components/BaseTransition.ts b/packages/runtime-core/src/components/BaseTransition.ts index a4e862335eb..9cb80b94ef0 100644 --- a/packages/runtime-core/src/components/BaseTransition.ts +++ b/packages/runtime-core/src/components/BaseTransition.ts @@ -22,6 +22,9 @@ import { RendererElement } from '../renderer' type Hook void> = T | T[] +const leaveCbKey = Symbol('_leaveCb') +const enterCbKey = Symbol('_enterCb') + export interface BaseTransitionProps { mode?: 'in-out' | 'out-in' | 'default' appear?: boolean @@ -89,8 +92,8 @@ export interface TransitionElement { // in persisted mode (e.g. v-show), the same element is toggled, so the // pending enter/leave callbacks may need to be cancelled if the state is toggled // before it finishes. - _enterCb?: PendingCallback - _leaveCb?: PendingCallback + [enterCbKey]?: PendingCallback + [leaveCbKey]?: PendingCallback } export function useTransitionState(): TransitionState { @@ -259,9 +262,9 @@ const BaseTransitionImpl: ComponentOptions = { ) leavingVNodesCache[String(oldInnerChild.key)] = oldInnerChild // early removal callback - el._leaveCb = () => { + el[leaveCbKey] = () => { earlyRemove() - el._leaveCb = undefined + el[leaveCbKey] = undefined delete enterHooks.delayedLeave } enterHooks.delayedLeave = delayedLeave @@ -366,18 +369,18 @@ export function resolveTransitionHooks( } } // for same element (v-show) - if (el._leaveCb) { - el._leaveCb(true /* cancelled */) + if (el[leaveCbKey]) { + el[leaveCbKey](true /* cancelled */) } // for toggled element with same key (v-if) const leavingVNode = leavingVNodesCache[key] if ( leavingVNode && isSameVNodeType(vnode, leavingVNode) && - leavingVNode.el!._leaveCb + (leavingVNode.el as TransitionElement)[leaveCbKey] ) { // force early removal (not cancelled) - leavingVNode.el!._leaveCb() + ;(leavingVNode.el as TransitionElement)[leaveCbKey]!() } callHook(hook, [el]) }, @@ -396,7 +399,7 @@ export function resolveTransitionHooks( } } let called = false - const done = (el._enterCb = (cancelled?) => { + const done = (el[enterCbKey] = (cancelled?) => { if (called) return called = true if (cancelled) { @@ -407,7 +410,7 @@ export function resolveTransitionHooks( if (hooks.delayedLeave) { hooks.delayedLeave() } - el._enterCb = undefined + el[enterCbKey] = undefined }) if (hook) { callAsyncHook(hook, [el, done]) @@ -418,15 +421,15 @@ export function resolveTransitionHooks( leave(el, remove) { const key = String(vnode.key) - if (el._enterCb) { - el._enterCb(true /* cancelled */) + if (el[enterCbKey]) { + el[enterCbKey](true /* cancelled */) } if (state.isUnmounting) { return remove() } callHook(onBeforeLeave, [el]) let called = false - const done = (el._leaveCb = (cancelled?) => { + const done = (el[leaveCbKey] = (cancelled?) => { if (called) return called = true remove() @@ -435,7 +438,7 @@ export function resolveTransitionHooks( } else { callHook(onAfterLeave, [el]) } - el._leaveCb = undefined + el[leaveCbKey] = undefined if (leavingVNodesCache[key] === vnode) { delete leavingVNodesCache[key] } diff --git a/packages/runtime-dom/__tests__/patchClass.spec.ts b/packages/runtime-dom/__tests__/patchClass.spec.ts index a784c7d543f..c8da741677a 100644 --- a/packages/runtime-dom/__tests__/patchClass.spec.ts +++ b/packages/runtime-dom/__tests__/patchClass.spec.ts @@ -1,5 +1,5 @@ import { patchProp } from '../src/patchProp' -import { ElementWithTransition } from '../src/components/Transition' +import { ElementWithTransition, vtcKey } from '../src/components/Transition' import { svgNS } from '../src/nodeOps' describe('runtime-dom: class patching', () => { @@ -13,12 +13,12 @@ describe('runtime-dom: class patching', () => { test('transition class', () => { const el = document.createElement('div') as ElementWithTransition - el._vtc = new Set(['bar', 'baz']) + el[vtcKey] = new Set(['bar', 'baz']) patchProp(el, 'class', null, 'foo') expect(el.className).toBe('foo bar baz') patchProp(el, 'class', null, null) expect(el.className).toBe('bar baz') - delete el._vtc + delete el[vtcKey] patchProp(el, 'class', null, 'foo') expect(el.className).toBe('foo') }) diff --git a/packages/runtime-dom/src/components/Transition.ts b/packages/runtime-dom/src/components/Transition.ts index eebfdccca35..b0675213298 100644 --- a/packages/runtime-dom/src/components/Transition.ts +++ b/packages/runtime-dom/src/components/Transition.ts @@ -32,12 +32,14 @@ export interface TransitionProps extends BaseTransitionProps { leaveToClass?: string } +export const vtcKey = Symbol('_vtc') + export interface ElementWithTransition extends HTMLElement { // _vtc = Vue Transition Classes. // Store the temporarily-added transition classes on the element // so that we can avoid overwriting them if the element's class is patched // during the transition. - _vtc?: Set + [vtcKey]?: Set } // DOM Transition is a higher-order-component based on the platform-agnostic @@ -295,18 +297,18 @@ function NumberOf(val: unknown): number { export function addTransitionClass(el: Element, cls: string) { cls.split(/\s+/).forEach(c => c && el.classList.add(c)) ;( - (el as ElementWithTransition)._vtc || - ((el as ElementWithTransition)._vtc = new Set()) + (el as ElementWithTransition)[vtcKey] || + ((el as ElementWithTransition)[vtcKey] = new Set()) ).add(cls) } export function removeTransitionClass(el: Element, cls: string) { cls.split(/\s+/).forEach(c => c && el.classList.remove(c)) - const { _vtc } = el as ElementWithTransition + const _vtc = (el as ElementWithTransition)[vtcKey] if (_vtc) { _vtc.delete(cls) if (!_vtc!.size) { - ;(el as ElementWithTransition)._vtc = undefined + ;(el as ElementWithTransition)[vtcKey] = undefined } } } diff --git a/packages/runtime-dom/src/components/TransitionGroup.ts b/packages/runtime-dom/src/components/TransitionGroup.ts index 5c78be26d72..fc5d260b91e 100644 --- a/packages/runtime-dom/src/components/TransitionGroup.ts +++ b/packages/runtime-dom/src/components/TransitionGroup.ts @@ -6,7 +6,8 @@ import { getTransitionInfo, resolveTransitionProps, TransitionPropsValidators, - forceReflow + forceReflow, + vtcKey } from './Transition' import { Fragment, @@ -29,7 +30,8 @@ import { extend } from '@vue/shared' const positionMap = new WeakMap() const newPositionMap = new WeakMap() - +const moveCbKey = Symbol('_moveCb') +const enterCbKey = Symbol('_enterCb') export type TransitionGroupProps = Omit & { tag?: string moveClass?: string @@ -80,13 +82,13 @@ const TransitionGroupImpl: ComponentOptions = { const style = el.style addTransitionClass(el, moveClass) style.transform = style.webkitTransform = style.transitionDuration = '' - const cb = ((el as any)._moveCb = (e: TransitionEvent) => { + const cb = ((el as any)[moveCbKey] = (e: TransitionEvent) => { if (e && e.target !== el) { return } if (!e || /transform$/.test(e.propertyName)) { el.removeEventListener('transitionend', cb) - ;(el as any)._moveCb = null + ;(el as any)[moveCbKey] = null removeTransitionClass(el, moveClass) } }) @@ -162,11 +164,11 @@ export const TransitionGroup = TransitionGroupImpl as unknown as { function callPendingCbs(c: VNode) { const el = c.el as any - if (el._moveCb) { - el._moveCb() + if (el[moveCbKey]) { + el[moveCbKey]() } - if (el._enterCb) { - el._enterCb() + if (el[enterCbKey]) { + el[enterCbKey]() } } @@ -198,8 +200,9 @@ function hasCSSTransform( // all other transition classes applied to ensure only the move class // is applied. const clone = el.cloneNode() as HTMLElement - if (el._vtc) { - el._vtc.forEach(cls => { + const _vtc = el[vtcKey] + if (_vtc) { + _vtc.forEach(cls => { cls.split(/\s+/).forEach(c => c && clone.classList.remove(c)) }) } diff --git a/packages/runtime-dom/src/directives/vModel.ts b/packages/runtime-dom/src/directives/vModel.ts index 2cf5f4cfc16..89cd5f9d49f 100644 --- a/packages/runtime-dom/src/directives/vModel.ts +++ b/packages/runtime-dom/src/directives/vModel.ts @@ -36,7 +36,9 @@ function onCompositionEnd(e: Event) { } } -type ModelDirective = ObjectDirective +const assignKey = Symbol('_assign') + +type ModelDirective = ObjectDirective // We are exporting the v-model runtime directly as vnode hooks so that it can // be tree-shaken in case v-model is never used. @@ -44,7 +46,7 @@ export const vModelText: ModelDirective< HTMLInputElement | HTMLTextAreaElement > = { created(el, { modifiers: { lazy, trim, number } }, vnode) { - el._assign = getModelAssigner(vnode) + el[assignKey] = getModelAssigner(vnode) const castToNumber = number || (vnode.props && vnode.props.type === 'number') addEventListener(el, lazy ? 'change' : 'input', e => { @@ -56,7 +58,7 @@ export const vModelText: ModelDirective< if (castToNumber) { domValue = looseToNumber(domValue) } - el._assign(domValue) + el[assignKey](domValue) }) if (trim) { addEventListener(el, 'change', () => { @@ -78,7 +80,7 @@ export const vModelText: ModelDirective< el.value = value == null ? '' : value }, beforeUpdate(el, { value, modifiers: { lazy, trim, number } }, vnode) { - el._assign = getModelAssigner(vnode) + el[assignKey] = getModelAssigner(vnode) // avoid clearing unresolved text. #2302 if ((el as any).composing) return if (document.activeElement === el && el.type !== 'range') { @@ -106,12 +108,12 @@ export const vModelCheckbox: ModelDirective = { // #4096 array checkboxes need to be deep traversed deep: true, created(el, _, vnode) { - el._assign = getModelAssigner(vnode) + el[assignKey] = getModelAssigner(vnode) addEventListener(el, 'change', () => { const modelValue = (el as any)._modelValue const elementValue = getValue(el) const checked = el.checked - const assign = el._assign + const assign = el[assignKey] if (isArray(modelValue)) { const index = looseIndexOf(modelValue, elementValue) const found = index !== -1 @@ -138,7 +140,7 @@ export const vModelCheckbox: ModelDirective = { // set initial checked on mount to wait for true-value/false-value mounted: setChecked, beforeUpdate(el, binding, vnode) { - el._assign = getModelAssigner(vnode) + el[assignKey] = getModelAssigner(vnode) setChecked(el, binding, vnode) } } @@ -163,13 +165,13 @@ function setChecked( export const vModelRadio: ModelDirective = { created(el, { value }, vnode) { el.checked = looseEqual(value, vnode.props!.value) - el._assign = getModelAssigner(vnode) + el[assignKey] = getModelAssigner(vnode) addEventListener(el, 'change', () => { - el._assign(getValue(el)) + el[assignKey](getValue(el)) }) }, beforeUpdate(el, { value, oldValue }, vnode) { - el._assign = getModelAssigner(vnode) + el[assignKey] = getModelAssigner(vnode) if (value !== oldValue) { el.checked = looseEqual(value, vnode.props!.value) } @@ -187,7 +189,7 @@ export const vModelSelect: ModelDirective = { .map((o: HTMLOptionElement) => number ? looseToNumber(getValue(o)) : getValue(o) ) - el._assign( + el[assignKey]( el.multiple ? isSetModel ? new Set(selectedVal) @@ -195,7 +197,7 @@ export const vModelSelect: ModelDirective = { : selectedVal[0] ) }) - el._assign = getModelAssigner(vnode) + el[assignKey] = getModelAssigner(vnode) }, // set value in mounted & updated because