diff --git a/.eslintignore b/.eslintignore deleted file mode 100644 index 2974196847e467..00000000000000 --- a/.eslintignore +++ /dev/null @@ -1,3 +0,0 @@ -dist -playground-temp -temp diff --git a/.eslintrc.cjs b/.eslintrc.cjs deleted file mode 100644 index e1188c5820af84..00000000000000 --- a/.eslintrc.cjs +++ /dev/null @@ -1,240 +0,0 @@ -// @ts-check -const { builtinModules } = require('node:module') -const { defineConfig } = require('eslint-define-config') -const pkg = require('./package.json') - -module.exports = defineConfig({ - root: true, - extends: [ - 'eslint:recommended', - 'plugin:n/recommended', - 'plugin:@typescript-eslint/recommended', - 'plugin:@typescript-eslint/stylistic', - 'plugin:regexp/recommended', - ], - ignorePatterns: ['packages/create-vite/template-**'], - plugins: ['import', 'regexp'], - parser: '@typescript-eslint/parser', - parserOptions: { - sourceType: 'module', - ecmaVersion: 2022, - }, - rules: { - eqeqeq: ['warn', 'always', { null: 'never' }], - 'no-debugger': ['error'], - 'no-empty': ['warn', { allowEmptyCatch: true }], - 'no-process-exit': 'off', - 'no-useless-escape': 'off', - 'prefer-const': [ - 'warn', - { - destructuring: 'all', - }, - ], - - 'n/no-process-exit': 'off', - 'n/no-missing-import': 'off', - 'n/no-missing-require': [ - 'error', - { - // for try-catching yarn pnp - allowModules: ['pnpapi', 'vite'], - tryExtensions: ['.ts', '.js', '.jsx', '.tsx', '.d.ts'], - }, - ], - 'n/no-extraneous-import': [ - 'error', - { - allowModules: ['vite', 'less', 'sass', 'vitest', 'unbuild'], - }, - ], - 'n/no-extraneous-require': [ - 'error', - { - allowModules: ['vite'], - }, - ], - 'n/no-deprecated-api': 'off', - 'n/no-unpublished-import': 'off', - 'n/no-unpublished-require': 'off', - 'n/no-unsupported-features/es-syntax': 'off', - - '@typescript-eslint/ban-ts-comment': 'error', - '@typescript-eslint/ban-types': 'off', // TODO: we should turn this on in a new PR - '@typescript-eslint/explicit-module-boundary-types': [ - 'error', - { allowArgumentsExplicitlyTypedAsAny: true }, - ], - '@typescript-eslint/no-empty-function': [ - 'error', - { allow: ['arrowFunctions'] }, - ], - '@typescript-eslint/no-empty-interface': 'off', - '@typescript-eslint/no-explicit-any': 'off', // maybe we should turn this on in a new PR - 'no-extra-semi': 'off', - '@typescript-eslint/no-extra-semi': 'off', // conflicts with prettier - '@typescript-eslint/no-inferrable-types': 'off', - '@typescript-eslint/no-unused-vars': 'off', // maybe we should turn this on in a new PR - '@typescript-eslint/no-var-requires': 'off', - '@typescript-eslint/consistent-type-imports': [ - 'error', - { prefer: 'type-imports', disallowTypeAnnotations: false }, - ], - // disable rules set in @typescript-eslint/stylistic v6 that wasn't set in @typescript-eslint/recommended v5 and which conflict with current code - // maybe we should turn them on in a new PR - '@typescript-eslint/array-type': 'off', - '@typescript-eslint/ban-tslint-comment': 'off', - '@typescript-eslint/consistent-generic-constructors': 'off', - '@typescript-eslint/consistent-indexed-object-style': 'off', - '@typescript-eslint/consistent-type-definitions': 'off', - '@typescript-eslint/prefer-for-of': 'off', - '@typescript-eslint/prefer-function-type': 'off', - - 'import/no-nodejs-modules': [ - 'error', - { allow: builtinModules.map((mod) => `node:${mod}`) }, - ], - 'import/no-duplicates': 'error', - 'import/order': 'error', - 'sort-imports': [ - 'error', - { - ignoreCase: false, - ignoreDeclarationSort: true, - ignoreMemberSort: false, - memberSyntaxSortOrder: ['none', 'all', 'multiple', 'single'], - allowSeparatedGroups: false, - }, - ], - - 'regexp/no-contradiction-with-assertion': 'error', - // in some cases using explicit letter-casing is more performant than the `i` flag - 'regexp/use-ignore-case': 'off', - }, - overrides: [ - { - files: ['packages/**'], - excludedFiles: '**/__tests__/**', - rules: { - 'no-restricted-globals': [ - 'error', - 'require', - '__dirname', - '__filename', - ], - }, - }, - { - files: 'packages/vite/**/*.*', - rules: { - 'n/no-restricted-require': [ - 'error', - Object.keys( - require('./packages/vite/package.json').devDependencies, - ).map((d) => ({ - name: d, - message: - `devDependencies can only be imported using ESM syntax so ` + - `that they are included in the rollup bundle. If you are trying to ` + - `lazy load a dependency, use (await import('dependency')).default instead.`, - })), - ], - }, - }, - { - files: ['packages/vite/src/node/**'], - rules: { - 'no-console': ['error'], - }, - }, - { - files: [ - 'packages/vite/src/types/**', - 'packages/vite/scripts/**', - '*.spec.ts', - ], - rules: { - 'n/no-extraneous-import': 'off', - }, - }, - { - files: ['packages/create-vite/template-*/**', '**/build.config.ts'], - rules: { - 'no-undef': 'off', - 'n/no-missing-import': 'off', - '@typescript-eslint/explicit-module-boundary-types': 'off', - }, - }, - { - files: ['playground/**'], - rules: { - 'n/no-extraneous-import': 'off', - 'n/no-extraneous-require': 'off', - 'n/no-missing-import': 'off', - 'n/no-missing-require': 'off', - // engine field doesn't exist in playgrounds - 'n/no-unsupported-features/es-builtins': [ - 'error', - { - version: pkg.engines.node, - }, - ], - 'n/no-unsupported-features/node-builtins': [ - 'error', - { - version: pkg.engines.node, - }, - ], - '@typescript-eslint/explicit-module-boundary-types': 'off', - }, - }, - { - files: ['playground/**'], - excludedFiles: '**/__tests__/**', - rules: { - 'no-undef': 'off', - 'no-empty': 'off', - 'no-constant-condition': 'off', - '@typescript-eslint/no-empty-function': 'off', - }, - }, - { - files: ['playground/**'], - excludedFiles: [ - 'playground/ssr-resolve/**', - 'playground/**/*{commonjs,cjs}*/**', - 'playground/**/*{commonjs,cjs}*', - 'playground/**/*dep*/**', - 'playground/resolve/browser-module-field2/index.web.js', - 'playground/resolve/browser-field/**', - 'playground/tailwind/**', // blocked by https://github.com/postcss/postcss-load-config/issues/239 - ], - rules: { - 'import/no-commonjs': 'error', - }, - }, - { - files: [ - 'playground/tsconfig-json/**', - 'playground/tsconfig-json-load-error/**', - ], - excludedFiles: '**/__tests__/**', - rules: { - '@typescript-eslint/ban-ts-comment': 'off', - }, - }, - { - files: ['*.js', '*.mjs', '*.cjs'], - rules: { - '@typescript-eslint/explicit-module-boundary-types': 'off', - }, - }, - { - files: ['*.d.ts'], - rules: { - '@typescript-eslint/triple-slash-reference': 'off', - }, - }, - ], - reportUnusedDisableDirectives: true, -}) diff --git a/.github/ISSUE_TEMPLATE/bug_report.yml b/.github/ISSUE_TEMPLATE/bug_report.yml index 0c4c5be491a3ad..91b3cfd45d7866 100644 --- a/.github/ISSUE_TEMPLATE/bug_report.yml +++ b/.github/ISSUE_TEMPLATE/bug_report.yml @@ -18,7 +18,7 @@ body: id: reproduction attributes: label: Reproduction - description: Please provide a link via [vite.new](https://vite.new/) or a link to a repo that can reproduce the problem you ran into. `npm create vite@latest` and `npm create vite-extra@latest` (for SSR or library repros) can be used as a starter template. A [minimal reproduction](https://stackoverflow.com/help/minimal-reproducible-example) is required ([Why?](https://antfu.me/posts/why-reproductions-are-required)). If a report is vague (e.g. just a generic error message) and has no reproduction, it will receive a "need reproduction" label. If no reproduction is provided after 3 days, it will be auto-closed. + description: Please provide a link via [vite.new](https://vite.new/) or a link to a repo that can reproduce the problem you ran into. `npm create vite@latest` and `npm create vite-extra@latest` (for SSR or library repros) can be used as a starter template. A [minimal reproduction](https://stackoverflow.com/help/minimal-reproducible-example) is required ([Why?](https://antfu.me/posts/why-reproductions-are-required)). If a report is vague (e.g. just a generic error message) and has no reproduction, it will receive a "needs reproduction" label. If no reproduction is provided after 3 days, it will be auto-closed. placeholder: Reproduction URL validations: required: true @@ -46,6 +46,7 @@ body: - npm - yarn - pnpm + - bun validations: required: true - type: textarea diff --git a/.github/PULL_REQUEST_TEMPLATE.md b/.github/PULL_REQUEST_TEMPLATE.md index c393c93c94e659..2734afc421a080 100644 --- a/.github/PULL_REQUEST_TEMPLATE.md +++ b/.github/PULL_REQUEST_TEMPLATE.md @@ -1,26 +1,14 @@ - - ### Description - - -### Additional context - - - ---- - -### What is the purpose of this pull request? + -- [ ] Bug fix -- [ ] New Feature -- [ ] Documentation update -- [ ] Other + diff --git a/.github/issue-workflow-dark.png b/.github/issue-workflow-dark.png deleted file mode 100644 index fe32a6dc507b0c..00000000000000 Binary files a/.github/issue-workflow-dark.png and /dev/null differ diff --git a/.github/issue-workflow.excalidraw b/.github/issue-workflow.excalidraw deleted file mode 100644 index fc12540b41969d..00000000000000 --- a/.github/issue-workflow.excalidraw +++ /dev/null @@ -1,2738 +0,0 @@ -{ - "type": "excalidraw", - "version": 2, - "source": "https://excalidraw.com", - "elements": [ - { - "type": "diamond", - "version": 265, - "versionNonce": 192248478, - "isDeleted": false, - "id": "sQbLVqjpgdZXg0DrdESrv", - "fillStyle": "hachure", - "strokeWidth": 1, - "strokeStyle": "solid", - "roughness": 1, - "opacity": 100, - "angle": 0, - "x": 735.9493670886075, - "y": 178.5833246155455, - "strokeColor": "#000000", - "backgroundColor": "transparent", - "width": 290.1012658227851, - "height": 161.833350768909, - "seed": 177114754, - "groupIds": [], - "strokeSharpness": "sharp", - "boundElements": [], - "updated": 1641948455776 - }, - { - "type": "text", - "version": 206, - "versionNonce": 749438530, - "isDeleted": false, - "id": "Zo2OUf2C5ZeVhoM2q7Uni", - "fillStyle": "hachure", - "strokeWidth": 1, - "strokeStyle": "solid", - "roughness": 1, - "opacity": 100, - "angle": 0, - "x": 799, - "y": 235.5, - "strokeColor": "#000000", - "backgroundColor": "transparent", - "width": 164, - "height": 48, - "seed": 478396126, - "groupIds": [], - "strokeSharpness": "sharp", - "boundElements": [], - "updated": 1641948455776, - "fontSize": 20, - "fontFamily": 3, - "text": "Followed issue\ntemplate?", - "baseline": 43, - "textAlign": "center", - "verticalAlign": "top", - "containerId": null, - "originalText": "Followed issue\ntemplate?" - }, - { - "type": "line", - "version": 216, - "versionNonce": 1785231070, - "isDeleted": false, - "id": "alQgWzqeIA8Wd_epahTcc", - "fillStyle": "hachure", - "strokeWidth": 1, - "strokeStyle": "solid", - "roughness": 1, - "opacity": 100, - "angle": 0, - "x": 883, - "y": 336, - "strokeColor": "#000000", - "backgroundColor": "transparent", - "width": 0, - "height": 100.47284166624985, - "seed": 1247524446, - "groupIds": [], - "strokeSharpness": "round", - "boundElements": [], - "updated": 1641948455776, - "startBinding": null, - "endBinding": null, - "lastCommittedPoint": null, - "startArrowhead": null, - "endArrowhead": null, - "points": [ - [ - 0, - 0 - ], - [ - 0, - 100.47284166624985 - ] - ] - }, - { - "type": "line", - "version": 377, - "versionNonce": 665153026, - "isDeleted": false, - "id": "fDfDOoQv4l7fM9iicquNl", - "fillStyle": "hachure", - "strokeWidth": 1, - "strokeStyle": "solid", - "roughness": 1, - "opacity": 100, - "angle": 0, - "x": 1161.8952582689835, - "y": 369.56739811912234, - "strokeColor": "#000000", - "backgroundColor": "transparent", - "width": 279.04014344843074, - "height": 0, - "seed": 109974722, - "groupIds": [], - "strokeSharpness": "round", - "boundElements": [], - "updated": 1641948455776, - "startBinding": null, - "endBinding": null, - "lastCommittedPoint": null, - "startArrowhead": null, - "endArrowhead": null, - "points": [ - [ - 0, - 0 - ], - [ - -279.04014344843074, - 0 - ] - ] - }, - { - "type": "text", - "version": 304, - "versionNonce": 573696798, - "isDeleted": false, - "id": "zMMCi3Zo6KpPyyx8G8eYr", - "fillStyle": "hachure", - "strokeWidth": 1, - "strokeStyle": "solid", - "roughness": 1, - "opacity": 100, - "angle": 0, - "x": 835.3743472748146, - "y": 397.6001419449459, - "strokeColor": "#000000", - "backgroundColor": "transparent", - "width": 29, - "height": 19, - "seed": 666855710, - "groupIds": [], - "strokeSharpness": "sharp", - "boundElements": [], - "updated": 1641948455776, - "fontSize": 16, - "fontFamily": 3, - "text": "YES", - "baseline": 15, - "textAlign": "center", - "verticalAlign": "top", - "containerId": null, - "originalText": "YES" - }, - { - "type": "text", - "version": 589, - "versionNonce": 1116495298, - "isDeleted": false, - "id": "Ztoo5I5eH0mNv9l39lH11", - "fillStyle": "hachure", - "strokeWidth": 1, - "strokeStyle": "solid", - "roughness": 1, - "opacity": 100, - "angle": 0, - "x": 1186.2924959296251, - "y": 397.47863964423703, - "strokeColor": "#000000", - "backgroundColor": "transparent", - "width": 20, - "height": 19, - "seed": 1575246878, - "groupIds": [], - "strokeSharpness": "sharp", - "boundElements": [], - "updated": 1641948455776, - "fontSize": 16, - "fontFamily": 3, - "text": "NO", - "baseline": 15, - "textAlign": "center", - "verticalAlign": "top", - "containerId": null, - "originalText": "NO" - }, - { - "type": "line", - "version": 357, - "versionNonce": 1094899550, - "isDeleted": false, - "id": "ESEzIw1uAeGDtzFQxjoUn", - "fillStyle": "hachure", - "strokeWidth": 1, - "strokeStyle": "solid", - "roughness": 1, - "opacity": 100, - "angle": 0, - "x": 1161.6266683902663, - "y": 370.5206504984035, - "strokeColor": "#000000", - "backgroundColor": "transparent", - "width": 0, - "height": 102.02490458900218, - "seed": 820054494, - "groupIds": [], - "strokeSharpness": "round", - "boundElements": [], - "updated": 1641948455776, - "startBinding": null, - "endBinding": null, - "lastCommittedPoint": null, - "startArrowhead": null, - "endArrowhead": null, - "points": [ - [ - 0, - 0 - ], - [ - 0, - 102.02490458900218 - ] - ] - }, - { - "type": "rectangle", - "version": 503, - "versionNonce": 176117122, - "isDeleted": false, - "id": "VAqo6Z6jbCX7jifpnRFcL", - "fillStyle": "hachure", - "strokeWidth": 1, - "strokeStyle": "solid", - "roughness": 1, - "opacity": 100, - "angle": 0, - "x": 1056.276152926349, - "y": 473.85054740562003, - "strokeColor": "#000000", - "backgroundColor": "transparent", - "width": 275.3695193143709, - "height": 82.49906843870326, - "seed": 1100774814, - "groupIds": [], - "strokeSharpness": "sharp", - "boundElements": [], - "updated": 1641948455776 - }, - { - "type": "text", - "version": 591, - "versionNonce": 1609317278, - "isDeleted": false, - "id": "LhHi_nLcEjXVWTrzIj_Aw", - "fillStyle": "hachure", - "strokeWidth": 1, - "strokeStyle": "solid", - "roughness": 1, - "opacity": 100, - "angle": 0, - "x": 1099.9609125835343, - "y": 491.10008162497166, - "strokeColor": "#000000", - "backgroundColor": "transparent", - "width": 188, - "height": 48, - "seed": 1527850654, - "groupIds": [], - "strokeSharpness": "sharp", - "boundElements": [], - "updated": 1641948455776, - "fontSize": 20, - "fontFamily": 3, - "text": "Close and ask to\nfollow template", - "baseline": 43, - "textAlign": "center", - "verticalAlign": "top", - "containerId": null, - "originalText": "Close and ask to\nfollow template" - }, - { - "type": "text", - "version": 551, - "versionNonce": 300687710, - "isDeleted": false, - "id": "-w-lI8s8rNBbK0EDBgrjg", - "fillStyle": "hachure", - "strokeWidth": 1, - "strokeStyle": "solid", - "roughness": 1, - "opacity": 100, - "angle": 0, - "x": 806.60554399834, - "y": 506.67439364859297, - "strokeColor": "#000000", - "backgroundColor": "transparent", - "width": 153, - "height": 24, - "seed": 1427727810, - "groupIds": [], - "strokeSharpness": "sharp", - "boundElements": [], - "updated": 1641948632442, - "fontSize": 20, - "fontFamily": 3, - "text": "Is duplicate?", - "baseline": 19, - "textAlign": "center", - "verticalAlign": "top", - "containerId": null, - "originalText": "Is duplicate?" - }, - { - "type": "line", - "version": 437, - "versionNonce": 1683953630, - "isDeleted": false, - "id": "Kkfv8iJRmQOZXVmxjq4VB", - "fillStyle": "hachure", - "strokeWidth": 1, - "strokeStyle": "solid", - "roughness": 1, - "opacity": 100, - "angle": 0, - "x": 883.8589168277284, - "y": 599.9686255820234, - "strokeColor": "#000000", - "backgroundColor": "transparent", - "width": 0, - "height": 100.47284166624985, - "seed": 377873922, - "groupIds": [], - "strokeSharpness": "round", - "boundElements": [], - "updated": 1641948455776, - "startBinding": null, - "endBinding": null, - "lastCommittedPoint": null, - "startArrowhead": null, - "endArrowhead": null, - "points": [ - [ - 0, - 0 - ], - [ - 0, - 100.47284166624985 - ] - ] - }, - { - "type": "text", - "version": 558, - "versionNonce": 1722113282, - "isDeleted": false, - "id": "XlHx-HWp0TSFlM-imDtj5", - "fillStyle": "hachure", - "strokeWidth": 1, - "strokeStyle": "solid", - "roughness": 1, - "opacity": 100, - "angle": 0, - "x": 845.3844268932406, - "y": 671.5687675269694, - "strokeColor": "#000000", - "backgroundColor": "transparent", - "width": 20, - "height": 19, - "seed": 1855837634, - "groupIds": [], - "strokeSharpness": "sharp", - "boundElements": [], - "updated": 1641948455776, - "fontSize": 16, - "fontFamily": 3, - "text": "NO", - "baseline": 15, - "textAlign": "center", - "verticalAlign": "top", - "containerId": null, - "originalText": "NO" - }, - { - "type": "text", - "version": 735, - "versionNonce": 1185704990, - "isDeleted": false, - "id": "KyQkN5f-OgFpz5uedfqMK", - "fillStyle": "hachure", - "strokeWidth": 1, - "strokeStyle": "solid", - "roughness": 1, - "opacity": 100, - "angle": 0, - "x": 1104.9975246719962, - "y": 756.829751760595, - "strokeColor": "#000000", - "backgroundColor": "transparent", - "width": 176, - "height": 48, - "seed": 282119490, - "groupIds": [], - "strokeSharpness": "sharp", - "boundElements": [], - "updated": 1641948455776, - "fontSize": 20, - "fontFamily": 3, - "text": "Close and point\nto duplicate", - "baseline": 43, - "textAlign": "center", - "verticalAlign": "top", - "containerId": null, - "originalText": "Close and point\nto duplicate" - }, - { - "type": "text", - "version": 591, - "versionNonce": 431385794, - "isDeleted": false, - "id": "Iegl-yl9ijRqFOpeSBldb", - "fillStyle": "hachure", - "strokeWidth": 1, - "strokeStyle": "solid", - "roughness": 1, - "opacity": 100, - "angle": 0, - "x": 805.8181494893245, - "y": 758.0208016745363, - "strokeColor": "#000000", - "backgroundColor": "transparent", - "width": 153, - "height": 48, - "seed": 92919042, - "groupIds": [], - "strokeSharpness": "sharp", - "boundElements": [], - "updated": 1641948455776, - "fontSize": 20, - "fontFamily": 3, - "text": "Has proper\nreproduction?", - "baseline": 43, - "textAlign": "center", - "verticalAlign": "top", - "containerId": null, - "originalText": "Has proper\nreproduction?" - }, - { - "type": "line", - "version": 546, - "versionNonce": 1422211166, - "isDeleted": false, - "id": "bJ3UV8aCWRq1vt_Tkw0jp", - "fillStyle": "hachure", - "strokeWidth": 1, - "strokeStyle": "solid", - "roughness": 1, - "opacity": 100, - "angle": 0, - "x": 882.0773648501048, - "y": 862.5450610034595, - "strokeColor": "#000000", - "backgroundColor": "transparent", - "width": 0, - "height": 100.47284166624985, - "seed": 326628510, - "groupIds": [], - "strokeSharpness": "round", - "boundElements": [], - "updated": 1641948455776, - "startBinding": null, - "endBinding": null, - "lastCommittedPoint": null, - "startArrowhead": null, - "endArrowhead": null, - "points": [ - [ - 0, - 0 - ], - [ - 0, - 100.47284166624985 - ] - ] - }, - { - "type": "line", - "version": 680, - "versionNonce": 797934722, - "isDeleted": false, - "id": "iXnyiAlfBjFRTxHo7LXqG", - "fillStyle": "hachure", - "strokeWidth": 1, - "strokeStyle": "solid", - "roughness": 1, - "opacity": 100, - "angle": 0, - "x": 1160.972623119087, - "y": 894.1124591225819, - "strokeColor": "#000000", - "backgroundColor": "transparent", - "width": 279.04014344843074, - "height": 0, - "seed": 2007346242, - "groupIds": [], - "strokeSharpness": "round", - "boundElements": [], - "updated": 1641948455776, - "startBinding": null, - "endBinding": null, - "lastCommittedPoint": null, - "startArrowhead": null, - "endArrowhead": null, - "points": [ - [ - 0, - 0 - ], - [ - -279.04014344843074, - 0 - ] - ] - }, - { - "type": "text", - "version": 679, - "versionNonce": 1987738782, - "isDeleted": false, - "id": "-V8am98wE-u1ZwjM5-_Dg", - "fillStyle": "hachure", - "strokeWidth": 1, - "strokeStyle": "solid", - "roughness": 1, - "opacity": 100, - "angle": 0, - "x": 833.102874915617, - "y": 934.1452029484055, - "strokeColor": "#000000", - "backgroundColor": "transparent", - "width": 29, - "height": 19, - "seed": 766174, - "groupIds": [], - "strokeSharpness": "sharp", - "boundElements": [], - "updated": 1641948455776, - "fontSize": 16, - "fontFamily": 3, - "text": "YES", - "baseline": 15, - "textAlign": "center", - "verticalAlign": "top", - "containerId": null, - "originalText": "YES" - }, - { - "type": "text", - "version": 934, - "versionNonce": 569353282, - "isDeleted": false, - "id": "pCFWl38d536W9NeeiBAFj", - "fillStyle": "hachure", - "strokeWidth": 1, - "strokeStyle": "solid", - "roughness": 1, - "opacity": 100, - "angle": 0, - "x": 1185.2789516888201, - "y": 934.1146097386056, - "strokeColor": "#000000", - "backgroundColor": "transparent", - "width": 20, - "height": 19, - "seed": 476763138, - "groupIds": [], - "strokeSharpness": "sharp", - "boundElements": [], - "updated": 1641948455776, - "fontSize": 16, - "fontFamily": 3, - "text": "NO", - "baseline": 15, - "textAlign": "center", - "verticalAlign": "top", - "containerId": null, - "originalText": "NO" - }, - { - "type": "line", - "version": 627, - "versionNonce": 1904031966, - "isDeleted": false, - "id": "OAYqmOtcU81wORAuzxSWw", - "fillStyle": "hachure", - "strokeWidth": 1, - "strokeStyle": "solid", - "roughness": 1, - "opacity": 100, - "angle": 0, - "x": 1160.7040332403699, - "y": 895.065711501863, - "strokeColor": "#000000", - "backgroundColor": "transparent", - "width": 0, - "height": 84.25681865453203, - "seed": 1398662430, - "groupIds": [], - "strokeSharpness": "round", - "boundElements": [], - "updated": 1641948455776, - "startBinding": null, - "endBinding": null, - "lastCommittedPoint": null, - "startArrowhead": null, - "endArrowhead": null, - "points": [ - [ - 0, - 0 - ], - [ - 0, - 84.25681865453203 - ] - ] - }, - { - "type": "text", - "version": 878, - "versionNonce": 1521863682, - "isDeleted": false, - "id": "sc6Al_aPs279jhxFkrLtM", - "fillStyle": "hachure", - "strokeWidth": 1, - "strokeStyle": "solid", - "roughness": 1, - "opacity": 100, - "angle": 0, - "x": 1091.446447122361, - "y": 1002.3719204352936, - "strokeColor": "#000000", - "backgroundColor": "transparent", - "width": 317, - "height": 24, - "seed": 517968222, - "groupIds": [], - "strokeSharpness": "sharp", - "boundElements": [], - "updated": 1641948455776, - "fontSize": 20, - "fontFamily": 3, - "text": "Label: \"needs reproduction\"", - "baseline": 19, - "textAlign": "center", - "verticalAlign": "top", - "containerId": null, - "originalText": "Label: \"needs reproduction\"" - }, - { - "type": "text", - "version": 720, - "versionNonce": 761163038, - "isDeleted": false, - "id": "K0mUQrR_xWSMwnC7n3pLQ", - "fillStyle": "hachure", - "strokeWidth": 1, - "strokeStyle": "solid", - "roughness": 1, - "opacity": 100, - "angle": 0, - "x": 816.9313570364943, - "y": 1018.9264620518948, - "strokeColor": "#000000", - "backgroundColor": "transparent", - "width": 129, - "height": 48, - "seed": 501331358, - "groupIds": [], - "strokeSharpness": "sharp", - "boundElements": [], - "updated": 1641948455776, - "fontSize": 20, - "fontFamily": 3, - "text": "Is actually\na bug?", - "baseline": 43, - "textAlign": "center", - "verticalAlign": "top", - "containerId": null, - "originalText": "Is actually\na bug?" - }, - { - "type": "text", - "version": 1046, - "versionNonce": 803273666, - "isDeleted": false, - "id": "kWwkE1CtF3bPL1IrH3nsI", - "fillStyle": "hachure", - "strokeWidth": 1, - "strokeStyle": "solid", - "roughness": 1, - "opacity": 100, - "angle": 0, - "x": 1099.946447122361, - "y": 1039.3719204352938, - "strokeColor": "#000000", - "backgroundColor": "transparent", - "width": 300, - "height": 38, - "seed": 1254767518, - "groupIds": [], - "strokeSharpness": "sharp", - "boundElements": [], - "updated": 1641948455776, - "fontSize": 16, - "fontFamily": 3, - "text": "bot will auto close if no update\nhas been made in 3 days", - "baseline": 34, - "textAlign": "center", - "verticalAlign": "top", - "containerId": null, - "originalText": "bot will auto close if no update\nhas been made in 3 days" - }, - { - "type": "diamond", - "version": 609, - "versionNonce": 2005866846, - "isDeleted": false, - "id": "EjTMjKTsLZaR41AMwsmNy", - "fillStyle": "hachure", - "strokeWidth": 1, - "strokeStyle": "solid", - "roughness": 1, - "opacity": 100, - "angle": 0, - "x": 738.0549110869475, - "y": 437.7577182641384, - "strokeColor": "#000000", - "backgroundColor": "transparent", - "width": 290.1012658227851, - "height": 161.833350768909, - "seed": 1803796226, - "groupIds": [], - "strokeSharpness": "sharp", - "boundElements": [], - "updated": 1641948455776 - }, - { - "type": "diamond", - "version": 511, - "versionNonce": 1603285890, - "isDeleted": false, - "id": "VfIJSVxD9gou9yN2KDONV", - "fillStyle": "hachure", - "strokeWidth": 1, - "strokeStyle": "solid", - "roughness": 1, - "opacity": 100, - "angle": 0, - "x": 737.267516577932, - "y": 701.1041262900818, - "strokeColor": "#000000", - "backgroundColor": "transparent", - "width": 290.1012658227851, - "height": 161.833350768909, - "seed": 1344307614, - "groupIds": [], - "strokeSharpness": "sharp", - "boundElements": [], - "updated": 1641948455776 - }, - { - "type": "diamond", - "version": 544, - "versionNonce": 1197489566, - "isDeleted": false, - "id": "HSSomUo5enDAFinc7SLgD", - "fillStyle": "hachure", - "strokeWidth": 1, - "strokeStyle": "solid", - "roughness": 1, - "opacity": 100, - "angle": 0, - "x": 736.3807241251018, - "y": 962.0097866674403, - "strokeColor": "#000000", - "backgroundColor": "transparent", - "width": 290.1012658227851, - "height": 161.833350768909, - "seed": 1448404318, - "groupIds": [], - "strokeSharpness": "sharp", - "boundElements": [], - "updated": 1641948455776 - }, - { - "type": "rectangle", - "version": 570, - "versionNonce": 201487170, - "isDeleted": false, - "id": "ngwqQYNo3Yjj_Nkg3D99a", - "fillStyle": "hachure", - "strokeWidth": 1, - "strokeStyle": "solid", - "roughness": 1, - "opacity": 100, - "angle": 0, - "x": 1055.3127650148108, - "y": 739.5802175412433, - "strokeColor": "#000000", - "backgroundColor": "transparent", - "width": 275.3695193143709, - "height": 82.49906843870326, - "seed": 1214881218, - "groupIds": [], - "strokeSharpness": "sharp", - "boundElements": [], - "updated": 1641948455776 - }, - { - "type": "rectangle", - "version": 682, - "versionNonce": 33741278, - "isDeleted": false, - "id": "nnx4AoKQIT9eP622eJ9AD", - "fillStyle": "hachure", - "strokeWidth": 1, - "strokeStyle": "solid", - "roughness": 1, - "opacity": 100, - "angle": 0, - "x": 1055.3127650148108, - "y": 981.0741934448577, - "strokeColor": "#000000", - "backgroundColor": "transparent", - "width": 389.2673642151006, - "height": 117.43882747484807, - "seed": 177151390, - "groupIds": [], - "strokeSharpness": "sharp", - "boundElements": [], - "updated": 1641948455776 - }, - { - "type": "line", - "version": 618, - "versionNonce": 932625154, - "isDeleted": false, - "id": "UbhoavCtdEmARfulbxS6B", - "fillStyle": "hachure", - "strokeWidth": 1, - "strokeStyle": "solid", - "roughness": 1, - "opacity": 100, - "angle": 0, - "x": 881.5581485734253, - "y": 1123.5920490269261, - "strokeColor": "#000000", - "backgroundColor": "transparent", - "width": 0, - "height": 100.47284166624985, - "seed": 2065819586, - "groupIds": [], - "strokeSharpness": "round", - "boundElements": [], - "updated": 1641948455776, - "startBinding": null, - "endBinding": null, - "lastCommittedPoint": null, - "startArrowhead": null, - "endArrowhead": null, - "points": [ - [ - 0, - 0 - ], - [ - 0, - 100.47284166624985 - ] - ] - }, - { - "type": "line", - "version": 914, - "versionNonce": 1049095710, - "isDeleted": false, - "id": "UL7ZiIflZEl47HrMlp-IL", - "fillStyle": "hachure", - "strokeWidth": 1, - "strokeStyle": "solid", - "roughness": 1, - "opacity": 100, - "angle": 0, - "x": 1638.7841025144658, - "y": 1155.1594471460483, - "strokeColor": "#000000", - "backgroundColor": "transparent", - "width": 757.3708391204892, - "height": 0, - "seed": 774040926, - "groupIds": [], - "strokeSharpness": "round", - "boundElements": [], - "updated": 1641948455777, - "startBinding": null, - "endBinding": null, - "lastCommittedPoint": null, - "startArrowhead": null, - "endArrowhead": null, - "points": [ - [ - 0, - 0 - ], - [ - -757.3708391204892, - 0 - ] - ] - }, - { - "type": "text", - "version": 751, - "versionNonce": 1087113922, - "isDeleted": false, - "id": "74qsx9VEJNHwAn5Hw1_WD", - "fillStyle": "hachure", - "strokeWidth": 1, - "strokeStyle": "solid", - "roughness": 1, - "opacity": 100, - "angle": 0, - "x": 832.5836586389375, - "y": 1195.192190971872, - "strokeColor": "#000000", - "backgroundColor": "transparent", - "width": 29, - "height": 19, - "seed": 1809031042, - "groupIds": [], - "strokeSharpness": "sharp", - "boundElements": [], - "updated": 1641948455777, - "fontSize": 16, - "fontFamily": 3, - "text": "YES", - "baseline": 15, - "textAlign": "center", - "verticalAlign": "top", - "containerId": null, - "originalText": "YES" - }, - { - "type": "text", - "version": 976, - "versionNonce": 165706334, - "isDeleted": false, - "id": "-1ioc8tEbSPtI5ozdVi75", - "fillStyle": "hachure", - "strokeWidth": 1, - "strokeStyle": "solid", - "roughness": 1, - "opacity": 100, - "angle": 0, - "x": 1663.7900384424438, - "y": 1195.1312947317692, - "strokeColor": "#000000", - "backgroundColor": "transparent", - "width": 20, - "height": 19, - "seed": 609849758, - "groupIds": [], - "strokeSharpness": "sharp", - "boundElements": [], - "updated": 1641948455777, - "fontSize": 16, - "fontFamily": 3, - "text": "NO", - "baseline": 15, - "textAlign": "center", - "verticalAlign": "top", - "containerId": null, - "originalText": "NO" - }, - { - "type": "line", - "version": 872, - "versionNonce": 1581343362, - "isDeleted": false, - "id": "mspcCj6jasZd4jMwqcYrg", - "fillStyle": "hachure", - "strokeWidth": 1, - "strokeStyle": "solid", - "roughness": 1, - "opacity": 100, - "angle": 0, - "x": 1639.18481696369, - "y": 1156.1126995253296, - "strokeColor": "#000000", - "backgroundColor": "transparent", - "width": 0, - "height": 74.0498029861119, - "seed": 351821634, - "groupIds": [], - "strokeSharpness": "round", - "boundElements": [], - "updated": 1641948455777, - "startBinding": null, - "endBinding": null, - "lastCommittedPoint": null, - "startArrowhead": null, - "endArrowhead": null, - "points": [ - [ - 0, - 0 - ], - [ - 0, - 74.0498029861119 - ] - ] - }, - { - "type": "text", - "version": 1111, - "versionNonce": 719158942, - "isDeleted": false, - "id": "f267tX8JXETN_7f-dFfC7", - "fillStyle": "hachure", - "strokeWidth": 1, - "strokeStyle": "solid", - "roughness": 1, - "opacity": 100, - "angle": 0, - "x": 769.9139104197111, - "y": 1245.010365053955, - "strokeColor": "#000000", - "backgroundColor": "transparent", - "width": 376, - "height": 24, - "seed": 1304375902, - "groupIds": [], - "strokeSharpness": "sharp", - "boundElements": [], - "updated": 1641948455777, - "fontSize": 20, - "fontFamily": 3, - "text": "1. Remove \"pending triage\" label", - "baseline": 19, - "textAlign": "left", - "verticalAlign": "top", - "containerId": null, - "originalText": "1. Remove \"pending triage\" label" - }, - { - "type": "rectangle", - "version": 988, - "versionNonce": 448119362, - "isDeleted": false, - "id": "vcDVvtyx6f0Wn0KbHWHvM", - "fillStyle": "hachure", - "strokeWidth": 1, - "strokeStyle": "solid", - "roughness": 1, - "opacity": 100, - "angle": 0, - "x": 735.280228312161, - "y": 1225.2999396508208, - "strokeColor": "#000000", - "backgroundColor": "transparent", - "width": 460.69593564367204, - "height": 252.52898004072858, - "seed": 1915041438, - "groupIds": [], - "strokeSharpness": "sharp", - "boundElements": [], - "updated": 1641948455777 - }, - { - "type": "text", - "version": 1193, - "versionNonce": 1714420446, - "isDeleted": false, - "id": "kCXoBQ8uydHh5UHMAVmsY", - "fillStyle": "hachure", - "strokeWidth": 1, - "strokeStyle": "solid", - "roughness": 1, - "opacity": 100, - "angle": 0, - "x": 769.9139104197111, - "y": 1289.5272898961812, - "strokeColor": "#000000", - "backgroundColor": "transparent", - "width": 212, - "height": 24, - "seed": 685064322, - "groupIds": [], - "strokeSharpness": "sharp", - "boundElements": [], - "updated": 1641948455777, - "fontSize": 20, - "fontFamily": 3, - "text": "2. Add \"bug\" label", - "baseline": 19, - "textAlign": "left", - "verticalAlign": "top", - "containerId": null, - "originalText": "2. Add \"bug\" label" - }, - { - "type": "text", - "version": 1326, - "versionNonce": 1359489538, - "isDeleted": false, - "id": "ISJ9XbIqLfUQiNlZWuJlb", - "fillStyle": "hachure", - "strokeWidth": 1, - "strokeStyle": "solid", - "roughness": 1, - "opacity": 100, - "angle": 0, - "x": 768.3266088324094, - "y": 1333.1721497092653, - "strokeColor": "#000000", - "backgroundColor": "transparent", - "width": 363, - "height": 72, - "seed": 875452290, - "groupIds": [], - "strokeSharpness": "sharp", - "boundElements": [], - "updated": 1641948455777, - "fontSize": 20, - "fontFamily": 3, - "text": "3. Add related feature label if\n applicable (e.g. \"bug: ssr\"\n or \"plugin: vue\")", - "baseline": 67, - "textAlign": "left", - "verticalAlign": "top", - "containerId": null, - "originalText": "3. Add related feature label if\n applicable (e.g. \"bug: ssr\"\n or \"plugin: vue\")" - }, - { - "type": "text", - "version": 1425, - "versionNonce": 1114173214, - "isDeleted": false, - "id": "o5xZIQge3zU5dqWbKuNe4", - "fillStyle": "hachure", - "strokeWidth": 1, - "strokeStyle": "solid", - "roughness": 1, - "opacity": 100, - "angle": 0, - "x": 768.3266088324094, - "y": 1427.2991338362494, - "strokeColor": "#000000", - "backgroundColor": "transparent", - "width": 388, - "height": 24, - "seed": 1678453826, - "groupIds": [], - "strokeSharpness": "sharp", - "boundElements": [], - "updated": 1641948455777, - "fontSize": 20, - "fontFamily": 3, - "text": "4. Add priority label (see below)", - "baseline": 19, - "textAlign": "left", - "verticalAlign": "top", - "containerId": null, - "originalText": "4. Add priority label (see below)" - }, - { - "type": "text", - "version": 903, - "versionNonce": 674732482, - "isDeleted": false, - "id": "VzxxMS2qarD3kj7kwWJeo", - "fillStyle": "hachure", - "strokeWidth": 1, - "strokeStyle": "solid", - "roughness": 1, - "opacity": 100, - "angle": 0, - "x": 1551.664548833127, - "y": 1289.5705520867325, - "strokeColor": "#000000", - "backgroundColor": "transparent", - "width": 176, - "height": 48, - "seed": 478062082, - "groupIds": [], - "strokeSharpness": "sharp", - "boundElements": [], - "updated": 1641948455777, - "fontSize": 20, - "fontFamily": 3, - "text": "Is the behavior\nintended?", - "baseline": 43, - "textAlign": "center", - "verticalAlign": "top", - "containerId": null, - "originalText": "Is the behavior\nintended?" - }, - { - "type": "diamond", - "version": 699, - "versionNonce": 1274633054, - "isDeleted": false, - "id": "5K7ULA43_7XI_Lbw38zmI", - "fillStyle": "hachure", - "strokeWidth": 1, - "strokeStyle": "solid", - "roughness": 1, - "opacity": 100, - "angle": 0, - "x": 1494.6139159217344, - "y": 1232.653876702278, - "strokeColor": "#000000", - "backgroundColor": "transparent", - "width": 290.1012658227851, - "height": 161.833350768909, - "seed": 1822962462, - "groupIds": [], - "strokeSharpness": "sharp", - "boundElements": [], - "updated": 1641948455777 - }, - { - "type": "text", - "version": 928, - "versionNonce": 258150786, - "isDeleted": false, - "id": "eyAyR-XwXjgrYdlkC6Og3", - "fillStyle": "hachure", - "strokeWidth": 1, - "strokeStyle": "solid", - "roughness": 1, - "opacity": 100, - "angle": 0, - "x": 805.3540409843638, - "y": 1555.4084520993458, - "strokeColor": "#000000", - "backgroundColor": "transparent", - "width": 152, - "height": 72, - "seed": 1243657054, - "groupIds": [], - "strokeSharpness": "sharp", - "boundElements": [], - "updated": 1641948455777, - "fontSize": 20, - "fontFamily": 3, - "text": "Does the\nbug make Vite\nunusable?", - "baseline": 67, - "textAlign": "center", - "verticalAlign": "top", - "containerId": null, - "originalText": "Does the\nbug make Vite\nunusable?" - }, - { - "type": "diamond", - "version": 705, - "versionNonce": 1496465310, - "isDeleted": false, - "id": "zS7O1N2taRTf739KkKDb0", - "fillStyle": "hachure", - "strokeWidth": 1, - "strokeStyle": "solid", - "roughness": 1, - "opacity": 100, - "angle": 0, - "x": 736.3034080729711, - "y": 1510.4917767148913, - "strokeColor": "#000000", - "backgroundColor": "transparent", - "width": 290.1012658227851, - "height": 161.833350768909, - "seed": 1811757442, - "groupIds": [], - "strokeSharpness": "sharp", - "boundElements": [], - "updated": 1641948455777 - }, - { - "type": "line", - "version": 742, - "versionNonce": 969511234, - "isDeleted": false, - "id": "US-PSZA6_UT_z_spAryVq", - "fillStyle": "hachure", - "strokeWidth": 1, - "strokeStyle": "solid", - "roughness": 1, - "opacity": 100, - "angle": 0, - "x": 881.0553396856625, - "y": 1477.9512520454418, - "strokeColor": "#000000", - "backgroundColor": "transparent", - "width": 0, - "height": 34.31975968008919, - "seed": 717559134, - "groupIds": [], - "strokeSharpness": "round", - "boundElements": [], - "updated": 1641948455777, - "startBinding": null, - "endBinding": null, - "lastCommittedPoint": null, - "startArrowhead": null, - "endArrowhead": null, - "points": [ - [ - 0, - 0 - ], - [ - 0, - 34.31975968008919 - ] - ] - }, - { - "type": "line", - "version": 814, - "versionNonce": 1313119198, - "isDeleted": false, - "id": "h3nfhOKNfGpCZMs8_nP8b", - "fillStyle": "hachure", - "strokeWidth": 1, - "strokeStyle": "solid", - "roughness": 1, - "opacity": 100, - "angle": 0, - "x": 1642.5139379373118, - "y": 1392.7900933483634, - "strokeColor": "#000000", - "backgroundColor": "transparent", - "width": 0, - "height": 30.545426859719328, - "seed": 1953011010, - "groupIds": [], - "strokeSharpness": "round", - "boundElements": [], - "updated": 1641948455777, - "startBinding": null, - "endBinding": null, - "lastCommittedPoint": null, - "startArrowhead": null, - "endArrowhead": null, - "points": [ - [ - 0, - 0 - ], - [ - 0, - 30.545426859719328 - ] - ] - }, - { - "type": "line", - "version": 1118, - "versionNonce": 449452290, - "isDeleted": false, - "id": "QjjbF35QLBSjRh2Uhiq4e", - "fillStyle": "hachure", - "strokeWidth": 1, - "strokeStyle": "solid", - "roughness": 1, - "opacity": 100, - "angle": 0, - "x": 1847.409196206294, - "y": 1424.357491467486, - "strokeColor": "#000000", - "backgroundColor": "transparent", - "width": 409.7726291513309, - "height": 0, - "seed": 1409800158, - "groupIds": [], - "strokeSharpness": "round", - "boundElements": [], - "updated": 1641948455777, - "startBinding": null, - "endBinding": null, - "lastCommittedPoint": null, - "startArrowhead": null, - "endArrowhead": null, - "points": [ - [ - 0, - 0 - ], - [ - -409.7726291513309, - 0 - ] - ] - }, - { - "type": "text", - "version": 915, - "versionNonce": 1631678494, - "isDeleted": false, - "id": "FRAEyymDWFym3AmT-mQdc", - "fillStyle": "hachure", - "strokeWidth": 1, - "strokeStyle": "solid", - "roughness": 1, - "opacity": 100, - "angle": 0, - "x": 1802.1397672002695, - "y": 1394.857175557787, - "strokeColor": "#000000", - "backgroundColor": "transparent", - "width": 20, - "height": 19, - "seed": 1128082462, - "groupIds": [], - "strokeSharpness": "sharp", - "boundElements": [], - "updated": 1641948455777, - "fontSize": 16, - "fontFamily": 3, - "text": "NO", - "baseline": 15, - "textAlign": "center", - "verticalAlign": "top", - "containerId": null, - "originalText": "NO" - }, - { - "type": "line", - "version": 838, - "versionNonce": 1642309826, - "isDeleted": false, - "id": "CDq3V6c7Pmbpwxe4acXk5", - "fillStyle": "hachure", - "strokeWidth": 1, - "strokeStyle": "solid", - "roughness": 1, - "opacity": 100, - "angle": 0, - "x": 1846.1406063275767, - "y": 1425.3107438467669, - "strokeColor": "#000000", - "backgroundColor": "transparent", - "width": 0, - "height": 42.26804123711344, - "seed": 2073144514, - "groupIds": [], - "strokeSharpness": "round", - "boundElements": [], - "updated": 1641948455777, - "startBinding": null, - "endBinding": null, - "lastCommittedPoint": null, - "startArrowhead": null, - "endArrowhead": null, - "points": [ - [ - 0, - 0 - ], - [ - 0, - 42.26804123711344 - ] - ] - }, - { - "type": "text", - "version": 1149, - "versionNonce": 1578744926, - "isDeleted": false, - "id": "hM1wTAvGc1wtJsLf0i7Kf", - "fillStyle": "hachure", - "strokeWidth": 1, - "strokeStyle": "solid", - "roughness": 1, - "opacity": 100, - "angle": 0, - "x": 1705.3908622494707, - "y": 1489.6169527801976, - "strokeColor": "#000000", - "backgroundColor": "transparent", - "width": 282, - "height": 24, - "seed": 20838494, - "groupIds": [], - "strokeSharpness": "sharp", - "boundElements": [], - "updated": 1641948455777, - "fontSize": 20, - "fontFamily": 3, - "text": "Keep open for discussion", - "baseline": 19, - "textAlign": "center", - "verticalAlign": "top", - "containerId": null, - "originalText": "Keep open for discussion" - }, - { - "type": "text", - "version": 1326, - "versionNonce": 1991590018, - "isDeleted": false, - "id": "fbQ5BH5Mm4CFml4NHSw9n", - "fillStyle": "hachure", - "strokeWidth": 1, - "strokeStyle": "solid", - "roughness": 1, - "opacity": 100, - "angle": 0, - "x": 1709.8908622494707, - "y": 1526.6169527801976, - "strokeColor": "#000000", - "backgroundColor": "transparent", - "width": 273, - "height": 19, - "seed": 366689438, - "groupIds": [], - "strokeSharpness": "sharp", - "boundElements": [], - "updated": 1641948455777, - "fontSize": 16, - "fontFamily": 3, - "text": "Remove \"pending triage\" label", - "baseline": 15, - "textAlign": "center", - "verticalAlign": "top", - "containerId": null, - "originalText": "Remove \"pending triage\" label" - }, - { - "type": "line", - "version": 991, - "versionNonce": 1788772510, - "isDeleted": false, - "id": "MS1ddNWaIQ5MEW6BHgdIo", - "fillStyle": "hachure", - "strokeWidth": 1, - "strokeStyle": "solid", - "roughness": 1, - "opacity": 100, - "angle": 0, - "x": 1437.4406369309029, - "y": 1426.8510831378217, - "strokeColor": "#000000", - "backgroundColor": "transparent", - "width": 0, - "height": 42.26804123711344, - "seed": 1072557086, - "groupIds": [], - "strokeSharpness": "round", - "boundElements": [], - "updated": 1641948455777, - "startBinding": null, - "endBinding": null, - "lastCommittedPoint": null, - "startArrowhead": null, - "endArrowhead": null, - "points": [ - [ - 0, - 0 - ], - [ - 0, - 42.26804123711344 - ] - ] - }, - { - "type": "text", - "version": 1185, - "versionNonce": 1962795074, - "isDeleted": false, - "id": "0qyN9aJ3ZVkpqPkKuy_fS", - "fillStyle": "hachure", - "strokeWidth": 1, - "strokeStyle": "solid", - "roughness": 1, - "opacity": 100, - "angle": 0, - "x": 1337.2462614139467, - "y": 1489.5018785478514, - "strokeColor": "#000000", - "backgroundColor": "transparent", - "width": 200, - "height": 24, - "seed": 552182530, - "groupIds": [], - "strokeSharpness": "sharp", - "boundElements": [], - "updated": 1641948455777, - "fontSize": 20, - "fontFamily": 3, - "text": "Explain and close", - "baseline": 19, - "textAlign": "center", - "verticalAlign": "top", - "containerId": null, - "originalText": "Explain and close" - }, - { - "type": "text", - "version": 1359, - "versionNonce": 1249950942, - "isDeleted": false, - "id": "_RMV2vqzS5jLLOkBklyf3", - "fillStyle": "hachure", - "strokeWidth": 1, - "strokeStyle": "solid", - "roughness": 1, - "opacity": 100, - "angle": 0, - "x": 1328.7462614139467, - "y": 1526.5018785478514, - "strokeColor": "#000000", - "backgroundColor": "transparent", - "width": 217, - "height": 19, - "seed": 1983350302, - "groupIds": [], - "strokeSharpness": "sharp", - "boundElements": [], - "updated": 1641948455777, - "fontSize": 16, - "fontFamily": 3, - "text": "point to docs if needed", - "baseline": 15, - "textAlign": "center", - "verticalAlign": "top", - "containerId": null, - "originalText": "point to docs if needed" - }, - { - "type": "rectangle", - "version": 1033, - "versionNonce": 681347074, - "isDeleted": false, - "id": "tO_0BspEaH0efc-UgMWhM", - "fillStyle": "hachure", - "strokeWidth": 1, - "strokeStyle": "solid", - "roughness": 1, - "opacity": 100, - "angle": 0, - "x": 1281.8598911343533, - "y": 1468.2041515574156, - "strokeColor": "#000000", - "backgroundColor": "transparent", - "width": 310.7727405591866, - "height": 101.30979521678366, - "seed": 1333916354, - "groupIds": [], - "strokeSharpness": "sharp", - "boundElements": [], - "updated": 1641948455777 - }, - { - "type": "text", - "version": 986, - "versionNonce": 592539934, - "isDeleted": false, - "id": "ggYg6ky4YdpuaDP4UQ-GM", - "fillStyle": "hachure", - "strokeWidth": 1, - "strokeStyle": "solid", - "roughness": 1, - "opacity": 100, - "angle": 0, - "x": 1459.1969387686004, - "y": 1396.3068525502197, - "strokeColor": "#000000", - "backgroundColor": "transparent", - "width": 29, - "height": 19, - "seed": 1126673054, - "groupIds": [], - "strokeSharpness": "sharp", - "boundElements": [], - "updated": 1641948455777, - "fontSize": 16, - "fontFamily": 3, - "text": "YES", - "baseline": 15, - "textAlign": "center", - "verticalAlign": "top", - "containerId": null, - "originalText": "YES" - }, - { - "type": "rectangle", - "version": 1213, - "versionNonce": 160446402, - "isDeleted": false, - "id": "Si230clokwDoUey4FowxX", - "fillStyle": "hachure", - "strokeWidth": 1, - "strokeStyle": "solid", - "roughness": 1, - "opacity": 100, - "angle": 0, - "x": 1668.961481217189, - "y": 1469.4055183326752, - "strokeColor": "#000000", - "backgroundColor": "transparent", - "width": 354.8587620645635, - "height": 101.30979521678366, - "seed": 345483230, - "groupIds": [], - "strokeSharpness": "sharp", - "boundElements": [], - "updated": 1641948455777 - }, - { - "type": "line", - "version": 675, - "versionNonce": 1082236254, - "isDeleted": false, - "id": "Wg6zEEc2hZHw309saLOjA", - "fillStyle": "hachure", - "strokeWidth": 1, - "strokeStyle": "solid", - "roughness": 1, - "opacity": 100, - "angle": 0, - "x": 882.6534155876153, - "y": 1671.162110835381, - "strokeColor": "#000000", - "backgroundColor": "transparent", - "width": 0, - "height": 100.47284166624985, - "seed": 775120350, - "groupIds": [], - "strokeSharpness": "round", - "boundElements": [], - "updated": 1641948455777, - "startBinding": null, - "endBinding": null, - "lastCommittedPoint": null, - "startArrowhead": null, - "endArrowhead": null, - "points": [ - [ - 0, - 0 - ], - [ - 0, - 100.47284166624985 - ] - ] - }, - { - "type": "line", - "version": 886, - "versionNonce": 938995586, - "isDeleted": false, - "id": "KwcnA5g6GzWSfwNGWT2zQ", - "fillStyle": "hachure", - "strokeWidth": 1, - "strokeStyle": "solid", - "roughness": 1, - "opacity": 100, - "angle": 0, - "x": 1452.906386425121, - "y": 1702.7295089545037, - "strokeColor": "#000000", - "backgroundColor": "transparent", - "width": 570.3978560169538, - "height": 0, - "seed": 226815746, - "groupIds": [], - "strokeSharpness": "round", - "boundElements": [], - "updated": 1641948455777, - "startBinding": null, - "endBinding": null, - "lastCommittedPoint": null, - "startArrowhead": null, - "endArrowhead": null, - "points": [ - [ - 0, - 0 - ], - [ - -570.3978560169538, - 0 - ] - ] - }, - { - "type": "text", - "version": 807, - "versionNonce": 1466057118, - "isDeleted": false, - "id": "VX-xsCbPecgdaJ41sJs5G", - "fillStyle": "hachure", - "strokeWidth": 1, - "strokeStyle": "solid", - "roughness": 1, - "opacity": 100, - "angle": 0, - "x": 838.6789256531275, - "y": 1742.7622527803273, - "strokeColor": "#000000", - "backgroundColor": "transparent", - "width": 29, - "height": 19, - "seed": 751237662, - "groupIds": [], - "strokeSharpness": "sharp", - "boundElements": [], - "updated": 1641948455777, - "fontSize": 16, - "fontFamily": 3, - "text": "YES", - "baseline": 15, - "textAlign": "center", - "verticalAlign": "top", - "containerId": null, - "originalText": "YES" - }, - { - "type": "text", - "version": 898, - "versionNonce": 1576022786, - "isDeleted": false, - "id": "yHflVaBJQqmGQRkR9GXOe", - "fillStyle": "hachure", - "strokeWidth": 1, - "strokeStyle": "solid", - "roughness": 1, - "opacity": 100, - "angle": 0, - "x": 765.0074077740048, - "y": 1815.543511883816, - "strokeColor": "#000000", - "backgroundColor": "transparent", - "width": 234, - "height": 72, - "seed": 97094302, - "groupIds": [], - "strokeSharpness": "sharp", - "boundElements": [], - "updated": 1641948755258, - "fontSize": 20, - "fontFamily": 3, - "text": "Does the bug\naffects the majority\nof users?", - "baseline": 67, - "textAlign": "center", - "verticalAlign": "top", - "containerId": null, - "originalText": "Does the bug\naffects the majority\nof users?" - }, - { - "type": "diamond", - "version": 673, - "versionNonce": 1260674526, - "isDeleted": false, - "id": "RfN8aFfNbf6juWW5DWt6M", - "fillStyle": "hachure", - "strokeWidth": 1, - "strokeStyle": "solid", - "roughness": 1, - "opacity": 100, - "angle": 0, - "x": 736.9567748626122, - "y": 1770.6268364993616, - "strokeColor": "#000000", - "backgroundColor": "transparent", - "width": 290.1012658227851, - "height": 161.833350768909, - "seed": 532317918, - "groupIds": [], - "strokeSharpness": "sharp", - "boundElements": [], - "updated": 1641948455777 - }, - { - "type": "line", - "version": 823, - "versionNonce": 775911170, - "isDeleted": false, - "id": "muvErataGmDyY0E5WCO-1", - "fillStyle": "hachure", - "strokeWidth": 1, - "strokeStyle": "solid", - "roughness": 1, - "opacity": 100, - "angle": 0, - "x": 882.9166281024866, - "y": 1933.6215702948407, - "strokeColor": "#000000", - "backgroundColor": "transparent", - "width": 0, - "height": 100.47284166624985, - "seed": 1826686366, - "groupIds": [], - "strokeSharpness": "round", - "boundElements": [], - "updated": 1641948455778, - "startBinding": null, - "endBinding": null, - "lastCommittedPoint": null, - "startArrowhead": null, - "endArrowhead": null, - "points": [ - [ - 0, - 0 - ], - [ - 0, - 100.47284166624985 - ] - ] - }, - { - "type": "line", - "version": 981, - "versionNonce": 1893265950, - "isDeleted": false, - "id": "HsO3NfLz8GC4Dz7iQIKkD", - "fillStyle": "hachure", - "strokeWidth": 1, - "strokeStyle": "solid", - "roughness": 1, - "opacity": 100, - "angle": 0, - "x": 1089.8528549664418, - "y": 1965.1889684139633, - "strokeColor": "#000000", - "backgroundColor": "transparent", - "width": 207.08111204340355, - "height": 0, - "seed": 298986306, - "groupIds": [], - "strokeSharpness": "round", - "boundElements": [], - "updated": 1641948455778, - "startBinding": null, - "endBinding": null, - "lastCommittedPoint": null, - "startArrowhead": null, - "endArrowhead": null, - "points": [ - [ - 0, - 0 - ], - [ - -207.08111204340355, - 0 - ] - ] - }, - { - "type": "text", - "version": 961, - "versionNonce": 1526548162, - "isDeleted": false, - "id": "kgPR6NfHGPY3o5ftAF847", - "fillStyle": "hachure", - "strokeWidth": 1, - "strokeStyle": "solid", - "roughness": 1, - "opacity": 100, - "angle": 0, - "x": 832.9421381679988, - "y": 2005.221712239787, - "strokeColor": "#000000", - "backgroundColor": "transparent", - "width": 29, - "height": 19, - "seed": 1277862366, - "groupIds": [], - "strokeSharpness": "sharp", - "boundElements": [], - "updated": 1641948455778, - "fontSize": 16, - "fontFamily": 3, - "text": "YES", - "baseline": 15, - "textAlign": "center", - "verticalAlign": "top", - "containerId": null, - "originalText": "YES" - }, - { - "type": "text", - "version": 1100, - "versionNonce": 1399914078, - "isDeleted": false, - "id": "MafZIHtvFdt6Znj_rF3o4", - "fillStyle": "hachure", - "strokeWidth": 1, - "strokeStyle": "solid", - "roughness": 1, - "opacity": 100, - "angle": 0, - "x": 1113.6939725169598, - "y": 2003.1608159996838, - "strokeColor": "#000000", - "backgroundColor": "transparent", - "width": 20, - "height": 19, - "seed": 1521447682, - "groupIds": [], - "strokeSharpness": "sharp", - "boundElements": [], - "updated": 1641948455778, - "fontSize": 16, - "fontFamily": 3, - "text": "NO", - "baseline": 15, - "textAlign": "center", - "verticalAlign": "top", - "containerId": null, - "originalText": "NO" - }, - { - "type": "line", - "version": 951, - "versionNonce": 384507522, - "isDeleted": false, - "id": "CKWqmxPG_A6nwwP-Qmfce", - "fillStyle": "hachure", - "strokeWidth": 1, - "strokeStyle": "solid", - "roughness": 1, - "opacity": 100, - "angle": 0, - "x": 1090.5432964927518, - "y": 1964.6270692780927, - "strokeColor": "#000000", - "backgroundColor": "transparent", - "width": 0, - "height": 68.9389053628438, - "seed": 7779870, - "groupIds": [], - "strokeSharpness": "round", - "boundElements": [], - "updated": 1641948455778, - "startBinding": null, - "endBinding": null, - "lastCommittedPoint": null, - "startArrowhead": null, - "endArrowhead": null, - "points": [ - [ - 0, - 0 - ], - [ - 0, - 68.9389053628438 - ] - ] - }, - { - "type": "text", - "version": 1290, - "versionNonce": 2008908446, - "isDeleted": false, - "id": "jFG-MF9igR98DJ-5OzIso", - "fillStyle": "hachure", - "strokeWidth": 1, - "strokeStyle": "solid", - "roughness": 1, - "opacity": 100, - "angle": 0, - "x": 786.7396231515502, - "y": 2056.7331392238157, - "strokeColor": "#000000", - "backgroundColor": "transparent", - "width": 118, - "height": 24, - "seed": 981243074, - "groupIds": [], - "strokeSharpness": "sharp", - "boundElements": [], - "updated": 1641948455778, - "fontSize": 20, - "fontFamily": 3, - "text": "p5: urgent", - "baseline": 19, - "textAlign": "center", - "verticalAlign": "top", - "containerId": null, - "originalText": "p5: urgent" - }, - { - "type": "rectangle", - "version": 1160, - "versionNonce": 247987778, - "isDeleted": false, - "id": "eHAFrOXDQ9bl6njKEHOO3", - "fillStyle": "hachure", - "strokeWidth": 1, - "strokeStyle": "solid", - "roughness": 1, - "opacity": 100, - "angle": 0, - "x": 741.1108286295326, - "y": 2033.9873325245144, - "strokeColor": "#000000", - "backgroundColor": "transparent", - "width": 209.257589044035, - "height": 69.49161339860213, - "seed": 626044034, - "groupIds": [], - "strokeSharpness": "sharp", - "boundElements": [], - "updated": 1641948455778 - }, - { - "type": "text", - "version": 1405, - "versionNonce": 544177886, - "isDeleted": false, - "id": "QfpGXu6cKkYc0l3P3kSbI", - "fillStyle": "hachure", - "strokeWidth": 1, - "strokeStyle": "solid", - "roughness": 1, - "opacity": 100, - "angle": 0, - "x": 1053.0691719277108, - "y": 2056.9149574056346, - "strokeColor": "#000000", - "backgroundColor": "transparent", - "width": 153, - "height": 24, - "seed": 1774456990, - "groupIds": [], - "strokeSharpness": "sharp", - "boundElements": [], - "updated": 1641948455778, - "fontSize": 20, - "fontFamily": 3, - "text": "p4: important", - "baseline": 19, - "textAlign": "center", - "verticalAlign": "top", - "containerId": null, - "originalText": "p4: important" - }, - { - "type": "rectangle", - "version": 1277, - "versionNonce": 2138236418, - "isDeleted": false, - "id": "IKZeMSoWRnegv9VYaXQ46", - "fillStyle": "hachure", - "strokeWidth": 1, - "strokeStyle": "solid", - "roughness": 1, - "opacity": 100, - "angle": 0, - "x": 1012.8191652844812, - "y": 2034.1691507063333, - "strokeColor": "#000000", - "backgroundColor": "transparent", - "width": 233.5000132864592, - "height": 69.49161339860213, - "seed": 280580162, - "groupIds": [], - "strokeSharpness": "sharp", - "boundElements": [], - "updated": 1641948455778 - }, - { - "type": "line", - "version": 649, - "versionNonce": 303559454, - "isDeleted": false, - "id": "nU4yBZdIQUUj3iFUrAKSS", - "fillStyle": "hachure", - "strokeWidth": 1, - "strokeStyle": "solid", - "roughness": 1, - "opacity": 100, - "angle": 0, - "x": 1163.7694127003945, - "y": 634.5772159715862, - "strokeColor": "#000000", - "backgroundColor": "transparent", - "width": 279.04014344843074, - "height": 0, - "seed": 476863362, - "groupIds": [], - "strokeSharpness": "round", - "boundElements": [], - "updated": 1641948455778, - "startBinding": null, - "endBinding": null, - "lastCommittedPoint": null, - "startArrowhead": null, - "endArrowhead": null, - "points": [ - [ - 0, - 0 - ], - [ - -279.04014344843074, - 0 - ] - ] - }, - { - "type": "text", - "version": 915, - "versionNonce": 837473730, - "isDeleted": false, - "id": "dPbyUUnJ74BiBtR08NNNr", - "fillStyle": "hachure", - "strokeWidth": 1, - "strokeStyle": "solid", - "roughness": 1, - "opacity": 100, - "angle": 0, - "x": 1184.666650361036, - "y": 660.9733059815493, - "strokeColor": "#000000", - "backgroundColor": "transparent", - "width": 29, - "height": 19, - "seed": 1941607838, - "groupIds": [], - "strokeSharpness": "sharp", - "boundElements": [], - "updated": 1641948455778, - "fontSize": 16, - "fontFamily": 3, - "text": "YES", - "baseline": 15, - "textAlign": "center", - "verticalAlign": "top", - "containerId": null, - "originalText": "YES" - }, - { - "type": "line", - "version": 628, - "versionNonce": 1462230878, - "isDeleted": false, - "id": "CBYnQSD2RkkXY-KjUY9XS", - "fillStyle": "hachure", - "strokeWidth": 1, - "strokeStyle": "solid", - "roughness": 1, - "opacity": 100, - "angle": 0, - "x": 1163.5008228216768, - "y": 635.5304683508673, - "strokeColor": "#000000", - "backgroundColor": "transparent", - "width": 0, - "height": 102.89551452317858, - "seed": 723098434, - "groupIds": [], - "strokeSharpness": "round", - "boundElements": [], - "updated": 1641948455778, - "startBinding": null, - "endBinding": null, - "lastCommittedPoint": null, - "startArrowhead": null, - "endArrowhead": null, - "points": [ - [ - 0, - 0 - ], - [ - 0, - 102.89551452317858 - ] - ] - }, - { - "type": "line", - "version": 1021, - "versionNonce": 2001653122, - "isDeleted": false, - "id": "l8yR5sBqXptSOVbigUPqh", - "fillStyle": "hachure", - "strokeWidth": 1, - "strokeStyle": "solid", - "roughness": 1, - "opacity": 100, - "angle": 0, - "x": 1454.4330956470337, - "y": 1700.4575046257976, - "strokeColor": "#000000", - "backgroundColor": "transparent", - "width": 0, - "height": 71.34763447797354, - "seed": 1398920734, - "groupIds": [], - "strokeSharpness": "round", - "boundElements": [], - "updated": 1641948455778, - "startBinding": null, - "endBinding": null, - "lastCommittedPoint": null, - "startArrowhead": null, - "endArrowhead": null, - "points": [ - [ - 0, - 0 - ], - [ - 0, - 71.34763447797354 - ] - ] - }, - { - "type": "text", - "version": 1146, - "versionNonce": 66077598, - "isDeleted": false, - "id": "oQa8WA77NVi3AEKLqB4nT", - "fillStyle": "hachure", - "strokeWidth": 1, - "strokeStyle": "solid", - "roughness": 1, - "opacity": 100, - "angle": 0, - "x": 1411.958605712546, - "y": 1742.932439382468, - "strokeColor": "#000000", - "backgroundColor": "transparent", - "width": 20, - "height": 19, - "seed": 1826923202, - "groupIds": [], - "strokeSharpness": "sharp", - "boundElements": [], - "updated": 1641948455778, - "fontSize": 16, - "fontFamily": 3, - "text": "NO", - "baseline": 15, - "textAlign": "center", - "verticalAlign": "top", - "containerId": null, - "originalText": "NO" - }, - { - "type": "text", - "version": 1254, - "versionNonce": 618515778, - "isDeleted": false, - "id": "nPokHFX6K-pxacGCTVYh7", - "fillStyle": "hachure", - "strokeWidth": 1, - "strokeStyle": "solid", - "roughness": 1, - "opacity": 100, - "angle": 0, - "x": 1365.787087833423, - "y": 1815.7136984859567, - "strokeColor": "#000000", - "backgroundColor": "transparent", - "width": 176, - "height": 72, - "seed": 993878622, - "groupIds": [], - "strokeSharpness": "sharp", - "boundElements": [], - "updated": 1641948455778, - "fontSize": 20, - "fontFamily": 3, - "text": "Are there\nworkarounds for\nthe bug?", - "baseline": 67, - "textAlign": "center", - "verticalAlign": "top", - "containerId": null, - "originalText": "Are there\nworkarounds for\nthe bug?" - }, - { - "type": "diamond", - "version": 1004, - "versionNonce": 2057445342, - "isDeleted": false, - "id": "lsD6TTbP-wED13SQGEYfA", - "fillStyle": "hachure", - "strokeWidth": 1, - "strokeStyle": "solid", - "roughness": 1, - "opacity": 100, - "angle": 0, - "x": 1308.7364549220304, - "y": 1770.7970231015022, - "strokeColor": "#000000", - "backgroundColor": "transparent", - "width": 290.1012658227851, - "height": 161.833350768909, - "seed": 730266242, - "groupIds": [], - "strokeSharpness": "sharp", - "boundElements": [], - "updated": 1641948455778 - }, - { - "type": "line", - "version": 1154, - "versionNonce": 267834626, - "isDeleted": false, - "id": "Un2yWLkc5__AjClolDNQC", - "fillStyle": "hachure", - "strokeWidth": 1, - "strokeStyle": "solid", - "roughness": 1, - "opacity": 100, - "angle": 0, - "x": 1454.6963081619049, - "y": 1933.7917568969813, - "strokeColor": "#000000", - "backgroundColor": "transparent", - "width": 0, - "height": 100.47284166624985, - "seed": 1885169310, - "groupIds": [], - "strokeSharpness": "round", - "boundElements": [], - "updated": 1641948455778, - "startBinding": null, - "endBinding": null, - "lastCommittedPoint": null, - "startArrowhead": null, - "endArrowhead": null, - "points": [ - [ - 0, - 0 - ], - [ - 0, - 100.47284166624985 - ] - ] - }, - { - "type": "line", - "version": 1336, - "versionNonce": 793670686, - "isDeleted": false, - "id": "3wTe3JwvBpm607Y1XB2m3", - "fillStyle": "hachure", - "strokeWidth": 1, - "strokeStyle": "solid", - "roughness": 1, - "opacity": 100, - "angle": 0, - "x": 1726.916387113522, - "y": 1965.3591550161034, - "strokeColor": "#000000", - "backgroundColor": "transparent", - "width": 272.36496413106465, - "height": 0, - "seed": 1535662658, - "groupIds": [], - "strokeSharpness": "round", - "boundElements": [], - "updated": 1641948455778, - "startBinding": null, - "endBinding": null, - "lastCommittedPoint": null, - "startArrowhead": null, - "endArrowhead": null, - "points": [ - [ - 0, - 0 - ], - [ - -272.36496413106465, - 0 - ] - ] - }, - { - "type": "text", - "version": 1292, - "versionNonce": 169403586, - "isDeleted": false, - "id": "pMnpI2Mh04kP31FrMqvFa", - "fillStyle": "hachure", - "strokeWidth": 1, - "strokeStyle": "solid", - "roughness": 1, - "opacity": 100, - "angle": 0, - "x": 1404.721818227417, - "y": 2005.391898841927, - "strokeColor": "#000000", - "backgroundColor": "transparent", - "width": 29, - "height": 19, - "seed": 1131602654, - "groupIds": [], - "strokeSharpness": "sharp", - "boundElements": [], - "updated": 1641948455778, - "fontSize": 16, - "fontFamily": 3, - "text": "YES", - "baseline": 15, - "textAlign": "center", - "verticalAlign": "top", - "containerId": null, - "originalText": "YES" - }, - { - "type": "text", - "version": 1450, - "versionNonce": 1810251870, - "isDeleted": false, - "id": "OcbZ7xCL2DhsyqpBsDsC1", - "fillStyle": "hachure", - "strokeWidth": 1, - "strokeStyle": "solid", - "roughness": 1, - "opacity": 100, - "angle": 0, - "x": 1748.4736525763788, - "y": 2003.3310026018241, - "strokeColor": "#000000", - "backgroundColor": "transparent", - "width": 20, - "height": 19, - "seed": 197640706, - "groupIds": [], - "strokeSharpness": "sharp", - "boundElements": [], - "updated": 1641948455778, - "fontSize": 16, - "fontFamily": 3, - "text": "NO", - "baseline": 15, - "textAlign": "center", - "verticalAlign": "top", - "containerId": null, - "originalText": "NO" - }, - { - "type": "line", - "version": 1301, - "versionNonce": 1558379650, - "isDeleted": false, - "id": "VShYhytzLb0pAjVSz_nlX", - "fillStyle": "hachure", - "strokeWidth": 1, - "strokeStyle": "solid", - "roughness": 1, - "opacity": 100, - "angle": 0, - "x": 1725.322976552171, - "y": 1964.7972558802328, - "strokeColor": "#000000", - "backgroundColor": "transparent", - "width": 0, - "height": 68.9389053628438, - "seed": 1192154910, - "groupIds": [], - "strokeSharpness": "round", - "boundElements": [], - "updated": 1641948455778, - "startBinding": null, - "endBinding": null, - "lastCommittedPoint": null, - "startArrowhead": null, - "endArrowhead": null, - "points": [ - [ - 0, - 0 - ], - [ - 0, - 68.9389053628438 - ] - ] - }, - { - "type": "text", - "version": 1642, - "versionNonce": 339379358, - "isDeleted": false, - "id": "x9tibphUwKZc-RUn7QLZL", - "fillStyle": "hachure", - "strokeWidth": 1, - "strokeStyle": "solid", - "roughness": 1, - "opacity": 100, - "angle": 0, - "x": 1350.9132426049077, - "y": 2056.903325825955, - "strokeColor": "#000000", - "backgroundColor": "transparent", - "width": 212, - "height": 24, - "seed": 390345154, - "groupIds": [], - "strokeSharpness": "sharp", - "boundElements": [], - "updated": 1641948455778, - "fontSize": 20, - "fontFamily": 3, - "text": "p2: has workaround", - "baseline": 19, - "textAlign": "center", - "verticalAlign": "top", - "containerId": null, - "originalText": "p2: has workaround" - }, - { - "type": "rectangle", - "version": 1536, - "versionNonce": 1175723074, - "isDeleted": false, - "id": "yQ1aFpIR9EQqb7VNHXXf8", - "fillStyle": "hachure", - "strokeWidth": 1, - "strokeStyle": "solid", - "roughness": 1, - "opacity": 100, - "angle": 0, - "x": 1311.3753571737996, - "y": 2034.1575191266538, - "strokeColor": "#000000", - "backgroundColor": "transparent", - "width": 291.0757708622167, - "height": 69.49161339860213, - "seed": 329401182, - "groupIds": [], - "strokeSharpness": "sharp", - "boundElements": [], - "updated": 1641948455778 - }, - { - "type": "text", - "version": 1777, - "versionNonce": 1778139358, - "isDeleted": false, - "id": "_SRs00rbZEDk1zn6_Vvzg", - "fillStyle": "hachure", - "strokeWidth": 1, - "strokeStyle": "solid", - "roughness": 1, - "opacity": 100, - "angle": 0, - "x": 1699.84885198713, - "y": 2057.085144007774, - "strokeColor": "#000000", - "backgroundColor": "transparent", - "width": 153, - "height": 24, - "seed": 555526530, - "groupIds": [], - "strokeSharpness": "sharp", - "boundElements": [], - "updated": 1641948455778, - "fontSize": 20, - "fontFamily": 3, - "text": "p3: minor bug", - "baseline": 19, - "textAlign": "center", - "verticalAlign": "top", - "containerId": null, - "originalText": "p3: minor bug" - }, - { - "type": "rectangle", - "version": 1623, - "versionNonce": 1938506754, - "isDeleted": false, - "id": "6dcJQjVFo_xqCMRkmOoSZ", - "fillStyle": "hachure", - "strokeWidth": 1, - "strokeStyle": "solid", - "roughness": 1, - "opacity": 100, - "angle": 0, - "x": 1659.5988453439004, - "y": 2034.3393373084728, - "strokeColor": "#000000", - "backgroundColor": "transparent", - "width": 233.5000132864592, - "height": 69.49161339860213, - "seed": 192817054, - "groupIds": [], - "strokeSharpness": "sharp", - "boundElements": [], - "updated": 1641948455778 - } - ], - "appState": { - "gridSize": null, - "viewBackgroundColor": "#ffffff" - }, - "files": {} -} \ No newline at end of file diff --git a/.github/issue-workflow.png b/.github/issue-workflow.png deleted file mode 100644 index 92b1de0633c229..00000000000000 Binary files a/.github/issue-workflow.png and /dev/null differ diff --git a/.github/pr-workflow-dark.png b/.github/pr-workflow-dark.png deleted file mode 100644 index 35a53f29637201..00000000000000 Binary files a/.github/pr-workflow-dark.png and /dev/null differ diff --git a/.github/pr-workflow.excalidraw b/.github/pr-workflow.excalidraw deleted file mode 100644 index bd85d16edf1749..00000000000000 --- a/.github/pr-workflow.excalidraw +++ /dev/null @@ -1,1954 +0,0 @@ -{ - "type": "excalidraw", - "version": 2, - "source": "https://excalidraw.com", - "elements": [ - { - "type": "diamond", - "version": 264, - "versionNonce": 290598110, - "isDeleted": false, - "id": "sQbLVqjpgdZXg0DrdESrv", - "fillStyle": "hachure", - "strokeWidth": 1, - "strokeStyle": "solid", - "roughness": 1, - "opacity": 100, - "angle": 0, - "x": 735.9493670886075, - "y": 178.5833246155455, - "strokeColor": "#000000", - "backgroundColor": "transparent", - "width": 290.1012658227851, - "height": 161.833350768909, - "seed": 177114754, - "groupIds": [], - "strokeSharpness": "sharp", - "boundElements": [], - "updated": 1641947086786 - }, - { - "type": "text", - "version": 205, - "versionNonce": 98179074, - "isDeleted": false, - "id": "Zo2OUf2C5ZeVhoM2q7Uni", - "fillStyle": "hachure", - "strokeWidth": 1, - "strokeStyle": "solid", - "roughness": 1, - "opacity": 100, - "angle": 0, - "x": 816, - "y": 235.5, - "strokeColor": "#000000", - "backgroundColor": "transparent", - "width": 130, - "height": 48, - "seed": 478396126, - "groupIds": [], - "strokeSharpness": "sharp", - "boundElements": [], - "updated": 1641947086786, - "fontSize": 20, - "fontFamily": 3, - "text": "Bug fix\nor feature?", - "baseline": 43, - "textAlign": "center", - "verticalAlign": "top", - "containerId": null, - "originalText": "Bug fix\nor feature?" - }, - { - "type": "text", - "version": 549, - "versionNonce": 2016741122, - "isDeleted": false, - "id": "-w-lI8s8rNBbK0EDBgrjg", - "fillStyle": "hachure", - "strokeWidth": 1, - "strokeStyle": "solid", - "roughness": 1, - "opacity": 100, - "angle": 0, - "x": 342.29080030842067, - "y": 499.5156029443476, - "strokeColor": "#000000", - "backgroundColor": "transparent", - "width": 246, - "height": 96, - "seed": 1427727810, - "groupIds": [], - "strokeSharpness": "sharp", - "boundElements": [], - "updated": 1641947086786, - "fontSize": 20, - "fontFamily": 3, - "text": "Is a \"strict fix\"\ni.e. fixes an obvious\noversight with no\nside effects?", - "baseline": 91, - "textAlign": "center", - "verticalAlign": "top", - "containerId": null, - "originalText": "Is a \"strict fix\"\ni.e. fixes an obvious\noversight with no\nside effects?" - }, - { - "type": "diamond", - "version": 608, - "versionNonce": 2122822558, - "isDeleted": false, - "id": "EjTMjKTsLZaR41AMwsmNy", - "fillStyle": "hachure", - "strokeWidth": 1, - "strokeStyle": "solid", - "roughness": 1, - "opacity": 100, - "angle": 0, - "x": 272.6763813887976, - "y": 440.0654105718307, - "strokeColor": "#000000", - "backgroundColor": "transparent", - "width": 385.22883783924595, - "height": 214.90038474503385, - "seed": 1803796226, - "groupIds": [], - "strokeSharpness": "sharp", - "boundElements": [], - "updated": 1641947086786 - }, - { - "type": "line", - "version": 1246, - "versionNonce": 1051442078, - "isDeleted": false, - "id": "YDKz6FIMNIlyOXN9eOsmk", - "fillStyle": "hachure", - "strokeWidth": 1, - "strokeStyle": "solid", - "roughness": 1, - "opacity": 100, - "angle": 0, - "x": 698.1506535081975, - "y": 685.3181565485763, - "strokeColor": "#000000", - "backgroundColor": "transparent", - "width": 456.28561526150804, - "height": 0, - "seed": 1533033346, - "groupIds": [], - "strokeSharpness": "round", - "boundElements": [], - "updated": 1641947086787, - "startBinding": null, - "endBinding": null, - "lastCommittedPoint": null, - "startArrowhead": null, - "endArrowhead": null, - "points": [ - [ - 0, - 0 - ], - [ - -456.28561526150804, - 0 - ] - ] - }, - { - "type": "text", - "version": 1237, - "versionNonce": 1279345538, - "isDeleted": false, - "id": "2GhSpfbAxb6KdwUH_dCib", - "fillStyle": "hachure", - "strokeWidth": 1, - "strokeStyle": "solid", - "roughness": 1, - "opacity": 100, - "angle": 0, - "x": 282.112356568573, - "y": 645.8893619128614, - "strokeColor": "#000000", - "backgroundColor": "transparent", - "width": 29, - "height": 19, - "seed": 913618334, - "groupIds": [], - "strokeSharpness": "sharp", - "boundElements": [], - "updated": 1641947344672, - "fontSize": 16, - "fontFamily": 3, - "text": "YES", - "baseline": 15, - "textAlign": "center", - "verticalAlign": "top", - "containerId": null, - "originalText": "YES" - }, - { - "type": "text", - "version": 1432, - "versionNonce": 1617063170, - "isDeleted": false, - "id": "8im9pL5oJz7NQJ8NLgnRe", - "fillStyle": "hachure", - "strokeWidth": 1, - "strokeStyle": "solid", - "roughness": 1, - "opacity": 100, - "angle": 0, - "x": 639.4795755329188, - "y": 645.3669272112203, - "strokeColor": "#000000", - "backgroundColor": "transparent", - "width": 20, - "height": 19, - "seed": 546405186, - "groupIds": [], - "strokeSharpness": "sharp", - "boundElements": [], - "updated": 1641947336848, - "fontSize": 16, - "fontFamily": 3, - "text": "NO", - "baseline": 15, - "textAlign": "center", - "verticalAlign": "top", - "containerId": null, - "originalText": "NO" - }, - { - "type": "line", - "version": 1227, - "versionNonce": 42851586, - "isDeleted": false, - "id": "k4djJr0-qduIuEKzBQYCv", - "fillStyle": "hachure", - "strokeWidth": 1, - "strokeStyle": "solid", - "roughness": 1, - "opacity": 100, - "angle": 0, - "x": 700.6365918164028, - "y": 684.7562574127057, - "strokeColor": "#000000", - "backgroundColor": "transparent", - "width": 0, - "height": 68.9389053628438, - "seed": 130568670, - "groupIds": [], - "strokeSharpness": "round", - "boundElements": [], - "updated": 1641947086788, - "startBinding": null, - "endBinding": null, - "lastCommittedPoint": null, - "startArrowhead": null, - "endArrowhead": null, - "points": [ - [ - 0, - 0 - ], - [ - 0, - 68.9389053628438 - ] - ] - }, - { - "type": "text", - "version": 1557, - "versionNonce": 462154782, - "isDeleted": false, - "id": "qHmyXdhasjA8eOdNVyBIr", - "fillStyle": "hachure", - "strokeWidth": 1, - "strokeStyle": "solid", - "roughness": 1, - "opacity": 100, - "angle": 0, - "x": 132.83291847520127, - "y": 776.8623273584287, - "strokeColor": "#000000", - "backgroundColor": "transparent", - "width": 282, - "height": 24, - "seed": 1964716162, - "groupIds": [], - "strokeSharpness": "sharp", - "boundElements": [], - "updated": 1641947086788, - "fontSize": 20, - "fontFamily": 3, - "text": "- Verify the fix locally", - "baseline": 19, - "textAlign": "left", - "verticalAlign": "top", - "containerId": null, - "originalText": "- Verify the fix locally" - }, - { - "type": "rectangle", - "version": 1566, - "versionNonce": 613346498, - "isDeleted": false, - "id": "j--1q7-LLu6zXaPFAqLAC", - "fillStyle": "hachure", - "strokeWidth": 1, - "strokeStyle": "solid", - "roughness": 1, - "opacity": 100, - "angle": 0, - "x": 100.20412395318385, - "y": 754.1165206591274, - "strokeColor": "#000000", - "backgroundColor": "transparent", - "width": 457.25758904403494, - "height": 167.4916133986022, - "seed": 1558079006, - "groupIds": [], - "strokeSharpness": "sharp", - "boundElements": [ - { - "id": "DNNnCdMRNJwMdJ9kccYg5", - "type": "arrow" - }, - { - "id": "BsVtWjyNfAaTD2juDwo4y", - "type": "arrow" - }, - { - "id": "B_fXkAEkXbcQLinuLeg3e", - "type": "arrow" - }, - { - "id": "9-0M_bcCrbauwjYk_9TfT", - "type": "arrow" - }, - { - "id": "HTv335lYKWtnkrIwtHQku", - "type": "arrow" - }, - { - "id": "aKq8DF78DWjpCfemhb5XX", - "type": "arrow" - }, - { - "id": "I3s4dbv-i4whr70NalFut", - "type": "arrow" - } - ], - "updated": 1641947086788 - }, - { - "type": "line", - "version": 537, - "versionNonce": 964295838, - "isDeleted": false, - "id": "l0bWHJVqhs_B31UG1rxwq", - "fillStyle": "hachure", - "strokeWidth": 1, - "strokeStyle": "solid", - "roughness": 1, - "opacity": 100, - "angle": 0, - "x": 240.26983738542606, - "y": 686.1858215233636, - "strokeColor": "#000000", - "backgroundColor": "transparent", - "width": 0, - "height": 68.93908875127318, - "seed": 1005211202, - "groupIds": [], - "strokeSharpness": "round", - "boundElements": [], - "updated": 1641947086788, - "startBinding": null, - "endBinding": null, - "lastCommittedPoint": null, - "startArrowhead": null, - "endArrowhead": null, - "points": [ - [ - 0, - 0 - ], - [ - 0, - 68.93908875127318 - ] - ] - }, - { - "type": "text", - "version": 1624, - "versionNonce": 816325698, - "isDeleted": false, - "id": "lnaMqZ2QkzVhowWY1bT9O", - "fillStyle": "hachure", - "strokeWidth": 1, - "strokeStyle": "solid", - "roughness": 1, - "opacity": 100, - "angle": 0, - "x": 132.83291847520127, - "y": 809.8623273584287, - "strokeColor": "#000000", - "backgroundColor": "transparent", - "width": 247, - "height": 24, - "seed": 163010974, - "groupIds": [], - "strokeSharpness": "sharp", - "boundElements": [ - { - "id": "UfG_juqIBi1ZgfOvHtTUA", - "type": "arrow" - } - ], - "updated": 1641947086788, - "fontSize": 20, - "fontFamily": 3, - "text": "- Review code quality", - "baseline": 19, - "textAlign": "left", - "verticalAlign": "top", - "containerId": null, - "originalText": "- Review code quality" - }, - { - "type": "text", - "version": 1686, - "versionNonce": 680311006, - "isDeleted": false, - "id": "eI2OuvQy_x4rapowl4CAr", - "fillStyle": "hachure", - "strokeWidth": 1, - "strokeStyle": "solid", - "roughness": 1, - "opacity": 100, - "angle": 0, - "x": 132.83291847520127, - "y": 841.8623273584287, - "strokeColor": "#000000", - "backgroundColor": "transparent", - "width": 388, - "height": 24, - "seed": 1270788766, - "groupIds": [], - "strokeSharpness": "sharp", - "boundElements": [], - "updated": 1641947086788, - "fontSize": 20, - "fontFamily": 3, - "text": "- Require test case if applicable", - "baseline": 19, - "textAlign": "left", - "verticalAlign": "top", - "containerId": null, - "originalText": "- Require test case if applicable" - }, - { - "type": "text", - "version": 1746, - "versionNonce": 2120109058, - "isDeleted": false, - "id": "Ab6Ypbk6c3nYNIMNKumY8", - "fillStyle": "hachure", - "strokeWidth": 1, - "strokeStyle": "solid", - "roughness": 1, - "opacity": 100, - "angle": 0, - "x": 132.83291847520127, - "y": 872.8623273584287, - "strokeColor": "#000000", - "backgroundColor": "transparent", - "width": 353, - "height": 24, - "seed": 1963246338, - "groupIds": [], - "strokeSharpness": "sharp", - "boundElements": [], - "updated": 1641947086788, - "fontSize": 20, - "fontFamily": 3, - "text": "- Request changes if necessary", - "baseline": 19, - "textAlign": "left", - "verticalAlign": "top", - "containerId": null, - "originalText": "- Request changes if necessary" - }, - { - "type": "text", - "version": 1797, - "versionNonce": 981136194, - "isDeleted": false, - "id": "d2OV-XorNMEgYi21Qgd2b", - "fillStyle": "hachure", - "strokeWidth": 1, - "strokeStyle": "solid", - "roughness": 1, - "opacity": 100, - "angle": 6.282101231183835, - "x": 286.9508930319074, - "y": 1016.6460651990882, - "strokeColor": "#000000", - "backgroundColor": "transparent", - "width": 83, - "height": 24, - "seed": 1674651870, - "groupIds": [], - "strokeSharpness": "sharp", - "boundElements": [], - "updated": 1641947086788, - "fontSize": 20, - "fontFamily": 3, - "text": "Approve", - "baseline": 19, - "textAlign": "left", - "verticalAlign": "top", - "containerId": null, - "originalText": "Approve" - }, - { - "type": "rectangle", - "version": 1945, - "versionNonce": 1968331230, - "isDeleted": false, - "id": "y3YlS22NXZHdevz-ZzYl6", - "fillStyle": "hachure", - "strokeWidth": 1, - "strokeStyle": "solid", - "roughness": 1, - "opacity": 100, - "angle": 6.282101231183835, - "x": 98.80297336665012, - "y": 993.901943508281, - "strokeColor": "#000000", - "backgroundColor": "transparent", - "width": 456.1871867260777, - "height": 69.49161339860235, - "seed": 1540938754, - "groupIds": [], - "strokeSharpness": "sharp", - "boundElements": [ - { - "id": "B_fXkAEkXbcQLinuLeg3e", - "type": "arrow" - }, - { - "id": "9-0M_bcCrbauwjYk_9TfT", - "type": "arrow" - }, - { - "id": "UfG_juqIBi1ZgfOvHtTUA", - "type": "arrow" - } - ], - "updated": 1641947086788 - }, - { - "id": "9-0M_bcCrbauwjYk_9TfT", - "type": "arrow", - "x": 327.0608817658424, - "y": 923.4043679975948, - "width": 1.1386196663628425, - "height": 65.95236612437759, - "angle": 0, - "strokeColor": "#000000", - "backgroundColor": "transparent", - "fillStyle": "hachure", - "strokeWidth": 1, - "strokeStyle": "solid", - "roughness": 1, - "opacity": 90, - "groupIds": [], - "strokeSharpness": "round", - "seed": 1731023746, - "version": 2104, - "versionNonce": 50281218, - "isDeleted": false, - "boundElements": null, - "updated": 1641947086788, - "points": [ - [ - 0, - 0 - ], - [ - -1.1386196663628425, - 65.95236612437759 - ] - ], - "lastCommittedPoint": null, - "startBinding": { - "elementId": "j--1q7-LLu6zXaPFAqLAC", - "focus": 0.0012831296632707053, - "gap": 1.7962339398652034 - }, - "endBinding": { - "elementId": "y3YlS22NXZHdevz-ZzYl6", - "focus": -0.007225371302232891, - "gap": 4.546242518557165 - }, - "startArrowhead": null, - "endArrowhead": "arrow" - }, - { - "type": "text", - "version": 1787, - "versionNonce": 468151838, - "isDeleted": false, - "id": "RBx7JQHKRVEHkjai36KOM", - "fillStyle": "hachure", - "strokeWidth": 1, - "strokeStyle": "solid", - "roughness": 1, - "opacity": 100, - "angle": 0, - "x": 672.0818031973912, - "y": 777.0349540879783, - "strokeColor": "#000000", - "backgroundColor": "transparent", - "width": 305, - "height": 48, - "seed": 1689229150, - "groupIds": [], - "strokeSharpness": "sharp", - "boundElements": [], - "updated": 1641947086788, - "fontSize": 20, - "fontFamily": 3, - "text": "Discuss the potential side\neffects of the fix, e.g.", - "baseline": 43, - "textAlign": "left", - "verticalAlign": "top", - "containerId": null, - "originalText": "Discuss the potential side\neffects of the fix, e.g." - }, - { - "type": "rectangle", - "version": 1792, - "versionNonce": 186944194, - "isDeleted": false, - "id": "rwXJrcQo_SplPMRBcV5RZ", - "fillStyle": "hachure", - "strokeWidth": 1, - "strokeStyle": "solid", - "roughness": 1, - "opacity": 100, - "angle": 0, - "x": 639.4530086753738, - "y": 752.9002584997883, - "strokeColor": "#000000", - "backgroundColor": "transparent", - "width": 412.8131445995905, - "height": 254.99161339860223, - "seed": 826185090, - "groupIds": [], - "strokeSharpness": "sharp", - "boundElements": [ - { - "id": "DNNnCdMRNJwMdJ9kccYg5", - "type": "arrow" - }, - { - "id": "BsVtWjyNfAaTD2juDwo4y", - "type": "arrow" - }, - { - "id": "B_fXkAEkXbcQLinuLeg3e", - "type": "arrow" - }, - { - "id": "UfG_juqIBi1ZgfOvHtTUA", - "type": "arrow" - }, - { - "id": "iECy_Q-M9rMIvIFp5TFMF", - "type": "arrow" - } - ], - "updated": 1641947086788 - }, - { - "type": "text", - "version": 1926, - "versionNonce": 1233773186, - "isDeleted": false, - "id": "8kqLvDOHHelyQUyrgia7I", - "fillStyle": "hachure", - "strokeWidth": 1, - "strokeStyle": "solid", - "roughness": 1, - "opacity": 100, - "angle": 0, - "x": 672.0818031973912, - "y": 843.4238429768673, - "strokeColor": "#000000", - "backgroundColor": "transparent", - "width": 340, - "height": 72, - "seed": 2009136030, - "groupIds": [], - "strokeSharpness": "sharp", - "boundElements": [], - "updated": 1641947086788, - "fontSize": 20, - "fontFamily": 3, - "text": "- Could it introduce implicit\n behavior changes in other\n cases?", - "baseline": 67, - "textAlign": "left", - "verticalAlign": "top", - "containerId": null, - "originalText": "- Could it introduce implicit\n behavior changes in other\n cases?" - }, - { - "type": "text", - "version": 1994, - "versionNonce": 1448935070, - "isDeleted": false, - "id": "dHM9p4Gz0tOJeZIIEdL4a", - "fillStyle": "hachure", - "strokeWidth": 1, - "strokeStyle": "solid", - "roughness": 1, - "opacity": 100, - "angle": 0, - "x": 672.0818031973912, - "y": 930.0349540879783, - "strokeColor": "#000000", - "backgroundColor": "transparent", - "width": 328, - "height": 48, - "seed": 496276802, - "groupIds": [], - "strokeSharpness": "sharp", - "boundElements": [], - "updated": 1641947086788, - "fontSize": 20, - "fontFamily": 3, - "text": "- Does it introduce too much\n changes?", - "baseline": 43, - "textAlign": "left", - "verticalAlign": "top", - "containerId": null, - "originalText": "- Does it introduce too much\n changes?" - }, - { - "type": "text", - "version": 1980, - "versionNonce": 149832258, - "isDeleted": false, - "id": "DIdjL8bcCP67EuOMJAPGm", - "fillStyle": "hachure", - "strokeWidth": 1, - "strokeStyle": "solid", - "roughness": 1, - "opacity": 100, - "angle": 0, - "x": 132.79811525412958, - "y": 1160.0947133527843, - "strokeColor": "#000000", - "backgroundColor": "transparent", - "width": 293, - "height": 48, - "seed": 1202955138, - "groupIds": [], - "strokeSharpness": "sharp", - "boundElements": [], - "updated": 1641947086788, - "fontSize": 20, - "fontFamily": 3, - "text": "Merge if approved by 2 or\nmore team members", - "baseline": 43, - "textAlign": "left", - "verticalAlign": "top", - "containerId": null, - "originalText": "Merge if approved by 2 or\nmore team members" - }, - { - "type": "rectangle", - "version": 2122, - "versionNonce": 2051814686, - "isDeleted": false, - "id": "62ZHSfzhqzsrh4dVzLQiy", - "fillStyle": "hachure", - "strokeWidth": 1, - "strokeStyle": "solid", - "roughness": 1, - "opacity": 100, - "angle": 0, - "x": 100.16932073211217, - "y": 1136.9600177645943, - "strokeColor": "#000000", - "backgroundColor": "transparent", - "width": 456.5631445995905, - "height": 294.0541133986022, - "seed": 2037108126, - "groupIds": [], - "strokeSharpness": "sharp", - "boundElements": [ - { - "id": "DNNnCdMRNJwMdJ9kccYg5", - "type": "arrow" - }, - { - "id": "BsVtWjyNfAaTD2juDwo4y", - "type": "arrow" - }, - { - "id": "B_fXkAEkXbcQLinuLeg3e", - "type": "arrow" - }, - { - "id": "UfG_juqIBi1ZgfOvHtTUA", - "type": "arrow" - }, - { - "id": "e19wFLrAWPdYqboa6YXdy", - "type": "arrow" - } - ], - "updated": 1641947126334 - }, - { - "type": "text", - "version": 2083, - "versionNonce": 1680910850, - "isDeleted": false, - "id": "V-myePWAh66gfv9Eh8hV1", - "fillStyle": "hachure", - "strokeWidth": 1, - "strokeStyle": "solid", - "roughness": 1, - "opacity": 100, - "angle": 0, - "x": 132.79811525412958, - "y": 1226.4836022416735, - "strokeColor": "#000000", - "backgroundColor": "transparent", - "width": 282, - "height": 24, - "seed": 2117591874, - "groupIds": [], - "strokeSharpness": "sharp", - "boundElements": [], - "updated": 1641947086788, - "fontSize": 20, - "fontFamily": 3, - "text": "- Use \"Squash and Merge\"", - "baseline": 19, - "textAlign": "left", - "verticalAlign": "top", - "containerId": null, - "originalText": "- Use \"Squash and Merge\"" - }, - { - "type": "text", - "version": 2202, - "versionNonce": 872967966, - "isDeleted": false, - "id": "6G0ZLaH1Ke-09BHdyVaN1", - "fillStyle": "hachure", - "strokeWidth": 1, - "strokeStyle": "solid", - "roughness": 1, - "opacity": 100, - "angle": 0, - "x": 132.79811525412958, - "y": 1266.5322133527843, - "strokeColor": "#000000", - "backgroundColor": "transparent", - "width": 363, - "height": 48, - "seed": 1479479682, - "groupIds": [], - "strokeSharpness": "sharp", - "boundElements": [], - "updated": 1641947086788, - "fontSize": 20, - "fontFamily": 3, - "text": "- Edit commit message to follow\n convention", - "baseline": 43, - "textAlign": "left", - "verticalAlign": "top", - "containerId": null, - "originalText": "- Edit commit message to follow\n convention" - }, - { - "type": "text", - "version": 2340, - "versionNonce": 1571347294, - "isDeleted": false, - "id": "qgXNTKjXhHEHFMg8H_1rC", - "fillStyle": "hachure", - "strokeWidth": 1, - "strokeStyle": "solid", - "roughness": 1, - "opacity": 100, - "angle": 0, - "x": 132.79811525412958, - "y": 1332.1572133527843, - "strokeColor": "#000000", - "backgroundColor": "transparent", - "width": 352, - "height": 72, - "seed": 203188702, - "groupIds": [], - "strokeSharpness": "sharp", - "boundElements": [], - "updated": 1641947086788, - "fontSize": 20, - "fontFamily": 3, - "text": "- In commit message body, list\n relevant issues being fixed\n e.g. \"fix #1234, fix #1235\"", - "baseline": 67, - "textAlign": "left", - "verticalAlign": "top", - "containerId": null, - "originalText": "- In commit message body, list\n relevant issues being fixed\n e.g. \"fix #1234, fix #1235\"" - }, - { - "id": "UfG_juqIBi1ZgfOvHtTUA", - "type": "arrow", - "x": 329.9373607609896, - "y": 1065.1741445425164, - "width": 1.830043903629985, - "height": 66.56469151842111, - "angle": 0, - "strokeColor": "#000000", - "backgroundColor": "transparent", - "fillStyle": "hachure", - "strokeWidth": 1, - "strokeStyle": "solid", - "roughness": 1, - "opacity": 90, - "groupIds": [], - "strokeSharpness": "round", - "seed": 893045570, - "version": 2142, - "versionNonce": 364026114, - "isDeleted": false, - "boundElements": null, - "updated": 1641947086788, - "points": [ - [ - 0, - 0 - ], - [ - -1.830043903629985, - 66.56469151842111 - ] - ], - "lastCommittedPoint": null, - "startBinding": { - "elementId": "y3YlS22NXZHdevz-ZzYl6", - "focus": -0.01765763497144198, - "gap": 1.7838626235180755 - }, - "endBinding": { - "elementId": "62ZHSfzhqzsrh4dVzLQiy", - "focus": -0.019495610197224435, - "gap": 5.221181703656839 - }, - "startArrowhead": null, - "endArrowhead": "arrow" - }, - { - "type": "text", - "version": 2067, - "versionNonce": 1193340958, - "isDeleted": false, - "id": "erEE8G8FKvYhRr0-5lMCt", - "fillStyle": "hachure", - "strokeWidth": 1, - "strokeStyle": "solid", - "roughness": 1, - "opacity": 100, - "angle": 0, - "x": 672.0818031973912, - "y": 1100.389792797655, - "strokeColor": "#000000", - "backgroundColor": "transparent", - "width": 341, - "height": 48, - "seed": 1659701278, - "groupIds": [], - "strokeSharpness": "sharp", - "boundElements": [], - "updated": 1641947086788, - "fontSize": 20, - "fontFamily": 3, - "text": "Add priority labels\n(See issue triaging workflow)", - "baseline": 43, - "textAlign": "left", - "verticalAlign": "top", - "containerId": null, - "originalText": "Add priority labels\n(See issue triaging workflow)" - }, - { - "type": "rectangle", - "version": 2040, - "versionNonce": 1807493314, - "isDeleted": false, - "id": "SsXRoiB6K85RJkG0cLHKt", - "fillStyle": "hachure", - "strokeWidth": 1, - "strokeStyle": "solid", - "roughness": 1, - "opacity": 100, - "angle": 0, - "x": 639.4530086753738, - "y": 1077.8680004352714, - "strokeColor": "#000000", - "backgroundColor": "transparent", - "width": 412.8131445995905, - "height": 95.31419404376325, - "seed": 1452430430, - "groupIds": [], - "strokeSharpness": "sharp", - "boundElements": [ - { - "id": "DNNnCdMRNJwMdJ9kccYg5", - "type": "arrow" - }, - { - "id": "BsVtWjyNfAaTD2juDwo4y", - "type": "arrow" - }, - { - "id": "B_fXkAEkXbcQLinuLeg3e", - "type": "arrow" - }, - { - "id": "UfG_juqIBi1ZgfOvHtTUA", - "type": "arrow" - } - ], - "updated": 1641947086788 - }, - { - "id": "iECy_Q-M9rMIvIFp5TFMF", - "type": "arrow", - "x": 846.1502354837385, - "y": 1009.0048773144631, - "width": 1.4724394828381264, - "height": 68.99666593246332, - "angle": 0, - "strokeColor": "#000000", - "backgroundColor": "transparent", - "fillStyle": "hachure", - "strokeWidth": 1, - "strokeStyle": "solid", - "roughness": 1, - "opacity": 90, - "groupIds": [], - "strokeSharpness": "round", - "seed": 488605278, - "version": 1617, - "versionNonce": 2124284062, - "isDeleted": false, - "boundElements": null, - "updated": 1641947086788, - "points": [ - [ - 0, - 0 - ], - [ - -1.4724394828381264, - 68.99666593246332 - ] - ], - "lastCommittedPoint": null, - "startBinding": { - "elementId": "rwXJrcQo_SplPMRBcV5RZ", - "focus": -0.014513930519579882, - "gap": 1.1130054160725535 - }, - "endBinding": null, - "startArrowhead": null, - "endArrowhead": "arrow" - }, - { - "id": "DSjLArh7-2UnOKdJGzomr", - "type": "arrow", - "x": 845.0001977768175, - "y": 1172.2951998951075, - "width": 2.652485747952369, - "height": 68.02463696164841, - "angle": 0, - "strokeColor": "#000000", - "backgroundColor": "transparent", - "fillStyle": "hachure", - "strokeWidth": 1, - "strokeStyle": "solid", - "roughness": 1, - "opacity": 90, - "groupIds": [], - "strokeSharpness": "round", - "seed": 1035607042, - "version": 1752, - "versionNonce": 1119248450, - "isDeleted": false, - "boundElements": null, - "updated": 1641947086788, - "points": [ - [ - 0, - 0 - ], - [ - -2.652485747952369, - 68.02463696164841 - ] - ], - "lastCommittedPoint": null, - "startBinding": null, - "endBinding": { - "elementId": "LgjfqFPAEinqw-48hD1DU", - "focus": -0.01634796150004538, - "gap": 2.064292610773464 - }, - "startArrowhead": null, - "endArrowhead": "arrow" - }, - { - "type": "text", - "version": 2141, - "versionNonce": 848313566, - "isDeleted": false, - "id": "MWIPixVB7po50KFcFrK-t", - "fillStyle": "hachure", - "strokeWidth": 1, - "strokeStyle": "solid", - "roughness": 1, - "opacity": 100, - "angle": 0, - "x": 720.7466777493628, - "y": 1266.7509039087658, - "strokeColor": "#000000", - "backgroundColor": "transparent", - "width": 247, - "height": 24, - "seed": 1252032606, - "groupIds": [], - "strokeSharpness": "sharp", - "boundElements": [], - "updated": 1641947086788, - "fontSize": 20, - "fontFamily": 3, - "text": "Await input from Evan", - "baseline": 19, - "textAlign": "center", - "verticalAlign": "top", - "containerId": null, - "originalText": "Await input from Evan" - }, - { - "type": "rectangle", - "version": 2154, - "versionNonce": 725944322, - "isDeleted": false, - "id": "LgjfqFPAEinqw-48hD1DU", - "fillStyle": "hachure", - "strokeWidth": 1, - "strokeStyle": "solid", - "roughness": 1, - "opacity": 100, - "angle": 0, - "x": 637.8401054495674, - "y": 1242.3841294675294, - "strokeColor": "#000000", - "backgroundColor": "transparent", - "width": 412.8131445995905, - "height": 72.73354888247324, - "seed": 233618562, - "groupIds": [], - "strokeSharpness": "sharp", - "boundElements": [ - { - "id": "DNNnCdMRNJwMdJ9kccYg5", - "type": "arrow" - }, - { - "id": "BsVtWjyNfAaTD2juDwo4y", - "type": "arrow" - }, - { - "id": "B_fXkAEkXbcQLinuLeg3e", - "type": "arrow" - }, - { - "id": "UfG_juqIBi1ZgfOvHtTUA", - "type": "arrow" - }, - { - "id": "iECy_Q-M9rMIvIFp5TFMF", - "type": "arrow" - }, - { - "id": "DSjLArh7-2UnOKdJGzomr", - "type": "arrow" - } - ], - "updated": 1641947086788 - }, - { - "id": "I3s4dbv-i4whr70NalFut", - "type": "arrow", - "x": 845.5170912834428, - "y": 1314.944326268947, - "width": 284, - "height": 498.0232558139535, - "angle": 0, - "strokeColor": "#000000", - "backgroundColor": "transparent", - "fillStyle": "hachure", - "strokeWidth": 1, - "strokeStyle": "dashed", - "roughness": 1, - "opacity": 90, - "groupIds": [], - "strokeSharpness": "round", - "seed": 1762514946, - "version": 1327, - "versionNonce": 1869864414, - "isDeleted": false, - "boundElements": null, - "updated": 1641947086788, - "points": [ - [ - 0, - 0 - ], - [ - -18.83720930232562, - 38.348837209302474 - ], - [ - -120, - 41.02325581395348 - ], - [ - -224, - 36.186046511627865 - ], - [ - -248, - -11 - ], - [ - -250, - -93 - ], - [ - -249, - -183 - ], - [ - -247, - -268 - ], - [ - -247, - -347 - ], - [ - -251.48837209302337, - -437.3023255813954 - ], - [ - -284, - -457 - ] - ], - "lastCommittedPoint": null, - "startBinding": null, - "endBinding": { - "elementId": "j--1q7-LLu6zXaPFAqLAC", - "focus": -0.5439174079059673, - "gap": 4.055378286224027 - }, - "startArrowhead": null, - "endArrowhead": "arrow" - }, - { - "type": "text", - "version": 1865, - "versionNonce": 1950155422, - "isDeleted": false, - "id": "JoRpLjElVazgNFj4cZiqq", - "fillStyle": "hachure", - "strokeWidth": 1, - "strokeStyle": "solid", - "roughness": 1, - "opacity": 100, - "angle": 0, - "x": 1164.7661471371505, - "y": 463.66908277859034, - "strokeColor": "#000000", - "backgroundColor": "transparent", - "width": 317, - "height": 24, - "seed": 1605394654, - "groupIds": [], - "strokeSharpness": "sharp", - "boundElements": [], - "updated": 1641947086789, - "fontSize": 20, - "fontFamily": 3, - "text": "- Discuss feature necessity", - "baseline": 19, - "textAlign": "left", - "verticalAlign": "top", - "containerId": null, - "originalText": "- Discuss feature necessity" - }, - { - "type": "rectangle", - "version": 1868, - "versionNonce": 886994718, - "isDeleted": false, - "id": "6f4EO1ZMWYeKarDz8375U", - "fillStyle": "hachure", - "strokeWidth": 1, - "strokeStyle": "solid", - "roughness": 1, - "opacity": 100, - "angle": 0, - "x": 1132.137352615133, - "y": 439.53438719040014, - "strokeColor": "#000000", - "backgroundColor": "transparent", - "width": 425.1208369072829, - "height": 279.60699801398687, - "seed": 2057905154, - "groupIds": [], - "strokeSharpness": "sharp", - "boundElements": [ - { - "id": "DNNnCdMRNJwMdJ9kccYg5", - "type": "arrow" - }, - { - "id": "BsVtWjyNfAaTD2juDwo4y", - "type": "arrow" - }, - { - "id": "B_fXkAEkXbcQLinuLeg3e", - "type": "arrow" - }, - { - "id": "UfG_juqIBi1ZgfOvHtTUA", - "type": "arrow" - }, - { - "id": "iECy_Q-M9rMIvIFp5TFMF", - "type": "arrow" - }, - { - "id": "Tz2EigpQnKb294z7ILDj_", - "type": "arrow" - } - ], - "updated": 1641947090794 - }, - { - "type": "text", - "version": 2048, - "versionNonce": 1627324126, - "isDeleted": false, - "id": "o7tqZAe2UwoTuGvhH7EIu", - "fillStyle": "hachure", - "strokeWidth": 1, - "strokeStyle": "solid", - "roughness": 1, - "opacity": 100, - "angle": 0, - "x": 1164.7661471371505, - "y": 501.05797166747925, - "strokeColor": "#000000", - "backgroundColor": "transparent", - "width": 293, - "height": 48, - "seed": 988583198, - "groupIds": [], - "strokeSharpness": "sharp", - "boundElements": [], - "updated": 1641947086789, - "fontSize": 20, - "fontFamily": 3, - "text": "- Is this the best way to\n address the need?", - "baseline": 43, - "textAlign": "left", - "verticalAlign": "top", - "containerId": null, - "originalText": "- Is this the best way to\n address the need?" - }, - { - "type": "text", - "version": 2082, - "versionNonce": 1906009602, - "isDeleted": false, - "id": "MWLzSueAo93vByEHBi54p", - "fillStyle": "hachure", - "strokeWidth": 1, - "strokeStyle": "solid", - "roughness": 1, - "opacity": 100, - "angle": 0, - "x": 1164.7661471371505, - "y": 564.6690827785903, - "strokeColor": "#000000", - "backgroundColor": "transparent", - "width": 247, - "height": 24, - "seed": 1190884638, - "groupIds": [], - "strokeSharpness": "sharp", - "boundElements": [], - "updated": 1641947086789, - "fontSize": 20, - "fontFamily": 3, - "text": "- Review code quality", - "baseline": 19, - "textAlign": "left", - "verticalAlign": "top", - "containerId": null, - "originalText": "- Review code quality" - }, - { - "type": "line", - "version": 465, - "versionNonce": 347566878, - "isDeleted": false, - "id": "ZqgvD11xSG3hXxpGNqSFO", - "fillStyle": "hachure", - "strokeWidth": 1, - "strokeStyle": "solid", - "roughness": 1, - "opacity": 100, - "angle": 0, - "x": 465.3990233049102, - "y": 653.9851862944989, - "strokeColor": "#000000", - "backgroundColor": "transparent", - "width": 0, - "height": 31.92990562768, - "seed": 1751912450, - "groupIds": [], - "strokeSharpness": "round", - "boundElements": [], - "updated": 1641947086789, - "startBinding": null, - "endBinding": null, - "lastCommittedPoint": null, - "startArrowhead": null, - "endArrowhead": null, - "points": [ - [ - 0, - 0 - ], - [ - 0, - 31.92990562768 - ] - ] - }, - { - "type": "line", - "version": 1467, - "versionNonce": 836128066, - "isDeleted": false, - "id": "kgReLARaeJtWLIucHDGFT", - "fillStyle": "hachure", - "strokeWidth": 1, - "strokeStyle": "solid", - "roughness": 1, - "opacity": 100, - "angle": 0, - "x": 1294.4362354574944, - "y": 371.9077923147119, - "strokeColor": "#000000", - "backgroundColor": "transparent", - "width": 831.391216879718, - "height": 0, - "seed": 122656158, - "groupIds": [], - "strokeSharpness": "round", - "boundElements": [], - "updated": 1641947086789, - "startBinding": null, - "endBinding": null, - "lastCommittedPoint": null, - "startArrowhead": null, - "endArrowhead": null, - "points": [ - [ - 0, - 0 - ], - [ - -831.391216879718, - 0 - ] - ] - }, - { - "type": "text", - "version": 1268, - "versionNonce": 1611760450, - "isDeleted": false, - "id": "BWA8XBcQGAcRC30wZHVzw", - "fillStyle": "hachure", - "strokeWidth": 1, - "strokeStyle": "solid", - "roughness": 1, - "opacity": 100, - "angle": 0, - "x": 500.83079843812163, - "y": 331.9405361405356, - "strokeColor": "#000000", - "backgroundColor": "transparent", - "width": 67, - "height": 19, - "seed": 122985282, - "groupIds": [], - "strokeSharpness": "sharp", - "boundElements": [], - "updated": 1641947346511, - "fontSize": 16, - "fontFamily": 3, - "text": "BUG FIX", - "baseline": 15, - "textAlign": "center", - "verticalAlign": "top", - "containerId": null, - "originalText": "BUG FIX" - }, - { - "type": "text", - "version": 1416, - "versionNonce": 836155870, - "isDeleted": false, - "id": "pDZYx50Ve5EU9-dHrW3yB", - "fillStyle": "hachure", - "strokeWidth": 1, - "strokeStyle": "solid", - "roughness": 1, - "opacity": 100, - "angle": 0, - "x": 1190.3134020178518, - "y": 331.4181014388942, - "strokeColor": "#000000", - "backgroundColor": "transparent", - "width": 67, - "height": 19, - "seed": 977744350, - "groupIds": [], - "strokeSharpness": "sharp", - "boundElements": [], - "updated": 1641947305581, - "fontSize": 16, - "fontFamily": 3, - "text": "FEATURE", - "baseline": 15, - "textAlign": "center", - "verticalAlign": "top", - "containerId": null, - "originalText": "FEATURE" - }, - { - "type": "line", - "version": 1233, - "versionNonce": 640545822, - "isDeleted": false, - "id": "jr3dxD_dUYR4pXR-X_uoJ", - "fillStyle": "hachure", - "strokeWidth": 1, - "strokeStyle": "solid", - "roughness": 1, - "opacity": 100, - "angle": 0, - "x": 1293.8165721474898, - "y": 369.8074316403798, - "strokeColor": "#000000", - "backgroundColor": "transparent", - "width": 0, - "height": 68.9389053628438, - "seed": 1449386754, - "groupIds": [], - "strokeSharpness": "round", - "boundElements": [], - "updated": 1641947086789, - "startBinding": null, - "endBinding": null, - "lastCommittedPoint": null, - "startArrowhead": null, - "endArrowhead": null, - "points": [ - [ - 0, - 0 - ], - [ - 0, - 68.9389053628438 - ] - ] - }, - { - "type": "line", - "version": 547, - "versionNonce": 317809858, - "isDeleted": false, - "id": "6P3oUr3EUMyTRa_-kJeNw", - "fillStyle": "hachure", - "strokeWidth": 1, - "strokeStyle": "solid", - "roughness": 1, - "opacity": 100, - "angle": 0, - "x": 465.4498177165133, - "y": 371.2369957510378, - "strokeColor": "#000000", - "backgroundColor": "transparent", - "width": 0, - "height": 68.93908875127318, - "seed": 478234142, - "groupIds": [], - "strokeSharpness": "round", - "boundElements": [], - "updated": 1641947086789, - "startBinding": null, - "endBinding": null, - "lastCommittedPoint": null, - "startArrowhead": null, - "endArrowhead": null, - "points": [ - [ - 0, - 0 - ], - [ - 0, - 68.93908875127318 - ] - ] - }, - { - "type": "line", - "version": 431, - "versionNonce": 1523957854, - "isDeleted": false, - "id": "Sl75qgdjG_g3QRVYYwaRC", - "fillStyle": "hachure", - "strokeWidth": 1, - "strokeStyle": "solid", - "roughness": 1, - "opacity": 100, - "angle": 0, - "x": 882.5790036359972, - "y": 339.03636052217286, - "strokeColor": "#000000", - "backgroundColor": "transparent", - "width": 0, - "height": 31.92990562768, - "seed": 1538536130, - "groupIds": [], - "strokeSharpness": "round", - "boundElements": [], - "updated": 1641947086789, - "startBinding": null, - "endBinding": null, - "lastCommittedPoint": null, - "startArrowhead": null, - "endArrowhead": null, - "points": [ - [ - 0, - 0 - ], - [ - 0, - 31.92990562768 - ] - ] - }, - { - "type": "text", - "version": 2118, - "versionNonce": 2033845406, - "isDeleted": false, - "id": "IbwDGf9_heqir9YyW_RVW", - "fillStyle": "hachure", - "strokeWidth": 1, - "strokeStyle": "solid", - "roughness": 1, - "opacity": 100, - "angle": 0, - "x": 1164.7661471371505, - "y": 604.6690827785903, - "strokeColor": "#000000", - "backgroundColor": "transparent", - "width": 235, - "height": 24, - "seed": 413268510, - "groupIds": [], - "strokeSharpness": "sharp", - "boundElements": [], - "updated": 1641947086789, - "fontSize": 20, - "fontFamily": 3, - "text": "- Add feature labels", - "baseline": 19, - "textAlign": "left", - "verticalAlign": "top", - "containerId": null, - "originalText": "- Add feature labels" - }, - { - "type": "text", - "version": 2210, - "versionNonce": 283705950, - "isDeleted": false, - "id": "YtIpxbcQQVbCSbV3q4QPH", - "fillStyle": "hachure", - "strokeWidth": 1, - "strokeStyle": "solid", - "roughness": 1, - "opacity": 100, - "angle": 0, - "x": 1164.7661471371505, - "y": 643.6690827785903, - "strokeColor": "#000000", - "backgroundColor": "transparent", - "width": 352, - "height": 48, - "seed": 665408450, - "groupIds": [], - "strokeSharpness": "sharp", - "boundElements": [], - "updated": 1641947089307, - "fontSize": 20, - "fontFamily": 3, - "text": "- Approve if you feel strongly\n that the feature is needed", - "baseline": 43, - "textAlign": "left", - "verticalAlign": "top", - "containerId": null, - "originalText": "- Approve if you feel strongly\n that the feature is needed" - }, - { - "type": "text", - "version": 2288, - "versionNonce": 1621749214, - "isDeleted": false, - "id": "_QI4-CZIy_nABUPn3RB7r", - "fillStyle": "hachure", - "strokeWidth": 1, - "strokeStyle": "solid", - "roughness": 1, - "opacity": 100, - "angle": 0, - "x": 1215.4374848433713, - "y": 814.0677609737774, - "strokeColor": "#000000", - "backgroundColor": "transparent", - "width": 247, - "height": 24, - "seed": 742373086, - "groupIds": [], - "strokeSharpness": "sharp", - "boundElements": [], - "updated": 1641947099203, - "fontSize": 20, - "fontFamily": 3, - "text": "Await input from Evan", - "baseline": 19, - "textAlign": "center", - "verticalAlign": "top", - "containerId": null, - "originalText": "Await input from Evan" - }, - { - "type": "rectangle", - "version": 2314, - "versionNonce": 2055471326, - "isDeleted": false, - "id": "cQeOM81IMRmQYPfSNDc_w", - "fillStyle": "hachure", - "strokeWidth": 1, - "strokeStyle": "solid", - "roughness": 1, - "opacity": 100, - "angle": 0, - "x": 1132.530912543576, - "y": 789.7009865325405, - "strokeColor": "#000000", - "backgroundColor": "transparent", - "width": 426.65929844574435, - "height": 72.73354888247324, - "seed": 31600130, - "groupIds": [], - "strokeSharpness": "sharp", - "boundElements": [ - { - "id": "DNNnCdMRNJwMdJ9kccYg5", - "type": "arrow" - }, - { - "id": "BsVtWjyNfAaTD2juDwo4y", - "type": "arrow" - }, - { - "id": "B_fXkAEkXbcQLinuLeg3e", - "type": "arrow" - }, - { - "id": "UfG_juqIBi1ZgfOvHtTUA", - "type": "arrow" - }, - { - "id": "iECy_Q-M9rMIvIFp5TFMF", - "type": "arrow" - }, - { - "id": "DSjLArh7-2UnOKdJGzomr", - "type": "arrow" - }, - { - "id": "Tz2EigpQnKb294z7ILDj_", - "type": "arrow" - }, - { - "id": "e19wFLrAWPdYqboa6YXdy", - "type": "arrow" - } - ], - "updated": 1641947126334 - }, - { - "type": "arrow", - "version": 1719, - "versionNonce": 806769858, - "isDeleted": false, - "id": "Tz2EigpQnKb294z7ILDj_", - "fillStyle": "hachure", - "strokeWidth": 1, - "strokeStyle": "solid", - "roughness": 1, - "opacity": 90, - "angle": 0, - "x": 1348.6842784701294, - "y": 721.2617356998531, - "strokeColor": "#000000", - "backgroundColor": "transparent", - "width": 1.5984643496256012, - "height": 64.5218005954606, - "seed": 1293204318, - "groupIds": [], - "strokeSharpness": "round", - "boundElements": [], - "updated": 1641947103757, - "startBinding": { - "elementId": "6f4EO1ZMWYeKarDz8375U", - "focus": -0.034730097781564113, - "gap": 2.120350495466141 - }, - "endBinding": { - "elementId": "cQeOM81IMRmQYPfSNDc_w", - "focus": 0.0010607736883342003, - "gap": 3.91745023722666 - }, - "lastCommittedPoint": null, - "startArrowhead": null, - "endArrowhead": "arrow", - "points": [ - [ - 0, - 0 - ], - [ - -1.5984643496256012, - 64.5218005954606 - ] - ] - }, - { - "id": "e19wFLrAWPdYqboa6YXdy", - "type": "arrow", - "x": 1351.2451771510634, - "y": 863.4523763583926, - "width": 784.6153846153845, - "height": 527.6923076923077, - "angle": 0, - "strokeColor": "#000000", - "backgroundColor": "transparent", - "fillStyle": "hachure", - "strokeWidth": 1, - "strokeStyle": "dashed", - "roughness": 1, - "opacity": 90, - "groupIds": [], - "strokeSharpness": "round", - "seed": 978982046, - "version": 477, - "versionNonce": 1389609758, - "isDeleted": false, - "boundElements": null, - "updated": 1641947142555, - "points": [ - [ - 0, - 0 - ], - [ - -3.0769230769230944, - 424.61538461538464 - ], - [ - -16.923076923076906, - 515.3846153846151 - ], - [ - -118.46153846153834, - 526.1538461538461 - ], - [ - -593.8461538461538, - 526.1538461538461 - ], - [ - -784.6153846153845, - 527.6923076923077 - ] - ], - "lastCommittedPoint": null, - "startBinding": { - "elementId": "cQeOM81IMRmQYPfSNDc_w", - "focus": -0.026477991878603318, - "gap": 1.0178409433789852 - }, - "endBinding": { - "elementId": "62ZHSfzhqzsrh4dVzLQiy", - "focus": 0.7327187841855894, - "gap": 9.89732720397626 - }, - "startArrowhead": null, - "endArrowhead": "arrow" - } - ], - "appState": { - "gridSize": null, - "viewBackgroundColor": "#ffffff" - }, - "files": {} -} \ No newline at end of file diff --git a/.github/pr-workflow.png b/.github/pr-workflow.png deleted file mode 100644 index 3d097dc47cfeca..00000000000000 Binary files a/.github/pr-workflow.png and /dev/null differ diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 83948b84d2cf07..1a6f0cbf5227ba 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -39,13 +39,13 @@ jobs: strategy: matrix: os: [ubuntu-latest] - node_version: [18, 20] + node_version: [18, 20, 22] include: # Active LTS + other OS - os: macos-latest node_version: 20 - os: windows-latest - node_version: 20.3.1 # https://github.com/nodejs/node/issues/48673 + node_version: 20 fail-fast: false name: "Build&Test: node-${{ matrix.node_version }}, ${{ matrix.os }}" @@ -58,7 +58,7 @@ jobs: - name: Get changed files id: changed-files - uses: tj-actions/changed-files@bfc49f4cff6934aa236c171f9bcbf1dd6b1ef438 # v40.0.1 + uses: tj-actions/changed-files@cc733854b1f224978ef800d29e4709d5ee2883e4 # v44.5.5 with: files: | docs/** @@ -69,7 +69,7 @@ jobs: - name: Install pnpm if: steps.changed-files.outputs.only_changed != 'true' - uses: pnpm/action-setup@v2.4.0 + uses: pnpm/action-setup@v4.0.0 - name: Set node version to ${{ matrix.node_version }} if: steps.changed-files.outputs.only_changed != 'true' @@ -93,12 +93,12 @@ jobs: if: runner.os == 'Windows' && steps.changed-files.outputs.only_changed != 'true' run: | echo "PLAYWRIGHT_BROWSERS_PATH=$HOME\.cache\playwright-bin" >> $env:GITHUB_ENV - $env:PLAYWRIGHT_VERSION="$(pnpm ls --depth 0 --json -w playwright-chromium | jq --raw-output '.[0].devDependencies[\"playwright-chromium\"].version')" + $env:PLAYWRIGHT_VERSION="$(pnpm ls --depth 0 --json -w playwright-chromium | jq --raw-output '.[0].devDependencies["playwright-chromium"].version')" echo "PLAYWRIGHT_VERSION=$env:PLAYWRIGHT_VERSION" >> $env:GITHUB_ENV - name: Cache Playwright's binary if: steps.changed-files.outputs.only_changed != 'true' - uses: actions/cache@v3 + uses: actions/cache@v4 with: key: ${{ runner.os }}-playwright-bin-v1-${{ env.PLAYWRIGHT_VERSION }} path: ${{ env.PLAYWRIGHT_BROWSERS_PATH }} @@ -129,17 +129,17 @@ jobs: lint: timeout-minutes: 10 runs-on: ubuntu-latest - name: "Lint: node-18, ubuntu-latest" + name: "Lint: node-20, ubuntu-latest" steps: - uses: actions/checkout@v4 - name: Install pnpm - uses: pnpm/action-setup@v2.4.0 + uses: pnpm/action-setup@v4.0.0 - - name: Set node version to 18 + - name: Set node version to 20 uses: actions/setup-node@v4 with: - node-version: 18 + node-version: 20 cache: "pnpm" - name: Install deps diff --git a/.github/workflows/ecosystem-ci-trigger.yml b/.github/workflows/ecosystem-ci-trigger.yml index 386004fa3a6d17..6a48065cb7759d 100644 --- a/.github/workflows/ecosystem-ci-trigger.yml +++ b/.github/workflows/ecosystem-ci-trigger.yml @@ -9,7 +9,7 @@ jobs: runs-on: ubuntu-latest if: github.repository == 'vitejs/vite' && github.event.issue.pull_request && startsWith(github.event.comment.body, '/ecosystem-ci run') steps: - - uses: actions/github-script@v6 + - uses: actions/github-script@v7 with: script: | const user = context.payload.sender.login @@ -45,7 +45,7 @@ jobs: }) throw new Error('not allowed') } - - uses: actions/github-script@v6 + - uses: actions/github-script@v7 id: get-pr-data with: script: | @@ -66,7 +66,7 @@ jobs: app_id: ${{ secrets.ECOSYSTEM_CI_GITHUB_APP_ID }} installation_retrieval_payload: "${{ github.repository_owner }}/vite-ecosystem-ci" private_key: ${{ secrets.ECOSYSTEM_CI_GITHUB_APP_PRIVATE_KEY }} - - uses: actions/github-script@v6 + - uses: actions/github-script@v7 id: trigger env: COMMENT: ${{ github.event.comment.body }} diff --git a/.github/workflows/issue-close-require.yml b/.github/workflows/issue-close-require.yml index cdf2042060945e..5347951bb52086 100644 --- a/.github/workflows/issue-close-require.yml +++ b/.github/workflows/issue-close-require.yml @@ -9,10 +9,10 @@ jobs: if: github.repository == 'vitejs/vite' runs-on: ubuntu-latest steps: - - name: need reproduction + - name: needs reproduction uses: actions-cool/issues-helper@v3 with: actions: "close-issues" token: ${{ secrets.GITHUB_TOKEN }} - labels: "need reproduction" + labels: "needs reproduction" inactive-day: 3 diff --git a/.github/workflows/issue-labeled.yml b/.github/workflows/issue-labeled.yml index 6c6c77737ceea5..db719cc3d467d9 100644 --- a/.github/workflows/issue-labeled.yml +++ b/.github/workflows/issue-labeled.yml @@ -16,7 +16,7 @@ jobs: actions: "remove-labels" token: ${{ secrets.GITHUB_TOKEN }} issue-number: ${{ github.event.issue.number }} - labels: "pending triage, need reproduction" + labels: "pending triage, needs reproduction" - name: remove pending if: contains(github.event.label.description, '(priority)') && contains(github.event.issue.labels.*.name, 'pending triage') @@ -36,13 +36,13 @@ jobs: issue-number: ${{ github.event.issue.number }} labels: "enhancement: pending triage" - - name: need reproduction - if: github.event.label.name == 'need reproduction' + - name: needs reproduction + if: github.event.label.name == 'needs reproduction' uses: actions-cool/issues-helper@v3 with: actions: "create-comment, remove-labels" token: ${{ secrets.GITHUB_TOKEN }} issue-number: ${{ github.event.issue.number }} body: | - Hello @${{ github.event.issue.user.login }}. Please provide a [minimal reproduction](https://stackoverflow.com/help/minimal-reproducible-example) using a GitHub repository or [StackBlitz](https://vite.new). Issues marked with `need reproduction` will be closed if they have no activity within 3 days. + Hello @${{ github.event.issue.user.login }}. Please provide a [minimal reproduction](https://stackoverflow.com/help/minimal-reproducible-example) using a GitHub repository or [StackBlitz](https://vite.new). Issues marked with `needs reproduction` will be closed if they have no activity within 3 days. labels: "pending triage" diff --git a/.github/workflows/lock-closed-issues.yml b/.github/workflows/lock-closed-issues.yml index 3b2846ff67d67c..fc3002d6d5d8ab 100644 --- a/.github/workflows/lock-closed-issues.yml +++ b/.github/workflows/lock-closed-issues.yml @@ -12,7 +12,7 @@ jobs: if: github.repository == 'vitejs/vite' runs-on: ubuntu-latest steps: - - uses: dessant/lock-threads@v4 + - uses: dessant/lock-threads@v5 with: github-token: ${{ secrets.GITHUB_TOKEN }} issue-inactive-days: "14" diff --git a/.github/workflows/publish-commit.yml b/.github/workflows/publish-commit.yml new file mode 100644 index 00000000000000..1f5d94cafada39 --- /dev/null +++ b/.github/workflows/publish-commit.yml @@ -0,0 +1,72 @@ +name: Publish Any Commit + +env: + # install playwright binary manually (because pnpm only runs install script once) + PLAYWRIGHT_SKIP_BROWSER_DOWNLOAD: "1" + +on: + push: + branches: + - main + - v6/environment-api + issue_comment: + types: [created] + +jobs: + build: + if: github.repository == 'vitejs/vite' && (github.event_name == 'push' || github.event.issue.pull_request && startsWith(github.event.comment.body, '/pkg-pr-new')) + runs-on: ubuntu-latest + + steps: + - if: github.event.issue.pull_request + uses: actions/github-script@v7 + with: + script: | + const user = context.payload.sender.login + console.log(`Validate user: ${user}`) + + let hasTriagePermission = false + try { + const { data } = await github.rest.repos.getCollaboratorPermissionLevel({ + owner: context.repo.owner, + repo: context.repo.repo, + username: user, + }); + hasTriagePermission = data.user.permissions.triage + } catch (e) { + console.warn(e) + } + + if (hasTriagePermission) { + console.log('Allowed') + await github.rest.reactions.createForIssueComment({ + owner: context.repo.owner, + repo: context.repo.repo, + comment_id: context.payload.comment.id, + content: '+1', + }) + } else { + console.log('Not allowed') + await github.rest.reactions.createForIssueComment({ + owner: context.repo.owner, + repo: context.repo.repo, + comment_id: context.payload.comment.id, + content: '-1', + }) + throw new Error('not allowed') + } + + - name: Checkout code + uses: actions/checkout@v4 + + - name: Install pnpm + uses: pnpm/action-setup@v4.0.0 + + - name: Install dependencies + run: pnpm install + + - name: Build + working-directory: ./packages/vite + run: pnpm build + + - run: pnpm dlx pkg-pr-new@0.0 publish --compact --pnpm ./packages/vite diff --git a/.github/workflows/publish.yml b/.github/workflows/publish.yml index e78141ef129140..37d8c135b458db 100644 --- a/.github/workflows/publish.yml +++ b/.github/workflows/publish.yml @@ -21,12 +21,12 @@ jobs: uses: actions/checkout@v4 - name: Install pnpm - uses: pnpm/action-setup@v2.4.0 + uses: pnpm/action-setup@v4.0.0 - - name: Set node version to 18 + - name: Set node version to 20 uses: actions/setup-node@v4 with: - node-version: 18 + node-version: 20 registry-url: https://registry.npmjs.org/ cache: "pnpm" diff --git a/.github/workflows/release-tag.yml b/.github/workflows/release-tag.yml index 26948527230128..ef12c0b8836dfb 100644 --- a/.github/workflows/release-tag.yml +++ b/.github/workflows/release-tag.yml @@ -19,6 +19,11 @@ jobs: - name: Get pkgName for tag id: tag run: | + # skip if alpha + if [[ $GITHUB_REF_NAME =~ alpha ]]; then + exit 0 + fi + # matching v2.0.0 / v2.0.0-beta.8 etc if [[ $GITHUB_REF_NAME =~ ^v.+ ]]; then pkgName="vite" @@ -31,6 +36,8 @@ jobs: echo "pkgName=$pkgName" >> $GITHUB_OUTPUT - name: Create Release for Tag + # only run if tag is not alpha + if: steps.tag.outputs.pkgName id: release_tag uses: yyx990803/release-tag@master env: diff --git a/.gitignore b/.gitignore index 3d534626f080dc..f70e82e087c59d 100644 --- a/.gitignore +++ b/.gitignore @@ -1,6 +1,7 @@ !**/glob-import/dir/node_modules .DS_Store .idea +.pnpm-store *.cpuprofile *.local *.log diff --git a/.npmrc b/.npmrc index f1b91089bd4116..ff6f1665b13102 100644 --- a/.npmrc +++ b/.npmrc @@ -1,6 +1,6 @@ hoist-pattern[]=ts-node # package/vite: postcss-load-config hoist-pattern[]=postcss # package/vite hoist-pattern[]=pug # playground/tailwind: @vue/compiler-sfc -strict-peer-dependencies=false shell-emulator=true auto-install-peers=false +dedupe-injected-deps=false diff --git a/.prettierignore b/.prettierignore index a9322e816629df..0fc71ddd114101 100644 --- a/.prettierignore +++ b/.prettierignore @@ -1,4 +1,5 @@ packages/*/CHANGELOG.md +packages/vite/src/node/ssr/runtime/__tests__/fixtures playground-temp/ dist/ temp/ @@ -8,5 +9,5 @@ pnpm-workspace.yaml playground/tsconfig-json-load-error/has-error/tsconfig.json playground/html/invalid.html playground/html/valid.html -playground/worker/classic-worker.js playground/external/public/slash@3.0.0.js +playground/ssr-html/public/slash@3.0.0.js diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index f00edd96523e24..f624e76897e578 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -31,6 +31,14 @@ To make this file used by `git blame`, you need to run the following command. git config --local blame.ignoreRevsFile .git-blame-ignore-revs ``` +## Documentation + +To develop the `docs/` site: + +1. Run `pnpm run build` in Vite's root folder. This will generate the types for `twoslash` to work in the code examples. If the types are not available, errors will be logged in step 2 but does not prevent the site from working. + +2. Run `pnpm run docs` in Vite's root folder. + ## Debugging To use breakpoints and explore code execution, you can use the ["Run and Debug"](https://code.visualstudio.com/docs/editor/debugging) feature from VS Code. @@ -68,7 +76,7 @@ You may wish to test your locally modified copy of Vite against another package ```json { "dependencies": { - "vite": "^4.0.0" + "vite": "^5.0.0" }, "pnpm": { "overrides": { @@ -166,7 +174,7 @@ For a mock dependency, make sure you add a `@vitejs/test-` prefix to the package ## Debug Logging -You can set the `DEBUG` environment variable to turn on debugging logs (e.g. `DEBUG="vite:resolve"`). To see all debug logs, you can set `DEBUG="vite:*"`, but be warned that it will be quite noisy. You can run `grep -r "createDebugger('vite:" packages/vite/src/` to see a list of available debug scopes. +You can set the `--debug` option to turn on debugging logs (e.g. `vite --debug resolve`). To see all debug logs, you can set `vite --debug *`, but be warned that it will be quite noisy. You can run `grep -r "createDebugger('vite:" packages/vite/src/` to see a list of available debug scopes. ## Pull Request Guidelines @@ -197,17 +205,45 @@ You can set the `DEBUG` environment variable to turn on debugging logs (e.g. `DE ### Issue Triaging Workflow - - - - +```mermaid +flowchart TD + start{Followed issue\ntemplate?} + start --NO--> close1[Close and ask to\nfollow template] + start --YES--> dupe{Is duplicate?} + dupe --YES--> close2[Close and point\nto duplicate] + dupe --NO--> repro{Has proper\nreproduction?} + repro --NO--> close3[Label: 'needs reproduction'\nbot will auto close if no update\nhas been made in 3 days] + repro --YES--> real{Is actually a bug?} + real --NO--> intended{Is the intended\nbehaviour?} + intended --YES--> explain[Explain and close\npoint to docs if needed] + intended --NO--> open[Keep open for discussion\nRemove 'pending triage' label] + real --YES--> real2["1. Remove 'pending triage' label\n2. Add related feature label if\napplicable (e.g. 'feat: ssr')\n3. Add priority and meta labels (see below)"] + real2 --> unusable{Does the\nbug make Vite\nunusable?} + unusable --YES--> maj{Does the bug\naffect the majority\nof Vite users?} + maj --YES--> p5[p5: urgent] + maj --NO--> p4[p4: important] + unusable --NO--> workarounds{Are there\nworkarounds for\nthe bug?} + workarounds --NO--> p3[p3: minor bug] + workarounds --YES--> p2[p2: edge case\nhas workaround] +``` ### Pull Request Review Workflow - - - - +```mermaid +flowchart TD + start{Bug fix\nor\nfeature} + start --BUG FIX--> strict_bug{"Is this a 'strict fix'?\ni.e. fixes an obvious\noversight with no\nside effects"} + start --FEATURE--> feature[- Discuss feature necessity\n- Is there a better way\nto address the need?\n- Review code quality\n- Add labels\n- Add to milestone\n- Add to Team Board] + feature -.-> approve_non_strict[- Run vite-ecosystem-ci if needed\n- Approve if you feel strongly\nthat the PR is needed\nand add to milestone] + strict_bug --YES--> strict[- Verify the fix locally\n- Review code quality\n- Require test case if applicable\n- Request changes if necessary\n- Add labels] + strict_bug --NO--> non_strict[Discuss the potential side\neffects of the fix, e.g.\n- Could it introduce implicit\nbehavior changes in other cases?\n- Does it introduce too much changes?\n- Add labels\n- Add to Team Board] + non_strict -.-> approve_non_strict + strict --> approve_strict[Approve if ready to be merged] + approve_strict --> merge_strict[Merge if approved by 2 or\nmore team members] + approve_non_strict -.-> merge_non_strict[Merge if approved by 2 or\nmore team members\nand the PR has been discussed\n in a team meeting] + merge_non_strict -.-> merge_extra + merge_strict --> merge_extra["- Use 'Squash and Merge'\n- Edit commit message to follow convention\n- In commit message body, list\nrelevant issues being fixed\ne.g. 'fix #1234, fix #1235'"] +``` ## Notes on Dependencies @@ -257,7 +293,7 @@ If you have publish access, the steps below explain how to cut a release for a p "Release" is done locally to generate the changelogs and git tags: 1. Make sure the git remote for https://github.com/vitejs/vite is set as `origin`. -2. In the `vite` project root `main` branch, run `git pull` and `pnpm i` to get it up-to-date. +2. In the `vite` project root `main` branch, run `git pull` and `pnpm i` to get it up-to-date. Then run `pnpm build`. 3. Run `pnpm release` and follow the prompts to cut a release for a package. It will generate the changelog, a git release tag, and push them to `origin`. You can run with the `--dry` flag to test it out. 4. When the command finishes, it will provide a link to https://github.com/vitejs/vite/actions/workflows/publish.yml. 5. Click the link to visit the page, and follow the next steps below. diff --git a/docs/.vitepress/buildEnd.config.ts b/docs/.vitepress/buildEnd.config.ts new file mode 100644 index 00000000000000..52a3625ceb3957 --- /dev/null +++ b/docs/.vitepress/buildEnd.config.ts @@ -0,0 +1,50 @@ +import path from 'node:path' +import { writeFileSync } from 'node:fs' +import { Feed } from 'feed' +import type { SiteConfig } from 'vitepress' +import { createContentLoader } from 'vitepress' + +const siteUrl = 'https://vitejs.dev' +const blogUrl = `${siteUrl}/blog` + +export const buildEnd = async (config: SiteConfig): Promise => { + const feed = new Feed({ + title: 'Vite', + description: 'Next Generation Frontend Tooling', + id: blogUrl, + link: blogUrl, + language: 'en', + image: 'https://vitejs.dev/og-image.png', + favicon: 'https://vitejs.dev/logo.svg', + copyright: 'Copyright © 2019-present Evan You & Vite Contributors', + }) + + const posts = await createContentLoader('blog/*.md', { + excerpt: true, + render: true, + }).load() + + posts.sort( + (a, b) => + +new Date(b.frontmatter.date as string) - + +new Date(a.frontmatter.date as string), + ) + + for (const { url, excerpt, frontmatter, html } of posts) { + feed.addItem({ + title: frontmatter.title, + id: `${siteUrl}${url}`, + link: `${siteUrl}${url}`, + description: excerpt, + content: html, + author: [ + { + name: frontmatter.author.name, + }, + ], + date: frontmatter.date, + }) + } + + writeFileSync(path.join(config.outDir, 'blog.rss'), feed.rss2()) +} diff --git a/docs/.vitepress/config.ts b/docs/.vitepress/config.ts index 9cc84c010ea6df..440a14d3aea2a2 100644 --- a/docs/.vitepress/config.ts +++ b/docs/.vitepress/config.ts @@ -1,4 +1,7 @@ -import { defineConfig, DefaultTheme } from 'vitepress' +import type { DefaultTheme } from 'vitepress' +import { defineConfig } from 'vitepress' +import { transformerTwoslash } from '@shikijs/vitepress-twoslash' +import { buildEnd } from './buildEnd.config' const ogDescription = 'Next Generation Frontend Tooling' const ogImage = 'https://vitejs.dev/og-image.png' @@ -30,34 +33,33 @@ const additionalTitle = ((): string => { } })() const versionLinks = ((): DefaultTheme.NavItemWithLink[] => { + const oldVersions: DefaultTheme.NavItemWithLink[] = [ + { + text: 'Vite 4 Docs', + link: 'https://v4.vitejs.dev', + }, + { + text: 'Vite 3 Docs', + link: 'https://v3.vitejs.dev', + }, + { + text: 'Vite 2 Docs', + link: 'https://v2.vitejs.dev', + }, + ] + switch (deployType) { case 'main': case 'local': return [ { - text: 'Vite 4 Docs (release)', + text: 'Vite 5 Docs (release)', link: 'https://vitejs.dev', }, - { - text: 'Vite 3 Docs', - link: 'https://v3.vitejs.dev', - }, - { - text: 'Vite 2 Docs', - link: 'https://v2.vitejs.dev', - }, + ...oldVersions, ] case 'release': - return [ - { - text: 'Vite 3 Docs', - link: 'https://v3.vitejs.dev', - }, - { - text: 'Vite 2 Docs', - link: 'https://v2.vitejs.dev', - }, - ] + return oldVersions } })() @@ -67,11 +69,17 @@ export default defineConfig({ head: [ ['link', { rel: 'icon', type: 'image/svg+xml', href: '/logo.svg' }], + [ + 'link', + { rel: 'alternate', type: 'application/rss+xml', href: '/blog.rss' }, + ], + ['link', { rel: 'me', href: 'https://m.webtoo.ls/@vite' }], ['meta', { property: 'og:type', content: 'website' }], ['meta', { property: 'og:title', content: ogTitle }], ['meta', { property: 'og:image', content: ogImage }], ['meta', { property: 'og:url', content: ogUrl }], ['meta', { property: 'og:description', content: ogDescription }], + ['meta', { property: 'og:site_name', content: 'vitejs' }], ['meta', { name: 'twitter:card', content: 'summary_large_image' }], ['meta', { name: 'twitter:site', content: '@vite_js' }], ['meta', { name: 'theme-color', content: '#646cff' }], @@ -93,6 +101,7 @@ export default defineConfig({ es: { label: 'Español', link: 'https://es.vitejs.dev' }, pt: { label: 'Português', link: 'https://pt.vitejs.dev' }, ko: { label: '한국어', link: 'https://ko.vitejs.dev' }, + de: { label: 'Deutsch', link: 'https://de.vitejs.dev' }, }, themeConfig: { @@ -137,9 +146,14 @@ export default defineConfig({ text: 'Resources', items: [ { text: 'Team', link: '/team' }, + { text: 'Blog', link: '/blog' }, { text: 'Releases', link: '/releases' }, { items: [ + { + text: 'Mastodon', + link: 'https://elk.zone/m.webtoo.ls/@vite', + }, { text: 'Twitter', link: 'https://twitter.com/vite_js', @@ -160,10 +174,6 @@ export default defineConfig({ text: 'DEV Community', link: 'https://dev.to/t/vite', }, - { - text: 'Rollup Plugins Compat', - link: 'https://vite-rollup-plugins.patak.dev/', - }, { text: 'Changelog', link: 'https://github.com/vitejs/vite/blob/main/packages/vite/CHANGELOG.md', @@ -272,6 +282,10 @@ export default defineConfig({ text: 'JavaScript API', link: '/guide/api-javascript', }, + { + text: 'Vite Runtime API', + link: '/guide/api-vite-runtime', + }, { text: 'Config Reference', link: '/config/', @@ -324,4 +338,19 @@ export default defineConfig({ level: [2, 3], }, }, + transformPageData(pageData) { + const canonicalUrl = `${ogUrl}/${pageData.relativePath}` + .replace(/\/index\.md$/, '/') + .replace(/\.md$/, '/') + pageData.frontmatter.head ??= [] + pageData.frontmatter.head.unshift( + ['link', { rel: 'canonical', href: canonicalUrl }], + ['meta', { property: 'og:title', content: pageData.title }], + ) + return pageData + }, + markdown: { + codeTransformers: [transformerTwoslash()], + }, + buildEnd, }) diff --git a/docs/.vitepress/theme/components/AsideSponsors.vue b/docs/.vitepress/theme/components/AsideSponsors.vue index 90e6addab959a5..92eef401923d87 100644 --- a/docs/.vitepress/theme/components/AsideSponsors.vue +++ b/docs/.vitepress/theme/components/AsideSponsors.vue @@ -18,5 +18,70 @@ const sponsors = computed(() => { + + diff --git a/docs/.vitepress/theme/components/BlogIndex.vue b/docs/.vitepress/theme/components/BlogIndex.vue new file mode 100644 index 00000000000000..cddd7c16ebedea --- /dev/null +++ b/docs/.vitepress/theme/components/BlogIndex.vue @@ -0,0 +1,46 @@ + + + + + diff --git a/docs/.vitepress/theme/components/blog.data.ts b/docs/.vitepress/theme/components/blog.data.ts new file mode 100644 index 00000000000000..39d45ec2b2b1a2 --- /dev/null +++ b/docs/.vitepress/theme/components/blog.data.ts @@ -0,0 +1,40 @@ +import { createContentLoader } from 'vitepress' + +interface Post { + title: string + url: string + date: { + time: number + string: string + } +} + +declare const data: Post[] +export { data } + +export default createContentLoader('blog/*.md', { + // excerpt: true, + transform(raw): Post[] { + return raw + .map(({ url, frontmatter }) => ({ + title: frontmatter.head.find((e) => e[1].property === 'og:title')[1] + .content, + url, + date: formatDate(frontmatter.date), + })) + .sort((a, b) => b.date.time - a.date.time) + }, +}) + +function formatDate(raw: string): Post['date'] { + const date = new Date(raw) + date.setUTCHours(12) + return { + time: +date, + string: date.toLocaleDateString('en-US', { + year: 'numeric', + month: 'long', + day: 'numeric', + }), + } +} diff --git a/docs/.vitepress/theme/composables/sponsor.ts b/docs/.vitepress/theme/composables/sponsor.ts index 4fd12e5facb1ee..622252593f7027 100644 --- a/docs/.vitepress/theme/composables/sponsor.ts +++ b/docs/.vitepress/theme/composables/sponsor.ts @@ -1,4 +1,4 @@ -import { ref, onMounted } from 'vue' +import { onMounted, onUnmounted, ref } from 'vue' interface Sponsors { special: Sponsor[] @@ -13,6 +13,10 @@ interface Sponsor { name: string img: string url: string + /** + * Expects to also have an **inversed** image with `-dark` postfix. + */ + hasDark?: true } // shared data across instances so we load only once. @@ -49,11 +53,59 @@ const viteSponsors: Pick = { url: 'https://remix.run/', img: '/remix.svg', }, + { + name: 'Nx', + url: 'https://nx.dev/', + img: '/nx.svg', + }, + { + name: 'Transloadit', + url: 'https://transloadit.com/?utm_source=vite&utm_medium=referral&utm_campaign=sponsorship&utm_content=website', + img: '/transloadit.svg', + hasDark: true, + }, + { + name: 'Huly', + url: 'https://huly.io/', + img: '/huly.svg', + }, + { + name: 'Handsontable', + url: 'https://handsontable.com/docs/react-data-grid/?utm_source=vite_docs&utm_medium=sponsorship&utm_campaign=library_sponsorship_2024', + img: '/handsontable.svg', + }, ], } +function toggleDarkLogos() { + if (data.value) { + const isDark = document.documentElement.classList.contains('dark') + data.value.forEach(({ items }) => { + items.forEach((s: Sponsor) => { + if (s.hasDark) { + s.img = isDark + ? s.img.replace(/(\.\w+)$/, '-dark$1') + : s.img.replace(/-dark(\.\w+)$/, '$1') + } + }) + }) + } +} + export function useSponsor() { onMounted(async () => { + const ob = new MutationObserver((list) => { + for (const m of list) { + if (m.attributeName === 'class') { + toggleDarkLogos() + } + } + }) + ob.observe(document.documentElement, { attributes: true }) + onUnmounted(() => { + ob.disconnect() + }) + if (data.value) { return } @@ -62,6 +114,7 @@ export function useSponsor() { const json = await result.json() data.value = mapSponsors(json) + toggleDarkLogos() }) return { @@ -84,7 +137,7 @@ function mapSponsors(sponsors: Sponsors) { { tier: 'Gold Sponsors', size: 'medium', - items: viteSponsors['gold'].concat(mapImgPath(sponsors['gold'])), + items: [...mapImgPath(sponsors['gold']), ...viteSponsors['gold']], }, ] } diff --git a/docs/.vitepress/theme/index.ts b/docs/.vitepress/theme/index.ts index a85c67e1df22f5..5fa0d638a3f9a1 100644 --- a/docs/.vitepress/theme/index.ts +++ b/docs/.vitepress/theme/index.ts @@ -1,6 +1,8 @@ import { h } from 'vue' import type { Theme } from 'vitepress' import DefaultTheme from 'vitepress/theme' +import TwoslashFloatingVue from '@shikijs/vitepress-twoslash/client' +import '@shikijs/vitepress-twoslash/style.css' import './styles/vars.css' import HomeSponsors from './components/HomeSponsors.vue' import AsideSponsors from './components/AsideSponsors.vue' @@ -16,5 +18,6 @@ export default { }, enhanceApp({ app }) { app.component('SvgImage', SvgImage) + app.use(TwoslashFloatingVue) }, } satisfies Theme diff --git a/docs/.vitepress/theme/styles/vars.css b/docs/.vitepress/theme/styles/vars.css index 58e5e79ff0f703..645a1e9222d613 100644 --- a/docs/.vitepress/theme/styles/vars.css +++ b/docs/.vitepress/theme/styles/vars.css @@ -118,3 +118,19 @@ .vp-sponsor.aside .vp-sponsor-grid.mini .vp-sponsor-grid-image { max-width: 124px; } + +.vp-sponsor-grid.big .vp-sponsor-grid-image { + max-height: 96px; +} + +.vp-sponsor-grid.mini .vp-sponsor-grid-image[alt='Bit'] { + max-height: 48px; +} + +.vp-sponsor-grid.xmini .vp-sponsor-grid-image[alt='JetBrains'] { + max-height: 54px; +} + +.vp-sponsor-grid.medium .vp-sponsor-grid-image[alt='JetBrains'] { + max-height: 100px; +} diff --git a/docs/.vitepress/tsconfig.json b/docs/.vitepress/tsconfig.json new file mode 100644 index 00000000000000..20b9618d576e3e --- /dev/null +++ b/docs/.vitepress/tsconfig.json @@ -0,0 +1,13 @@ +{ + "compilerOptions": { + "target": "ES2022", + "module": "ESNext", + "moduleResolution": "Bundler", + "strict": true, + "noImplicitOverride": true, + "noUnusedLocals": true, + "esModuleInterop": true, + "noEmit": true + }, + "exclude": ["cache", "dist"] +} diff --git a/docs/_data/team.js b/docs/_data/team.js index 72641a597cee55..a58e4cabf42935 100644 --- a/docs/_data/team.js +++ b/docs/_data/team.js @@ -22,6 +22,7 @@ export const core = [ links: [ { icon: 'github', link: 'https://github.com/patak-dev' }, { icon: 'twitter', link: 'https://twitter.com/patak_dev' }, + { icon: 'mastodon', link: 'https://elk.zone/m.webtoo.ls/@patak' }, ], sponsor: 'https://github.com/sponsors/patak-dev', }, @@ -35,6 +36,7 @@ export const core = [ links: [ { icon: 'github', link: 'https://github.com/antfu' }, { icon: 'twitter', link: 'https://twitter.com/antfu7' }, + { icon: 'mastodon', link: 'https://elk.zone/m.webtoo.ls/@antfu' }, ], sponsor: 'https://github.com/sponsors/antfu', }, @@ -46,6 +48,7 @@ export const core = [ links: [ { icon: 'github', link: 'https://github.com/bluwy' }, { icon: 'twitter', link: 'https://twitter.com/bluwyoo' }, + { icon: 'mastodon', link: 'https://elk.zone/m.webtoo.ls/@bluwy' }, ], sponsor: 'https://bjornlu.com/sponsor', }, @@ -53,25 +56,48 @@ export const core = [ avatar: 'https://github.com/sapphi-red.png', name: 'green', title: 'Web Developer', - desc: 'Vite team member. Call me sapphi or green or midori ;)', + desc: 'Vite core team member. Call me sapphi or green or midori ;)', links: [ { icon: 'github', link: 'https://github.com/sapphi-red' }, { icon: 'twitter', link: 'https://twitter.com/sapphi_red' }, + { icon: 'mastodon', link: 'https://elk.zone/m.webtoo.ls/@sapphi_red' }, ], sponsor: 'https://github.com/sponsors/sapphi-red', }, { - avatar: 'https://github.com/sodatea.png', - name: 'Haoqun Jiang', - title: 'Developer', - org: 'Vue.js', - orgLink: 'https://vuejs.org/', - desc: 'Vue/Vite core team member. Full-time open sourcerer.', + avatar: 'https://github.com/ArnaudBarre.png', + name: 'Arnaud Barré', + title: 'Frontend Developer', + desc: 'Passionate by tooling around TypeScript and React.', links: [ - { icon: 'github', link: 'https://github.com/sodatea' }, - { icon: 'twitter', link: 'https://twitter.com/haoqunjiang' }, + { icon: 'github', link: 'https://github.com/ArnaudBarre' }, + { icon: 'twitter', link: 'https://twitter.com/_ArnaudBarre' }, + { icon: 'mastodon', link: 'https://elk.zone/m.webtoo.ls/@ArnaudBarre' }, ], - sponsor: 'https://github.com/sponsors/sodatea', + sponsor: 'https://github.com/sponsors/ArnaudBarre', + }, + { + avatar: 'https://github.com/dominikg.png', + name: 'Dominik G.', + title: 'Resident CI Expert', + desc: 'Team Member of Vite and Svelte', + links: [ + { icon: 'github', link: 'https://github.com/dominikg' }, + { icon: 'mastodon', link: 'https://elk.zone/m.webtoo.ls/@dominikg' }, + ], + sponsor: 'https://github.com/sponsors/dominikg', + }, + { + avatar: 'https://github.com/sheremet-va.png', + name: 'Vladimir', + title: 'Core team member of Vitest & Vite', + desc: 'An open source fullstack developer', + links: [ + { icon: 'github', link: 'https://github.com/sheremet-va' }, + { icon: 'mastodon', link: 'https://elk.zone/m.webtoo.ls/@sheremet_va' }, + { icon: 'twitter', link: 'https://twitter.com/sheremet_va' }, + ], + sponsor: 'https://github.com/sponsors/sheremet-va', }, { avatar: 'https://github.com/Shinigami92.png', @@ -82,10 +108,27 @@ export const core = [ desc: 'Passionate TypeScript enthusiast working extensively with Vue SPA and pug.', links: [ { icon: 'github', link: 'https://github.com/Shinigami92' }, - { icon: 'twitter', link: 'https://twitter.com/Shini_92' }, + { icon: 'mastodon', link: 'https://elk.zone/mas.to/@Shini92' }, ], sponsor: 'https://github.com/sponsors/Shinigami92', }, + { + avatar: 'https://github.com/sodatea.png', + name: 'Haoqun Jiang', + title: 'Developer', + org: 'Vue.js', + orgLink: 'https://vuejs.org/', + desc: 'Vue/Vite core team member. Full-time open sourcerer.', + links: [ + { icon: 'github', link: 'https://github.com/sodatea' }, + { icon: 'twitter', link: 'https://twitter.com/haoqunjiang' }, + { icon: 'mastodon', link: 'https://elk.zone/m.webtoo.ls/@haoqun' }, + ], + sponsor: 'https://github.com/sponsors/sodatea', + }, +] + +export const emeriti = [ { avatar: 'https://i.imgur.com/KMed6rQ.jpeg', name: 'Alec Larson', @@ -95,7 +138,6 @@ export const core = [ { icon: 'github', link: 'https://github.com/aleclarson' }, { icon: 'twitter', link: 'https://twitter.com/retropragma' }, ], - sponsor: 'https://github.com/sponsors/aleclarson', }, { avatar: 'https://github.com/poyoho.png', @@ -107,40 +149,6 @@ export const core = [ { icon: 'twitter', link: 'https://twitter.com/yoho_po' }, ], }, - { - avatar: 'https://github.com/ArnaudBarre.png', - name: 'Arnaud Barré', - title: 'Frontend Developer', - desc: 'Passionate by tooling around TypeScript and React.', - links: [{ icon: 'github', link: 'https://github.com/ArnaudBarre' }], - sponsor: 'https://github.com/sponsors/ArnaudBarre', - }, - { - avatar: 'https://github.com/dominikg.png', - name: 'Dominik G.', - title: 'Resident CI Expert', - desc: 'Team Member of Vite and Svelte', - links: [ - { icon: 'github', link: 'https://github.com/dominikg' }, - { icon: 'mastodon', link: 'https://elk.zone/m.webtoo.ls/@dominikg' }, - ], - sponsor: 'https://github.com/sponsors/dominikg', - }, - { - avatar: 'https://github.com/sheremet-va.png', - name: 'Vladimir', - title: 'Core team member of Vitest & Vite', - desc: 'An open source fullstack developer', - links: [ - { icon: 'github', link: 'https://github.com/sheremet-va' }, - { icon: 'mastodon', link: 'https://elk.zone/m.webtoo.ls/@sheremet_va' }, - { icon: 'twitter', link: 'https://twitter.com/sheremet_va' }, - ], - sponsor: 'https://github.com/sponsors/sheremet-va', - }, -] - -export const emeriti = [ { avatar: 'https://github.com/ygj6.png', name: 'ygj6', diff --git a/docs/blog.md b/docs/blog.md new file mode 100644 index 00000000000000..f3f8c6298284b1 --- /dev/null +++ b/docs/blog.md @@ -0,0 +1,13 @@ +--- +sidebar: false +editLink: false +outline: false +--- + + + +# Latest From the Vite Blog + + diff --git a/docs/blog/announcing-vite2.md b/docs/blog/announcing-vite2.md index d81debc9b38d69..11cd6d52307812 100644 --- a/docs/blog/announcing-vite2.md +++ b/docs/blog/announcing-vite2.md @@ -1,5 +1,22 @@ --- +title: Announcing Vite 2.0 +author: + - name: The Vite Team sidebar: false +date: 2021-02-16 +head: + - - meta + - property: og:type + content: website + - - meta + - property: og:title + content: Announcing Vite 2.0 + - - meta + - property: og:url + content: https://vitejs.dev/blog/announcing-vite2 + - - meta + - property: og:description + content: Vite 2 Release Announcement --- # Announcing Vite 2.0 diff --git a/docs/blog/announcing-vite3.md b/docs/blog/announcing-vite3.md index dba46bb8a05981..4f25309ca72f04 100644 --- a/docs/blog/announcing-vite3.md +++ b/docs/blog/announcing-vite3.md @@ -1,4 +1,8 @@ --- +title: Vite 3.0 is out! +author: + name: The Vite Team +date: 2022-07-23 sidebar: false head: - - meta @@ -25,7 +29,7 @@ head: _July 23, 2022_ - Check out the [Vite 4.0 announcement](./announcing-vite4.md) -In February last year, [Evan You](https://twitter.com/youyuxi) released Vite 2. Since then, its adoption has grown non-stop, reaching more than 1 million npm downloads per week. A sprawling ecosystem rapidly formed after the release. Vite is powering a renewed innovation race in Web frameworks. [Nuxt 3](https://v3.nuxtjs.org/) uses Vite by default. [SvelteKit](https://kit.svelte.dev/), [Astro](https://astro.build/), [Hydrogen](https://hydrogen.shopify.dev/), and [SolidStart](https://docs.solidjs.com/start) are all built with Vite. [Laravel has now decided to use Vite by default](https://laravel.com/docs/9.x/vite). [Vite Ruby](https://vite-ruby.netlify.app/) shows how Vite can improve Rails DX. [Vitest](https://vitest.dev) is making strides as a Vite-native alternative to Jest. Vite is behind [Cypress](https://docs.cypress.io/guides/component-testing/writing-your-first-component-test) and [Playwright](https://playwright.dev/docs/test-components)'s new Component Testing features, Storybook has [Vite as an official builder](https://github.com/storybookjs/builder-vite). And [the list goes on](https://patak.dev/vite/ecosystem.html). Maintainers from most of these projects got involved in improving the Vite core itself, working closely with the Vite [team](https://vitejs.dev/team) and other contributors. +In February last year, [Evan You](https://twitter.com/youyuxi) released Vite 2. Since then, its adoption has grown non-stop, reaching more than 1 million npm downloads per week. A sprawling ecosystem rapidly formed after the release. Vite is powering a renewed innovation race in Web frameworks. [Nuxt 3](https://v3.nuxtjs.org/) uses Vite by default. [SvelteKit](https://kit.svelte.dev/), [Astro](https://astro.build/), [Hydrogen](https://hydrogen.shopify.dev/), and [SolidStart](https://docs.solidjs.com/quick-start) are all built with Vite. [Laravel has now decided to use Vite by default](https://laravel.com/docs/9.x/vite). [Vite Ruby](https://vite-ruby.netlify.app/) shows how Vite can improve Rails DX. [Vitest](https://vitest.dev) is making strides as a Vite-native alternative to Jest. Vite is behind [Cypress](https://docs.cypress.io/guides/component-testing/writing-your-first-component-test) and [Playwright](https://playwright.dev/docs/test-components)'s new Component Testing features, Storybook has [Vite as an official builder](https://github.com/storybookjs/builder-vite). And [the list goes on](https://patak.dev/vite/ecosystem.html). Maintainers from most of these projects got involved in improving the Vite core itself, working closely with the Vite [team](https://vitejs.dev/team) and other contributors. ![Vite 3 Announcement Cover Image](/og-image-announcing-vite3.png) diff --git a/docs/blog/announcing-vite4-3.md b/docs/blog/announcing-vite4-3.md index 756c3579d1bed0..534a2fdcb41bd6 100644 --- a/docs/blog/announcing-vite4-3.md +++ b/docs/blog/announcing-vite4-3.md @@ -1,4 +1,8 @@ --- +title: Vite 4.3 is out! +author: + name: The Vite Team +date: 2023-04-20 sidebar: false head: - - meta diff --git a/docs/blog/announcing-vite4.md b/docs/blog/announcing-vite4.md index 950ac637a3c902..587d439733ab38 100644 --- a/docs/blog/announcing-vite4.md +++ b/docs/blog/announcing-vite4.md @@ -1,4 +1,8 @@ --- +title: Vite 4.0 is out! +author: + name: The Vite Team +date: 2022-12-09 sidebar: false head: - - meta @@ -23,7 +27,7 @@ head: # Vite 4.0 is out! -_December 9, 2022_ +_December 9, 2022_ - Check out the [Vite 5.0 announcement](./announcing-vite5.md) Vite 3 [was released](./announcing-vite3.md) five months ago. npm downloads per week have gone from 1 million to 2.5 million since then. The ecosystem has matured too, and continues to grow. In this year's [Jamstack Conf survey](https://twitter.com/vite_js/status/1589665610119585793), usage among the community jumped from 14% to 32% while keeping a high 9.7 satisfaction score. We saw the stable releases of [Astro 1.0](https://astro.build/), [Nuxt 3](https://v3.nuxtjs.org/), and other Vite-powered frameworks that are innovating and collaborating: [SvelteKit](https://kit.svelte.dev/), [Solid Start](https://www.solidjs.com/blog/introducing-solidstart), [Qwik City](https://qwik.builder.io/qwikcity/overview/). Storybook announced first-class support for Vite as one of its main features for [Storybook 7.0](https://storybook.js.org/blog/first-class-vite-support-in-storybook/). Deno now [supports Vite](https://www.youtube.com/watch?v=Zjojo9wdvmY). [Vitest](https://vitest.dev) adoption is exploding, it will soon represent half of Vite's npm downloads. Nx is also investing in the ecosystem, and [officially supports Vite](https://nx.dev/packages/vite). @@ -38,7 +42,7 @@ Today, the Vite [team](https://vitejs.dev/team) with the help of our ecosystem p Quick links: - [Docs](/) -- [Migration Guide](/guide/migration) +- [Migration Guide](https://v4.vitejs.dev/guide/migration.html) - [Changelog](https://github.com/vitejs/vite/blob/main/packages/vite/CHANGELOG.md#400-2022-12-09) Docs in other languages: @@ -87,7 +91,7 @@ This double loading could occur since a `.css` file will be emitted and it's lik import stuff from './global.css?inline' ``` -Learn more in the [Migration Guide](/guide/migration). +Learn more in the [Migration Guide](https://v4.vitejs.dev/guide/migration.html). ## Environment Variables diff --git a/docs/blog/announcing-vite5-1.md b/docs/blog/announcing-vite5-1.md new file mode 100644 index 00000000000000..35ee61c172870e --- /dev/null +++ b/docs/blog/announcing-vite5-1.md @@ -0,0 +1,132 @@ +--- +title: Vite 5.1 is out! +author: + name: The Vite Team +date: 2024-02-08 +sidebar: false +head: + - - meta + - property: og:type + content: website + - - meta + - property: og:title + content: Announcing Vite 5.1 + - - meta + - property: og:image + content: https://vitejs.dev/og-image-announcing-vite5-1.png + - - meta + - property: og:url + content: https://vitejs.dev/blog/announcing-vite5-1 + - - meta + - property: og:description + content: Vite 5.1 Release Announcement + - - meta + - name: twitter:card + content: summary_large_image +--- + +# Vite 5.1 is out! + +_February 8, 2024_ + +![Vite 5.1 Announcement Cover Image](/og-image-announcing-vite5-1.png) + +Vite 5 [was released](./announcing-vite5.md) last November, and it represented another big leap for Vite and the ecosystem. A few weeks ago we celebrated 10 million weekly npm downloads and 900 contributors to the Vite repo. Today, we're excited to announce the release of Vite 5.1. + +Quick links: [Docs](/), [Changelog](https://github.com/vitejs/vite/blob/main/packages/vite/CHANGELOG.md#510-2024-02-08) + +Docs in other languages: [简体中文](https://cn.vitejs.dev/), [日本語](https://ja.vitejs.dev/), [Español](https://es.vitejs.dev/), [Português](https://pt.vitejs.dev/), [한국어](https://ko.vitejs.dev/), [Deutsch](https://de.vitejs.dev/) + +Try Vite 5.1 online in StackBlitz: [vanilla](https://vite.new/vanilla-ts), [vue](https://vite.new/vue-ts), [react](https://vite.new/react-ts), [preact](https://vite.new/preact-ts), [lit](https://vite.new/lit-ts), [svelte](https://vite.new/svelte-ts), [solid](https://vite.new/solid-ts), [qwik](https://vite.new/qwik-ts). + +If you're new to Vite, we suggest reading first the [Getting Started](/guide/) and [Features](/guide/features) guides. + +To stay up to date, follow us on [X](https://x.com/vite_js) or [Mastodon](https://webtoo.ls/@vite). + +## Vite Runtime API + +Vite 5.1 adds experimental support for a new Vite Runtime API. It allows running any code by processing it with Vite plugins first. It is different from `server.ssrLoadModule` because the runtime implementation is decoupled from the server. This lets library and framework authors implement their own layer of communication between the server and the runtime. This new API is intended to replace Vite's current SSR primitives once it is stable. + +The new API brings many benefits: + +- Support for HMR during SSR. +- It is decoupled from the server, so there is no limit on how many clients can use a single server - every client has its own module cache (you can even communicate with it how you want - using message channel/fetch call/direct function call/websocket). +- It doesn't depend on any node/bun/deno built-in APIs, so it can run in any environment. +- It's easy to integrate with tools that have their own mechanism to run code (you can provide a runner to use `eval` instead of `new AsyncFunction` for example). + +The initial idea [was proposed by Pooya Parsa](https://github.com/nuxt/vite/pull/201) and implemented by [Anthony Fu](https://github.com/antfu) as the [vite-node](https://github.com/vitest-dev/vitest/tree/main/packages/vite-node#readme) package to [power Nuxt 3 Dev SSR](https://antfu.me/posts/dev-ssr-on-nuxt) and later also used as the base for [Vitest](https://vitest.dev). So the general idea of vite-node has been battle-tested for quite some time now. This is a new iteration of the API by [Vladimir Sheremet](https://github.com/sheremet-va), who had already re-implemented vite-node in Vitest and took the learnings to make the API even more powerful and flexible when adding it to Vite Core. The PR was one year in the makings, you can see the evolution and discussions with ecosystem maintainers [here](https://github.com/vitejs/vite/issues/12165). + +Read more in the [Vite Runtime API guide](/guide/api-vite-runtime) and [give us feedback](https://github.com/vitejs/vite/discussions/15774). + +## Features + +### Improved support for `.css?url` + +Import CSS files as URLs now works reliably and correctly. This was the last remaining hurdle in Remix's move to Vite. See ([#15259](https://github.com/vitejs/vite/issues/15259)). + +### `build.assetsInlineLimit` now supports a callback + +Users can now [provide a callback](/config/build-options.html#build-assetsinlinelimit) that returns a boolean to opt-in or opt-out of inlining for specific assets. If `undefined` is returned, the default logic applies. See ([#15366](https://github.com/vitejs/vite/issues/15366)). + +### Improved HMR for circular import + +In Vite 5.0, accepted modules within circular imports always triggered a full page reload even if they can be handled fine in the client. This is now relaxed to allow HMR to apply without a full page reload, but if any error happens during HMR, the page will be reloaded. See ([#15118](https://github.com/vitejs/vite/issues/15118)). + +### Support `ssr.external: true` to externalize all SSR packages + +Historically, Vite externalizes all packages except for linked packages. This new option can be used to force externalize all packages including linked packages too. This is handy in tests within monorepos where we want to emulate the usual case of all packages externalized, or when using `ssrLoadModule` to load an arbitrary file and we want to always external packages as we don't care about HMR. See ([#10939](https://github.com/vitejs/vite/issues/10939)). + +### Expose `close` method in the preview server + +The preview server now exposes a `close` method, which will properly teardown the server including all opened socket connections. See ([#15630](https://github.com/vitejs/vite/issues/15630)). + +## Performance improvements + +Vite keeps getting faster with each release, and Vite 5.1 is packed with performance improvements. We measured the loading time for 10K modules (25 level deep tree) using [vite-dev-server-perf](https://github.com/yyx990803/vite-dev-server-perf) for all minor versions from Vite 4.0. This is a good benchmark to measure the effect of Vite's bundle-less approach. Each module is a small TypeScript file with a counter and imports to other files in the tree, so this mostly measuring the time it takes to do the requests a separate modules. In Vite 4.0, loading 10K modules took 8 seconds on a M1 MAX. We had a breakthrough in [Vite 4.3 were we focused on performance](./announcing-vite4-3.md), and we were able to load them in 6.35 seconds. In Vite 5.1, we managed to do another performance leap. Vite is now serving the 10K modules in 5.35 seconds. + +![Vite 10K Modules Loading time progression](/vite5-1-10K-modules-loading-time.png) + +The results of this benchmark run on Headless Puppeteer and are a good way to compare versions. They don't represent the time as experienced by users though. When running the same 10K modules in an Incognito window is Chrome, we have: + +| 10K Modules | Vite 5.0 | Vite 5.1 | +| --------------------- | :------: | :------: | +| Loading time | 2892ms | 2765ms | +| Loading time (cached) | 2778ms | 2477ms | +| Full reload | 2003ms | 1878ms | +| Full reload (cached) | 1682ms | 1604ms | + +### Run CSS preprocessors in threads + +Vite now has opt-in support for running CSS preprocessors in threads. You can enable it using [`css.preprocessorMaxWorkers: true`](/config/shared-options.html#css-preprocessormaxworkers). For a Vuetify 2 project, dev startup time was reduced by 40% with this feature enabled. There is [performance comparison for others setups in the PR](https://github.com/vitejs/vite/pull/13584#issuecomment-1678827918). See ([#13584](https://github.com/vitejs/vite/issues/13584)). [Give Feedback](https://github.com/vitejs/vite/discussions/15835). + +### New options to improve server cold starts + +You can set `optimizeDeps.holdUntilCrawlEnd: false` to switch to a new strategy for deps optimization that may help in big projects. We're considering switching to this strategy by default in the future. [Give Feedback](https://github.com/vitejs/vite/discussions/15834). ([#15244](https://github.com/vitejs/vite/issues/15244)) + +### Faster resolving with cached checks + +The `fs.cachedChecks` optimization is now enabled by default. In Windows, `tryFsResolve` was ~14x faster with it, and resolving ids overall got a ~5x speed up in the triangle benchmark. ([#15704](https://github.com/vitejs/vite/issues/15704)) + +### Internal performance improvements + +The dev server had several incremental performance gains. A new middleware to short-circuit on 304 ([#15586](https://github.com/vitejs/vite/issues/15586)). We avoided `parseRequest` in hot paths ([#15617](https://github.com/vitejs/vite/issues/15617)). Rollup is now properly lazy loaded ([#15621](https://github.com/vitejs/vite/issues/15621)) + +## Deprecations + +We continue to reduce Vite's API surface where possible to make the project maintainable long term. + +### Deprecated `as` option in `import.meta.glob` + +The standard moved to [Import Attributes](https://github.com/tc39/proposal-import-attributes), but we don't plan to replace `as` with a new option at this point. Instead, it is recommended that the user switches to `query`. See ([#14420](https://github.com/vitejs/vite/issues/14420)). + +### Removed experimental build-time pre-bundling + +Build-time pre-bundling, an experimental feature added in Vite 3, is removed. With Rollup 4 switching its parser to native, and Rolldown being worked on, both the performance and the dev-vs-build inconsistency story for this feature are no longer valid. We want to continue improving dev/build consistency, and have concluded that using Rolldown for "prebundling during dev" and "production builds" is the better bet moving forward. Rolldown may also implement caching in a way that is a lot more efficient during build than deps prebundling. See ([#15184](https://github.com/vitejs/vite/issues/15184)). + +## Get Involved + +We are grateful to the [900 contributors to Vite Core](https://github.com/vitejs/vite/graphs/contributors), and the maintainers of plugins, integrations, tools, and translations that keeps pushing the ecosystem forward. If you're enjoying Vite, we invite you to participate and help us. Check out our [Contributing Guide](https://github.com/vitejs/vite/blob/main/CONTRIBUTING.md), and jump into [triaging issues](https://github.com/vitejs/vite/issues), [reviewing PRs](https://github.com/vitejs/vite/pulls), answering questions at [GitHub Discussions](https://github.com/vitejs/vite/discussions) and helping others in the community in [Vite Land](https://chat.vitejs.dev). + +## Acknowledgments + +Vite 5.1 is possible thanks to our community of contributors, maintainers in the ecosystem, and the [Vite Team](/team). A shout out to the individuals and companies sponsoring Vite development. [StackBlitz](https://stackblitz.com/), [Nuxt Labs](https://nuxtlabs.com/), and [Astro](https://astro.build) for hiring Vite team members. And also to the sponsors on [Vite's GitHub Sponsors](https://github.com/sponsors/vitejs), [Vite's Open Collective](https://opencollective.com/vite), and [Evan You's GitHub Sponsors](https://github.com/sponsors/yyx990803). diff --git a/docs/blog/announcing-vite5.md b/docs/blog/announcing-vite5.md new file mode 100644 index 00000000000000..dd4c1d46a275b0 --- /dev/null +++ b/docs/blog/announcing-vite5.md @@ -0,0 +1,110 @@ +--- +title: Vite 5.0 is out! +author: + name: The Vite Team +date: 2023-11-16 +sidebar: false +head: + - - meta + - property: og:type + content: website + - - meta + - property: og:title + content: Announcing Vite 5 + - - meta + - property: og:image + content: https://vitejs.dev/og-image-announcing-vite5.png + - - meta + - property: og:url + content: https://vitejs.dev/blog/announcing-vite5 + - - meta + - property: og:description + content: Vite 5 Release Announcement + - - meta + - name: twitter:card + content: summary_large_image +--- + +# Vite 5.0 is out! + +_November 16, 2023_ + +![Vite 5 Announcement Cover Image](/og-image-announcing-vite5.png) + +Vite 4 [was released](./announcing-vite4.md) almost a year ago, and it served as a solid base for the ecosystem. npm downloads per week jumped from 2.5 million to 7.5 million, as projects keep building on a shared infrastructure. Frameworks continued to innovate, and on top of [Astro](https://astro.build/), [Nuxt](https://nuxt.com/), [SvelteKit](https://kit.svelte.dev/), [Solid Start](https://www.solidjs.com/blog/introducing-solidstart), [Qwik City](https://qwik.builder.io/qwikcity/overview/), between others, we saw new frameworks joining and making the ecosystem stronger. [RedwoodJS](https://redwoodjs.com/) and [Remix](https://remix.run/) switching to Vite paves the way for further adoption in the React ecosystem. [Vitest](https://vitest.dev) kept growing at an even faster pace than Vite. Its team has been hard at work and will soon [release Vitest 1.0](https://github.com/vitest-dev/vitest/issues/3596). The story of Vite when used with other tools such as [Storybook](https://storybook.js.org), [Nx](https://nx.dev), and [Playwright](https://playwright.dev) kept improving, and the same goes for environments, with Vite dev working both in [Deno](https://deno.com) and [Bun](https://bun.sh). + +We had the second edition of [ViteConf](https://viteconf.org/23/replay) a month ago, hosted by [StackBlitz](https://stackblitz.com). Like last year, most of the projects in the ecosystem got together to share ideas and connect to keep expanding the commons. We're also seeing new pieces complement the meta-framework tool belt like [Volar](https://volarjs.dev/) and [Nitro](https://nitro.unjs.io/). The Rollup team released [Rollup 4](https://rollupjs.org) that same day, a tradition Lukas started last year. + +Six months ago, Vite 4.3 [was released](./announcing-vite4.md). This release significantly improved the dev server performance. However, there is still ample room for improvement. At ViteConf, [Evan You unveiled Vite's long-term plan to work on Rolldown](https://www.youtube.com/watch?v=hrdwQHoAp0M), a Rust-port of Rollup with compatible APIs. Once it is ready, we intend to use it in Vite Core to take on the tasks of both Rollup and esbuild. This will mean a boost in build performance (and later on in dev performance too as we move perf-sensitive parts of Vite itself to Rust), and a big reduction of inconsistencies between dev and build. Rolldown is currently in early stages and the team is preparing to open source the codebase before the end of the year. Stay tuned! + +Today, we mark another big milestone in Vite's path. The Vite [team](/team), [contributors](https://github.com/vitejs/vite/graphs/contributors), and ecosystem partners, are excited to announce the release of Vite 5. Vite is now using [Rollup 4](https://github.com/vitejs/vite/pull/14508), which already represents a big boost in build performance. And there are also new options to improve your dev server performance profile. + +Vite 5 focuses on cleaning up the API (removing deprecated features) and streamlines several features closing long-standing issues, for example switching `define` to use proper AST replacements instead of regexes. We also continue to take steps to future-proof Vite (Node.js 18+ is now required, and [the CJS Node API has been deprecated](/guide/migration#deprecate-cjs-node-api)). + +Quick links: + +- [Docs](/) +- [Migration Guide](/guide/migration) +- [Changelog](https://github.com/vitejs/vite/blob/main/packages/vite/CHANGELOG.md#500-2023-11-16) + +Docs in other languages: + +- [简体中文](https://cn.vitejs.dev/) +- [日本語](https://ja.vitejs.dev/) +- [Español](https://es.vitejs.dev/) +- [Português](https://pt.vitejs.dev/) +- [한국어](https://ko.vitejs.dev/) +- [Deutsch](https://de.vitejs.dev/) (new translation!) + +If you're new to Vite, we suggest reading first the [Getting Started](/guide/) and [Features](/guide/features) guides. + +We appreciate the more than [850 contributors to Vite Core](https://github.com/vitejs/vite/graphs/contributors), and the maintainers and contributors of Vite plugins, integrations, tools, and translations that have helped us reach here. We encourage you to get involved and continue to improve Vite with us. You can learn more at our [Contributing Guide](https://github.com/vitejs/vite/blob/main/CONTRIBUTING.md). To get started, we recommend [triaging issues](https://github.com/vitejs/vite/issues), [reviewing PRs](https://github.com/vitejs/vite/pulls), sending failing tests PRs based on open issues, and helping others in [Discussions](https://github.com/vitejs/vite/discussions) and Vite Land's [help forum](https://discord.com/channels/804011606160703521/1019670660856942652). You'll learn a lot along the way and have a smooth path to further contributions to the project. If you have doubts, join us on our [Discord community](http://chat.vitejs.dev/) and say hi on the [#contributing channel](https://discord.com/channels/804011606160703521/804439875226173480). + +To stay up to date, follow us on [X](https://twitter.com/vite_js) or [Mastodon](https://webtoo.ls/@vite). + +## Quick start with Vite 5 + +Use `pnpm create vite` to scaffold a Vite project with your preferred framework, or open a started template online to play with Vite 5 using [vite.new](https://vite.new). You can also run `pnpm create vite-extra` to get access to templates from other frameworks and runtimes (Solid, Deno, SSR, and library starters). `create vite-extra` templates are also available when you run `create vite` under the `Others` option. + +Note that Vite starter templates are intended to be used as a playground to test Vite with different frameworks. When building your next project, we recommend reaching out to the starters recommended by each framework. Some frameworks now redirect in `create vite` to their starters too (`create-vue` and `Nuxt 3` for Vue, and `SvelteKit` for Svelte). + +## Node.js Support + +Vite no longer supports Node.js 14 / 16 / 17 / 19, which reached its EOL. Node.js 18 / 20+ is now required. + +## Performance + +On top of Rollup 4's build performance improvements, there is a new guide to help you identify and fix common performance issues at [https://vitejs.dev/guide/performance](/guide/performance). + +Vite 5 also introduces [server.warmup](/guide/performance.html#warm-up-frequently-used-files), a new feature to improve startup time. It lets you define a list of modules that should be pre-transformed as soon as the server starts. When using [`--open` or `server.open`](/config/server-options.html#server-open), Vite will also automatically warm up the entry point of your app or the provided URL to open. + +## Main Changes + +- [Vite is now powered by Rollup 4](/guide/migration#rollup-4) +- [The CJS Node API has been deprecated](/guide/migration#deprecate-cjs-node-api) +- [Rework `define` and `import.meta.env.*` replacement strategy](/guide/migration#rework-define-and-import-meta-env-replacement-strategy) +- [SSR externalized modules value now matches production](/guide/migration#ssr-externalized-modules-value-now-matches-production) +- [`worker.plugins` is now a function](/guide/migration#worker-plugins-is-now-a-function) +- [Allow path containing `.` to fallback to index.html](/guide/migration#allow-path-containing-to-fallback-to-index-html) +- [Align dev and preview HTML serving behavior](/guide/migration#align-dev-and-preview-html-serving-behaviour) +- [Manifest files are now generated in `.vite` directory by default](/guide/migration#manifest-files-are-now-generated-in-vite-directory-by-default) +- [CLI shortcuts require an additional `Enter` press](/guide/migration#cli-shortcuts-require-an-additional-enter-press) +- [Update `experimentalDecorators` and `useDefineForClassFields` TypeScript behavior](/guide/migration#update-experimentaldecorators-and-usedefineforclassfields-typescript-behaviour) +- [Remove `--https` flag and `https: true`](/guide/migration#remove-https-flag-and-https-true) +- [Remove `resolvePackageEntry` and `resolvePackageData` APIs](/guide/migration#remove-resolvepackageentry-and-resolvepackagedata-apis) +- [Removes previously deprecated APIs](/guide/migration#removed-deprecated-apis) +- [Read more about advanced changes affecting plugin and tool authors](/guide/migration#advanced) + +## Migrating to Vite 5 + +We have worked with ecosystem partners to ensure a smooth migration to this new major. Once again, [vite-ecosystem-ci](https://www.youtube.com/watch?v=7L4I4lDzO48) has been crucial to help us make bolder changes while avoiding regressions. We're thrilled to see other ecosystems adopt similar schemes to improve the collaboration between their projects and downstream maintainers. + +For most projects, the update to Vite 5 should be straight forward. But we advise reviewing the [detailed Migration Guide](/guide/migration) before upgrading. + +A low level breakdown with the full list of changes to Vite core can be found at the [Vite 5 Changelog](https://github.com/vitejs/vite/blob/main/packages/vite/CHANGELOG.md#500-2023-11-16). + +## Acknowledgments + +Vite 5 is the result of long hours of work by our community of contributors, downstream maintainers, plugins authors, and the [Vite Team](/team). A big shout out to [Bjorn Lu](https://twitter.com/bluwyoo) for leading the release process for this major. + +We're also thankful to individuals and companies sponsoring Vite development. [StackBlitz](https://stackblitz.com/), [Nuxt Labs](https://nuxtlabs.com/), and [Astro](https://astro.build) continue to invest in Vite by hiring Vite team members. A shout out to sponsors on [Vite's GitHub Sponsors](https://github.com/sponsors/vitejs), [Vite's Open Collective](https://opencollective.com/vite), and [Evan You's GitHub Sponsors](https://github.com/sponsors/yyx990803). A special mention to [Remix](https://remix.run/) for becoming a Gold sponsor and contributing back after switching to Vite. diff --git a/docs/config/build-options.md b/docs/config/build-options.md index 757847c41e3d74..3713daf534b349 100644 --- a/docs/config/build-options.md +++ b/docs/config/build-options.md @@ -48,11 +48,19 @@ type ResolveModulePreloadDependenciesFn = ( The `resolveDependencies` function will be called for each dynamic import with a list of the chunks it depends on, and it will also be called for each chunk imported in entry HTML files. A new dependencies array can be returned with these filtered or more dependencies injected, and their paths modified. The `deps` paths are relative to the `build.outDir`. Returning a relative path to the `hostId` for `hostType === 'js'` is allowed, in which case `new URL(dep, import.meta.url)` is used to get an absolute path when injecting this module preload in the HTML head. -```js +```js twoslash +/** @type {import('vite').UserConfig} */ +const config = { + // prettier-ignore + build: { +// ---cut-before--- modulePreload: { resolveDependencies: (filename, deps, { hostId, hostType }) => { return deps.filter(condition) - } + }, +}, +// ---cut-after--- + }, } ``` @@ -82,11 +90,13 @@ Specify the directory to nest generated assets under (relative to `build.outDir` ## build.assetsInlineLimit -- **Type:** `number` +- **Type:** `number` | `((filePath: string, content: Buffer) => boolean | undefined)` - **Default:** `4096` (4 KiB) Imported or referenced assets that are smaller than this threshold will be inlined as base64 URLs to avoid extra http requests. Set to `0` to disable inlining altogether. +If a callback is passed, a boolean can be returned to opt-in or opt-out. If nothing is returned the default logic applies. + Git LFS placeholders are automatically excluded from inlining because they do not contain the content of the file they represent. ::: tip Note @@ -191,7 +201,7 @@ During the SSR build, static assets aren't emitted as it is assumed they would b ## build.minify - **Type:** `boolean | 'terser' | 'esbuild'` -- **Default:** `'esbuild'` +- **Default:** `'esbuild'` for client build, `false` for SSR build Set to `false` to disable minification, or specify the minifier to use. The default is [esbuild](https://github.com/evanw/esbuild) which is 20 ~ 40x faster than terser and only 1 ~ 2% worse compression. [Benchmarks](https://github.com/privatenumber/minification-benchmarks) diff --git a/docs/config/dep-optimization-options.md b/docs/config/dep-optimization-options.md index ec499f4d9ccf16..43152b738e483d 100644 --- a/docs/config/dep-optimization-options.md +++ b/docs/config/dep-optimization-options.md @@ -8,7 +8,7 @@ By default, Vite will crawl all your `.html` files to detect dependencies that need to be pre-bundled (ignoring `node_modules`, `build.outDir`, `__tests__` and `coverage`). If `build.rollupOptions.input` is specified, Vite will crawl those entry points instead. -If neither of these fit your needs, you can specify custom entries using this option - the value should be a [fast-glob pattern](https://github.com/mrmlnc/fast-glob#basic-syntax) or array of patterns that are relative from Vite project root. This will overwrite default entries inference. Only `node_modules` and `build.outDir` folders will be ignored by default when `optimizeDeps.entries` is explicitly defined. If other folders need to be ignored, you can use an ignore pattern as part of the entries list, marked with an initial `!`. +If neither of these fit your needs, you can specify custom entries using this option - the value should be a [fast-glob pattern](https://github.com/mrmlnc/fast-glob#basic-syntax) or array of patterns that are relative from Vite project root. This will overwrite default entries inference. Only `node_modules` and `build.outDir` folders will be ignored by default when `optimizeDeps.entries` is explicitly defined. If other folders need to be ignored, you can use an ignore pattern as part of the entries list, marked with an initial `!`. If you don't want to ignore `node_modules` and `build.outDir`, you can specify using literal string paths (without fast-glob patterns) instead. ## optimizeDeps.exclude @@ -19,7 +19,9 @@ Dependencies to exclude from pre-bundling. :::warning CommonJS CommonJS dependencies should not be excluded from optimization. If an ESM dependency is excluded from optimization, but has a nested CommonJS dependency, the CommonJS dependency should be added to `optimizeDeps.include`. Example: -```js +```js twoslash +import { defineConfig } from 'vite' +// ---cut--- export default defineConfig({ optimizeDeps: { include: ['esm-dep > cjs-dep'], @@ -35,9 +37,11 @@ export default defineConfig({ By default, linked packages not inside `node_modules` are not pre-bundled. Use this option to force a linked package to be pre-bundled. -**Experimental:** If you're using a library with many deep imports, you can also specify a trailing glob pattern to pre-bundle all deep imports at once. This will avoid constantly pre-bundling whenever a new deep import is used. For example: +**Experimental:** If you're using a library with many deep imports, you can also specify a trailing glob pattern to pre-bundle all deep imports at once. This will avoid constantly pre-bundling whenever a new deep import is used. [Give Feedback](https://github.com/vitejs/vite/discussions/15833). For example: -```js +```js twoslash +import { defineConfig } from 'vite' +// ---cut--- export default defineConfig({ optimizeDeps: { include: ['my-lib/components/**/*.vue'], @@ -47,7 +51,17 @@ export default defineConfig({ ## optimizeDeps.esbuildOptions -- **Type:** [`EsbuildBuildOptions`](https://esbuild.github.io/api/#simple-options) +- **Type:** [`Omit`](https://www.typescriptlang.org/docs/handbook/utility-types.html#omittype-keys)`<`[`EsbuildBuildOptions`](https://esbuild.github.io/api/#simple-options)`, +| 'bundle' +| 'entryPoints' +| 'external' +| 'write' +| 'watch' +| 'outdir' +| 'outfile' +| 'outbase' +| 'outExtension' +| 'metafile'>` Options to pass to esbuild during the dep scanning and optimization. @@ -62,18 +76,27 @@ Certain options are omitted since changing them would not be compatible with Vit Set to `true` to force dependency pre-bundling, ignoring previously cached optimized dependencies. +## optimizeDeps.holdUntilCrawlEnd + +- **Experimental:** [Give Feedback](https://github.com/vitejs/vite/discussions/15834) +- **Type:** `boolean` +- **Default:** `true` + +When enabled, it will hold the first optimized deps results until all static imports are crawled on cold start. This avoids the need for full-page reloads when new dependencies are discovered and they trigger the generation of new common chunks. If all dependencies are found by the scanner plus the explicitly defined ones in `include`, it is better to disable this option to let the browser process more requests in parallel. + ## optimizeDeps.disabled +- **Deprecated** - **Experimental:** [Give Feedback](https://github.com/vitejs/vite/discussions/13839) - **Type:** `boolean | 'build' | 'dev'` - **Default:** `'build'` -Disables dependencies optimizations, `true` disables the optimizer during build and dev. Pass `'build'` or `'dev'` to only disable the optimizer in one of the modes. Dependency optimization is enabled by default in dev only. +This option is deprecated. As of Vite 5.1, pre-bundling of dependencies during build have been removed. Setting `optimizeDeps.disabled` to `true` or `'dev'` disables the optimizer, and configured to `false` or `'build'` leaves the optimizer during dev enabled. -:::warning -Optimizing dependencies in build mode is **experimental**. If enabled, it removes one of the most significant differences between dev and prod. [`@rollup/plugin-commonjs`](https://github.com/rollup/plugins/tree/master/packages/commonjs) is no longer needed in this case since esbuild converts CJS-only dependencies to ESM. +To disable the optimizer completely, use `optimizeDeps.noDiscovery: true` to disallow automatic discovery of dependencies and leave `optimizeDeps.include` undefined or empty. -If you want to try this build strategy, you can use `optimizeDeps.disabled: false`. `@rollup/plugin-commonjs` can be removed by passing `build.commonjsOptions: { include: [] }`. +:::warning +Optimizing dependencies during build time was an **experimental** feature. Projects trying out this strategy also removed `@rollup/plugin-commonjs` using `build.commonjsOptions: { include: [] }`. If you did so, a warning will guide you to re-enable it to support CJS only packages while bundling. ::: ## optimizeDeps.needsInterop diff --git a/docs/config/index.md b/docs/config/index.md index 6932c7f29dc595..e599295b3bd80b 100644 --- a/docs/config/index.md +++ b/docs/config/index.md @@ -50,7 +50,9 @@ Vite also directly supports TS config files. You can use `vite.config.ts` with t If the config needs to conditionally determine options based on the command (`serve` or `build`), the [mode](/guide/env-and-mode) being used, if it's an SSR build (`isSsrBuild`), or is previewing the build (`isPreview`), it can export a function instead: -```js +```js twoslash +import { defineConfig } from 'vite' +// ---cut--- export default defineConfig(({ command, mode, isSsrBuild, isPreview }) => { if (command === 'serve') { return { @@ -65,7 +67,7 @@ export default defineConfig(({ command, mode, isSsrBuild, isPreview }) => { }) ``` -It is important to note that in Vite's API the `command` value is `serve` during dev (in the cli `vite`, `vite dev`, and `vite serve` are aliases), and `build` when building for production (`vite build`). +It is important to note that in Vite's API the `command` value is `serve` during dev (in the cli [`vite`](/guide/cli#vite), `vite dev`, and `vite serve` are aliases), and `build` when building for production ([`vite build`](/guide/cli#vite-build)). `isSsrBuild` and `isPreview` are additional optional flags to differentiate the kind of `build` and `serve` commands respectively. Some tools that load the Vite config may not support these flags and will pass `undefined` instead. Hence, it's recommended to use explicit comparison against `true` and `false`. @@ -73,7 +75,9 @@ It is important to note that in Vite's API the `command` value is `serve` during If the config needs to call async functions, it can export an async function instead. And this async function can also be passed through `defineConfig` for improved intellisense support: -```js +```js twoslash +import { defineConfig } from 'vite' +// ---cut--- export default defineConfig(async ({ command, mode }) => { const data = await asyncFunction() return { @@ -88,7 +92,7 @@ Environmental Variables can be obtained from `process.env` as usual. Note that Vite doesn't load `.env` files by default as the files to load can only be determined after evaluating the Vite config, for example, the `root` and `envDir` options affect the loading behaviour. However, you can use the exported `loadEnv` helper to load the specific `.env` file if needed. -```js +```js twoslash import { defineConfig, loadEnv } from 'vite' export default defineConfig(({ command, mode }) => { diff --git a/docs/config/preview-options.md b/docs/config/preview-options.md index 7d7aca988cda87..6c4370142d5140 100644 --- a/docs/config/preview-options.md +++ b/docs/config/preview-options.md @@ -46,7 +46,7 @@ Set to `true` to exit if port is already in use, instead of automatically trying ## preview.https -- **Type:** `boolean | https.ServerOptions` +- **Type:** `https.ServerOptions` - **Default:** [`server.https`](./server-options#server-https) Enable TLS + HTTP/2. Note this downgrades to TLS only when the [`server.proxy` option](./server-options#server-proxy) is also used. diff --git a/docs/config/server-options.md b/docs/config/server-options.md index 9efdd443fea150..a9d5d52df3c826 100644 --- a/docs/config/server-options.md +++ b/docs/config/server-options.md @@ -18,10 +18,10 @@ The first case is when `localhost` is used. Node.js under v17 reorders the resul You can set [`dns.setDefaultResultOrder('verbatim')`](https://nodejs.org/api/dns.html#dns_dns_setdefaultresultorder_order) to disable the reordering behavior. Vite will then print the address as `localhost`. -```js +```js twoslash // vite.config.js import { defineConfig } from 'vite' -import dns from 'dns' +import dns from 'node:dns' dns.setDefaultResultOrder('verbatim') @@ -90,7 +90,7 @@ Configure custom proxy rules for the dev server. Expects an object of `{ key: op Note that if you are using non-relative [`base`](/config/shared-options.md#base), you must prefix each key with that `base`. -Extends [`http-proxy`](https://github.com/http-party/node-http-proxy#options). Additional options are [here](https://github.com/vitejs/vite/blob/main/packages/vite/src/node/server/middlewares/proxy.ts#L12). +Extends [`http-proxy`](https://github.com/http-party/node-http-proxy#options). Additional options are [here](https://github.com/vitejs/vite/blob/main/packages/vite/src/node/server/middlewares/proxy.ts#L13). In some cases, you might also want to configure the underlying dev server (e.g. to add custom middlewares to the internal [connect](https://github.com/senchalabs/connect) app). In order to do that, you need to write your own [plugin](/guide/using-plugins.html) and use [configureServer](/guide/api-plugin.html#configureserver) function. @@ -123,9 +123,11 @@ export default defineConfig({ }, }, // Proxying websockets or socket.io: ws://localhost:5173/socket.io -> ws://localhost:5174/socket.io + // Exercise caution using `rewriteWsOrigin` as it can leave the proxying open to CSRF attacks. '/socket.io': { target: 'ws://localhost:5174', ws: true, + rewriteWsOrigin: true, }, }, }, @@ -152,6 +154,8 @@ Disable or configure HMR connection (in cases where the HMR websocket must use a Set `server.hmr.overlay` to `false` to disable the server error overlay. +`protocol` sets the WebSocket protocol used for the HMR connection: `ws` (WebSocket) or `wss` (WebSocket Secure). + `clientPort` is an advanced option that overrides the port only on the client side, allowing you to serve the websocket on a different port than the client code looks for it on. When `server.hmr.server` is defined, Vite will process the HMR connection requests through the provided server. If not in middleware mode, Vite will attempt to process HMR connection requests through the existing server. This can be helpful when using self-signed certificates or when you want to expose Vite over a network on a single port. @@ -202,7 +206,7 @@ export default defineConfig({ File system watcher options to pass on to [chokidar](https://github.com/paulmillr/chokidar#api). -The Vite server watcher watches the `root` and skips the `.git/` and `node_modules/` directories by default. When updating a watched file, Vite will apply HMR and update the page only if needed. +The Vite server watcher watches the `root` and skips the `.git/`, `node_modules/`, and Vite's `cacheDir` and `build.outDir` directories by default. When updating a watched file, Vite will apply HMR and update the page only if needed. If set to `null`, no files will be watched. `server.watcher` will provide a compatible event emitter, but calling `add` or `unwatch` will have no effect. @@ -236,7 +240,7 @@ Create Vite server in middleware mode. - **Example:** -```js +```js twoslash import express from 'express' import { createServer as createViteServer } from 'vite' @@ -356,9 +360,9 @@ export default defineConfig({ // in their paths to the ignore list. sourcemapIgnoreList(sourcePath, sourcemapPath) { return sourcePath.includes('node_modules') - } - } -}; + }, + }, +}) ``` ::: tip Note diff --git a/docs/config/shared-options.md b/docs/config/shared-options.md index cbf0abe12acc6d..19eb96e0f74439 100644 --- a/docs/config/shared-options.md +++ b/docs/config/shared-options.md @@ -13,11 +13,12 @@ See [Project Root](/guide/#index-html-and-project-root) for more details. - **Type:** `string` - **Default:** `/` +- **Related:** [`server.origin`](/config/server-options.md#server-origin) Base public path when served in development or production. Valid values include: - Absolute URL pathname, e.g. `/foo/` -- Full URL, e.g. `https://foo.com/` +- Full URL, e.g. `https://bar.com/foo/` (The origin part won't be used in development so the value is the same as `/foo/`) - Empty string or `./` (for embedded deployment) See [Public Base Path](/guide/build#public-base-path) for more details. @@ -162,26 +163,43 @@ Enabling this setting causes vite to determine file identity by the original fil - **Related:** [esbuild#preserve-symlinks](https://esbuild.github.io/api/#preserve-symlinks), [webpack#resolve.symlinks ](https://webpack.js.org/configuration/resolve/#resolvesymlinks) +## html.cspNonce + +- **Type:** `string` +- **Related:** [Content Security Policy (CSP)](/guide/features#content-security-policy-csp) + +A nonce value placeholder that will be used when generating script / style tags. Setting this value will also generate a meta tag with nonce value. + ## css.modules - **Type:** ```ts interface CSSModulesOptions { + getJSON?: ( + cssFileName: string, + json: Record, + outputFileName: string, + ) => void scopeBehaviour?: 'global' | 'local' globalModulePaths?: RegExp[] + exportGlobals?: boolean generateScopedName?: | string | ((name: string, filename: string, css: string) => string) hashPrefix?: string /** - * default: null + * default: undefined */ localsConvention?: | 'camelCase' | 'camelCaseOnly' | 'dashes' | 'dashesOnly' - | null + | (( + originalClassName: string, + generatedClassName: string, + inputFile: string, + ) => string) } ``` @@ -211,17 +229,12 @@ Specify options to pass to CSS pre-processors. The file extensions are used as k - `less` - [Options](https://lesscss.org/usage/#less-options). - `styl`/`stylus` - Only [`define`](https://stylus-lang.com/docs/js.html#define-name-node) is supported, which can be passed as an object. -All preprocessor options also support the `additionalData` option, which can be used to inject extra code for each style content. Note that if you include actual styles and not just variables, those styles will be duplicated in the final bundle. - -Example: +**Example:** ```js export default defineConfig({ css: { preprocessorOptions: { - scss: { - additionalData: `$injectedColor: orange;`, - }, less: { math: 'parens-division', }, @@ -235,6 +248,34 @@ export default defineConfig({ }) ``` +### css.preprocessorOptions[extension].additionalData + +- **Type:** `string | ((source: string, filename: string) => (string | { content: string; map?: SourceMap }))` + +This option can be used to inject extra code for each style content. Note that if you include actual styles and not just variables, those styles will be duplicated in the final bundle. + +**Example:** + +```js +export default defineConfig({ + css: { + preprocessorOptions: { + scss: { + additionalData: `$injectedColor: orange;`, + }, + }, + }, +}) +``` + +## css.preprocessorMaxWorkers + +- **Experimental:** [Give Feedback](https://github.com/vitejs/vite/discussions/15835) +- **Type:** `number | true` +- **Default:** `0` (does not create any workers and run in the main thread) + +If this option is set, CSS preprocessors will run in workers when possible. `true` means the number of CPUs minus 1. + ## css.devSourcemap - **Experimental:** [Give Feedback](https://github.com/vitejs/vite/discussions/13845) @@ -251,6 +292,10 @@ Whether to enable sourcemaps during dev. Selects the engine used for CSS processing. Check out [Lightning CSS](../guide/features.md#lightning-css) for more information. +::: info Duplicate `@import`s +Note that postcss (postcss-import) has a different behavior with duplicated `@import` from browsers. See [postcss/postcss-import#462](https://github.com/postcss/postcss-import/issues/462). +::: + ## css.lightningcss - **Experimental:** [Give Feedback](https://github.com/vitejs/vite/discussions/13835) @@ -374,7 +419,7 @@ Adjust console output verbosity. Default is `'info'`. Use a custom logger to log messages. You can use Vite's `createLogger` API to get the default logger and customize it to, for example, change the message or filter out certain warnings. -```js +```ts twoslash import { createLogger, defineConfig } from 'vite' const logger = createLogger() diff --git a/docs/config/ssr-options.md b/docs/config/ssr-options.md index d5f96bdb415fca..d925490e53dcb2 100644 --- a/docs/config/ssr-options.md +++ b/docs/config/ssr-options.md @@ -2,17 +2,25 @@ ## ssr.external -- **Type:** `string[]` +- **Type:** `string[] | true` - **Related:** [SSR Externals](/guide/ssr#ssr-externals) -Force externalize dependencies for SSR. +Externalize the given dependencies and their transitive dependencies for SSR. By default, all dependencies are externalized except for linked dependencies (for HMR). If you prefer to externalize the linked dependency, you can pass its name to this option. + +If `true`, all dependencies including linked dependencies are externalized. + +Note that the explicitly listed dependencies (using `string[]` type) will always take priority if they're also listed in `ssr.noExternal` (using any type). ## ssr.noExternal - **Type:** `string | RegExp | (string | RegExp)[] | true` - **Related:** [SSR Externals](/guide/ssr#ssr-externals) -Prevent listed dependencies from being externalized for SSR. If `true`, no dependencies are externalized. +Prevent listed dependencies from being externalized for SSR, which they will get bundled in build. By default, only linked dependencies are not externalized (for HMR). If you prefer to externalize the linked dependency, you can pass its name to the `ssr.external` option. + +If `true`, no dependencies are externalized. However, dependencies explicitly listed in `ssr.external` (using `string[]` type) can take priority and still be externalized. If `ssr.target: 'node'` is set, Node.js built-ins will also be externalized by default. + +Note that if both `ssr.noExternal: true` and `ssr.external: true` are configured, `ssr.noExternal` takes priority and no dependencies are externalized. ## ssr.target @@ -26,7 +34,7 @@ Build target for the SSR server. - **Type:** `string[]` - **Related:** [Resolve Conditions](./shared-options.md#resolve-conditions) -Defaults to the the root [`resolve.conditions`](./shared-options.md#resolve-conditions). +Defaults to the root [`resolve.conditions`](./shared-options.md#resolve-conditions). These conditions are used in the plugin pipeline, and only affect non-externalized dependencies during the SSR build. Use `ssr.resolve.externalConditions` to affect externalized imports. diff --git a/docs/guide/api-hmr.md b/docs/guide/api-hmr.md index e55f0af47090e0..e9a44eb0aaca88 100644 --- a/docs/guide/api-hmr.md +++ b/docs/guide/api-hmr.md @@ -8,15 +8,15 @@ The manual HMR API is primarily intended for framework and tooling authors. As a Vite exposes its manual HMR API via the special `import.meta.hot` object: -```ts +```ts twoslash +import type { ModuleNamespace } from 'vite/types/hot.d.ts' +import type { InferCustomEventPayload } from 'vite/types/customEvent.d.ts' + +// ---cut--- interface ImportMeta { readonly hot?: ViteHotContext } -type ModuleNamespace = Record & { - [Symbol.toStringTag]: 'Module' -} - interface ViteHotContext { readonly data: any @@ -32,7 +32,6 @@ interface ViteHotContext { prune(cb: (data: any) => void): void invalidate(message?: string): void - // `InferCustomEventPayload` provides types for built-in Vite events on( event: T, cb: (payload: InferCustomEventPayload) => void, @@ -67,7 +66,9 @@ Vite provides type definitions for `import.meta.hot` in [`vite/client.d.ts`](htt For a module to self-accept, use `import.meta.hot.accept` with a callback which receives the updated module: -```js +```js twoslash +import 'vite/client' +// ---cut--- export const count = 1 if (import.meta.hot) { @@ -90,7 +91,13 @@ Vite requires that the call to this function appears as `import.meta.hot.accept( A module can also accept updates from direct dependencies without reloading itself: -```js +```js twoslash +// @filename: /foo.d.ts +export declare const foo: () => void + +// @filename: /example.js +import 'vite/client' +// ---cut--- import { foo } from './foo.js' foo() @@ -117,7 +124,9 @@ if (import.meta.hot) { A self-accepting module or a module that expects to be accepted by others can use `hot.dispose` to clean-up any persistent side effects created by its updated copy: -```js +```js twoslash +import 'vite/client' +// ---cut--- function setupSideEffect() {} setupSideEffect() @@ -133,7 +142,9 @@ if (import.meta.hot) { Register a callback that will call when the module is no longer imported on the page. Compared to `hot.dispose`, this can be used if the source code cleans up side-effects by itself on updates and you only need to clean-up when it's removed from the page. Vite currently uses this for `.css` imports. -```js +```js twoslash +import 'vite/client' +// ---cut--- function setupOrReuseSideEffect() {} setupOrReuseSideEffect() @@ -151,7 +162,9 @@ The `import.meta.hot.data` object is persisted across different instances of the Note that re-assignment of `data` itself is not supported. Instead, you should mutate properties of the `data` object so information added from other handlers are preserved. -```js +```js twoslash +import 'vite/client' +// ---cut--- // ok import.meta.hot.data.someValue = 'hello' @@ -169,7 +182,9 @@ A self-accepting module may realize during runtime that it can't handle a HMR up Note that you should always call `import.meta.hot.accept` even if you plan to call `invalidate` immediately afterwards, or else the HMR client won't listen for future changes to the self-accepting module. To communicate your intent clearly, we recommend calling `invalidate` within the `accept` callback like so: -```js +```js twoslash +import 'vite/client' +// ---cut--- import.meta.hot.accept((module) => { // You may use the new module instance to decide whether to invalidate. if (cannotHandleUpdate(module)) { @@ -197,7 +212,7 @@ Custom HMR events can also be sent from plugins. See [handleHotUpdate](./api-plu ## `hot.off(event, cb)` -Remove callback from the event listeners +Remove callback from the event listeners. ## `hot.send(event, data)` @@ -205,4 +220,10 @@ Send custom events back to Vite's dev server. If called before connected, the data will be buffered and sent once the connection is established. -See [Client-server Communication](/guide/api-plugin.html#client-server-communication) for more details. +See [Client-server Communication](/guide/api-plugin.html#client-server-communication) for more details, including a section on [Typing Custom Events](/guide/api-plugin.html#typescript-for-custom-events). + +## Further Reading + +If you'd like to learn more about how to use the HMR API and how it works under-the-hood. Check out these resources: + +- [Hot Module Replacement is Easy](https://bjornlu.com/blog/hot-module-replacement-is-easy) diff --git a/docs/guide/api-javascript.md b/docs/guide/api-javascript.md index 1ff419058f2706..573c8c32a394c6 100644 --- a/docs/guide/api-javascript.md +++ b/docs/guide/api-javascript.md @@ -12,32 +12,66 @@ async function createServer(inlineConfig?: InlineConfig): Promise **Example Usage:** -```js -import { fileURLToPath } from 'url' +```ts twoslash +import { fileURLToPath } from 'node:url' import { createServer } from 'vite' const __dirname = fileURLToPath(new URL('.', import.meta.url)) -;(async () => { - const server = await createServer({ - // any valid user config options, plus `mode` and `configFile` - configFile: false, - root: __dirname, - server: { - port: 1337, - }, - }) - await server.listen() - - server.printUrls() - server.bindCLIShortcuts({ print: true }) -})() +const server = await createServer({ + // any valid user config options, plus `mode` and `configFile` + configFile: false, + root: __dirname, + server: { + port: 1337, + }, +}) +await server.listen() + +server.printUrls() +server.bindCLIShortcuts({ print: true }) ``` ::: tip NOTE When using `createServer` and `build` in the same Node.js process, both functions rely on `process.env.NODE_ENV` to work properly, which also depends on the `mode` config option. To prevent conflicting behavior, set `process.env.NODE_ENV` or the `mode` of the two APIs to `development`. Otherwise, you can spawn a child process to run the APIs separately. ::: +::: tip NOTE +When using [middleware mode](/config/server-options.html#server-middlewaremode) combined with [proxy config for WebSocket](/config/server-options.html#server-proxy), the parent http server should be provided in `middlewareMode` to bind the proxy correctly. + +
+Example + +```ts twoslash +import http from 'http' +import { createServer } from 'vite' + +const parentServer = http.createServer() // or express, koa, etc. + +const vite = await createServer({ + server: { + // Enable middleware mode + middlewareMode: { + // Provide the parent http server for proxy WebSocket + server: parentServer, + }, + proxy: { + '/ws': { + target: 'ws://localhost:3000', + // Proxying WebSocket + ws: true, + }, + }, + }, +}) + +// @noErrors: 2339 +parentServer.use(vite.middlewares) +``` + +
+::: + ## `InlineConfig` The `InlineConfig` interface extends `UserConfig` with additional properties: @@ -109,7 +143,11 @@ interface ViteDevServer { /** * Apply Vite built-in HTML transforms and any plugin HTML transforms. */ - transformIndexHtml(url: string, html: string): Promise + transformIndexHtml( + url: string, + html: string, + originalUrl?: string, + ): Promise /** * Load a given URL as an instantiated module for SSR. */ @@ -144,9 +182,21 @@ interface ViteDevServer { * Bind CLI shortcuts */ bindCLIShortcuts(options?: BindCLIShortcutsOptions): void + /** + * Calling `await server.waitForRequestsIdle(id)` will wait until all static imports + * are processed. If called from a load or transform plugin hook, the id needs to be + * passed as a parameter to avoid deadlocks. Calling this function after the first + * static imports section of the module graph has been processed will resolve immediately. + * @experimental + */ + waitForRequestsIdle: (ignoredId?: string) => Promise } ``` +:::info +`waitForRequestsIdle` is meant to be used as a escape hatch to improve DX for features that can't be implemented following the on-demand nature of the Vite dev server. It can be used during startup by tools like Tailwind to delay generating the app CSS classes until the app code has been seen, avoiding flashes of style changes. When this function is used in a load or transform hook, and the default HTTP1 server is used, one of the six http channels will be blocked until the server processes all static imports. Vite's dependency optimizer currently uses this function to avoid full-page reloads on missing dependencies by delaying loading of pre-bundled dependencies until all imported dependencies have been collected from static imported sources. Vite may switch to a different strategy in a future major release, setting `optimizeDeps.crawlUntilStaticImports: false` by default to avoid the performance hit in large applications during cold start. +::: + ## `build` **Type Signature:** @@ -159,24 +209,22 @@ async function build( **Example Usage:** -```js -import path from 'path' -import { fileURLToPath } from 'url' +```ts twoslash +import path from 'node:path' +import { fileURLToPath } from 'node:url' import { build } from 'vite' const __dirname = fileURLToPath(new URL('.', import.meta.url)) -;(async () => { - await build({ - root: path.resolve(__dirname, './project'), - base: '/foo/', - build: { - rollupOptions: { - // ... - }, +await build({ + root: path.resolve(__dirname, './project'), + base: '/foo/', + build: { + rollupOptions: { + // ... }, - }) -})() + }, +}) ``` ## `preview` @@ -189,20 +237,19 @@ async function preview(inlineConfig?: InlineConfig): Promise **Example Usage:** -```js +```ts twoslash import { preview } from 'vite' -;(async () => { - const previewServer = await preview({ - // any valid user config options, plus `mode` and `configFile` - preview: { - port: 8080, - open: true, - }, - }) - previewServer.printUrls() - previewServer.bindCLIShortcuts({ print: true }) -})() +const previewServer = await preview({ + // any valid user config options, plus `mode` and `configFile` + preview: { + port: 8080, + open: true, + }, +}) + +previewServer.printUrls() +previewServer.bindCLIShortcuts({ print: true }) ``` ## `PreviewServer` @@ -277,7 +324,17 @@ Deeply merge two Vite configs. `isRoot` represents the level within the Vite con You can use the `defineConfig` helper to merge a config in callback form with another config: -```ts +```ts twoslash +import { + defineConfig, + mergeConfig, + type UserConfigFnObject, + type UserConfig, +} from 'vite' +declare const configAsCallback: UserConfigFnObject +declare const configAsObject: UserConfig + +// ---cut--- export default defineConfig((configEnv) => mergeConfig(configAsCallback(configEnv), configAsObject), ) @@ -358,6 +415,7 @@ async function loadConfigFromFile( configFile?: string, configRoot: string = process.cwd(), logLevel?: LogLevel, + customLogger?: Logger, ): Promise<{ path: string config: UserConfig @@ -366,3 +424,30 @@ async function loadConfigFromFile( ``` Load a Vite config file manually with esbuild. + +## `preprocessCSS` + +- **Experimental:** [Give Feedback](https://github.com/vitejs/vite/discussions/13815) + +**Type Signature:** + +```ts +async function preprocessCSS( + code: string, + filename: string, + config: ResolvedConfig, +): Promise + +interface PreprocessCSSResult { + code: string + map?: SourceMapInput + modules?: Record + deps?: Set +} +``` + +Pre-processes `.css`, `.scss`, `.sass`, `.less`, `.styl` and `.stylus` files to plain CSS so it can be used in browsers or parsed by other tools. Similar to the [built-in CSS pre-processing support](/guide/features#css-pre-processors), the corresponding pre-processor must be installed if used. + +The pre-processor used is inferred from the `filename` extension. If the `filename` ends with `.module.{ext}`, it is inferred as a [CSS module](https://github.com/css-modules/css-modules) and the returned result will include a `modules` object mapping the original class names to the transformed ones. + +Note that pre-processing will not resolve URLs in `url()` or `image-set()`. diff --git a/docs/guide/api-plugin.md b/docs/guide/api-plugin.md index d2f3b6ff8e25c9..852a6e408cfb37 100644 --- a/docs/guide/api-plugin.md +++ b/docs/guide/api-plugin.md @@ -402,6 +402,7 @@ Vite plugins can also provide hooks that serve Vite-specific purposes. These hoo ### `handleHotUpdate` - **Type:** `(ctx: HmrContext) => Array | void | Promise | void>` +- **See also:** [HMR API](./api-hmr) Perform custom HMR update handling. The hook receives a context object with the following signature: @@ -423,6 +424,25 @@ Vite plugins can also provide hooks that serve Vite-specific purposes. These hoo - Filter and narrow down the affected module list so that the HMR is more accurate. + - Return an empty array and perform a full reload: + + ```js + handleHotUpdate({ server, modules, timestamp }) { + server.ws.send({ type: 'full-reload' }) + // Invalidate modules manually + const invalidatedModules = new Set() + for (const mod of modules) { + server.moduleGraph.invalidateModule( + mod, + invalidatedModules, + timestamp, + true + ) + } + return [] + } + ``` + - Return an empty array and perform complete custom HMR handling by sending custom events to the client: ```js @@ -458,6 +478,8 @@ A Vite plugin can additionally specify an `enforce` property (similar to webpack - User plugins with `enforce: 'post'` - Vite post build plugins (minify, manifest, reporting) +Note that this is separate from hooks ordering, those are still separately subject to their `order` attribute [as usual for Rollup hooks](https://rollupjs.org/plugin-development/#build-hooks). + ## Conditional Application By default plugins are invoked for both serve and build. In cases where a plugin needs to be conditionally applied only during serve or build, use the `apply` property to only invoke them during `'build'` or `'serve'`: @@ -509,8 +531,6 @@ export default defineConfig({ }) ``` -Check out [Vite Rollup Plugins](https://vite-rollup-plugins.patak.dev) for a list of compatible official Rollup plugins with usage instructions. - ## Path Normalization Vite normalizes paths while resolving ids to use POSIX separators ( / ) while preserving the volume in Windows. On the other hand, Rollup keeps resolved paths untouched by default, so resolved ids have win32 separators ( \\ ) in Windows. However, Rollup plugins use a [`normalizePath` utility function](https://github.com/rollup/plugins/tree/master/packages/pluginutils#normalizepath) from `@rollup/pluginutils` internally, which converts separators to POSIX before performing comparisons. This means that when these plugins are used in Vite, the `include` and `exclude` config pattern and other similar paths against resolved ids comparisons work correctly. @@ -534,7 +554,7 @@ Since Vite 2.9, we provide some utilities for plugins to help handle the communi ### Server to Client -On the plugin side, we could use `server.ws.send` to broadcast events to all the clients: +On the plugin side, we could use `server.ws.send` to broadcast events to the client: ```js // vite.config.js @@ -543,7 +563,6 @@ export default defineConfig({ { // ... configureServer(server) { - // Example: wait for a client to connect before sending a message server.ws.on('connection', () => { server.ws.send('my:greetings', { msg: 'hello' }) }) @@ -559,7 +578,9 @@ We recommend **always prefixing** your event names to avoid collisions with othe On the client side, use [`hot.on`](/guide/api-hmr.html#hot-on-event-cb) to listen to the events: -```ts +```ts twoslash +import 'vite/client' +// ---cut--- // client side if (import.meta.hot) { import.meta.hot.on('my:greetings', (data) => { @@ -601,16 +622,40 @@ export default defineConfig({ ### TypeScript for Custom Events -It is possible to type custom events by extending the `CustomEventMap` interface: +Internally, vite infers the type of a payload from the `CustomEventMap` interface, it is possible to type custom events by extending the interface: + +:::tip Note +Make sure to include the `.d.ts` extension when specifying TypeScript declaration files. Otherwise, Typescript may not know which file the module is trying to extend. +::: ```ts // events.d.ts -import 'vite/types/customEvent' +import 'vite/types/customEvent.d.ts' -declare module 'vite/types/customEvent' { +declare module 'vite/types/customEvent.d.ts' { interface CustomEventMap { 'custom:foo': { msg: string } // 'event-key': payload } } ``` + +This interface extension is utilized by `InferCustomEventPayload` to infer the payload type for event `T`. For more information on how this interface is utilized, refer to the [HMR API Documentation](./api-hmr#hmr-api). + +```ts twoslash +import 'vite/client' +import type { InferCustomEventPayload } from 'vite/types/customEvent.d.ts' +declare module 'vite/types/customEvent.d.ts' { + interface CustomEventMap { + 'custom:foo': { msg: string } + } +} +// ---cut--- +type CustomFooPayload = InferCustomEventPayload<'custom:foo'> +import.meta.hot?.on('custom:foo', (payload) => { + // The type of payload will be { msg: string } +}) +import.meta.hot?.on('unknown:event', (payload) => { + // The type of payload will be any +}) +``` diff --git a/docs/guide/api-vite-runtime.md b/docs/guide/api-vite-runtime.md new file mode 100644 index 00000000000000..9aa579d268ddcf --- /dev/null +++ b/docs/guide/api-vite-runtime.md @@ -0,0 +1,236 @@ +# Vite Runtime API + +:::warning Low-level API +This API was introduced in Vite 5.1 as an experimental feature. It was added to [gather feedback](https://github.com/vitejs/vite/discussions/15774). There will likely be breaking changes, so make sure to pin the Vite version to `~5.1.0` when using it. This is a low-level API meant for library and framework authors. If your goal is to create an application, make sure to check out the higher-level SSR plugins and tools at [Awesome Vite SSR section](https://github.com/vitejs/awesome-vite#ssr) first. + +Currently, the API is being revised as the [Environment API](https://github.com/vitejs/vite/discussions/16358) which is released at `^6.0.0-alpha.0`. +::: + +The "Vite Runtime" is a tool that allows running any code by processing it with Vite plugins first. It is different from `server.ssrLoadModule` because the runtime implementation is decoupled from the server. This allows library and framework authors to implement their own layer of communication between the server and the runtime. + +One of the goals of this feature is to provide a customizable API to process and run the code. Vite provides enough tools to use Vite Runtime out of the box, but users can build upon it if their needs do not align with Vite's built-in implementation. + +All APIs can be imported from `vite/runtime` unless stated otherwise. + +## `ViteRuntime` + +**Type Signature:** + +```ts +export class ViteRuntime { + constructor( + public options: ViteRuntimeOptions, + public runner: ViteModuleRunner, + private debug?: ViteRuntimeDebugger, + ) {} + /** + * URL to execute. Accepts file path, server path, or id relative to the root. + */ + public async executeUrl(url: string): Promise + /** + * Entry point URL to execute. Accepts file path, server path or id relative to the root. + * In the case of a full reload triggered by HMR, this is the module that will be reloaded. + * If this method is called multiple times, all entry points will be reloaded one at a time. + */ + public async executeEntrypoint(url: string): Promise + /** + * Clear all caches including HMR listeners. + */ + public clearCache(): void + /** + * Clears all caches, removes all HMR listeners, and resets source map support. + * This method doesn't stop the HMR connection. + */ + public async destroy(): Promise + /** + * Returns `true` if the runtime has been destroyed by calling `destroy()` method. + */ + public isDestroyed(): boolean +} +``` + +::: tip Advanced Usage +If you are just migrating from `server.ssrLoadModule` and want to support HMR, consider using [`createViteRuntime`](#createviteruntime) instead. +::: + +The `ViteRuntime` class requires `root` and `fetchModule` options when initiated. Vite exposes `ssrFetchModule` on the [`server`](/guide/api-javascript) instance for easier integration with Vite SSR. Vite also exports `fetchModule` from its main entry point - it doesn't make any assumptions about how the code is running unlike `ssrFetchModule` that expects the code to run using `new Function`. This can be seen in source maps that these functions return. + +Runner in `ViteRuntime` is responsible for executing the code. Vite exports `ESModulesRunner` out of the box, it uses `new AsyncFunction` to run the code. You can provide your own implementation if your JavaScript runtime doesn't support unsafe evaluation. + +The two main methods that runtime exposes are `executeUrl` and `executeEntrypoint`. The only difference between them is that all modules executed by `executeEntrypoint` will be reexecuted if HMR triggers `full-reload` event. Be aware that Vite Runtime doesn't update `exports` object when this happens (it overrides it), you would need to run `executeUrl` or get the module from `moduleCache` again if you rely on having the latest `exports` object. + +**Example Usage:** + +```js +import { ViteRuntime, ESModulesRunner } from 'vite/runtime' +import { root, fetchModule } from './rpc-implementation.js' + +const runtime = new ViteRuntime( + { + root, + fetchModule, + // you can also provide hmr.connection to support HMR + }, + new ESModulesRunner(), +) + +await runtime.executeEntrypoint('/src/entry-point.js') +``` + +## `ViteRuntimeOptions` + +```ts +export interface ViteRuntimeOptions { + /** + * Root of the project + */ + root: string + /** + * A method to get the information about the module. + * For SSR, Vite exposes `server.ssrFetchModule` function that you can use here. + * For other runtime use cases, Vite also exposes `fetchModule` from its main entry point. + */ + fetchModule: FetchFunction + /** + * Configure how source maps are resolved. Prefers `node` if `process.setSourceMapsEnabled` is available. + * Otherwise it will use `prepareStackTrace` by default which overrides `Error.prepareStackTrace` method. + * You can provide an object to configure how file contents and source maps are resolved for files that were not processed by Vite. + */ + sourcemapInterceptor?: + | false + | 'node' + | 'prepareStackTrace' + | InterceptorOptions + /** + * Disable HMR or configure HMR options. + */ + hmr?: + | false + | { + /** + * Configure how HMR communicates between the client and the server. + */ + connection: HMRRuntimeConnection + /** + * Configure HMR logger. + */ + logger?: false | HMRLogger + } + /** + * Custom module cache. If not provided, it creates a separate module cache for each ViteRuntime instance. + */ + moduleCache?: ModuleCacheMap +} +``` + +## `ViteModuleRunner` + +**Type Signature:** + +```ts +export interface ViteModuleRunner { + /** + * Run code that was transformed by Vite. + * @param context Function context + * @param code Transformed code + * @param id ID that was used to fetch the module + */ + runViteModule( + context: ViteRuntimeModuleContext, + code: string, + id: string, + ): Promise + /** + * Run externalized module. + * @param file File URL to the external module + */ + runExternalModule(file: string): Promise +} +``` + +Vite exports `ESModulesRunner` that implements this interface by default. It uses `new AsyncFunction` to run code, so if the code has inlined source map it should contain an [offset of 2 lines](https://tc39.es/ecma262/#sec-createdynamicfunction) to accommodate for new lines added. This is done automatically by `server.ssrFetchModule`. If your runner implementation doesn't have this constraint, you should use `fetchModule` (exported from `vite`) directly. + +## HMRRuntimeConnection + +**Type Signature:** + +```ts +export interface HMRRuntimeConnection { + /** + * Checked before sending messages to the client. + */ + isReady(): boolean + /** + * Send message to the client. + */ + send(message: string): void + /** + * Configure how HMR is handled when this connection triggers an update. + * This method expects that connection will start listening for HMR updates and call this callback when it's received. + */ + onUpdate(callback: (payload: HMRPayload) => void): void +} +``` + +This interface defines how HMR communication is established. Vite exports `ServerHMRConnector` from the main entry point to support HMR during Vite SSR. The `isReady` and `send` methods are usually called when the custom event is triggered (like, `import.meta.hot.send("my-event")`). + +`onUpdate` is called only once when the new runtime is initiated. It passed down a method that should be called when connection triggers the HMR event. The implementation depends on the type of connection (as an example, it can be `WebSocket`/`EventEmitter`/`MessageChannel`), but it usually looks something like this: + +```js +function onUpdate(callback) { + this.connection.on('hmr', (event) => callback(event.data)) +} +``` + +The callback is queued and it will wait for the current update to be resolved before processing the next update. Unlike the browser implementation, HMR updates in Vite Runtime wait until all listeners (like, `vite:beforeUpdate`/`vite:beforeFullReload`) are finished before updating the modules. + +## `createViteRuntime` + +**Type Signature:** + +```ts +async function createViteRuntime( + server: ViteDevServer, + options?: MainThreadRuntimeOptions, +): Promise +``` + +**Example Usage:** + +```js +import { createServer } from 'vite' + +const __dirname = fileURLToPath(new URL('.', import.meta.url)) + +;(async () => { + const server = await createServer({ + root: __dirname, + }) + await server.listen() + + const runtime = await createViteRuntime(server) + await runtime.executeEntrypoint('/src/entry-point.js') +})() +``` + +This method serves as an easy replacement for `server.ssrLoadModule`. Unlike `ssrLoadModule`, `createViteRuntime` provides HMR support out of the box. You can pass down [`options`](#mainthreadruntimeoptions) to customize how SSR runtime behaves to suit your needs. + +## `MainThreadRuntimeOptions` + +```ts +export interface MainThreadRuntimeOptions + extends Omit { + /** + * Disable HMR or configure HMR logger. + */ + hmr?: + | false + | { + logger?: false | HMRLogger + } + /** + * Provide a custom module runner. This controls how the code is executed. + */ + runner?: ViteModuleRunner +} +``` diff --git a/docs/guide/assets.md b/docs/guide/assets.md index dff926f2c2da31..4f81d04e496a80 100644 --- a/docs/guide/assets.md +++ b/docs/guide/assets.md @@ -7,7 +7,9 @@ Importing a static asset will return the resolved public URL when it is served: -```js +```js twoslash +import 'vite/client' +// ---cut--- import imgUrl from './img.png' document.getElementById('hero-img').src = imgUrl ``` @@ -30,11 +32,25 @@ The behavior is similar to webpack's `file-loader`. The difference is that the i - TypeScript, by default, does not recognize static asset imports as valid modules. To fix this, include [`vite/client`](./features#client-types). +::: tip Inlining SVGs through `url()` +When passing a URL of SVG to a manually constructed `url()` by JS, the variable should be wrapped within double quotes. + +```js twoslash +import 'vite/client' +// ---cut--- +import imgUrl from './img.svg' +document.getElementById('hero-img').style.background = `url("${imgUrl}")` +``` + +::: + ### Explicit URL Imports Assets that are not included in the internal list or in `assetsInclude`, can be explicitly imported as a URL using the `?url` suffix. This is useful, for example, to import [Houdini Paint Worklets](https://houdini.how/usage). -```js +```js twoslash +import 'vite/client' +// ---cut--- import workletURL from 'extra-scalloped-border/worklet.js?url' CSS.paintWorklet.addModule(workletURL) ``` @@ -43,7 +59,9 @@ CSS.paintWorklet.addModule(workletURL) Assets can be imported as strings using the `?raw` suffix. -```js +```js twoslash +import 'vite/client' +// ---cut--- import shaderString from './shader.glsl?raw' ``` @@ -51,19 +69,25 @@ import shaderString from './shader.glsl?raw' Scripts can be imported as web workers with the `?worker` or `?sharedworker` suffix. -```js +```js twoslash +import 'vite/client' +// ---cut--- // Separate chunk in the production build import Worker from './shader.js?worker' const worker = new Worker() ``` -```js +```js twoslash +import 'vite/client' +// ---cut--- // sharedworker import SharedWorker from './shader.js?sharedworker' const sharedWorker = new SharedWorker() ``` -```js +```js twoslash +import 'vite/client' +// ---cut--- // Inlined as base64 strings import InlineWorker from './shader.js?worker&inline' ``` diff --git a/docs/guide/backend-integration.md b/docs/guide/backend-integration.md index 6e391e48b23261..8509082bbdf2ea 100644 --- a/docs/guide/backend-integration.md +++ b/docs/guide/backend-integration.md @@ -8,7 +8,9 @@ If you need a custom integration, you can follow the steps in this guide to conf 1. In your Vite config, configure the entry and enable build manifest: - ```js + ```js twoslash + import { defineConfig } from 'vite' + // ---cut--- // vite.config.js export default defineConfig({ build: { @@ -60,22 +62,36 @@ If you need a custom integration, you can follow the steps in this guide to conf ```json { - "main.js": { - "file": "assets/main.4889e940.js", - "src": "main.js", + "_shared-!~{003}~.js": { + "file": "assets/shared-ChJ_j-JJ.css", + "src": "_shared-!~{003}~.js" + }, + "_shared-B7PI925R.js": { + "file": "assets/shared-B7PI925R.js", + "name": "shared", + "css": ["assets/shared-ChJ_j-JJ.css"] + }, + "baz.js": { + "file": "assets/baz-B2H3sXNv.js", + "name": "baz", + "src": "baz.js", + "isDynamicEntry": true + }, + "views/bar.js": { + "file": "assets/bar-gkvgaI9m.js", + "name": "bar", + "src": "views/bar.js", "isEntry": true, - "dynamicImports": ["views/foo.js"], - "css": ["assets/main.b82dbe22.css"], - "assets": ["assets/asset.0ab0f9cd.png"] + "imports": ["_shared-B7PI925R.js"], + "dynamicImports": ["baz.js"] }, "views/foo.js": { - "file": "assets/foo.869aea0d.js", + "file": "assets/foo-BRBmoGS9.js", + "name": "foo", "src": "views/foo.js", - "isDynamicEntry": true, - "imports": ["_shared.83069a53.js"] - }, - "_shared.83069a53.js": { - "file": "assets/shared.83069a53.js" + "isEntry": true, + "imports": ["_shared-B7PI925R.js"], + "css": ["assets/foo-5UjPuW-k.css"] } } ``` @@ -85,10 +101,54 @@ If you need a custom integration, you can follow the steps in this guide to conf - For non entry chunks, the key is the base name of the generated file prefixed with `_`. - Chunks will contain information on its static and dynamic imports (both are keys that map to the corresponding chunk in the manifest), and also its corresponding CSS and asset files (if any). - You can use this file to render links or preload directives with hashed filenames (note: the syntax here is for explanation only, substitute with your server templating language): +4. You can use this file to render links or preload directives with hashed filenames. + + Here is an example HTML template to render the proper links. The syntax here is for + explanation only, substitute with your server templating language. The `importedChunks` + function is for illustration and isn't provided by Vite. ```html - - + + + + + + + + + + + + + ``` + + Specifically, a backend generating HTML should include the following tags given a manifest + file and an entry point: + + - A `` tag for each file in the entry point chunk's `css` list + - Recursively follow all chunks in the entry point's `imports` list and include a + `` tag for each CSS file of each imported chunk. + - A tag for the `file` key of the entry point chunk (` + + + ``` + + While the following should be included for the entry point `views/bar.js`: + + ```html + + + + ``` diff --git a/docs/guide/build.md b/docs/guide/build.md index 1ce6aeb6f7580d..6c4f2a1e5523ab 100644 --- a/docs/guide/build.md +++ b/docs/guide/build.md @@ -13,7 +13,7 @@ The production bundle assumes support for modern JavaScript. By default, Vite ta You can specify custom targets via the [`build.target` config option](/config/build-options.md#build-target), where the lowest target is `es2015`. -Note that by default, Vite only handles syntax transforms and **does not cover polyfills**. You can check out [Polyfill.io](https://polyfill.io/) which is a service that automatically generates polyfill bundles based on the user's browser UserAgent string. +Note that by default, Vite only handles syntax transforms and **does not cover polyfills**. You can check out https://cdnjs.cloudflare.com/polyfill/ which automatically generates polyfill bundles based on the user's browser UserAgent string. Legacy browsers can be supported via [@vitejs/plugin-legacy](https://github.com/vitejs/vite/tree/main/packages/plugin-legacy), which will automatically generate legacy chunks and corresponding ES language feature polyfills. The legacy chunks are conditionally loaded only in browsers that do not have native ESM support. @@ -34,7 +34,6 @@ For advanced base path control, check out [Advanced Base Options](#advanced-base The build can be customized via various [build config options](/config/build-options.md). Specifically, you can directly adjust the underlying [Rollup options](https://rollupjs.org/configuration-options/) via `build.rollupOptions`: ```js -// vite.config.js export default defineConfig({ build: { rollupOptions: { @@ -48,21 +47,19 @@ For example, you can specify multiple Rollup outputs with plugins that are only ## Chunking Strategy -You can configure how chunks are split using `build.rollupOptions.output.manualChunks` (see [Rollup docs](https://rollupjs.org/configuration-options/#output-manualchunks)). Until Vite 2.8, the default chunking strategy divided the chunks into `index` and `vendor`. It is a good strategy for some SPAs, but it is hard to provide a general solution for every Vite target use case. From Vite 2.9, `manualChunks` is no longer modified by default. You can continue to use the Split Vendor Chunk strategy by adding the `splitVendorChunkPlugin` in your config file: +You can configure how chunks are split using `build.rollupOptions.output.manualChunks` (see [Rollup docs](https://rollupjs.org/configuration-options/#output-manualchunks)). If you use a framework, refer to their documentation for configuring how chunks are splitted. -```js -// vite.config.js -import { splitVendorChunkPlugin } from 'vite' -export default defineConfig({ - plugins: [splitVendorChunkPlugin()], +## Load Error Handling + +Vite emits `vite:preloadError` event when it fails to load dynamic imports. `event.payload` contains the original import error. If you call `event.preventDefault()`, the error will not be thrown. + +```js twoslash +window.addEventListener('vite:preloadError', (event) => { + window.location.reload() // for example, refresh the page }) ``` -This strategy is also provided as a `splitVendorChunk({ cache: SplitVendorChunkCache })` factory, in case composition with custom logic is needed. `cache.reset()` needs to be called at `buildStart` for build watch mode to work correctly in this case. - -::: warning -You should use `build.rollupOptions.output.manualChunks` function form when using this plugin. If the object form is used, the plugin won't have any effect. -::: +When a new deployment occurs, the hosting service may delete the assets from previous deployments. As a result, a user who visited your site before the new deployment might encounter an import error. This error happens because the assets running on that user's device are outdated and it tries to import the corresponding old chunk, which is deleted. This event is useful for addressing this situation. ## Rebuild on files changes @@ -99,7 +96,7 @@ During dev, simply navigate or link to `/nested/` - it works as expected, just l During build, all you need to do is to specify multiple `.html` files as entry points: -```js +```js twoslash // vite.config.js import { resolve } from 'path' import { defineConfig } from 'vite' @@ -126,7 +123,7 @@ When you are developing a browser-oriented library, you are likely spending most When it is time to bundle your library for distribution, use the [`build.lib` config option](/config/build-options.md#build-lib). Make sure to also externalize any dependencies that you do not want to bundle into your library, e.g. `vue` or `react`: -```js +```js twoslash // vite.config.js import { resolve } from 'path' import { defineConfig } from 'vite' @@ -219,7 +216,7 @@ If the `package.json` does not contain `"type": "module"`, Vite will generate di ::: ::: tip Environment Variables -In library mode, all `import.meta.env.*` usage are statically replaced when building for production. However, `process.env.*` usage are not, so that consumers of your library can dynamically change it. If this is undesirable, you can use `define: { 'process.env.NODE_ENV': '"production"' }` for example to statically replace them. +In library mode, all [`import.meta.env.*`](./env-and-mode.md) usage are statically replaced when building for production. However, `process.env.*` usage are not, so that consumers of your library can dynamically change it. If this is undesirable, you can use `define: { 'process.env.NODE_ENV': '"production"' }` for example to statically replace them, or use [`esm-env`](https://github.com/benmccann/esm-env) for better compatibility with bundlers and runtimes. ::: ::: warning Advanced Usage @@ -241,32 +238,45 @@ A user may choose to deploy in three different paths: A single static [base](#public-base-path) isn't enough in these scenarios. Vite provides experimental support for advanced base options during build, using `experimental.renderBuiltUrl`. -```ts +```ts twoslash +import type { UserConfig } from 'vite' +// prettier-ignore +const config: UserConfig = { +// ---cut-before--- experimental: { - renderBuiltUrl(filename: string, { hostType }: { hostType: 'js' | 'css' | 'html' }) { + renderBuiltUrl(filename, { hostType }) { if (hostType === 'js') { return { runtime: `window.__toCdnUrl(${JSON.stringify(filename)})` } } else { return { relative: true } } - } + }, +}, +// ---cut-after--- } ``` If the hashed assets and public files aren't deployed together, options for each group can be defined independently using asset `type` included in the second `context` param given to the function. -```ts +```ts twoslash +import type { UserConfig } from 'vite' +import path from 'node:path' +// prettier-ignore +const config: UserConfig = { +// ---cut-before--- experimental: { - renderBuiltUrl(filename: string, { hostId, hostType, type }: { hostId: string, hostType: 'js' | 'css' | 'html', type: 'public' | 'asset' }) { + renderBuiltUrl(filename, { hostId, hostType, type }) { if (type === 'public') { return 'https://www.domain.com/' + filename - } - else if (path.extname(hostId) === '.js') { + } else if (path.extname(hostId) === '.js') { return { runtime: `window.__assetsPath(${JSON.stringify(filename)})` } - } - else { + } else { return 'https://cdn.domain.com/assets/' + filename } - } + }, +}, +// ---cut-after--- } ``` + +Note that the `filename` passed is a decoded URL, and if the function returns a URL string, it should also be decoded. Vite will handle the encoding automatically when rendering the URLs. If an object with `runtime` is returned, encoding should be handled yourself where needed as the runtime code will be rendered as is. diff --git a/docs/guide/cli.md b/docs/guide/cli.md index 08134c4c839a8b..8256f0811ddf29 100644 --- a/docs/guide/cli.md +++ b/docs/guide/cli.md @@ -4,7 +4,7 @@ ### `vite` -Start Vite dev server in the current directory. +Start Vite dev server in the current directory. `vite dev` and `vite serve` are aliases for `vite`. #### Usage @@ -58,7 +58,6 @@ vite build [root] | `--minify [minifier]` | Enable/disable minification, or specify minifier to use (default: `"esbuild"`) (`boolean \| "terser" \| "esbuild"`) | | `--manifest [name]` | Emit build manifest json (`boolean \| string`) | | `--ssrManifest [name]` | Emit ssr manifest json (`boolean \| string`) | -| `--force` | Force the optimizer to ignore the cache and re-bundle (experimental)(`boolean`) | | `--emptyOutDir` | Force empty outDir when it's outside of root (`boolean`) | | `-w, --watch` | Rebuilds when modules have changed on disk (`boolean`) | | `-c, --config ` | Use specified config file (`string`) | diff --git a/docs/guide/dep-pre-bundling.md b/docs/guide/dep-pre-bundling.md index 0bc25b6e69ff1e..e387a6ae1b8a48 100644 --- a/docs/guide/dep-pre-bundling.md +++ b/docs/guide/dep-pre-bundling.md @@ -37,7 +37,9 @@ In a monorepo setup, a dependency may be a linked package from the same repo. Vi However, this requires the linked dep to be exported as ESM. If not, you can add the dependency to [`optimizeDeps.include`](/config/dep-optimization-options.md#optimizedeps-include) and [`build.commonjsOptions.include`](/config/build-options.md#build-commonjsoptions) in your config. -```js +```js twoslash +import { defineConfig } from 'vite' +// ---cut--- export default defineConfig({ optimizeDeps: { include: ['linked-dep'], @@ -60,7 +62,7 @@ A typical use case for `optimizeDeps.include` or `optimizeDeps.exclude` is when Both `include` and `exclude` can be used to deal with this. If the dependency is large (with many internal modules) or is CommonJS, then you should include it; If the dependency is small and is already valid ESM, you can exclude it and let the browser load it directly. -You can further customize esbuild too with the [`optimizeDeps.esbuildOptions` option](/config/dep-optimization-options.md#optimizedeps-esbuildoptions). For example, adding an esbuild plugin to handle special files in dependencies. +You can further customize esbuild too with the [`optimizeDeps.esbuildOptions` option](/config/dep-optimization-options.md#optimizedeps-esbuildoptions). For example, adding an esbuild plugin to handle special files in dependencies or changing the [build `target`](https://esbuild.github.io/api/#target). ## Caching diff --git a/docs/guide/env-and-mode.md b/docs/guide/env-and-mode.md index 888b516821b7b4..7c0c58190c9db6 100644 --- a/docs/guide/env-and-mode.md +++ b/docs/guide/env-and-mode.md @@ -2,7 +2,7 @@ ## Env Variables -Vite exposes env variables on the special **`import.meta.env`** object. Some built-in variables are available in all cases: +Vite exposes env variables on the special **`import.meta.env`** object, which are statically replaced at build time. Some built-in variables are available in all cases: - **`import.meta.env.MODE`**: {string} the [mode](#modes) the app is running in. @@ -46,10 +46,15 @@ DB_PASSWORD=foobar Only `VITE_SOME_KEY` will be exposed as `import.meta.env.VITE_SOME_KEY` to your client source code, but `DB_PASSWORD` will not. ```js -console.log(import.meta.env.VITE_SOME_KEY) // 123 +console.log(import.meta.env.VITE_SOME_KEY) // "123" console.log(import.meta.env.DB_PASSWORD) // undefined ``` +:::tip Env parsing + +As shown above, `VITE_SOME_KEY` is a number but returns a string when parsed. The same would also happen for boolean env variables. Make sure to convert to the desired type when using it in your code. +::: + Also, Vite uses [dotenv-expand](https://github.com/motdotla/dotenv-expand) to expand variables out of the box. To learn more about the syntax, check out [their docs](https://github.com/motdotla/dotenv-expand#what-rules-does-the-expansion-engine-follow). Note that if you want to use `$` inside your environment value, you have to escape it with `\`. @@ -74,7 +79,7 @@ If you want to customize the env variables prefix, see the [envPrefix](/config/s By default, Vite provides type definitions for `import.meta.env` in [`vite/client.d.ts`](https://github.com/vitejs/vite/blob/main/packages/vite/client.d.ts). While you can define more custom env variables in `.env.[mode]` files, you may want to get TypeScript IntelliSense for user-defined env variables that are prefixed with `VITE_`. -To achieve this, you can create an `env.d.ts` in `src` directory, then augment `ImportMetaEnv` like this: +To achieve this, you can create an `vite-env.d.ts` in `src` directory, then augment `ImportMetaEnv` like this: ```typescript /// @@ -89,7 +94,7 @@ interface ImportMeta { } ``` -If your code relies on types from browser environments such as [DOM](https://github.com/microsoft/TypeScript/blob/main/lib/lib.dom.d.ts) and [WebWorker](https://github.com/microsoft/TypeScript/blob/main/lib/lib.webworker.d.ts), you can update the [lib](https://www.typescriptlang.org/tsconfig#lib) field in `tsconfig.json`. +If your code relies on types from browser environments such as [DOM](https://github.com/microsoft/TypeScript/blob/main/src/lib/dom.generated.d.ts) and [WebWorker](https://github.com/microsoft/TypeScript/blob/main/src/lib/webworker.generated.d.ts), you can update the [lib](https://www.typescriptlang.org/tsconfig#lib) field in `tsconfig.json`. ```json { @@ -97,6 +102,11 @@ If your code relies on types from browser environments such as [DOM](https://git } ``` +:::warning Imports will break type augmentation + +If the `ImportMetaEnv` augmentation does not work, make sure you do not have any `import` statements in `vite-env.d.ts`. See the [TypeScript documentation](https://www.typescriptlang.org/docs/handbook/2/modules.html#how-javascript-modules-are-defined) for more information. +::: + ## HTML Env Replacement Vite also supports replacing env variables in HTML files. Any properties in `import.meta.env` can be used in HTML files with a special `%ENV_NAME%` syntax: @@ -108,6 +118,8 @@ Vite also supports replacing env variables in HTML files. Any properties in `imp If the env doesn't exist in `import.meta.env`, e.g. `%NON_EXISTENT%`, it will be ignored and not replaced, unlike `import.meta.env.NON_EXISTENT` in JS where it's replaced as `undefined`. +Given that Vite is used by many frameworks, it is intentionally unopinionated about complex replacements like conditionals. Vite can be extended using [an existing userland plugin](https://github.com/vitejs/awesome-vite#transformers) or a custom plugin that implements the [`transformIndexHtml` hook](./api-plugin#transformindexhtml). + ## Modes By default, the dev server (`dev` command) runs in `development` mode and the `build` command runs in `production` mode. @@ -140,3 +152,35 @@ As `vite build` runs a production build by default, you can also change this and # .env.testing NODE_ENV=development ``` + +## NODE_ENV and Modes + +It's important to note that `NODE_ENV` (`process.env.NODE_ENV`) and modes are two different concepts. Here's how different commands affect the `NODE_ENV` and mode: + +| Command | NODE_ENV | Mode | +| ---------------------------------------------------- | --------------- | --------------- | +| `vite build` | `"production"` | `"production"` | +| `vite build --mode development` | `"production"` | `"development"` | +| `NODE_ENV=development vite build` | `"development"` | `"production"` | +| `NODE_ENV=development vite build --mode development` | `"development"` | `"development"` | + +The different values of `NODE_ENV` and mode also reflect on its corresponding `import.meta.env` properties: + +| Command | `import.meta.env.PROD` | `import.meta.env.DEV` | +| ---------------------- | ---------------------- | --------------------- | +| `NODE_ENV=production` | `true` | `false` | +| `NODE_ENV=development` | `false` | `true` | +| `NODE_ENV=other` | `false` | `true` | + +| Command | `import.meta.env.MODE` | +| -------------------- | ---------------------- | +| `--mode production` | `"production"` | +| `--mode development` | `"development"` | +| `--mode staging` | `"staging"` | + +:::tip `NODE_ENV` in `.env` files + +`NODE_ENV=...` can be set in the command, and also in your `.env` file. If `NODE_ENV` is specified in a `.env.[mode]` file, the mode can be used to control its value. However, both `NODE_ENV` and modes remain as two different concepts. + +The main benefit with `NODE_ENV=...` in the command is that it allows Vite to detect the value early. It also allows you to read `process.env.NODE_ENV` in your Vite config as Vite can only load the env files once the config is evaluated. +::: diff --git a/docs/guide/features.md b/docs/guide/features.md index 6b195cd5ec41cb..75940b8d17310c 100644 --- a/docs/guide/features.md +++ b/docs/guide/features.md @@ -1,6 +1,6 @@ # Features -At the very basic level, developing using Vite is not that much different from using a static file server. However, Vite provides many enhancements over native ESM imports to support various features that are typically seen in bundler-based setups. +At the very basic level, developing using Vite is not that different from using a static file server. However, Vite provides many enhancements over native ESM imports to support various features that are typically seen in bundler-based setups. ## NPM Dependency Resolving and Pre-Bundling @@ -65,7 +65,7 @@ It is because `esbuild` only performs transpilation without type information, it You must set `"isolatedModules": true` in your `tsconfig.json` under `compilerOptions`, so that TS will warn you against the features that do not work with isolated transpilation. -However, some libraries (e.g. [`vue`](https://github.com/vuejs/core/issues/1228)) don't work well with `"isolatedModules": true`. You can use `"skipLibCheck": true` to temporarily suppress the errors until it is fixed upstream. +If a dependency doesn't work well with `"isolatedModules": true`. You can use `"skipLibCheck": true` to temporarily suppress the errors until it is fixed upstream. #### `useDefineForClassFields` @@ -111,7 +111,9 @@ As such, it is recommended to set `target` to `ESNext` or `ES2022` or newer, or - [`experimentalDecorators`](https://www.typescriptlang.org/tsconfig#experimentalDecorators) - [`alwaysStrict`](https://www.typescriptlang.org/tsconfig#alwaysStrict) -If migrating your codebase to `"isolatedModules": true` is an insurmountable effort, you may be able to get around it with a third-party plugin such as [rollup-plugin-friendly-type-imports](https://www.npmjs.com/package/rollup-plugin-friendly-type-imports). However, this approach is not officially supported by Vite. +::: tip `skipLibCheck` +Vite starter templates have `"skipLibCheck": "true"` by default to avoid typechecking dependencies, as they may choose to only support specific versions and configurations of TypeScript. You can learn more at [vuejs/vue-cli#5688](https://github.com/vuejs/vue-cli/pull/5688). +::: ### Client Types @@ -172,9 +174,9 @@ Vite provides first-class Vue support: Vue users should use the official [@vitejs/plugin-vue-jsx](https://github.com/vitejs/vite-plugin-vue/tree/main/packages/plugin-vue-jsx) plugin, which provides Vue 3 specific features including HMR, global component resolving, directives and slots. -If not using JSX with React or Vue, custom `jsxFactory` and `jsxFragment` can be configured using the [`esbuild` option](/config/shared-options.md#esbuild). For example for Preact: +If using JSX without React or Vue, custom `jsxFactory` and `jsxFragment` can be configured using the [`esbuild` option](/config/shared-options.md#esbuild). For example for Preact: -```js +```js twoslash // vite.config.js import { defineConfig } from 'vite' @@ -190,7 +192,7 @@ More details in [esbuild docs](https://esbuild.github.io/content-types/#jsx). You can inject the JSX helpers using `jsxInject` (which is a Vite-only option) to avoid manual imports: -```js +```js twoslash // vite.config.js import { defineConfig } from 'vite' @@ -228,7 +230,9 @@ Any CSS file ending with `.module.css` is considered a [CSS modules file](https: } ``` -```js +```js twoslash +import 'vite/client' +// ---cut--- import classes from './example.module.css' document.getElementById('foo').className = classes.red ``` @@ -237,7 +241,9 @@ CSS modules behavior can be configured via the [`css.modules` option](/config/sh If `css.modules.localsConvention` is set to enable camelCase locals (e.g. `localsConvention: 'camelCaseOnly'`), you can also use named imports: -```js +```js twoslash +import 'vite/client' +// ---cut--- // .apply-color -> applyColor import { applyColor } from './example.module.css' document.getElementById('foo').className = applyColor @@ -272,7 +278,9 @@ You can also use CSS modules combined with pre-processors by prepending `.module The automatic injection of CSS contents can be turned off via the `?inline` query parameter. In this case, the processed CSS string is returned as the module's default export as usual, but the styles aren't injected to the page. -```js +```js twoslash +import 'vite/client' +// ---cut--- import './foo.css' // will be injected into the page import otherStyles from './bar.css?inline' // will not be injected ``` @@ -289,7 +297,7 @@ Starting from Vite 4.4, there is experimental support for [Lightning CSS](https: npm add -D lightningcss ``` -If enabled, CSS files will be processed by Lightning CSS instead of PostCSS. To configure it, you can pass Lightning CSS options to the [`css.lightingcss`](../config/shared-options.md#css-lightningcss) config option. +If enabled, CSS files will be processed by Lightning CSS instead of PostCSS. To configure it, you can pass Lightning CSS options to the [`css.lightningcss`](../config/shared-options.md#css-lightningcss) config option. To configure CSS Modules, you'll use [`css.lightningcss.cssModules`](https://lightningcss.dev/css-modules.html) instead of [`css.modules`](../config/shared-options.md#css-modules) (which configures the way PostCSS handles CSS modules). @@ -303,29 +311,39 @@ By default, Vite uses esbuild to minify CSS. Lightning CSS can also be used as t Importing a static asset will return the resolved public URL when it is served: -```js +```js twoslash +import 'vite/client' +// ---cut--- import imgUrl from './img.png' document.getElementById('hero-img').src = imgUrl ``` Special queries can modify how assets are loaded: -```js +```js twoslash +import 'vite/client' +// ---cut--- // Explicitly load assets as URL import assetAsURL from './asset.js?url' ``` -```js +```js twoslash +import 'vite/client' +// ---cut--- // Load assets as strings import assetAsString from './shader.glsl?raw' ``` -```js +```js twoslash +import 'vite/client' +// ---cut--- // Load Web Workers import Worker from './worker.js?worker' ``` -```js +```js twoslash +import 'vite/client' +// ---cut--- // Web Workers inlined as base64 strings at build time import InlineWorker from './worker.js?worker&inline' ``` @@ -336,7 +354,9 @@ More details in [Static Asset Handling](./assets). JSON files can be directly imported - named imports are also supported: -```js +```js twoslash +import 'vite/client' +// ---cut--- // import the entire object import json from './example.json' // import a root field as named exports - helps with tree-shaking! @@ -347,7 +367,9 @@ import { field } from './example.json' Vite supports importing multiple modules from the file system via the special `import.meta.glob` function: -```js +```js twoslash +import 'vite/client' +// ---cut--- const modules = import.meta.glob('./dir/*.js') ``` @@ -373,7 +395,9 @@ for (const path in modules) { Matched files are by default lazy-loaded via dynamic import and will be split into separate chunks during build. If you'd rather import all the modules directly (e.g. relying on side-effects in these modules to be applied first), you can pass `{ eager: true }` as the second argument: -```js +```js twoslash +import 'vite/client' +// ---cut--- const modules = import.meta.glob('./dir/*.js', { eager: true }) ``` @@ -389,31 +413,13 @@ const modules = { } ``` -### Glob Import As - -`import.meta.glob` also supports importing files as strings (similar to [Importing Asset as String](https://vitejs.dev/guide/assets.html#importing-asset-as-string)) with the [Import Reflection](https://github.com/tc39/proposal-import-reflection) syntax: - -```js -const modules = import.meta.glob('./dir/*.js', { as: 'raw', eager: true }) -``` - -The above will be transformed into the following: - -```js -// code produced by vite -const modules = { - './dir/foo.js': 'export default "foo"\n', - './dir/bar.js': 'export default "bar"\n', -} -``` - -`{ as: 'url' }` is also supported for loading assets as URLs. - ### Multiple Patterns The first argument can be an array of globs, for example -```js +```js twoslash +import 'vite/client' +// ---cut--- const modules = import.meta.glob(['./dir/*.js', './another/*.js']) ``` @@ -421,7 +427,9 @@ const modules = import.meta.glob(['./dir/*.js', './another/*.js']) Negative glob patterns are also supported (prefixed with `!`). To ignore some files from the result, you can add exclude glob patterns to the first argument: -```js +```js twoslash +import 'vite/client' +// ---cut--- const modules = import.meta.glob(['./dir/*.js', '!**/bar.js']) ``` @@ -436,7 +444,9 @@ const modules = { It's possible to only import parts of the modules with the `import` options. -```ts +```ts twoslash +import 'vite/client' +// ---cut--- const modules = import.meta.glob('./dir/*.js', { import: 'setup' }) ``` @@ -450,7 +460,9 @@ const modules = { When combined with `eager` it's even possible to have tree-shaking enabled for those modules. -```ts +```ts twoslash +import 'vite/client' +// ---cut--- const modules = import.meta.glob('./dir/*.js', { import: 'setup', eager: true, @@ -469,7 +481,9 @@ const modules = { Set `import` to `default` to import the default export. -```ts +```ts twoslash +import 'vite/client' +// ---cut--- const modules = import.meta.glob('./dir/*.js', { import: 'default', eager: true, @@ -488,22 +502,43 @@ const modules = { #### Custom Queries -You can also use the `query` option to provide custom queries to imports for other plugins to consume. +You can also use the `query` option to provide queries to imports, for example, to import assets [as a string](https://vitejs.dev/guide/assets.html#importing-asset-as-string) or [as a url](https://vitejs.dev/guide/assets.html#importing-asset-as-url): -```ts -const modules = import.meta.glob('./dir/*.js', { - query: { foo: 'bar', bar: true }, +```ts twoslash +import 'vite/client' +// ---cut--- +const moduleStrings = import.meta.glob('./dir/*.svg', { + query: '?raw', + import: 'default', +}) +const moduleUrls = import.meta.glob('./dir/*.svg', { + query: '?url', + import: 'default', }) ``` ```ts // code produced by vite: -const modules = { - './dir/foo.js': () => import('./dir/foo.js?foo=bar&bar=true'), - './dir/bar.js': () => import('./dir/bar.js?foo=bar&bar=true'), +const moduleStrings = { + './dir/foo.svg': () => import('./dir/foo.js?raw').then((m) => m['default']), + './dir/bar.svg': () => import('./dir/bar.js?raw').then((m) => m['default']), +} +const moduleUrls = { + './dir/foo.svg': () => import('./dir/foo.js?url').then((m) => m['default']), + './dir/bar.svg': () => import('./dir/bar.js?url').then((m) => m['default']), } ``` +You can also provide custom queries for other plugins to consume: + +```ts twoslash +import 'vite/client' +// ---cut--- +const modules = import.meta.glob('./dir/*.js', { + query: { foo: 'bar', bar: true }, +}) +``` + ### Glob Import Caveats Note that: @@ -528,7 +563,9 @@ Note that variables only represent file names one level deep. If `file` is `'foo Pre-compiled `.wasm` files can be imported with `?init`. The default export will be an initialization function that returns a Promise of the [`WebAssembly.Instance`](https://developer.mozilla.org/en-US/docs/WebAssembly/JavaScript_interface/Instance): -```js +```js twoslash +import 'vite/client' +// ---cut--- import init from './example.wasm?init' init().then((instance) => { @@ -538,7 +575,10 @@ init().then((instance) => { The init function can also take an importObject which is passed along to [`WebAssembly.instantiate`](https://developer.mozilla.org/en-US/docs/WebAssembly/JavaScript_interface/instantiate) as its second argument: -```js +```js twoslash +import 'vite/client' +import init from './example.wasm?init' +// ---cut--- init({ imports: { someFunc: () => { @@ -561,7 +601,9 @@ Use [`vite-plugin-wasm`](https://github.com/Menci/vite-plugin-wasm) or other com If you need access to the `Module` object, e.g. to instantiate it multiple times, use an [explicit URL import](./assets#explicit-url-imports) to resolve the asset, and then perform the instantiation: -```js +```js twoslash +import 'vite/client' +// ---cut--- import wasmUrl from 'foo.wasm?url' const main = async () => { @@ -581,7 +623,9 @@ See the issue [Support wasm in SSR](https://github.com/vitejs/vite/issues/8882). Here is an alternative, assuming the project base is the current directory: -```js +```js twoslash +import 'vite/client' +// ---cut--- import wasmUrl from 'foo.wasm?url' import { readFile } from 'node:fs/promises' @@ -615,32 +659,62 @@ const worker = new Worker(new URL('./worker.js', import.meta.url), { }) ``` +The worker detection will only work if the `new URL()` constructor is used directly inside the `new Worker()` declaration. Additionally, all options parameters must be static values (i.e. string literals). + ### Import with Query Suffixes A web worker script can be directly imported by appending `?worker` or `?sharedworker` to the import request. The default export will be a custom worker constructor: -```js +```js twoslash +import 'vite/client' +// ---cut--- import MyWorker from './worker?worker' const worker = new MyWorker() ``` -The worker script can also use ESM `import` statements instead of `importScripts()`. **Note**: During dev this relies on [browser native support](https://caniuse.com/?search=module%20worker), but for the production build it is compiled away. +The worker script can also use ESM `import` statements instead of `importScripts()`. **Note**: During development this relies on [browser native support](https://caniuse.com/?search=module%20worker), but for the production build it is compiled away. By default, the worker script will be emitted as a separate chunk in the production build. If you wish to inline the worker as base64 strings, add the `inline` query: -```js +```js twoslash +import 'vite/client' +// ---cut--- import MyWorker from './worker?worker&inline' ``` If you wish to retrieve the worker as a URL, add the `url` query: -```js +```js twoslash +import 'vite/client' +// ---cut--- import MyWorker from './worker?worker&url' ``` See [Worker Options](/config/worker-options.md) for details on configuring the bundling of all workers. +## Content Security Policy (CSP) + +To deploy CSP, certain directives or configs must be set due to Vite's internals. + +### [`'nonce-{RANDOM}'`](https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Content-Security-Policy/Sources#nonce-base64-value) + +When [`html.cspNonce`](/config/shared-options#html-cspnonce) is set, Vite adds a nonce attribute with the specified value to any ` diff --git a/docs/package.json b/docs/package.json new file mode 100644 index 00000000000000..15ba56ec977f20 --- /dev/null +++ b/docs/package.json @@ -0,0 +1,17 @@ +{ + "name": "docs", + "private": true, + "type": "module", + "scripts": { + "docs": "vitepress dev", + "docs-build": "vitepress build", + "docs-serve": "vitepress serve" + }, + "devDependencies": { + "@shikijs/vitepress-twoslash": "^1.10.0", + "@types/express": "^4.17.21", + "feed": "^4.2.2", + "vitepress": "1.2.3", + "vue": "^3.4.31" + } +} diff --git a/docs/public/handsontable.svg b/docs/public/handsontable.svg new file mode 100644 index 00000000000000..865447fd65b4b4 --- /dev/null +++ b/docs/public/handsontable.svg @@ -0,0 +1 @@ +Handsontable_logo_color \ No newline at end of file diff --git a/docs/public/huly.svg b/docs/public/huly.svg new file mode 100644 index 00000000000000..8a2631436c64c9 --- /dev/null +++ b/docs/public/huly.svg @@ -0,0 +1,6 @@ + + + + + + diff --git a/docs/public/logo-uwu.png b/docs/public/logo-uwu.png new file mode 100644 index 00000000000000..e45e40af12a3e2 Binary files /dev/null and b/docs/public/logo-uwu.png differ diff --git a/docs/public/nx.svg b/docs/public/nx.svg new file mode 100644 index 00000000000000..b97dfb0e6381ee --- /dev/null +++ b/docs/public/nx.svg @@ -0,0 +1,8 @@ + + + + + + + + diff --git a/docs/public/og-image-announcing-vite5-1.png b/docs/public/og-image-announcing-vite5-1.png new file mode 100644 index 00000000000000..cebfc993994a50 Binary files /dev/null and b/docs/public/og-image-announcing-vite5-1.png differ diff --git a/docs/public/og-image-announcing-vite5.png b/docs/public/og-image-announcing-vite5.png new file mode 100644 index 00000000000000..56040e518ae1bf Binary files /dev/null and b/docs/public/og-image-announcing-vite5.png differ diff --git a/docs/public/transloadit-dark.svg b/docs/public/transloadit-dark.svg new file mode 100644 index 00000000000000..f402f7a97d4d28 --- /dev/null +++ b/docs/public/transloadit-dark.svg @@ -0,0 +1,17 @@ + + + + + + diff --git a/docs/public/transloadit.svg b/docs/public/transloadit.svg new file mode 100644 index 00000000000000..6033c48566d90e --- /dev/null +++ b/docs/public/transloadit.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/docs/public/vite5-1-10K-modules-loading-time.png b/docs/public/vite5-1-10K-modules-loading-time.png new file mode 100644 index 00000000000000..8c94ed9bf1f2ed Binary files /dev/null and b/docs/public/vite5-1-10K-modules-loading-time.png differ diff --git a/docs/releases.md b/docs/releases.md index 31599406414403..4ea42cb0de5e0f 100644 --- a/docs/releases.md +++ b/docs/releases.md @@ -4,13 +4,7 @@ Vite releases follow [Semantic Versioning](https://semver.org/). You can see the A full changelog of past releases is [available on GitHub](https://github.com/vitejs/vite/blob/main/packages/vite/CHANGELOG.md). -::: tip note -The next Vite Major release will happen after the Node 16 EOL in September. - -Check out the [Vite 5 discussion](https://github.com/vitejs/vite/discussions/12466) for more information. -::: - -## Release Cycle​ +## Release Cycle Vite does not have a fixed release cycle. @@ -24,7 +18,7 @@ The Vite team partners with the main projects in the ecosystem to test new Vite ## Semantic Versioning Edge Cases -### TypeScript Definitions​ +### TypeScript Definitions We may ship incompatible changes to TypeScript definitions between minor versions. This is because: @@ -40,16 +34,16 @@ We may ship incompatible changes to TypeScript definitions between minor version Non-LTS Node.js versions (odd-numbered) are not tested as part of Vite's CI, but they should still work before their [EOL](https://endoflife.date/nodejs). -## Pre Releases​ +## Pre Releases Minor releases typically go through a non-fixed number of beta releases. Major releases will go through an alpha phase and a beta phase. Pre-releases allow early adopters and maintainers from the Ecosystem to do integration and stability testing, and provide feedback. Do not use pre-releases in production. All pre-releases are considered unstable and may ship breaking changes in between. Always pin to exact versions when using pre-releases. -## Deprecations​ +## Deprecations We periodically deprecate features that have been superseded by better alternatives in Minor releases. Deprecated features will continue to work with a type or logged warning. They will be removed in the next major release after entering deprecated status. The [Migration Guide](https://vitejs.dev/guide/migration.html) for each major will list these removals and document an upgrade path for them. -## Experimental Features​ +## Experimental Features -Some features are marked as experimental when released in a stable version of Vite. Experimental features allows us to gather real-world experience to influence their final design. The goal is to let users provide feedback by testing them in production. Experimental features themselves are considered unstable, and should only be used in a controlled manner. These features may change between Minors, so users must pin their Vite version when they rely on them. +Some features are marked as experimental when released in a stable version of Vite. Experimental features allow us to gather real-world experience to influence their final design. The goal is to let users provide feedback by testing them in production. Experimental features themselves are considered unstable, and should only be used in a controlled manner. These features may change between Minors, so users must pin their Vite version when they rely on them. We will create [a GitHub discussion](https://github.com/vitejs/vite/discussions/categories/feedback?discussions_q=is%3Aopen+label%3Aexperimental+category%3AFeedback) for each experimental feature. diff --git a/eslint.config.js b/eslint.config.js new file mode 100644 index 00000000000000..0b103b51776cef --- /dev/null +++ b/eslint.config.js @@ -0,0 +1,288 @@ +// @ts-check +import { builtinModules, createRequire } from 'node:module' +import eslint from '@eslint/js' +import pluginN from 'eslint-plugin-n' +import pluginImportX from 'eslint-plugin-import-x' +import pluginRegExp from 'eslint-plugin-regexp' +import tseslint from 'typescript-eslint' +import globals from 'globals' + +const require = createRequire(import.meta.url) +const pkg = require('./package.json') +const pkgVite = require('./packages/vite/package.json') + +export default tseslint.config( + { + ignores: [ + 'packages/create-vite/template-*', + '**/dist/**', + '**/fixtures/**', + '**/playground-temp/**', + '**/temp/**', + '**/.vitepress/cache/**', + '**/*.snap', + ], + }, + eslint.configs.recommended, + ...tseslint.configs.recommended, + ...tseslint.configs.stylistic, + pluginRegExp.configs['flat/recommended'], + { + name: 'main', + languageOptions: { + parser: tseslint.parser, + parserOptions: { + sourceType: 'module', + ecmaVersion: 2022, + }, + globals: { + ...globals.es2021, + ...globals.node, + }, + }, + plugins: { + n: pluginN, + 'import-x': pluginImportX, + }, + rules: { + 'n/no-exports-assign': 'error', + 'n/no-unpublished-bin': 'error', + 'n/no-unsupported-features/es-builtins': 'error', + 'n/no-unsupported-features/node-builtins': 'error', + 'n/process-exit-as-throw': 'error', + 'n/hashbang': 'error', + + eqeqeq: ['warn', 'always', { null: 'never' }], + 'no-debugger': ['error'], + 'no-empty': ['warn', { allowEmptyCatch: true }], + 'no-process-exit': 'off', + 'no-useless-escape': 'off', + 'prefer-const': [ + 'warn', + { + destructuring: 'all', + }, + ], + + 'n/no-missing-require': [ + 'error', + { + // for try-catching yarn pnp + allowModules: ['pnpapi', 'vite'], + tryExtensions: ['.ts', '.js', '.jsx', '.tsx', '.d.ts'], + }, + ], + 'n/no-extraneous-import': [ + 'error', + { + allowModules: ['vite', 'less', 'sass', 'vitest', 'unbuild'], + }, + ], + 'n/no-extraneous-require': [ + 'error', + { + allowModules: ['vite'], + }, + ], + + '@typescript-eslint/ban-ts-comment': 'error', + '@typescript-eslint/ban-types': 'off', // TODO: we should turn this on in a new PR + '@typescript-eslint/explicit-module-boundary-types': [ + 'error', + { allowArgumentsExplicitlyTypedAsAny: true }, + ], + '@typescript-eslint/no-empty-function': [ + 'error', + { allow: ['arrowFunctions'] }, + ], + '@typescript-eslint/no-empty-interface': 'off', + '@typescript-eslint/no-explicit-any': 'off', // maybe we should turn this on in a new PR + 'no-extra-semi': 'off', + '@typescript-eslint/no-extra-semi': 'off', // conflicts with prettier + '@typescript-eslint/no-inferrable-types': 'off', + '@typescript-eslint/no-unused-vars': 'off', // maybe we should turn this on in a new PR + '@typescript-eslint/no-var-requires': 'off', + '@typescript-eslint/consistent-type-imports': [ + 'error', + { prefer: 'type-imports', disallowTypeAnnotations: false }, + ], + // disable rules set in @typescript-eslint/stylistic v6 that wasn't set in @typescript-eslint/recommended v5 and which conflict with current code + // maybe we should turn them on in a new PR + '@typescript-eslint/array-type': 'off', + '@typescript-eslint/ban-tslint-comment': 'off', + '@typescript-eslint/consistent-generic-constructors': 'off', + '@typescript-eslint/consistent-indexed-object-style': 'off', + '@typescript-eslint/consistent-type-definitions': 'off', + '@typescript-eslint/prefer-for-of': 'off', + '@typescript-eslint/prefer-function-type': 'off', + + 'import-x/no-nodejs-modules': [ + 'error', + { allow: builtinModules.map((mod) => `node:${mod}`) }, + ], + 'import-x/no-duplicates': 'error', + 'import-x/order': 'error', + 'sort-imports': [ + 'error', + { + ignoreCase: false, + ignoreDeclarationSort: true, + ignoreMemberSort: false, + memberSyntaxSortOrder: ['none', 'all', 'multiple', 'single'], + allowSeparatedGroups: false, + }, + ], + + 'regexp/no-contradiction-with-assertion': 'error', + // in some cases using explicit letter-casing is more performant than the `i` flag + 'regexp/use-ignore-case': 'off', + }, + }, + { + name: 'vite/globals', + files: ['packages/**/*.?([cm])[jt]s?(x)'], + ignores: ['**/__tests__/**'], + rules: { + 'no-restricted-globals': ['error', 'require', '__dirname', '__filename'], + }, + }, + { + name: 'vite/node', + files: ['packages/vite/src/node/**/*.?([cm])[jt]s?(x)'], + rules: { + 'no-console': ['error'], + 'n/no-restricted-require': [ + 'error', + Object.keys(pkgVite.devDependencies).map((d) => ({ + name: d, + message: + `devDependencies can only be imported using ESM syntax so ` + + `that they are included in the rollup bundle. If you are trying to ` + + `lazy load a dependency, use (await import('dependency')).default instead.`, + })), + ], + }, + }, + { + name: 'playground/enforce-esm', + files: ['playground/**/*.?([cm])[jt]s?(x)'], + ignores: [ + 'playground/ssr-resolve/**', + 'playground/**/*{commonjs,cjs}*/**', + 'playground/**/*{commonjs,cjs}*', + 'playground/**/*dep*/**', + 'playground/resolve/browser-module-field2/index.web.js', + 'playground/resolve/browser-field/**', + 'playground/tailwind/**', // blocked by https://github.com/postcss/postcss-load-config/issues/239 + ], + rules: { + 'import-x/no-commonjs': 'error', + }, + }, + { + name: 'playground/test', + files: ['playground/**/__tests__/**/*.?([cm])[jt]s?(x)'], + rules: { + // engine field doesn't exist in playgrounds + 'n/no-unsupported-features/es-builtins': [ + 'error', + { + version: pkg.engines.node, + }, + ], + 'n/no-unsupported-features/node-builtins': [ + 'error', + { + version: pkg.engines.node, + // ideally we would like to allow all experimental features + // https://github.com/eslint-community/eslint-plugin-n/issues/199 + ignores: ['fetch'], + }, + ], + }, + }, + + { + name: 'disables/vite/client', + files: ['packages/vite/src/client/**/*.?([cm])[jt]s?(x)'], + ignores: ['**/__tests__/**'], + rules: { + 'n/no-unsupported-features/node-builtins': 'off', + }, + }, + { + name: 'disables/vite/types', + files: [ + 'packages/vite/src/types/**/*.?([cm])[jt]s?(x)', + 'packages/vite/scripts/**/*.?([cm])[jt]s?(x)', + '**/*.spec.ts', + ], + rules: { + 'n/no-extraneous-import': 'off', + }, + }, + { + name: 'disables/create-vite/templates', + files: [ + 'packages/create-vite/template-*/**/*.?([cm])[jt]s?(x)', + '**/build.config.ts', + ], + rules: { + 'no-undef': 'off', + 'n/no-missing-import': 'off', + 'n/no-extraneous-import': 'off', + 'n/no-extraneous-require': 'off', + '@typescript-eslint/explicit-module-boundary-types': 'off', + }, + }, + { + name: 'disables/playground', + files: ['playground/**/*.?([cm])[jt]s?(x)', 'docs/**/*.?([cm])[jt]s?(x)'], + rules: { + 'n/no-extraneous-import': 'off', + 'n/no-extraneous-require': 'off', + 'n/no-missing-import': 'off', + 'n/no-missing-require': 'off', + 'n/no-unsupported-features/es-builtins': 'off', + 'n/no-unsupported-features/node-builtins': 'off', + '@typescript-eslint/explicit-module-boundary-types': 'off', + 'no-undef': 'off', + 'no-empty': 'off', + 'no-constant-condition': 'off', + '@typescript-eslint/no-empty-function': 'off', + }, + }, + { + name: 'disables/playground/tsconfig-json', + files: [ + 'playground/tsconfig-json/**/*.?([cm])[jt]s?(x)', + 'playground/tsconfig-json-load-error/**/*.?([cm])[jt]s?(x)', + ], + ignores: ['**/__tests__/**'], + rules: { + '@typescript-eslint/ban-ts-comment': 'off', + }, + }, + { + name: 'disables/js', + files: ['**/*.js', '**/*.mjs', '**/*.cjs'], + rules: { + '@typescript-eslint/explicit-module-boundary-types': 'off', + }, + }, + { + name: 'disables/dts', + files: ['**/*.d.ts'], + rules: { + '@typescript-eslint/triple-slash-reference': 'off', + }, + }, + { + name: 'disables/test', + files: ['**/__tests__/**/*.?([cm])[jt]s?(x)'], + rules: { + 'no-console': 'off', + '@typescript-eslint/ban-ts-comment': 'off', + }, + }, +) diff --git a/netlify.toml b/netlify.toml index d78254570def67..b8a4deadd38fbd 100644 --- a/netlify.toml +++ b/netlify.toml @@ -1,5 +1,5 @@ [build.environment] - NODE_VERSION = "18" + NODE_VERSION = "20" # don't need playwright for docs build PLAYWRIGHT_SKIP_BROWSER_DOWNLOAD = "1" [build] diff --git a/package.json b/package.json index a2f07dd2b200b3..1a3b2566830736 100644 --- a/package.json +++ b/package.json @@ -26,14 +26,13 @@ "test": "run-s test-unit test-serve test-build", "test-serve": "vitest run -c vitest.config.e2e.ts", "test-build": "VITE_TEST_BUILD=1 vitest run -c vitest.config.e2e.ts", - "test-build-without-plugin-commonjs": "VITE_TEST_WITHOUT_PLUGIN_COMMONJS=1 pnpm test-build", "test-unit": "vitest run", "test-docs": "pnpm run docs-build", "debug-serve": "VITE_DEBUG_SERVE=1 vitest run -c vitest.config.e2e.ts", "debug-build": "VITE_TEST_BUILD=1 VITE_PRESERVE_BUILD_ARTIFACTS=1 vitest run -c vitest.config.e2e.ts", - "docs": "vitepress dev docs", - "docs-build": "vitepress build docs", - "docs-serve": "vitepress serve docs", + "docs": "pnpm --filter=docs run docs", + "docs-build": "pnpm --filter=docs run docs-build", + "docs-serve": "pnpm --filter=docs run docs-serve", "build": "pnpm -r --filter='./packages/*' run build", "dev": "pnpm -r --parallel --filter='./packages/*' run dev", "release": "tsx scripts/release.ts", @@ -41,51 +40,45 @@ "ci-docs": "run-s build docs-build" }, "devDependencies": { - "@babel/types": "^7.23.0", - "@rollup/plugin-typescript": "^11.1.5", - "@types/babel__core": "^7.20.3", - "@types/babel__preset-env": "^7.9.4", - "@types/convert-source-map": "^2.0.2", - "@types/cross-spawn": "^6.0.4", - "@types/debug": "^4.1.10", - "@types/estree": "^1.0.3", - "@types/etag": "^1.8.2", - "@types/fs-extra": "^11.0.3", - "@types/json-stable-stringify": "^1.0.35", - "@types/less": "^3.0.5", - "@types/micromatch": "^4.0.4", - "@types/node": "^20.8.10", - "@types/picomatch": "^2.3.2", - "@types/sass": "~1.43.1", - "@types/stylus": "^0.48.41", - "@types/ws": "^8.5.8", - "@typescript-eslint/eslint-plugin": "^6.9.0", - "@typescript-eslint/parser": "^6.9.0", + "@eslint/js": "^9.6.0", + "@types/babel__core": "^7.20.5", + "@types/babel__preset-env": "^7.9.7", + "@types/convert-source-map": "^2.0.3", + "@types/cross-spawn": "^6.0.6", + "@types/debug": "^4.1.12", + "@types/estree": "^1.0.5", + "@types/etag": "^1.8.3", + "@types/fs-extra": "^11.0.4", + "@types/less": "^3.0.6", + "@types/micromatch": "^4.0.9", + "@types/node": "^20.14.9", + "@types/picomatch": "^2.3.4", + "@types/stylus": "^0.48.42", + "@types/ws": "^8.5.10", "@vitejs/release-scripts": "^1.3.1", - "conventional-changelog-cli": "^3.0.0", - "eslint": "^8.52.0", - "eslint-define-config": "^1.24.1", - "eslint-plugin-import": "^2.29.0", - "eslint-plugin-n": "^16.2.0", - "eslint-plugin-regexp": "^2.1.1", - "execa": "^8.0.1", - "fs-extra": "^11.1.1", - "lint-staged": "^15.0.2", - "npm-run-all2": "^6.1.1", - "picocolors": "^1.0.0", - "playwright-chromium": "^1.39.0", - "prettier": "3.0.3", - "rimraf": "^5.0.5", - "rollup": "^4.2.0", - "simple-git-hooks": "^2.9.0", - "tslib": "^2.6.2", - "tsx": "^3.14.0", + "conventional-changelog-cli": "^5.0.0", + "eslint": "^9.6.0", + "eslint-plugin-import-x": "^0.5.3", + "eslint-plugin-n": "^17.9.0", + "eslint-plugin-regexp": "^2.6.0", + "execa": "^9.3.0", + "fs-extra": "^11.2.0", + "globals": "^15.7.0", + "lint-staged": "^15.2.7", + "npm-run-all2": "^6.2.0", + "picocolors": "^1.0.1", + "playwright-chromium": "^1.45.0", + "prettier": "3.3.2", + "rimraf": "^5.0.7", + "rollup": "^4.13.0", + "rollup-plugin-esbuild": "^6.1.1", + "simple-git-hooks": "^2.11.1", + "tslib": "^2.6.3", + "tsx": "^4.16.0", "typescript": "^5.2.2", - "unbuild": "^2.0.0", + "typescript-eslint": "^7.15.0", "vite": "workspace:*", - "vitepress": "1.0.0-rc.24", - "vitest": "^0.34.6", - "vue": "^3.3.7" + "vitest": "^1.6.0" }, "simple-git-hooks": { "pre-commit": "pnpm exec lint-staged --concurrent false" @@ -104,23 +97,16 @@ "eslint --cache --fix" ] }, - "packageManager": "pnpm@8.10.2", + "packageManager": "pnpm@9.4.0", "pnpm": { "overrides": { "vite": "workspace:*" }, - "packageExtensions": { - "acorn-walk": { - "peerDependencies": { - "acorn": "*" - } - } - }, "patchedDependencies": { - "chokidar@3.5.3": "patches/chokidar@3.5.3.patch", - "sirv@2.0.3": "patches/sirv@2.0.3.patch", - "dotenv-expand@10.0.0": "patches/dotenv-expand@10.0.0.patch", - "postcss-load-config@4.0.1": "patches/postcss-load-config@4.0.1.patch" + "acorn@8.12.0": "patches/acorn@8.12.0.patch", + "chokidar@3.6.0": "patches/chokidar@3.6.0.patch", + "http-proxy@1.18.1": "patches/http-proxy@1.18.1.patch", + "sirv@2.0.4": "patches/sirv@2.0.4.patch" }, "peerDependencyRules": { "allowedVersions": { diff --git a/packages/create-vite/CHANGELOG.md b/packages/create-vite/CHANGELOG.md index 5afed0cd758d04..95896664ee87e8 100644 --- a/packages/create-vite/CHANGELOG.md +++ b/packages/create-vite/CHANGELOG.md @@ -1,3 +1,91 @@ +## 5.3.0 (2024-06-21) + +* fix(create-vite): revert eslint 9 upgrade in templates (#17511) ([86cf1b4](https://github.com/vitejs/vite/commit/86cf1b4)), closes [#17511](https://github.com/vitejs/vite/issues/17511) +* fix(create-vite): update tsconfig with moduleDetection true (#17468) ([7b240e4](https://github.com/vitejs/vite/commit/7b240e4)), closes [#17468](https://github.com/vitejs/vite/issues/17468) +* fix(deps): update all non-major dependencies (#16258) ([7caef42](https://github.com/vitejs/vite/commit/7caef42)), closes [#16258](https://github.com/vitejs/vite/issues/16258) +* fix(deps): update all non-major dependencies (#16376) ([58a2938](https://github.com/vitejs/vite/commit/58a2938)), closes [#16376](https://github.com/vitejs/vite/issues/16376) +* fix(deps): update all non-major dependencies (#16488) ([2d50be2](https://github.com/vitejs/vite/commit/2d50be2)), closes [#16488](https://github.com/vitejs/vite/issues/16488) +* fix(deps): update all non-major dependencies (#16549) ([2d6a13b](https://github.com/vitejs/vite/commit/2d6a13b)), closes [#16549](https://github.com/vitejs/vite/issues/16549) +* fix(deps): update all non-major dependencies (#16603) ([6711553](https://github.com/vitejs/vite/commit/6711553)), closes [#16603](https://github.com/vitejs/vite/issues/16603) +* fix(deps): update all non-major dependencies (#16660) ([bf2f014](https://github.com/vitejs/vite/commit/bf2f014)), closes [#16660](https://github.com/vitejs/vite/issues/16660) +* fix(deps): update all non-major dependencies (#17321) ([4a89766](https://github.com/vitejs/vite/commit/4a89766)), closes [#17321](https://github.com/vitejs/vite/issues/17321) +* fix(deps): update all non-major dependencies (#17430) ([4453d35](https://github.com/vitejs/vite/commit/4453d35)), closes [#17430](https://github.com/vitejs/vite/issues/17430) +* fix(deps): update all non-major dependencies (#17494) ([bf123f2](https://github.com/vitejs/vite/commit/bf123f2)), closes [#17494](https://github.com/vitejs/vite/issues/17494) +* chore(create-vite): update IDE support instructions in helloworld components (#16605) ([a265282](https://github.com/vitejs/vite/commit/a265282)), closes [#16605](https://github.com/vitejs/vite/issues/16605) +* chore(deps): update all non-major dependencies (#16325) ([a78e265](https://github.com/vitejs/vite/commit/a78e265)), closes [#16325](https://github.com/vitejs/vite/issues/16325) +* chore(deps): update all non-major dependencies (#16722) ([b45922a](https://github.com/vitejs/vite/commit/b45922a)), closes [#16722](https://github.com/vitejs/vite/issues/16722) +* chore(deps): update all non-major dependencies (#17373) ([f2d52f1](https://github.com/vitejs/vite/commit/f2d52f1)), closes [#17373](https://github.com/vitejs/vite/issues/17373) +* chore(deps): update dependency eslint to v9 (#16661) ([6c10662](https://github.com/vitejs/vite/commit/6c10662)), closes [#16661](https://github.com/vitejs/vite/issues/16661) +* chore(deps): update dependency execa to v9 (#16662) ([76d1642](https://github.com/vitejs/vite/commit/76d1642)), closes [#16662](https://github.com/vitejs/vite/issues/16662) +* feat(create-vite): add help usage (#16390) ([1d9bfc0](https://github.com/vitejs/vite/commit/1d9bfc0)), closes [#16390](https://github.com/vitejs/vite/issues/16390) +* feat(create-vite): use "solution" tsconfig so that vite.config.ts is type checked (#15913) ([cf3f40c](https://github.com/vitejs/vite/commit/cf3f40c)), closes [#15913](https://github.com/vitejs/vite/issues/15913) +* docs(create-vite): link to Vue docs for IDE support info (#16225) ([520bb89](https://github.com/vitejs/vite/commit/520bb89)), closes [#16225](https://github.com/vitejs/vite/issues/16225) + + + +## 5.2.3 (2024-03-20) + +* docs: update volar name and remove takeover mode related docs (#16171) ([0a56177](https://github.com/vitejs/vite/commit/0a56177)), closes [#16171](https://github.com/vitejs/vite/issues/16171) +* fix(create-vite): remove vue3 deprecated plugin (TypeScript Vue Plugin) (#16158) ([1645fc0](https://github.com/vitejs/vite/commit/1645fc0)), closes [#16158](https://github.com/vitejs/vite/issues/16158) +* fix(create-vite): switch to default Remix template (#16203) ([ea480df](https://github.com/vitejs/vite/commit/ea480df)), closes [#16203](https://github.com/vitejs/vite/issues/16203) +* chore(deps): update all non-major dependencies (#16186) ([842643d](https://github.com/vitejs/vite/commit/842643d)), closes [#16186](https://github.com/vitejs/vite/issues/16186) +* chore(deps): update dependency vue-tsc to v2 (#16187) ([72104f6](https://github.com/vitejs/vite/commit/72104f6)), closes [#16187](https://github.com/vitejs/vite/issues/16187) + + + +## 5.2.2 (2024-03-11) + +* chore(deps): update all non-major dependencies (#16028) ([7cfe80d](https://github.com/vitejs/vite/commit/7cfe80d)), closes [#16028](https://github.com/vitejs/vite/issues/16028) +* chore(deps): update all non-major dependencies (#16131) ([a862ecb](https://github.com/vitejs/vite/commit/a862ecb)), closes [#16131](https://github.com/vitejs/vite/issues/16131) +* fix(create-vite): ts error in the svelte-ts template (#16031) ([ff4c834](https://github.com/vitejs/vite/commit/ff4c834)), closes [#16031](https://github.com/vitejs/vite/issues/16031) + + + +## 5.2.1 (2024-02-21) + +* fix(create-vite): remove tsc command from qwik template (#15982) ([5e05f10](https://github.com/vitejs/vite/commit/5e05f10)), closes [#15982](https://github.com/vitejs/vite/issues/15982) +* fix(deps): update all non-major dependencies (#15959) ([571a3fd](https://github.com/vitejs/vite/commit/571a3fd)), closes [#15959](https://github.com/vitejs/vite/issues/15959) +* fix(qwik template): change preview script (#15975) ([725589a](https://github.com/vitejs/vite/commit/725589a)), closes [#15975](https://github.com/vitejs/vite/issues/15975) +* feat(create-vite): add custom remix option for React (#15995) ([f3b195c](https://github.com/vitejs/vite/commit/f3b195c)), closes [#15995](https://github.com/vitejs/vite/issues/15995) +* chore(deps): update all non-major dependencies (#15874) ([d16ce5d](https://github.com/vitejs/vite/commit/d16ce5d)), closes [#15874](https://github.com/vitejs/vite/issues/15874) +* chore(deps): update typescript-eslint monorepo to v7 (major) (#15960) ([7b9e927](https://github.com/vitejs/vite/commit/7b9e927)), closes [#15960](https://github.com/vitejs/vite/issues/15960) + + + +## 5.2.0 (2024-02-08) + +* fix(create-vite): turn off `react/jsx-no-target-blank` ESLint rule in React JS template (#15672) ([a6f39e8](https://github.com/vitejs/vite/commit/a6f39e8)), closes [#15672](https://github.com/vitejs/vite/issues/15672) +* fix(deps): update all non-major dependencies (#15375) ([ab56227](https://github.com/vitejs/vite/commit/ab56227)), closes [#15375](https://github.com/vitejs/vite/issues/15375) +* fix(deps): update all non-major dependencies (#15603) ([109fb80](https://github.com/vitejs/vite/commit/109fb80)), closes [#15603](https://github.com/vitejs/vite/issues/15603) +* fix(deps): update all non-major dependencies (#15675) ([4d9363a](https://github.com/vitejs/vite/commit/4d9363a)), closes [#15675](https://github.com/vitejs/vite/issues/15675) +* fix(deps): update all non-major dependencies (#15803) ([e0a6ef2](https://github.com/vitejs/vite/commit/e0a6ef2)), closes [#15803](https://github.com/vitejs/vite/issues/15803) +* feat(create-vite): allow overwrite in command line (#15808) ([1882c73](https://github.com/vitejs/vite/commit/1882c73)), closes [#15808](https://github.com/vitejs/vite/issues/15808) +* feat(create-vite): set "strict: true" in tsconfig.node.json (#15820) ([5e5ca7d](https://github.com/vitejs/vite/commit/5e5ca7d)), closes [#15820](https://github.com/vitejs/vite/issues/15820) +* docs: changed bunx create-vite to bun create vite (#15646) ([f3c11bb](https://github.com/vitejs/vite/commit/f3c11bb)), closes [#15646](https://github.com/vitejs/vite/issues/15646) +* chore(deps): update dependency @vitejs/plugin-vue to v5 (#15474) ([17857e7](https://github.com/vitejs/vite/commit/17857e7)), closes [#15474](https://github.com/vitejs/vite/issues/15474) + + + +## 5.1.0 (2023-12-12) + +* fix(deps): update all non-major dependencies (#15233) ([ad3adda](https://github.com/vitejs/vite/commit/ad3adda)), closes [#15233](https://github.com/vitejs/vite/issues/15233) +* fix(deps): update all non-major dependencies (#15304) ([bb07f60](https://github.com/vitejs/vite/commit/bb07f60)), closes [#15304](https://github.com/vitejs/vite/issues/15304) +* feat(cli): allow initializing non-empty directory (#15272) ([00669e1](https://github.com/vitejs/vite/commit/00669e1)), closes [#15272](https://github.com/vitejs/vite/issues/15272) +* chore(deps): update all non-major dependencies (#15145) ([7ff2c0a](https://github.com/vitejs/vite/commit/7ff2c0a)), closes [#15145](https://github.com/vitejs/vite/issues/15145) + + + +## 5.0.0 (2023-11-16) + +* feat(create-vite): update templates for vite 5 (#15007) ([e208697](https://github.com/vitejs/vite/commit/e208697)), closes [#15007](https://github.com/vitejs/vite/issues/15007) +* fix(create-vite): remove repeated styles in vue-template (#14766) ([0fed210](https://github.com/vitejs/vite/commit/0fed210)), closes [#14766](https://github.com/vitejs/vite/issues/14766) +* fix(deps): update all non-major dependencies (#14729) ([d5d96e7](https://github.com/vitejs/vite/commit/d5d96e7)), closes [#14729](https://github.com/vitejs/vite/issues/14729) +* fix(deps): update all non-major dependencies (#14883) ([e5094e5](https://github.com/vitejs/vite/commit/e5094e5)), closes [#14883](https://github.com/vitejs/vite/issues/14883) +* fix(deps): update all non-major dependencies (#14961) ([0bb3995](https://github.com/vitejs/vite/commit/0bb3995)), closes [#14961](https://github.com/vitejs/vite/issues/14961) +* chore(deps): update dependency eslint-plugin-react-refresh to ^0.4.4 (#14795) ([7881457](https://github.com/vitejs/vite/commit/7881457)), closes [#14795](https://github.com/vitejs/vite/issues/14795) + + + ## 5.0.0-beta.1 (2023-10-19) * chore(create-vite): update dependencies (#14698) ([bd82c30](https://github.com/vitejs/vite/commit/bd82c30)), closes [#14698](https://github.com/vitejs/vite/issues/14698) diff --git a/packages/create-vite/README.md b/packages/create-vite/README.md index 6d016ef5f4ed8c..a6f879fcb9e48d 100644 --- a/packages/create-vite/README.md +++ b/packages/create-vite/README.md @@ -26,7 +26,7 @@ $ pnpm create vite With Bun: ```bash -$ bunx create-vite +$ bun create vite ``` Then follow the prompts! @@ -44,7 +44,7 @@ yarn create vite my-vue-app --template vue pnpm create vite my-vue-app --template vue # Bun -bunx create-vite my-vue-app --template vue +bun create vite my-vue-app --template vue ``` Currently supported template presets include: diff --git a/packages/create-vite/__tests__/cli.spec.ts b/packages/create-vite/__tests__/cli.spec.ts index 0db7338415e75a..8f03bf7d2df0c0 100644 --- a/packages/create-vite/__tests__/cli.spec.ts +++ b/packages/create-vite/__tests__/cli.spec.ts @@ -1,5 +1,5 @@ import { join } from 'node:path' -import type { ExecaSyncReturnValue, SyncOptions } from 'execa' +import type { SyncOptions, SyncResult } from 'execa' import { execaCommandSync } from 'execa' import fs from 'fs-extra' import { afterEach, beforeAll, expect, test } from 'vitest' @@ -9,10 +9,10 @@ const CLI_PATH = join(__dirname, '..') const projectName = 'test-app' const genPath = join(__dirname, projectName) -const run = ( +const run = ( args: string[], - options: SyncOptions = {}, -): ExecaSyncReturnValue => { + options?: SO, +): SyncResult => { return execaCommandSync(`node ${CLI_PATH} ${args.join(' ')}`, options) } @@ -97,3 +97,21 @@ test('works with the -t alias', () => { expect(stdout).toContain(`Scaffolding project in ${genPath}`) expect(templateFiles).toEqual(generatedFiles) }) + +test('accepts command line override for --overwrite', () => { + createNonEmptyDir() + const { stdout } = run(['.', '--overwrite', 'ignore'], { cwd: genPath }) + expect(stdout).not.toContain(`Current directory is not empty.`) +}) + +test('return help usage how to use create-vite', () => { + const { stdout } = run(['--help'], { cwd: __dirname }) + const message = 'Usage: create-vite [OPTION]... [DIRECTORY]' + expect(stdout).toContain(message) +}) + +test('return help usage how to use create-vite with -h alias', () => { + const { stdout } = run(['--h'], { cwd: __dirname }) + const message = 'Usage: create-vite [OPTION]... [DIRECTORY]' + expect(stdout).toContain(message) +}) diff --git a/packages/create-vite/package.json b/packages/create-vite/package.json index bb671f55dc79d5..a3ac165c85201f 100644 --- a/packages/create-vite/package.json +++ b/packages/create-vite/package.json @@ -1,6 +1,6 @@ { "name": "create-vite", - "version": "5.0.0-beta.1", + "version": "5.3.0", "type": "module", "license": "MIT", "author": "Evan You", @@ -33,8 +33,8 @@ "homepage": "https://github.com/vitejs/vite/tree/main/packages/create-vite#readme", "funding": "https://github.com/vitejs/vite?sponsor=1", "devDependencies": { - "@types/minimist": "^1.2.4", - "@types/prompts": "^2.4.7", + "@types/minimist": "^1.2.5", + "@types/prompts": "^2.4.9", "cross-spawn": "^7.0.3", "kolorist": "^1.8.0", "minimist": "^1.2.8", diff --git a/packages/create-vite/src/index.ts b/packages/create-vite/src/index.ts index 20212c337580dd..463c78cf669817 100755 --- a/packages/create-vite/src/index.ts +++ b/packages/create-vite/src/index.ts @@ -20,11 +20,36 @@ import { // Avoids autoconversion to number of the project name by defining that the args // non associated with an option ( _ ) needs to be parsed as a string. See #4606 const argv = minimist<{ - t?: string template?: string -}>(process.argv.slice(2), { string: ['_'] }) + help?: boolean +}>(process.argv.slice(2), { + default: { help: false }, + alias: { h: 'help', t: 'template' }, + string: ['_'], +}) const cwd = process.cwd() +// prettier-ignore +const helpMessage = `\ +Usage: create-vite [OPTION]... [DIRECTORY] + +Create a new Vite project in JavaScript or TypeScript. +With no arguments, start the CLI in interactive mode. + +Options: + -t, --template NAME use a specific template + +Available templates: +${yellow ('vanilla-ts vanilla' )} +${green ('vue-ts vue' )} +${cyan ('react-ts react' )} +${cyan ('react-swc-ts react-swc')} +${magenta ('preact-ts preact' )} +${lightRed ('lit-ts lit' )} +${red ('svelte-ts svelte' )} +${blue ('solid-ts solid' )} +${lightBlue('qwik-ts qwik' )}` + type ColorFunc = (str: string | number) => string type Framework = { name: string @@ -111,6 +136,12 @@ const FRAMEWORKS: Framework[] = [ display: 'JavaScript + SWC', color: yellow, }, + { + name: 'custom-remix', + display: 'Remix ↗', + color: cyan, + customCommand: 'npm create remix@latest TARGET_DIR', + }, ], }, { @@ -245,6 +276,12 @@ async function init() { const argTargetDir = formatTargetDir(argv._[0]) const argTemplate = argv.template || argv.t + const help = argv.help + if (help) { + console.log(helpMessage) + return + } + let targetDir = argTargetDir || defaultTargetDir const getProjectName = () => targetDir === '.' ? path.basename(path.resolve()) : targetDir @@ -253,6 +290,10 @@ async function init() { 'projectName' | 'overwrite' | 'packageName' | 'framework' | 'variant' > + prompts.override({ + overwrite: argv.overwrite, + }) + try { result = await prompts( [ @@ -267,17 +308,32 @@ async function init() { }, { type: () => - !fs.existsSync(targetDir) || isEmpty(targetDir) ? null : 'confirm', + !fs.existsSync(targetDir) || isEmpty(targetDir) ? null : 'select', name: 'overwrite', message: () => (targetDir === '.' ? 'Current directory' : `Target directory "${targetDir}"`) + - ` is not empty. Remove existing files and continue?`, + ` is not empty. Please choose how to proceed:`, + initial: 0, + choices: [ + { + title: 'Remove existing files and continue', + value: 'yes', + }, + { + title: 'Cancel operation', + value: 'no', + }, + { + title: 'Ignore files and continue', + value: 'ignore', + }, + ], }, { - type: (_, { overwrite }: { overwrite?: boolean }) => { - if (overwrite === false) { + type: (_, { overwrite }: { overwrite?: string }) => { + if (overwrite === 'no') { throw new Error(red('✖') + ' Operation cancelled') } return null @@ -342,7 +398,7 @@ async function init() { const root = path.join(cwd, targetDir) - if (overwrite) { + if (overwrite === 'yes') { emptyDir(root) } else if (!fs.existsSync(root)) { fs.mkdirSync(root, { recursive: true }) @@ -393,7 +449,9 @@ async function init() { const [command, ...args] = fullCustomCommand.split(' ') // we replace TARGET_DIR here because targetDir may include a space - const replacedArgs = args.map((arg) => arg.replace('TARGET_DIR', targetDir)) + const replacedArgs = args.map((arg) => + arg.replace('TARGET_DIR', () => targetDir), + ) const { status } = spawn.sync(command, replacedArgs, { stdio: 'inherit', }) @@ -524,7 +582,7 @@ function setupReactSwc(root: string, isTs: boolean) { editFile(path.resolve(root, 'package.json'), (content) => { return content.replace( /"@vitejs\/plugin-react": ".+?"/, - `"@vitejs/plugin-react-swc": "^3.3.2"`, + `"@vitejs/plugin-react-swc": "^3.5.0"`, ) }) editFile( diff --git a/packages/create-vite/template-lit-ts/package.json b/packages/create-vite/template-lit-ts/package.json index 64bd3e38ccdff4..be7c7c9cc257ec 100644 --- a/packages/create-vite/template-lit-ts/package.json +++ b/packages/create-vite/template-lit-ts/package.json @@ -9,10 +9,10 @@ "preview": "vite preview" }, "dependencies": { - "lit": "^3.0.1" + "lit": "^3.1.4" }, "devDependencies": { "typescript": "^5.2.2", - "vite": "^5.0.0-beta.13" + "vite": "^5.3.2" } } diff --git a/packages/create-vite/template-lit-ts/tsconfig.json b/packages/create-vite/template-lit-ts/tsconfig.json index 69e31ac92882e3..d689ddbd308a54 100644 --- a/packages/create-vite/template-lit-ts/tsconfig.json +++ b/packages/create-vite/template-lit-ts/tsconfig.json @@ -12,6 +12,7 @@ "allowImportingTsExtensions": true, "resolveJsonModule": true, "isolatedModules": true, + "moduleDetection": "force", "noEmit": true, /* Linting */ diff --git a/packages/create-vite/template-lit/package.json b/packages/create-vite/template-lit/package.json index 81979702df847f..1a37fd91de72d2 100644 --- a/packages/create-vite/template-lit/package.json +++ b/packages/create-vite/template-lit/package.json @@ -9,9 +9,9 @@ "preview": "vite preview" }, "dependencies": { - "lit": "^3.0.1" + "lit": "^3.1.4" }, "devDependencies": { - "vite": "^5.0.0-beta.13" + "vite": "^5.3.2" } } diff --git a/packages/create-vite/template-preact-ts/package.json b/packages/create-vite/template-preact-ts/package.json index 95c5ff727f9e2c..c098b29eda13b8 100644 --- a/packages/create-vite/template-preact-ts/package.json +++ b/packages/create-vite/template-preact-ts/package.json @@ -5,15 +5,15 @@ "type": "module", "scripts": { "dev": "vite", - "build": "tsc && vite build", + "build": "tsc -b && vite build", "preview": "vite preview" }, "dependencies": { - "preact": "^10.18.1" + "preact": "^10.22.1" }, "devDependencies": { - "@preact/preset-vite": "^2.6.0", + "@preact/preset-vite": "^2.8.3", "typescript": "^5.2.2", - "vite": "^5.0.0-beta.13" + "vite": "^5.3.2" } } diff --git a/packages/create-vite/template-preact-ts/tsconfig.app.json b/packages/create-vite/template-preact-ts/tsconfig.app.json new file mode 100644 index 00000000000000..43648503db4c9e --- /dev/null +++ b/packages/create-vite/template-preact-ts/tsconfig.app.json @@ -0,0 +1,32 @@ +{ + "compilerOptions": { + "composite": true, + "tsBuildInfoFile": "./node_modules/.tmp/tsconfig.app.tsbuildinfo", + "target": "ES2020", + "useDefineForClassFields": true, + "module": "ESNext", + "lib": ["ES2020", "DOM", "DOM.Iterable"], + "skipLibCheck": true, + "paths": { + "react": ["./node_modules/preact/compat/"], + "react-dom": ["./node_modules/preact/compat/"] + }, + + /* Bundler mode */ + "moduleResolution": "bundler", + "allowImportingTsExtensions": true, + "resolveJsonModule": true, + "isolatedModules": true, + "moduleDetection": "force", + "noEmit": true, + "jsx": "react-jsx", + "jsxImportSource": "preact", + + /* Linting */ + "strict": true, + "noUnusedLocals": true, + "noUnusedParameters": true, + "noFallthroughCasesInSwitch": true + }, + "include": ["src"] +} diff --git a/packages/create-vite/template-preact-ts/tsconfig.json b/packages/create-vite/template-preact-ts/tsconfig.json index d13245791d3674..ea9d0cd8255683 100644 --- a/packages/create-vite/template-preact-ts/tsconfig.json +++ b/packages/create-vite/template-preact-ts/tsconfig.json @@ -1,30 +1,11 @@ { - "compilerOptions": { - "target": "ES2020", - "useDefineForClassFields": true, - "module": "ESNext", - "lib": ["ES2020", "DOM", "DOM.Iterable"], - "skipLibCheck": true, - "paths": { - "react": ["./node_modules/preact/compat/"], - "react-dom": ["./node_modules/preact/compat/"] + "files": [], + "references": [ + { + "path": "./tsconfig.app.json" }, - - /* Bundler mode */ - "moduleResolution": "bundler", - "allowImportingTsExtensions": true, - "resolveJsonModule": true, - "isolatedModules": true, - "noEmit": true, - "jsx": "react-jsx", - "jsxImportSource": "preact", - - /* Linting */ - "strict": true, - "noUnusedLocals": true, - "noUnusedParameters": true, - "noFallthroughCasesInSwitch": true - }, - "include": ["src"], - "references": [{ "path": "./tsconfig.node.json" }] + { + "path": "./tsconfig.node.json" + } + ] } diff --git a/packages/create-vite/template-preact-ts/tsconfig.node.json b/packages/create-vite/template-preact-ts/tsconfig.node.json index 42872c59f5b01c..3afdd6e38438be 100644 --- a/packages/create-vite/template-preact-ts/tsconfig.node.json +++ b/packages/create-vite/template-preact-ts/tsconfig.node.json @@ -1,10 +1,13 @@ { "compilerOptions": { "composite": true, + "tsBuildInfoFile": "./node_modules/.tmp/tsconfig.node.tsbuildinfo", "skipLibCheck": true, "module": "ESNext", "moduleResolution": "bundler", - "allowSyntheticDefaultImports": true + "allowSyntheticDefaultImports": true, + "strict": true, + "noEmit": true }, "include": ["vite.config.ts"] } diff --git a/packages/create-vite/template-preact/package.json b/packages/create-vite/template-preact/package.json index fdf20eaecf1bd1..a0a553338694ac 100644 --- a/packages/create-vite/template-preact/package.json +++ b/packages/create-vite/template-preact/package.json @@ -9,10 +9,10 @@ "preview": "vite preview" }, "dependencies": { - "preact": "^10.18.1" + "preact": "^10.22.1" }, "devDependencies": { - "@preact/preset-vite": "^2.6.0", - "vite": "^5.0.0-beta.13" + "@preact/preset-vite": "^2.8.3", + "vite": "^5.3.2" } } diff --git a/packages/create-vite/template-qwik-ts/package.json b/packages/create-vite/template-qwik-ts/package.json index ef7f1a6a037c29..3da012571197c8 100644 --- a/packages/create-vite/template-qwik-ts/package.json +++ b/packages/create-vite/template-qwik-ts/package.json @@ -5,14 +5,15 @@ "type": "module", "scripts": { "dev": "vite", - "build": "tsc && vite build", - "preview": "vite preview" + "build": "tsc -b && vite build", + "preview": "serve dist" }, "devDependencies": { + "serve": "^14.2.3", "typescript": "^5.2.2", - "vite": "^5.0.0-beta.13" + "vite": "^5.3.2" }, "dependencies": { - "@builder.io/qwik": "^1.2.15" + "@builder.io/qwik": "^1.6.0" } } diff --git a/packages/create-vite/template-qwik-ts/tsconfig.app.json b/packages/create-vite/template-qwik-ts/tsconfig.app.json new file mode 100644 index 00000000000000..2bf219f042afb7 --- /dev/null +++ b/packages/create-vite/template-qwik-ts/tsconfig.app.json @@ -0,0 +1,28 @@ +{ + "compilerOptions": { + "composite": true, + "tsBuildInfoFile": "./node_modules/.tmp/tsconfig.app.tsbuildinfo", + "target": "ES2020", + "useDefineForClassFields": true, + "module": "ESNext", + "lib": ["ES2020", "DOM", "DOM.Iterable"], + "skipLibCheck": true, + + /* Bundler mode */ + "moduleResolution": "bundler", + "allowImportingTsExtensions": true, + "resolveJsonModule": true, + "isolatedModules": true, + "moduleDetection": "force", + "noEmit": true, + "jsx": "react-jsx", + "jsxImportSource": "@builder.io/qwik", + + /* Linting */ + "strict": true, + "noUnusedLocals": true, + "noUnusedParameters": true, + "noFallthroughCasesInSwitch": true + }, + "include": ["src"] +} diff --git a/packages/create-vite/template-qwik-ts/tsconfig.json b/packages/create-vite/template-qwik-ts/tsconfig.json index 9a262f041fc748..ea9d0cd8255683 100644 --- a/packages/create-vite/template-qwik-ts/tsconfig.json +++ b/packages/create-vite/template-qwik-ts/tsconfig.json @@ -1,26 +1,11 @@ { - "compilerOptions": { - "target": "ES2020", - "useDefineForClassFields": true, - "module": "ESNext", - "lib": ["ES2020", "DOM", "DOM.Iterable"], - "skipLibCheck": true, - - /* Bundler mode */ - "moduleResolution": "bundler", - "allowImportingTsExtensions": true, - "resolveJsonModule": true, - "isolatedModules": true, - "noEmit": true, - "jsx": "react-jsx", - "jsxImportSource": "@builder.io/qwik", - - /* Linting */ - "strict": true, - "noUnusedLocals": true, - "noUnusedParameters": true, - "noFallthroughCasesInSwitch": true - }, - "include": ["src"], - "references": [{ "path": "./tsconfig.node.json" }] + "files": [], + "references": [ + { + "path": "./tsconfig.app.json" + }, + { + "path": "./tsconfig.node.json" + } + ] } diff --git a/packages/create-vite/template-qwik-ts/tsconfig.node.json b/packages/create-vite/template-qwik-ts/tsconfig.node.json index 42872c59f5b01c..3afdd6e38438be 100644 --- a/packages/create-vite/template-qwik-ts/tsconfig.node.json +++ b/packages/create-vite/template-qwik-ts/tsconfig.node.json @@ -1,10 +1,13 @@ { "compilerOptions": { "composite": true, + "tsBuildInfoFile": "./node_modules/.tmp/tsconfig.node.tsbuildinfo", "skipLibCheck": true, "module": "ESNext", "moduleResolution": "bundler", - "allowSyntheticDefaultImports": true + "allowSyntheticDefaultImports": true, + "strict": true, + "noEmit": true }, "include": ["vite.config.ts"] } diff --git a/packages/create-vite/template-qwik/package.json b/packages/create-vite/template-qwik/package.json index ef7f1a6a037c29..b8ef00283b68ec 100644 --- a/packages/create-vite/template-qwik/package.json +++ b/packages/create-vite/template-qwik/package.json @@ -5,14 +5,14 @@ "type": "module", "scripts": { "dev": "vite", - "build": "tsc && vite build", - "preview": "vite preview" + "build": "vite build", + "preview": "serve dist" }, "devDependencies": { - "typescript": "^5.2.2", - "vite": "^5.0.0-beta.13" + "serve": "^14.2.3", + "vite": "^5.3.2" }, "dependencies": { - "@builder.io/qwik": "^1.2.15" + "@builder.io/qwik": "^1.6.0" } } diff --git a/packages/create-vite/template-react-ts/package.json b/packages/create-vite/template-react-ts/package.json index 9bc5e3858211d1..f5cf282df8a623 100644 --- a/packages/create-vite/template-react-ts/package.json +++ b/packages/create-vite/template-react-ts/package.json @@ -5,24 +5,24 @@ "type": "module", "scripts": { "dev": "vite", - "build": "tsc && vite build", + "build": "tsc -b && vite build", "lint": "eslint . --ext ts,tsx --report-unused-disable-directives --max-warnings 0", "preview": "vite preview" }, "dependencies": { - "react": "^18.2.0", - "react-dom": "^18.2.0" + "react": "^18.3.1", + "react-dom": "^18.3.1" }, "devDependencies": { - "@types/react": "^18.2.33", - "@types/react-dom": "^18.2.14", - "@typescript-eslint/eslint-plugin": "^6.9.0", - "@typescript-eslint/parser": "^6.9.0", - "@vitejs/plugin-react": "^4.1.0", - "eslint": "^8.52.0", - "eslint-plugin-react-hooks": "^4.6.0", - "eslint-plugin-react-refresh": "^0.4.4", + "@types/react": "^18.3.3", + "@types/react-dom": "^18.3.0", + "@typescript-eslint/eslint-plugin": "^7.15.0", + "@typescript-eslint/parser": "^7.15.0", + "@vitejs/plugin-react": "^4.3.1", + "eslint": "^8.57.0", + "eslint-plugin-react-hooks": "^4.6.2", + "eslint-plugin-react-refresh": "^0.4.7", "typescript": "^5.2.2", - "vite": "^5.0.0-beta.13" + "vite": "^5.3.2" } } diff --git a/packages/create-vite/template-react-ts/tsconfig.app.json b/packages/create-vite/template-react-ts/tsconfig.app.json new file mode 100644 index 00000000000000..d739292ae01436 --- /dev/null +++ b/packages/create-vite/template-react-ts/tsconfig.app.json @@ -0,0 +1,27 @@ +{ + "compilerOptions": { + "composite": true, + "tsBuildInfoFile": "./node_modules/.tmp/tsconfig.app.tsbuildinfo", + "target": "ES2020", + "useDefineForClassFields": true, + "lib": ["ES2020", "DOM", "DOM.Iterable"], + "module": "ESNext", + "skipLibCheck": true, + + /* Bundler mode */ + "moduleResolution": "bundler", + "allowImportingTsExtensions": true, + "resolveJsonModule": true, + "isolatedModules": true, + "moduleDetection": "force", + "noEmit": true, + "jsx": "react-jsx", + + /* Linting */ + "strict": true, + "noUnusedLocals": true, + "noUnusedParameters": true, + "noFallthroughCasesInSwitch": true + }, + "include": ["src"] +} diff --git a/packages/create-vite/template-react-ts/tsconfig.json b/packages/create-vite/template-react-ts/tsconfig.json index a7fc6fbf23de2a..ea9d0cd8255683 100644 --- a/packages/create-vite/template-react-ts/tsconfig.json +++ b/packages/create-vite/template-react-ts/tsconfig.json @@ -1,25 +1,11 @@ { - "compilerOptions": { - "target": "ES2020", - "useDefineForClassFields": true, - "lib": ["ES2020", "DOM", "DOM.Iterable"], - "module": "ESNext", - "skipLibCheck": true, - - /* Bundler mode */ - "moduleResolution": "bundler", - "allowImportingTsExtensions": true, - "resolveJsonModule": true, - "isolatedModules": true, - "noEmit": true, - "jsx": "react-jsx", - - /* Linting */ - "strict": true, - "noUnusedLocals": true, - "noUnusedParameters": true, - "noFallthroughCasesInSwitch": true - }, - "include": ["src"], - "references": [{ "path": "./tsconfig.node.json" }] + "files": [], + "references": [ + { + "path": "./tsconfig.app.json" + }, + { + "path": "./tsconfig.node.json" + } + ] } diff --git a/packages/create-vite/template-react-ts/tsconfig.node.json b/packages/create-vite/template-react-ts/tsconfig.node.json index 42872c59f5b01c..3afdd6e38438be 100644 --- a/packages/create-vite/template-react-ts/tsconfig.node.json +++ b/packages/create-vite/template-react-ts/tsconfig.node.json @@ -1,10 +1,13 @@ { "compilerOptions": { "composite": true, + "tsBuildInfoFile": "./node_modules/.tmp/tsconfig.node.tsbuildinfo", "skipLibCheck": true, "module": "ESNext", "moduleResolution": "bundler", - "allowSyntheticDefaultImports": true + "allowSyntheticDefaultImports": true, + "strict": true, + "noEmit": true }, "include": ["vite.config.ts"] } diff --git a/packages/create-vite/template-react/.eslintrc.cjs b/packages/create-vite/template-react/.eslintrc.cjs index 4dcb43901a687f..3e212e1d4307a3 100644 --- a/packages/create-vite/template-react/.eslintrc.cjs +++ b/packages/create-vite/template-react/.eslintrc.cjs @@ -12,6 +12,7 @@ module.exports = { settings: { react: { version: '18.2' } }, plugins: ['react-refresh'], rules: { + 'react/jsx-no-target-blank': 'off', 'react-refresh/only-export-components': [ 'warn', { allowConstantExport: true }, diff --git a/packages/create-vite/template-react/package.json b/packages/create-vite/template-react/package.json index f3fd48fea70e9d..f88081ec9e6bd8 100644 --- a/packages/create-vite/template-react/package.json +++ b/packages/create-vite/template-react/package.json @@ -10,17 +10,17 @@ "preview": "vite preview" }, "dependencies": { - "react": "^18.2.0", - "react-dom": "^18.2.0" + "react": "^18.3.1", + "react-dom": "^18.3.1" }, "devDependencies": { - "@types/react": "^18.2.33", - "@types/react-dom": "^18.2.14", - "@vitejs/plugin-react": "^4.1.0", - "eslint": "^8.52.0", - "eslint-plugin-react": "^7.33.2", - "eslint-plugin-react-hooks": "^4.6.0", - "eslint-plugin-react-refresh": "^0.4.4", - "vite": "^5.0.0-beta.13" + "@types/react": "^18.3.3", + "@types/react-dom": "^18.3.0", + "@vitejs/plugin-react": "^4.3.1", + "eslint": "^8.57.0", + "eslint-plugin-react": "^7.34.3", + "eslint-plugin-react-hooks": "^4.6.2", + "eslint-plugin-react-refresh": "^0.4.7", + "vite": "^5.3.2" } } diff --git a/packages/create-vite/template-solid-ts/package.json b/packages/create-vite/template-solid-ts/package.json index 17015cfa428849..6361dcf27b8ffd 100644 --- a/packages/create-vite/template-solid-ts/package.json +++ b/packages/create-vite/template-solid-ts/package.json @@ -5,15 +5,15 @@ "type": "module", "scripts": { "dev": "vite", - "build": "tsc && vite build", + "build": "tsc -b && vite build", "preview": "vite preview" }, "dependencies": { - "solid-js": "^1.8.4" + "solid-js": "^1.8.18" }, "devDependencies": { "typescript": "^5.2.2", - "vite": "^5.0.0-beta.13", - "vite-plugin-solid": "^2.7.2" + "vite": "^5.3.2", + "vite-plugin-solid": "^2.10.2" } } diff --git a/packages/create-vite/template-solid-ts/tsconfig.app.json b/packages/create-vite/template-solid-ts/tsconfig.app.json new file mode 100644 index 00000000000000..348fb41806f3ff --- /dev/null +++ b/packages/create-vite/template-solid-ts/tsconfig.app.json @@ -0,0 +1,28 @@ +{ + "compilerOptions": { + "composite": true, + "tsBuildInfoFile": "./node_modules/.tmp/tsconfig.app.tsbuildinfo", + "target": "ES2020", + "useDefineForClassFields": true, + "module": "ESNext", + "lib": ["ES2020", "DOM", "DOM.Iterable"], + "skipLibCheck": true, + + /* Bundler mode */ + "moduleResolution": "bundler", + "allowImportingTsExtensions": true, + "resolveJsonModule": true, + "isolatedModules": true, + "moduleDetection": "force", + "noEmit": true, + "jsx": "preserve", + "jsxImportSource": "solid-js", + + /* Linting */ + "strict": true, + "noUnusedLocals": true, + "noUnusedParameters": true, + "noFallthroughCasesInSwitch": true + }, + "include": ["src"] +} diff --git a/packages/create-vite/template-solid-ts/tsconfig.json b/packages/create-vite/template-solid-ts/tsconfig.json index 3999958409cd1d..ea9d0cd8255683 100644 --- a/packages/create-vite/template-solid-ts/tsconfig.json +++ b/packages/create-vite/template-solid-ts/tsconfig.json @@ -1,26 +1,11 @@ { - "compilerOptions": { - "target": "ES2020", - "useDefineForClassFields": true, - "module": "ESNext", - "lib": ["ES2020", "DOM", "DOM.Iterable"], - "skipLibCheck": true, - - /* Bundler mode */ - "moduleResolution": "bundler", - "allowImportingTsExtensions": true, - "resolveJsonModule": true, - "isolatedModules": true, - "noEmit": true, - "jsx": "preserve", - "jsxImportSource": "solid-js", - - /* Linting */ - "strict": true, - "noUnusedLocals": true, - "noUnusedParameters": true, - "noFallthroughCasesInSwitch": true - }, - "include": ["src"], - "references": [{ "path": "./tsconfig.node.json" }] + "files": [], + "references": [ + { + "path": "./tsconfig.app.json" + }, + { + "path": "./tsconfig.node.json" + } + ] } diff --git a/packages/create-vite/template-solid-ts/tsconfig.node.json b/packages/create-vite/template-solid-ts/tsconfig.node.json index 42872c59f5b01c..3afdd6e38438be 100644 --- a/packages/create-vite/template-solid-ts/tsconfig.node.json +++ b/packages/create-vite/template-solid-ts/tsconfig.node.json @@ -1,10 +1,13 @@ { "compilerOptions": { "composite": true, + "tsBuildInfoFile": "./node_modules/.tmp/tsconfig.node.tsbuildinfo", "skipLibCheck": true, "module": "ESNext", "moduleResolution": "bundler", - "allowSyntheticDefaultImports": true + "allowSyntheticDefaultImports": true, + "strict": true, + "noEmit": true }, "include": ["vite.config.ts"] } diff --git a/packages/create-vite/template-solid/package.json b/packages/create-vite/template-solid/package.json index 28065b344f429a..99a0e233d95a7c 100644 --- a/packages/create-vite/template-solid/package.json +++ b/packages/create-vite/template-solid/package.json @@ -9,10 +9,10 @@ "preview": "vite preview" }, "dependencies": { - "solid-js": "^1.8.4" + "solid-js": "^1.8.18" }, "devDependencies": { - "vite": "^5.0.0-beta.13", - "vite-plugin-solid": "^2.7.2" + "vite": "^5.3.2", + "vite-plugin-solid": "^2.10.2" } } diff --git a/packages/create-vite/template-svelte-ts/package.json b/packages/create-vite/template-svelte-ts/package.json index bd416eb20d2b9f..dfd0420910e170 100644 --- a/packages/create-vite/template-svelte-ts/package.json +++ b/packages/create-vite/template-svelte-ts/package.json @@ -7,15 +7,15 @@ "dev": "vite", "build": "vite build", "preview": "vite preview", - "check": "svelte-check --tsconfig ./tsconfig.json" + "check": "svelte-check --tsconfig ./tsconfig.json && tsc -p tsconfig.node.json" }, "devDependencies": { - "@sveltejs/vite-plugin-svelte": "^3.0.0-next.2", - "@tsconfig/svelte": "^5.0.2", - "svelte": "^4.2.2", - "svelte-check": "^3.5.2", - "tslib": "^2.6.2", + "@sveltejs/vite-plugin-svelte": "^3.1.1", + "@tsconfig/svelte": "^5.0.4", + "svelte": "^4.2.18", + "svelte-check": "^3.8.4", + "tslib": "^2.6.3", "typescript": "^5.2.2", - "vite": "^5.0.0-beta.13" + "vite": "^5.3.2" } } diff --git a/packages/create-vite/template-svelte-ts/src/main.ts b/packages/create-vite/template-svelte-ts/src/main.ts index 8a909a15a0ebff..4d67e2ac2af7ef 100644 --- a/packages/create-vite/template-svelte-ts/src/main.ts +++ b/packages/create-vite/template-svelte-ts/src/main.ts @@ -2,7 +2,7 @@ import './app.css' import App from './App.svelte' const app = new App({ - target: document.getElementById('app'), + target: document.getElementById('app')!, }) export default app diff --git a/packages/create-vite/template-svelte-ts/tsconfig.json b/packages/create-vite/template-svelte-ts/tsconfig.json index 5fb548f2b4f61a..df56300cc65f66 100644 --- a/packages/create-vite/template-svelte-ts/tsconfig.json +++ b/packages/create-vite/template-svelte-ts/tsconfig.json @@ -13,7 +13,8 @@ */ "allowJs": true, "checkJs": true, - "isolatedModules": true + "isolatedModules": true, + "moduleDetection": "force" }, "include": ["src/**/*.ts", "src/**/*.js", "src/**/*.svelte"], "references": [{ "path": "./tsconfig.node.json" }] diff --git a/packages/create-vite/template-svelte-ts/tsconfig.node.json b/packages/create-vite/template-svelte-ts/tsconfig.node.json index 494bfe0835347c..6c2d8703f35ab1 100644 --- a/packages/create-vite/template-svelte-ts/tsconfig.node.json +++ b/packages/create-vite/template-svelte-ts/tsconfig.node.json @@ -1,9 +1,12 @@ { "compilerOptions": { "composite": true, + "tsBuildInfoFile": "./node_modules/.tmp/tsconfig.node.tsbuildinfo", "skipLibCheck": true, "module": "ESNext", - "moduleResolution": "bundler" + "moduleResolution": "bundler", + "strict": true, + "noEmit": true }, "include": ["vite.config.ts"] } diff --git a/packages/create-vite/template-svelte/package.json b/packages/create-vite/template-svelte/package.json index 550db4b3eef105..230e3a5e1bd012 100644 --- a/packages/create-vite/template-svelte/package.json +++ b/packages/create-vite/template-svelte/package.json @@ -9,8 +9,8 @@ "preview": "vite preview" }, "devDependencies": { - "@sveltejs/vite-plugin-svelte": "^3.0.0-next.2", - "svelte": "^4.2.2", - "vite": "^5.0.0-beta.13" + "@sveltejs/vite-plugin-svelte": "^3.1.1", + "svelte": "^4.2.18", + "vite": "^5.3.2" } } diff --git a/packages/create-vite/template-vanilla-ts/package.json b/packages/create-vite/template-vanilla-ts/package.json index e636147bd68a0f..b572532756b1bf 100644 --- a/packages/create-vite/template-vanilla-ts/package.json +++ b/packages/create-vite/template-vanilla-ts/package.json @@ -10,6 +10,6 @@ }, "devDependencies": { "typescript": "^5.2.2", - "vite": "^5.0.0-beta.13" + "vite": "^5.3.2" } } diff --git a/packages/create-vite/template-vanilla-ts/tsconfig.json b/packages/create-vite/template-vanilla-ts/tsconfig.json index 75abdef2659446..7bb0db29851c74 100644 --- a/packages/create-vite/template-vanilla-ts/tsconfig.json +++ b/packages/create-vite/template-vanilla-ts/tsconfig.json @@ -11,6 +11,7 @@ "allowImportingTsExtensions": true, "resolveJsonModule": true, "isolatedModules": true, + "moduleDetection": "force", "noEmit": true, /* Linting */ diff --git a/packages/create-vite/template-vanilla/package.json b/packages/create-vite/template-vanilla/package.json index f7b023f0855165..985973e1444d58 100644 --- a/packages/create-vite/template-vanilla/package.json +++ b/packages/create-vite/template-vanilla/package.json @@ -9,6 +9,6 @@ "preview": "vite preview" }, "devDependencies": { - "vite": "^5.0.0-beta.13" + "vite": "^5.3.2" } } diff --git a/packages/create-vite/template-vue-ts/.vscode/extensions.json b/packages/create-vite/template-vue-ts/.vscode/extensions.json index c0a6e5a48110e4..a7cea0b0678120 100644 --- a/packages/create-vite/template-vue-ts/.vscode/extensions.json +++ b/packages/create-vite/template-vue-ts/.vscode/extensions.json @@ -1,3 +1,3 @@ { - "recommendations": ["Vue.volar", "Vue.vscode-typescript-vue-plugin"] + "recommendations": ["Vue.volar"] } diff --git a/packages/create-vite/template-vue-ts/README.md b/packages/create-vite/template-vue-ts/README.md index ef72fd52424558..33895ab2002862 100644 --- a/packages/create-vite/template-vue-ts/README.md +++ b/packages/create-vite/template-vue-ts/README.md @@ -2,17 +2,4 @@ This template should help get you started developing with Vue 3 and TypeScript in Vite. The template uses Vue 3 ` asset for (const { start, end, url } of scriptUrls) { - if (!isExcludedUrl(url)) { - s.update(start, end, await urlToBuiltUrl(url, id, config, this)) - } else if (checkPublicFile(url, config)) { - s.update(start, end, toOutputPublicFilePath(url)) + if (checkPublicFile(url, config)) { + s.update( + start, + end, + partialEncodeURIPath(toOutputPublicFilePath(url)), + ) + } else if (!isExcludedUrl(url)) { + s.update( + start, + end, + partialEncodeURIPath(await urlToBuiltUrl(url, id, config, this)), + ) } } @@ -649,6 +719,12 @@ export function buildHtmlPlugin(config: ResolvedConfig): Plugin { attrs: { ...(isAsync ? { async: true } : {}), type: 'module', + // crossorigin must be set not only for serving assets in a different origin + // but also to make it possible to preload the script using ``. + // ` + +

direct

+

inline

+

from-js

+

dynamic

+

js: error

+

dynamic-js: error

+

inline-js: error

+

double-nonce-js: error

+ + diff --git a/playground/csp/index.js b/playground/csp/index.js new file mode 100644 index 00000000000000..465359baca8297 --- /dev/null +++ b/playground/csp/index.js @@ -0,0 +1,5 @@ +import './from-js.css' + +document.querySelector('.js').textContent = 'js: ok' + +import('./dynamic.js') diff --git a/playground/csp/linked.css b/playground/csp/linked.css new file mode 100644 index 00000000000000..51636e6cfad81f --- /dev/null +++ b/playground/csp/linked.css @@ -0,0 +1,3 @@ +.linked { + color: blue; +} diff --git a/playground/csp/package.json b/playground/csp/package.json new file mode 100644 index 00000000000000..e8a834d93abd25 --- /dev/null +++ b/playground/csp/package.json @@ -0,0 +1,12 @@ +{ + "name": "@vitejs/test-csp", + "private": true, + "version": "0.0.0", + "type": "module", + "scripts": { + "debug": "node --inspect-brk ../../packages/vite/bin/vite", + "dev": "vite", + "build": "vite build", + "preview": "vite preview" + } +} diff --git a/playground/csp/vite.config.js b/playground/csp/vite.config.js new file mode 100644 index 00000000000000..84d6d92ba0d0bb --- /dev/null +++ b/playground/csp/vite.config.js @@ -0,0 +1,67 @@ +import fs from 'node:fs/promises' +import url from 'node:url' +import path from 'node:path' +import crypto from 'node:crypto' +import { defineConfig } from 'vite' + +const __dirname = path.dirname(url.fileURLToPath(import.meta.url)) + +const noncePlaceholder = '#$NONCE$#' +const createNonce = () => crypto.randomBytes(16).toString('base64') + +/** + * @param {import('node:http').ServerResponse} res + * @param {string} nonce + */ +const setNonceHeader = (res, nonce) => { + res.setHeader( + 'Content-Security-Policy', + `default-src 'nonce-${nonce}'; connect-src 'self'`, + ) +} + +/** + * @param {string} file + * @param {(input: string, originalUrl: string) => Promise} transform + * @returns {import('vite').Connect.NextHandleFunction} + */ +const createMiddleware = (file, transform) => async (req, res) => { + const nonce = createNonce() + setNonceHeader(res, nonce) + const content = await fs.readFile(path.join(__dirname, file), 'utf-8') + const transformedContent = await transform(content, req.originalUrl) + res.setHeader('Content-Type', 'text/html') + res.end(transformedContent.replaceAll(noncePlaceholder, nonce)) +} + +export default defineConfig({ + plugins: [ + { + name: 'nonce-inject', + config() { + return { + appType: 'custom', + html: { + cspNonce: noncePlaceholder, + }, + } + }, + configureServer({ transformIndexHtml, middlewares }) { + return () => { + middlewares.use( + createMiddleware('./index.html', (input, originalUrl) => + transformIndexHtml(originalUrl, input), + ), + ) + } + }, + configurePreviewServer({ middlewares }) { + return () => { + middlewares.use( + createMiddleware('./dist/index.html', async (input) => input), + ) + } + }, + }, + ], +}) diff --git a/playground/css-codesplit-cjs/__tests__/css-codesplit.spec.ts b/playground/css-codesplit-cjs/__tests__/css-codesplit-cjs.spec.ts similarity index 100% rename from playground/css-codesplit-cjs/__tests__/css-codesplit.spec.ts rename to playground/css-codesplit-cjs/__tests__/css-codesplit-cjs.spec.ts diff --git a/playground/css-codesplit-cjs/main.js b/playground/css-codesplit-cjs/main.js index 4234674de9dee5..766759f9bd79f4 100644 --- a/playground/css-codesplit-cjs/main.js +++ b/playground/css-codesplit-cjs/main.js @@ -1,6 +1,5 @@ import './style.css' import './main.css' -document.getElementById( - 'app', -).innerHTML = `

This should be red

This should be blue

` +document.getElementById('app').innerHTML = + `

This should be red

This should be blue

` diff --git a/playground/css-codesplit/__tests__/css-codesplit.spec.ts b/playground/css-codesplit/__tests__/css-codesplit.spec.ts index 2f7d5ab5fc5fba..cc54d865a6795e 100644 --- a/playground/css-codesplit/__tests__/css-codesplit.spec.ts +++ b/playground/css-codesplit/__tests__/css-codesplit.spec.ts @@ -3,6 +3,7 @@ import { findAssetFile, getColor, isBuild, + listAssets, page, readManifest, untilUpdated, @@ -12,6 +13,7 @@ test('should load all stylesheets', async () => { expect(await getColor('h1')).toBe('red') expect(await getColor('h2')).toBe('blue') expect(await getColor('.dynamic')).toBe('green') + expect(await getColor('.async-js')).toBe('blue') expect(await getColor('.chunk')).toBe('magenta') }) @@ -40,7 +42,12 @@ describe.runIf(isBuild)('build', () => { expect(findAssetFile(/style-.*\.js$/)).toBe('') expect(findAssetFile('main.*.js$')).toMatch(`/* empty css`) expect(findAssetFile('other.*.js$')).toMatch(`/* empty css`) - expect(findAssetFile(/async.*\.js$/)).toBe('') + expect(findAssetFile(/async-[-\w]{8}\.js$/)).toBe('') + + const assets = listAssets() + expect(assets).not.toContainEqual( + expect.stringMatching(/async-js-[-\w]{8}\.js$/), + ) }) test('should remove empty chunk, HTML without JS', async () => { diff --git a/playground/css-codesplit/async-js.css b/playground/css-codesplit/async-js.css new file mode 100644 index 00000000000000..ed61a7f513c277 --- /dev/null +++ b/playground/css-codesplit/async-js.css @@ -0,0 +1,3 @@ +.async-js { + color: blue; +} diff --git a/playground/css-codesplit/async-js.js b/playground/css-codesplit/async-js.js new file mode 100644 index 00000000000000..2ce31a1e741d2d --- /dev/null +++ b/playground/css-codesplit/async-js.js @@ -0,0 +1,2 @@ +// a JS file that becomes an empty file but imports CSS files +import './async-js.css' diff --git a/playground/css-codesplit/index.html b/playground/css-codesplit/index.html index 7d2a4991f20e0a..38885fa7ccb5ed 100644 --- a/playground/css-codesplit/index.html +++ b/playground/css-codesplit/index.html @@ -2,6 +2,7 @@

This should be red

This should be blue

This should be green

+

This should be blue

This should not be yellow

This should be yellow

diff --git a/playground/css-codesplit/main.js b/playground/css-codesplit/main.js index e548142add8786..ec266fa003156d 100644 --- a/playground/css-codesplit/main.js +++ b/playground/css-codesplit/main.js @@ -9,6 +9,7 @@ import chunkCssUrl from './chunk.css?url' globalThis.__test_chunkCssUrl = chunkCssUrl import('./async.css') +import('./async-js') import('./inline.css?inline').then((css) => { document.querySelector('.dynamic-inline').textContent = css.default diff --git a/playground/css-lightningcss-proxy/package.json b/playground/css-lightningcss-proxy/package.json index 571c2596490cfa..ad6b503221a9f6 100644 --- a/playground/css-lightningcss-proxy/package.json +++ b/playground/css-lightningcss-proxy/package.json @@ -9,7 +9,7 @@ "preview": "vite preview" }, "devDependencies": { - "lightningcss": "^1.22.0", - "express": "^4.18.2" + "lightningcss": "^1.25.1", + "express": "^4.19.2" } } diff --git a/playground/css-lightningcss/__tests__/lightningcss.spec.ts b/playground/css-lightningcss/__tests__/css-lightningcss.spec.ts similarity index 100% rename from playground/css-lightningcss/__tests__/lightningcss.spec.ts rename to playground/css-lightningcss/__tests__/css-lightningcss.spec.ts diff --git a/playground/css-lightningcss/package.json b/playground/css-lightningcss/package.json index 99c99b415e2db4..6844ef9ffb0959 100644 --- a/playground/css-lightningcss/package.json +++ b/playground/css-lightningcss/package.json @@ -9,6 +9,6 @@ "preview": "vite preview" }, "devDependencies": { - "lightningcss": "^1.22.0" + "lightningcss": "^1.25.1" } } diff --git a/playground/css-no-codesplit/__tests__/css-no-codesplit.spec.ts b/playground/css-no-codesplit/__tests__/css-no-codesplit.spec.ts new file mode 100644 index 00000000000000..5110ef3a77ff7b --- /dev/null +++ b/playground/css-no-codesplit/__tests__/css-no-codesplit.spec.ts @@ -0,0 +1,17 @@ +import { describe, expect, test } from 'vitest' +import { expectWithRetry, getColor, isBuild, listAssets } from '~utils' + +test('should load all stylesheets', async () => { + expect(await getColor('.shared-linked')).toBe('blue') + await expectWithRetry(() => getColor('.async-js')).toBe('blue') +}) + +describe.runIf(isBuild)('build', () => { + test('should remove empty chunk', async () => { + const assets = listAssets() + expect(assets).not.toContainEqual( + expect.stringMatching(/shared-linked-.*\.js$/), + ) + expect(assets).not.toContainEqual(expect.stringMatching(/async-js-.*\.js$/)) + }) +}) diff --git a/playground/css-no-codesplit/async-js.css b/playground/css-no-codesplit/async-js.css new file mode 100644 index 00000000000000..ed61a7f513c277 --- /dev/null +++ b/playground/css-no-codesplit/async-js.css @@ -0,0 +1,3 @@ +.async-js { + color: blue; +} diff --git a/playground/css-no-codesplit/async-js.js b/playground/css-no-codesplit/async-js.js new file mode 100644 index 00000000000000..2ce31a1e741d2d --- /dev/null +++ b/playground/css-no-codesplit/async-js.js @@ -0,0 +1,2 @@ +// a JS file that becomes an empty file but imports CSS files +import './async-js.css' diff --git a/playground/css-no-codesplit/index.html b/playground/css-no-codesplit/index.html new file mode 100644 index 00000000000000..e7673c84e45933 --- /dev/null +++ b/playground/css-no-codesplit/index.html @@ -0,0 +1,5 @@ + + + +

shared linked: this should be blue

+

async JS importing CSS: this should be blue

diff --git a/playground/css-no-codesplit/index.js b/playground/css-no-codesplit/index.js new file mode 100644 index 00000000000000..44b33fda36a9cd --- /dev/null +++ b/playground/css-no-codesplit/index.js @@ -0,0 +1 @@ +import('./async-js') diff --git a/playground/css-no-codesplit/package.json b/playground/css-no-codesplit/package.json new file mode 100644 index 00000000000000..61d806d3d264fa --- /dev/null +++ b/playground/css-no-codesplit/package.json @@ -0,0 +1,12 @@ +{ + "name": "@vitejs/test-css-no-codesplit", + "private": true, + "version": "0.0.0", + "type": "module", + "scripts": { + "dev": "vite", + "build": "vite build", + "debug": "node --inspect-brk ../../packages/vite/bin/vite", + "preview": "vite preview" + } +} diff --git a/playground/css-no-codesplit/shared-linked.css b/playground/css-no-codesplit/shared-linked.css new file mode 100644 index 00000000000000..51857a50efca1f --- /dev/null +++ b/playground/css-no-codesplit/shared-linked.css @@ -0,0 +1,3 @@ +.shared-linked { + color: blue; +} diff --git a/playground/css-no-codesplit/sub.html b/playground/css-no-codesplit/sub.html new file mode 100644 index 00000000000000..f535a771d06482 --- /dev/null +++ b/playground/css-no-codesplit/sub.html @@ -0,0 +1 @@ + diff --git a/playground/css-no-codesplit/vite.config.js b/playground/css-no-codesplit/vite.config.js new file mode 100644 index 00000000000000..f48d875832b928 --- /dev/null +++ b/playground/css-no-codesplit/vite.config.js @@ -0,0 +1,14 @@ +import { resolve } from 'node:path' +import { defineConfig } from 'vite' + +export default defineConfig({ + build: { + cssCodeSplit: false, + rollupOptions: { + input: { + index: resolve(__dirname, './index.html'), + sub: resolve(__dirname, './sub.html'), + }, + }, + }, +}) diff --git a/playground/css-sourcemap/__tests__/css-sourcemap.spec.ts b/playground/css-sourcemap/__tests__/css-sourcemap.spec.ts index 6706b5dcaae510..6c6472c848823d 100644 --- a/playground/css-sourcemap/__tests__/css-sourcemap.spec.ts +++ b/playground/css-sourcemap/__tests__/css-sourcemap.spec.ts @@ -138,6 +138,7 @@ describe.runIf(isServe)('serve', () => { const map = extractSourcemap(css) expect(formatSourcemapForSnapshot(map)).toMatchInlineSnapshot(` { + "ignoreList": [], "mappings": "AACE;EACE", "sources": [ "/root/imported.sass", @@ -158,6 +159,7 @@ describe.runIf(isServe)('serve', () => { const map = extractSourcemap(css) expect(formatSourcemapForSnapshot(map)).toMatchInlineSnapshot(` { + "ignoreList": [], "mappings": "AACE;EACE", "sources": [ "/root/imported.module.sass", @@ -178,6 +180,7 @@ describe.runIf(isServe)('serve', () => { const map = extractSourcemap(css) expect(formatSourcemapForSnapshot(map)).toMatchInlineSnapshot(` { + "ignoreList": [], "mappings": "AACE;EACE", "sources": [ "/root/imported.less", @@ -200,6 +203,7 @@ describe.runIf(isServe)('serve', () => { const map = extractSourcemap(css) expect(formatSourcemapForSnapshot(map)).toMatchInlineSnapshot(` { + "ignoreList": [], "mappings": "AACE;EACE,cAAM", "sources": [ "/root/imported.styl", diff --git a/playground/css-sourcemap/__tests__/lib-entry/lib-entry.spec.ts b/playground/css-sourcemap/__tests__/lib-entry/css-sourcemap-lib-entry.spec.ts similarity index 100% rename from playground/css-sourcemap/__tests__/lib-entry/lib-entry.spec.ts rename to playground/css-sourcemap/__tests__/lib-entry/css-sourcemap-lib-entry.spec.ts diff --git a/playground/css-sourcemap/package.json b/playground/css-sourcemap/package.json index 5fe1540ce52f68..9762af32c1e214 100644 --- a/playground/css-sourcemap/package.json +++ b/playground/css-sourcemap/package.json @@ -11,9 +11,9 @@ }, "devDependencies": { "less": "^4.2.0", - "magic-string": "^0.30.5", - "sass": "^1.69.5", - "stylus": "^0.60.0", + "magic-string": "^0.30.10", + "sass": "^1.77.6", + "stylus": "^0.63.0", "sugarss": "^4.0.1" } } diff --git a/playground/css/__tests__/css.spec.ts b/playground/css/__tests__/css.spec.ts index b424faace62b81..cb7af939bbd152 100644 --- a/playground/css/__tests__/css.spec.ts +++ b/playground/css/__tests__/css.spec.ts @@ -74,6 +74,9 @@ test('sass', async () => { const atImport = await page.$('.sass-at-import') const atImportAlias = await page.$('.sass-at-import-alias') const urlStartsWithVariable = await page.$('.sass-url-starts-with-variable') + const urlStartsWithFunctionCall = await page.$( + '.sass-url-starts-with-function-call', + ) const partialImport = await page.$('.sass-partial') expect(await getColor(imported)).toBe('orange') @@ -86,6 +89,9 @@ test('sass', async () => { expect(await getBg(urlStartsWithVariable)).toMatch( isBuild ? /ok-[-\w]+\.png/ : `${viteTestUrl}/ok.png`, ) + expect(await getBg(urlStartsWithFunctionCall)).toMatch( + isBuild ? /ok-[-\w]+\.png/ : `${viteTestUrl}/ok.png`, + ) expect(await getColor(partialImport)).toBe('orchid') editFile('sass.scss', (code) => @@ -292,6 +298,10 @@ test('@import dependency that @import another dependency', async () => { expect(await getColor('.css-proxy-dep')).toBe('purple') }) +test('@import scss dependency that has @import with a css extension pointing to another dependency', async () => { + expect(await getColor('.scss-proxy-dep')).toBe('purple') +}) + test('@import dependency w/out package scss', async () => { expect(await getColor('.sass-dep')).toBe('lavender') }) @@ -428,12 +438,26 @@ test('minify css', async () => { expect(cssFile).not.toMatch('#ffff00b3') }) +test('?url', async () => { + expect(await getColor('.url-imported-css')).toBe('yellow') +}) + test('?raw', async () => { const rawImportCss = await page.$('.raw-imported-css') expect(await rawImportCss.textContent()).toBe( readFileSync(require.resolve('../raw-imported.css'), 'utf-8'), ) + + if (!isBuild) { + editFile('raw-imported.css', (code) => + code.replace('color: yellow', 'color: blue'), + ) + await untilUpdated( + () => page.textContent('.raw-imported-css'), + 'color: blue', + ) + } }) test('import css in less', async () => { @@ -462,6 +486,10 @@ test('aliased css has content', async () => { expect(await getColor('.aliased-module')).toBe('blue') }) +test('resolve imports field in CSS', async () => { + expect(await getColor('.imports-field')).toBe('red') +}) + test.runIf(isBuild)('warning can be suppressed by esbuild.logOverride', () => { serverLogs.forEach((log) => { // no warning from esbuild css minifier @@ -508,3 +536,15 @@ test('async css order with css modules', async () => { test('@import scss', async () => { expect(await getColor('.at-import-scss')).toBe('red') }) + +test.runIf(isBuild)('manual chunk path', async () => { + // assert that the manual-chunk css is output in the directory specified in manualChunk (#12072) + expect( + findAssetFile(/dir\/dir2\/manual-chunk-[-\w]{8}\.css$/), + ).not.toBeUndefined() +}) + +test.runIf(isBuild)('CSS modules should be treeshaken if not used', () => { + const css = findAssetFile(/\.css$/, undefined, undefined, true) + expect(css).not.toContain('treeshake-module-b') +}) diff --git a/playground/css/__tests__/no-css-minify/no-css-minify.spec.ts b/playground/css/__tests__/no-css-minify/css-no-css-minify.spec.ts similarity index 100% rename from playground/css/__tests__/no-css-minify/no-css-minify.spec.ts rename to playground/css/__tests__/no-css-minify/css-no-css-minify.spec.ts diff --git a/playground/css/__tests__/postcss-plugins-different-dir/postcss-plugins-different-dir.spec.ts b/playground/css/__tests__/postcss-plugins-different-dir/css-postcss-plugins-different-dir.spec.ts similarity index 100% rename from playground/css/__tests__/postcss-plugins-different-dir/postcss-plugins-different-dir.spec.ts rename to playground/css/__tests__/postcss-plugins-different-dir/css-postcss-plugins-different-dir.spec.ts diff --git a/playground/css/__tests__/same-file-name/same-file-name.spec.ts b/playground/css/__tests__/same-file-name/css-same-file-name.spec.ts similarity index 100% rename from playground/css/__tests__/same-file-name/same-file-name.spec.ts rename to playground/css/__tests__/same-file-name/css-same-file-name.spec.ts diff --git a/playground/css/imports-field.css b/playground/css/imports-field.css new file mode 100644 index 00000000000000..9120b6c04f9150 --- /dev/null +++ b/playground/css/imports-field.css @@ -0,0 +1,3 @@ +.imports-field { + color: red; +} diff --git a/playground/css/imports-imports-field.css b/playground/css/imports-imports-field.css new file mode 100644 index 00000000000000..2f4167c3033ec4 --- /dev/null +++ b/playground/css/imports-imports-field.css @@ -0,0 +1 @@ +@import '#imports'; diff --git a/playground/css/index.html b/playground/css/index.html index 2016c1b3c72ca8..a0e92b205e79f6 100644 --- a/playground/css/index.html +++ b/playground/css/index.html @@ -33,6 +33,9 @@

CSS

@import from SASS _partial: This should be orchid

url starts with variable

+

+ url starts with function call +

Imported SASS string:


   

@@ -102,6 +105,8 @@

CSS

Imported SASS module:


 
+  

CSS modules should treeshake in build

+

Imported compose/from CSS/SASS module:

CSS modules composes path resolving: this should be turquoise @@ -143,6 +148,10 @@

CSS

@import dependency that @import another dependency: this should be purple

+

+ @import dependency that has @import with a css extension pointing to another + dependency: this should be purple +

PostCSS dir-dependency: this should be grey

@@ -178,6 +187,8 @@

CSS


 
+  

URL Support

+

Raw Support


 
@@ -191,6 +202,9 @@ 

CSS

import '#alias': this should be blue


   

import '#alias-module': this should be blue

+ +

Imports field

+

import '#imports': this should be red

diff --git a/playground/js-sourcemap/__tests__/js-sourcemap.spec.ts b/playground/js-sourcemap/__tests__/js-sourcemap.spec.ts index b4e63e66290a74..c4f7af623fb724 100644 --- a/playground/js-sourcemap/__tests__/js-sourcemap.spec.ts +++ b/playground/js-sourcemap/__tests__/js-sourcemap.spec.ts @@ -1,4 +1,6 @@ -import { URL } from 'node:url' +import { URL, fileURLToPath } from 'node:url' +import { promisify } from 'node:util' +import { execFile } from 'node:child_process' import { describe, expect, test } from 'vitest' import { mapFileCommentRegex } from 'convert-source-map' import { commentSourceMap } from '../foo-with-sourcemap-plugin' @@ -31,6 +33,27 @@ if (!isBuild) { `) }) + test('plugin return sourcemap with `sources: [""]`', async () => { + const res = await page.request.get(new URL('./zoo.js', page.url()).href) + const js = await res.text() + expect(js).toContain('// add comment') + + const map = extractSourcemap(js) + expect(formatSourcemapForSnapshot(map)).toMatchInlineSnapshot(` + { + "mappings": "AAAA,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC;", + "sources": [ + "zoo.js", + ], + "sourcesContent": [ + "export const zoo = 'zoo' + ", + ], + "version": 3, + } + `) + }) + test('js with inline sourcemap injected by a plugin', async () => { const res = await page.request.get( new URL('./foo-with-sourcemap.js', page.url()).href, @@ -113,10 +136,11 @@ describe.runIf(isBuild)('build tests', () => { }) test('sourcemap is correct when preload information is injected', async () => { - const map = findAssetFile(/after-preload-dynamic.*\.js\.map/) + const map = findAssetFile(/after-preload-dynamic-[-\w]{8}\.js\.map/) expect(formatSourcemapForSnapshot(JSON.parse(map))).toMatchInlineSnapshot(` { - "mappings": "k2BAAA,OAAO,2BAAuB,EAAC,wBAE/B,QAAQ,IAAI,uBAAuB", + "ignoreList": [], + "mappings": ";w+BAAA,OAAO,2BAAuB,EAAC,wBAE/B,QAAQ,IAAI,uBAAuB", "sources": [ "../../after-preload-dynamic.js", ], @@ -129,5 +153,58 @@ describe.runIf(isBuild)('build tests', () => { "version": 3, } `) + // verify sourcemap comment is preserved at the last line + const js = findAssetFile(/after-preload-dynamic-[-\w]{8}\.js$/) + expect(js).toMatch( + /\n\/\/# sourceMappingURL=after-preload-dynamic-[-\w]{8}\.js\.map\n$/, + ) + }) + + test('__vite__mapDeps injected after banner', async () => { + const js = findAssetFile(/after-preload-dynamic-hashbang-[-\w]{8}\.js$/) + expect(js.split('\n').slice(0, 2)).toEqual([ + '#!/usr/bin/env node', + expect.stringContaining('const __vite__mapDeps=(i'), + ]) + }) + + test('no unused __vite__mapDeps', async () => { + const js = findAssetFile(/after-preload-dynamic-no-dep-[-\w]{8}\.js$/) + expect(js).not.toMatch(/__vite__mapDeps/) + }) + + test('sourcemap is correct when using object as "define" value', async () => { + const map = findAssetFile(/with-define-object.*\.js\.map/) + expect(formatSourcemapForSnapshot(JSON.parse(map))).toMatchInlineSnapshot(` + { + "mappings": "qBAEA,SAASA,GAAO,CACJC,GACZ,CAEA,SAASA,GAAY,CAEX,QAAA,MAAM,qBAAsBC,CAAkB,CACxD,CAEAF,EAAK", + "sources": [ + "../../with-define-object.ts", + ], + "sourcesContent": [ + "// test complicated stack since broken sourcemap + // might still look correct with a simple case + function main() { + mainInner() + } + + function mainInner() { + // @ts-expect-error "define" + console.trace('with-define-object', __testDefineObject) + } + + main() + ", + ], + "version": 3, + } + `) + }) + + test('correct sourcemap during ssr dev when using object as "define" value', async () => { + const execFileAsync = promisify(execFile) + await execFileAsync('node', ['test-ssr-dev.js'], { + cwd: fileURLToPath(new URL('..', import.meta.url)), + }) }) }) diff --git a/playground/js-sourcemap/after-preload-dynamic-hashbang.js b/playground/js-sourcemap/after-preload-dynamic-hashbang.js new file mode 100644 index 00000000000000..918cdeff7932a4 --- /dev/null +++ b/playground/js-sourcemap/after-preload-dynamic-hashbang.js @@ -0,0 +1,5 @@ +// hashbang is injected via rollupOptions.output.banner + +import('./dynamic/dynamic-foo') + +console.log('after preload dynamic hashbang') diff --git a/playground/js-sourcemap/after-preload-dynamic-no-dep.js b/playground/js-sourcemap/after-preload-dynamic-no-dep.js new file mode 100644 index 00000000000000..55f150702d02dc --- /dev/null +++ b/playground/js-sourcemap/after-preload-dynamic-no-dep.js @@ -0,0 +1,3 @@ +import('./dynamic/dynamic-no-dep') + +console.log('after preload dynamic no dep') diff --git a/playground/js-sourcemap/dynamic/dynamic-no-dep.js b/playground/js-sourcemap/dynamic/dynamic-no-dep.js new file mode 100644 index 00000000000000..d2291ff747319c --- /dev/null +++ b/playground/js-sourcemap/dynamic/dynamic-no-dep.js @@ -0,0 +1 @@ +console.log('dynamic/dynamic-no-dep') diff --git a/playground/js-sourcemap/importee-pkg/index.js b/playground/js-sourcemap/importee-pkg/index.js index a96b15202fba44..95403a93f0d308 100644 --- a/playground/js-sourcemap/importee-pkg/index.js +++ b/playground/js-sourcemap/importee-pkg/index.js @@ -1,2 +1,2 @@ -// eslint-disable-next-line import/no-commonjs +// eslint-disable-next-line import-x/no-commonjs exports.foo = 'foo' diff --git a/playground/js-sourcemap/index.html b/playground/js-sourcemap/index.html index f669bf4fc102aa..7a91852f4ebe18 100644 --- a/playground/js-sourcemap/index.html +++ b/playground/js-sourcemap/index.html @@ -6,4 +6,8 @@

JS Sourcemap

+ + + + diff --git a/playground/js-sourcemap/package.json b/playground/js-sourcemap/package.json index b002697756a24c..9ae7f9d89ac3e7 100644 --- a/playground/js-sourcemap/package.json +++ b/playground/js-sourcemap/package.json @@ -10,6 +10,7 @@ "preview": "vite preview" }, "dependencies": { - "@vitejs/test-importee-pkg": "file:importee-pkg" + "@vitejs/test-importee-pkg": "file:importee-pkg", + "magic-string": "^0.30.10" } } diff --git a/playground/js-sourcemap/plugin-foo.js b/playground/js-sourcemap/plugin-foo.js new file mode 100644 index 00000000000000..cb356468240d50 --- /dev/null +++ b/playground/js-sourcemap/plugin-foo.js @@ -0,0 +1 @@ +export const foo = 'foo' diff --git a/playground/js-sourcemap/test-ssr-dev.js b/playground/js-sourcemap/test-ssr-dev.js new file mode 100644 index 00000000000000..c414f058517283 --- /dev/null +++ b/playground/js-sourcemap/test-ssr-dev.js @@ -0,0 +1,38 @@ +import assert from 'node:assert' +import { fileURLToPath } from 'node:url' +import { createServer } from 'vite' + +async function runTest() { + const server = await createServer({ + root: fileURLToPath(new URL('.', import.meta.url)), + configFile: false, + optimizeDeps: { + noDiscovery: true, + }, + server: { + middlewareMode: true, + hmr: false, + }, + define: { + __testDefineObject: '{ "hello": "test" }', + }, + }) + const mod = await server.ssrLoadModule('/with-define-object-ssr.ts') + const error = await getError(() => mod.error()) + server.ssrFixStacktrace(error) + assert.match(error.stack, /at errorInner (.*with-define-object-ssr.ts:7:9)/) + await server.close() +} + +async function getError(f) { + let error + try { + await f() + } catch (e) { + error = e + } + assert.ok(error) + return error +} + +runTest() diff --git a/playground/js-sourcemap/vite.config.js b/playground/js-sourcemap/vite.config.js index efebbc5ca00dee..f47c89eff07ebf 100644 --- a/playground/js-sourcemap/vite.config.js +++ b/playground/js-sourcemap/vite.config.js @@ -1,18 +1,39 @@ import { defineConfig } from 'vite' import transformFooWithInlineSourceMap from './foo-with-sourcemap-plugin' +import { transformZooWithSourcemapPlugin } from './zoo-with-sourcemap-plugin' export default defineConfig({ - plugins: [transformFooWithInlineSourceMap()], + plugins: [ + transformFooWithInlineSourceMap(), + transformZooWithSourcemapPlugin(), + ], build: { sourcemap: true, rollupOptions: { output: { manualChunks(name) { - if (name.includes('after-preload-dynamic')) { + if (name.endsWith('after-preload-dynamic.js')) { return 'after-preload-dynamic' } + if (name.endsWith('after-preload-dynamic-hashbang.js')) { + return 'after-preload-dynamic-hashbang' + } + if (name.endsWith('after-preload-dynamic-no-dep.js')) { + return 'after-preload-dynamic-no-dep' + } + if (name.includes('with-define-object')) { + return 'with-define-object' + } + }, + banner(chunk) { + if (chunk.name.endsWith('after-preload-dynamic-hashbang')) { + return '#!/usr/bin/env node' + } }, }, }, }, + define: { + __testDefineObject: '{ "hello": "test" }', + }, }) diff --git a/playground/js-sourcemap/with-define-object-ssr.ts b/playground/js-sourcemap/with-define-object-ssr.ts new file mode 100644 index 00000000000000..9ff85230025e2d --- /dev/null +++ b/playground/js-sourcemap/with-define-object-ssr.ts @@ -0,0 +1,8 @@ +export function error() { + errorInner() +} + +function errorInner() { + // @ts-expect-error "define" + throw new Error('with-define-object: ' + JSON.stringify(__testDefineObject)) +} diff --git a/playground/js-sourcemap/with-define-object.ts b/playground/js-sourcemap/with-define-object.ts new file mode 100644 index 00000000000000..5a9f8e2ddd43d9 --- /dev/null +++ b/playground/js-sourcemap/with-define-object.ts @@ -0,0 +1,12 @@ +// test complicated stack since broken sourcemap +// might still look correct with a simple case +function main() { + mainInner() +} + +function mainInner() { + // @ts-expect-error "define" + console.trace('with-define-object', __testDefineObject) +} + +main() diff --git a/playground/js-sourcemap/zoo-with-sourcemap-plugin.ts b/playground/js-sourcemap/zoo-with-sourcemap-plugin.ts new file mode 100644 index 00000000000000..6c493278d166c8 --- /dev/null +++ b/playground/js-sourcemap/zoo-with-sourcemap-plugin.ts @@ -0,0 +1,18 @@ +import MagicString from 'magic-string' +import type { Plugin } from 'vite' + +export const transformZooWithSourcemapPlugin: () => Plugin = () => ({ + name: 'sourcemap', + transform(code, id) { + if (id.includes('zoo.js')) { + const ms = new MagicString(code) + ms.append('// add comment') + return { + code: ms.toString(), + // NOTE: MagicString without `filename` option generates + // a sourcemap with `sources: ['']` or `sources: [null]` + map: ms.generateMap({ hires: true }), + } + } + }, +}) diff --git a/playground/js-sourcemap/zoo.js b/playground/js-sourcemap/zoo.js new file mode 100644 index 00000000000000..286343f930d3c3 --- /dev/null +++ b/playground/js-sourcemap/zoo.js @@ -0,0 +1 @@ +export const zoo = 'zoo' diff --git a/playground/json/__tests__/csr/json.spec.ts b/playground/json/__tests__/csr/json-csr.spec.ts similarity index 100% rename from playground/json/__tests__/csr/json.spec.ts rename to playground/json/__tests__/csr/json-csr.spec.ts diff --git a/playground/json/__tests__/ssr/json.spec.ts b/playground/json/__tests__/ssr/json-ssr.spec.ts similarity index 100% rename from playground/json/__tests__/ssr/json.spec.ts rename to playground/json/__tests__/ssr/json-ssr.spec.ts diff --git a/playground/json/package.json b/playground/json/package.json index 4a2467d9463644..e5eb9f6f3f4df7 100644 --- a/playground/json/package.json +++ b/playground/json/package.json @@ -14,7 +14,7 @@ }, "devDependencies": { "@vitejs/test-json-module": "file:./json-module", - "express": "^4.18.2", - "vue": "^3.3.7" + "express": "^4.19.2", + "vue": "^3.4.31" } } diff --git a/playground/json/server.js b/playground/json/server.js index 982b7dcc09d30b..fea792d4e6ab91 100644 --- a/playground/json/server.js +++ b/playground/json/server.js @@ -69,7 +69,7 @@ export async function createServer(root = process.cwd(), hmrPort) { } const htmlLoc = resolve(`.${url}`) - let html = fs.readFileSync(htmlLoc, 'utf8') + let html = fs.readFileSync(htmlLoc, 'utf-8') html = await vite.transformIndexHtml(url, html) res.status(200).set({ 'Content-Type': 'text/html' }).end(html) diff --git a/playground/legacy/__tests__/client-and-ssr/client-legacy-ssr-sequential-builds.spec.ts b/playground/legacy/__tests__/client-and-ssr/legacy-client-legacy-ssr-sequential-builds.spec.ts similarity index 100% rename from playground/legacy/__tests__/client-and-ssr/client-legacy-ssr-sequential-builds.spec.ts rename to playground/legacy/__tests__/client-and-ssr/legacy-client-legacy-ssr-sequential-builds.spec.ts diff --git a/playground/legacy/__tests__/legacy.spec.ts b/playground/legacy/__tests__/legacy.spec.ts index 405c61f47e2700..cc747761588ca5 100644 --- a/playground/legacy/__tests__/legacy.spec.ts +++ b/playground/legacy/__tests__/legacy.spec.ts @@ -56,8 +56,8 @@ test('generates assets', async () => { 'index-legacy: text/html', 'chunk-async: text/html', 'chunk-async-legacy: text/html', - 'immutable-chunk: application/javascript', - 'immutable-chunk-legacy: application/javascript', + 'immutable-chunk: text/javascript', + 'immutable-chunk-legacy: text/javascript', 'polyfills-legacy: text/html', ].join('\n') : [ @@ -120,11 +120,13 @@ describe.runIf(isBuild)('build', () => { const terserPattern = /^(?:!function|System.register)/ expect(findAssetFile(/chunk-async-legacy/)).toMatch(terserPattern) - expect(findAssetFile(/chunk-async\./)).not.toMatch(terserPattern) + expect(findAssetFile(/chunk-async(?!-legacy)/)).not.toMatch(terserPattern) expect(findAssetFile(/immutable-chunk-legacy/)).toMatch(terserPattern) - expect(findAssetFile(/immutable-chunk\./)).not.toMatch(terserPattern) + expect(findAssetFile(/immutable-chunk(?!-legacy)/)).not.toMatch( + terserPattern, + ) expect(findAssetFile(/index-legacy/)).toMatch(terserPattern) - expect(findAssetFile(/index\./)).not.toMatch(terserPattern) + expect(findAssetFile(/index(?!-legacy)/)).not.toMatch(terserPattern) expect(findAssetFile(/polyfills-legacy/)).toMatch(terserPattern) }) @@ -149,4 +151,13 @@ describe.runIf(isBuild)('build', () => { ), ).toBeFalsy() }) + + test('should have only modern entry files guarded', async () => { + const guard = /(import\s*\()|(import.meta)|(async\s*function\*)/ + expect(findAssetFile(/index(?!-legacy)/)).toMatch(guard) + expect(findAssetFile(/polyfills(?!-legacy)/)).toMatch(guard) + + expect(findAssetFile(/chunk-async(?!-legacy)/)).not.toMatch(guard) + expect(findAssetFile(/index-legacy/)).not.toMatch(guard) + }) }) diff --git a/playground/legacy/__tests__/no-polyfills-no-systemjs/no-polyfills-no-systemjs.spec.ts b/playground/legacy/__tests__/no-polyfills-no-systemjs/legacy-no-polyfills-no-systemjs.spec.ts similarity index 100% rename from playground/legacy/__tests__/no-polyfills-no-systemjs/no-polyfills-no-systemjs.spec.ts rename to playground/legacy/__tests__/no-polyfills-no-systemjs/legacy-no-polyfills-no-systemjs.spec.ts diff --git a/playground/legacy/__tests__/no-polyfills/no-polyfills.spec.ts b/playground/legacy/__tests__/no-polyfills/legacy-no-polyfills.spec.ts similarity index 100% rename from playground/legacy/__tests__/no-polyfills/no-polyfills.spec.ts rename to playground/legacy/__tests__/no-polyfills/legacy-no-polyfills.spec.ts diff --git a/playground/legacy/__tests__/watch/legacy-styles-only-entry-watch.spec.ts b/playground/legacy/__tests__/watch/legacy-styles-only-entry-watch.spec.ts new file mode 100644 index 00000000000000..8dfffc5aa4f83a --- /dev/null +++ b/playground/legacy/__tests__/watch/legacy-styles-only-entry-watch.spec.ts @@ -0,0 +1,47 @@ +import { expect, test } from 'vitest' +import { + editFile, + findAssetFile, + isBuild, + notifyRebuildComplete, + readManifest, + watcher, +} from '~utils' + +test.runIf(isBuild)('rebuilds styles only entry on change', async () => { + expect(findAssetFile(/style-only-entry-.+\.css/, 'watch')).toContain( + '#ff69b4', + ) + expect(findAssetFile(/style-only-entry-legacy-.+\.js/, 'watch')).toContain( + '#ff69b4', + ) + expect(findAssetFile(/polyfills-legacy-.+\.js/, 'watch')).toBeTruthy() + const numberOfManifestEntries = Object.keys(readManifest('watch')).length + expect(numberOfManifestEntries).toBe(3) + + editFile( + 'style-only-entry.css', + (originalContents) => originalContents.replace('#ff69b4', '#ffb6c1'), + true, + ) + await notifyRebuildComplete(watcher) + + const updatedManifest = readManifest('watch') + expect(Object.keys(updatedManifest)).toHaveLength(numberOfManifestEntries) + + // We must use the file referenced in the manifest here, + // since there'll be different versions of the file with different hashes. + const reRenderedCssFile = findAssetFile( + updatedManifest['style-only-entry.css']!.file.substring('assets/'.length), + 'watch', + ) + expect(reRenderedCssFile).toContain('#ffb6c1') + const reRenderedCssLegacyFile = findAssetFile( + updatedManifest['style-only-entry-legacy.css']!.file.substring( + 'assets/'.length, + ), + 'watch', + ) + expect(reRenderedCssLegacyFile).toContain('#ffb6c1') + expect(findAssetFile(/polyfills-legacy-.+\.js/, 'watch')).toBeTruthy() +}) diff --git a/playground/legacy/package.json b/playground/legacy/package.json index 61047309b5595c..fb8189605451ac 100644 --- a/playground/legacy/package.json +++ b/playground/legacy/package.json @@ -10,12 +10,14 @@ "build:multiple-output": "vite --config ./vite.config-multiple-output.js build", "build:no-polyfills": "vite --config ./vite.config-no-polyfills.js build", "build:no-polyfills-no-systemjs": "vite --config ./vite.config-no-polyfills-no-systemjs.js build", + "build:watch": "vite --config ./vite.config-watch.js build --debug legacy", "debug": "node --inspect-brk ../../packages/vite/bin/vite", "preview": "vite preview" }, "devDependencies": { + "vite": "workspace:*", "@vitejs/plugin-legacy": "workspace:*", - "express": "^4.18.2", - "terser": "^5.22.0" + "express": "^4.19.2", + "terser": "^5.31.1" } } diff --git a/playground/legacy/style-only-entry.css b/playground/legacy/style-only-entry.css new file mode 100644 index 00000000000000..2212f7a84ad32f --- /dev/null +++ b/playground/legacy/style-only-entry.css @@ -0,0 +1,3 @@ +:root { + background: #ff69b4; +} diff --git a/playground/legacy/vite.config-no-polyfills-no-systemjs.js b/playground/legacy/vite.config-no-polyfills-no-systemjs.js index 3e3c93f31d63ff..388ee5a3ba71a4 100644 --- a/playground/legacy/vite.config-no-polyfills-no-systemjs.js +++ b/playground/legacy/vite.config-no-polyfills-no-systemjs.js @@ -2,8 +2,8 @@ import path from 'node:path' import legacy from '@vitejs/plugin-legacy' import { defineConfig } from 'vite' -export default defineConfig({ - base: './', +export default defineConfig(({ isPreview }) => ({ + base: !isPreview ? './' : '/no-polyfills-no-systemjs/', plugins: [ legacy({ renderModernChunks: false, @@ -25,7 +25,4 @@ export default defineConfig({ }, }, }, - testConfig: { - previewBase: '/no-polyfills-no-systemjs/', - }, -}) +})) diff --git a/playground/legacy/vite.config-no-polyfills.js b/playground/legacy/vite.config-no-polyfills.js index e1bb5d364851a1..a33a34aa20e556 100644 --- a/playground/legacy/vite.config-no-polyfills.js +++ b/playground/legacy/vite.config-no-polyfills.js @@ -2,8 +2,8 @@ import path from 'node:path' import legacy from '@vitejs/plugin-legacy' import { defineConfig } from 'vite' -export default defineConfig({ - base: './', +export default defineConfig(({ isPreview }) => ({ + base: !isPreview ? './' : '/no-polyfills/', plugins: [ legacy({ renderModernChunks: false, @@ -24,7 +24,4 @@ export default defineConfig({ }, }, }, - testConfig: { - previewBase: '/no-polyfills/', - }, -}) +})) diff --git a/playground/legacy/vite.config-watch.js b/playground/legacy/vite.config-watch.js new file mode 100644 index 00000000000000..48d57f3e988edc --- /dev/null +++ b/playground/legacy/vite.config-watch.js @@ -0,0 +1,17 @@ +import { resolve } from 'node:path' +import legacy from '@vitejs/plugin-legacy' +import { defineConfig } from 'vite' + +export default defineConfig({ + plugins: [legacy()], + build: { + manifest: true, + rollupOptions: { + input: { + 'style-only-entry': resolve(__dirname, 'style-only-entry.css'), + }, + }, + watch: {}, + outDir: 'dist/watch', + }, +}) diff --git a/playground/lib/__tests__/lib.spec.ts b/playground/lib/__tests__/lib.spec.ts index 021d7d1bc636b5..af144de44e9c46 100644 --- a/playground/lib/__tests__/lib.spec.ts +++ b/playground/lib/__tests__/lib.spec.ts @@ -67,6 +67,14 @@ describe.runIf(isBuild)('build', () => { expect(code).toMatch(/await import\("\.\/message-[-\w]{8}.js"\)/) }) + test('Library mode does not have any reference to pure CSS chunks', async () => { + const code = readFile('dist/lib/dynamic-import-message.es.mjs') + + // Does not import pure CSS chunks and replaced by `Promise.resolve({})` instead + expect(code).not.toMatch(/await import\("\.\/dynamic-[-\w]{8}.js"\)/) + expect(code).toMatch(/await Promise.resolve\(\{.*\}\)/) + }) + test('@import hoist', async () => { serverLogs.forEach((log) => { // no warning from esbuild css minifier diff --git a/playground/lib/__tests__/serve.ts b/playground/lib/__tests__/serve.ts index 249bc7de4fec43..009bc3711825a6 100644 --- a/playground/lib/__tests__/serve.ts +++ b/playground/lib/__tests__/serve.ts @@ -27,6 +27,8 @@ export async function serve(): Promise<{ close(): Promise }> { root: rootDir, logLevel: 'silent', server: { + port, + strictPort: true, watch: { usePolling: true, interval: 100, @@ -43,7 +45,7 @@ export async function serve(): Promise<{ close(): Promise }> { ).listen() // use resolved port/base from server const devBase = viteServer.config.base === '/' ? '' : viteServer.config.base - setViteUrl(`http://localhost:${viteServer.config.server.port}${devBase}`) + setViteUrl(`http://localhost:${port}${devBase}`) await page.goto(viteTestUrl) return viteServer diff --git a/playground/lib/package.json b/playground/lib/package.json index a38060db5e43f2..5765873ebd503c 100644 --- a/playground/lib/package.json +++ b/playground/lib/package.json @@ -10,6 +10,6 @@ "preview": "vite preview" }, "devDependencies": { - "sirv": "^2.0.3" + "sirv": "^2.0.4" } } diff --git a/playground/multiple-entrypoints/package.json b/playground/multiple-entrypoints/package.json index 695945ac4c31f9..01466d46bdb535 100644 --- a/playground/multiple-entrypoints/package.json +++ b/playground/multiple-entrypoints/package.json @@ -10,6 +10,6 @@ "preview": "vite preview" }, "devDependencies": { - "sass": "^1.69.5" + "sass": "^1.77.6" } } diff --git a/playground/nested-deps/__tests__/nested-deps.spec.ts b/playground/nested-deps/__tests__/nested-deps.spec.ts index 04618ece544ad9..d59c571db832f1 100644 --- a/playground/nested-deps/__tests__/nested-deps.spec.ts +++ b/playground/nested-deps/__tests__/nested-deps.spec.ts @@ -14,4 +14,5 @@ test('handle nested package', async () => { // expect(await page.textContent('.nested-e')).toBe('1') expect(await page.textContent('.absolute-f')).toBe('F@2.0.0') + expect(await page.textContent('.self-referencing')).toBe('true') }) diff --git a/playground/nested-deps/index.html b/playground/nested-deps/index.html index 86dbc149fff32e..d06916f77b7860 100644 --- a/playground/nested-deps/index.html +++ b/playground/nested-deps/index.html @@ -24,6 +24,9 @@

exclude dependency of pre-bundled dependency

absolute dependency path:

+

self referencing

+

+
 
 
+

Import the CommonJS external package that omits the js suffix

+
+
+
+
+ + + + diff --git a/playground/optimize-deps/package.json b/playground/optimize-deps/package.json index 34546d0178a7ca..a209d073676471 100644 --- a/playground/optimize-deps/package.json +++ b/playground/optimize-deps/package.json @@ -4,13 +4,13 @@ "type": "module", "version": "0.0.0", "scripts": { - "dev": "vite", + "dev": "vite --force", "build": "vite build", "debug": "node --inspect-brk ../../packages/vite/bin/vite", "preview": "vite preview" }, "dependencies": { - "axios": "^1.6.0", + "axios": "^1.7.2", "clipboard": "^2.0.11", "@vitejs/longfilename-aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa": "file:./longfilename", "@vitejs/test-dep-alias-using-absolute-path": "file:./dep-alias-using-absolute-path", @@ -20,11 +20,13 @@ "@vitejs/test-dep-cjs-with-assets": "file:./dep-cjs-with-assets", "@vitejs/test-dep-css-require": "file:./dep-css-require", "@vitejs/test-dep-esbuild-plugin-transform": "file:./dep-esbuild-plugin-transform", + "@vitejs/test-dep-incompatible": "file:./dep-incompatible", "@vitejs/test-dep-linked": "link:./dep-linked", "@vitejs/test-dep-linked-include": "link:./dep-linked-include", "@vitejs/test-dep-node-env": "file:./dep-node-env", "@vitejs/test-dep-not-js": "file:./dep-not-js", "@vitejs/test-dep-optimize-exports-with-glob": "file:./dep-optimize-exports-with-glob", + "@vitejs/test-dep-optimize-exports-with-root-glob": "file:./dep-optimize-exports-with-root-glob", "@vitejs/test-dep-optimize-with-glob": "file:./dep-optimize-with-glob", "@vitejs/test-dep-relative-to-main": "file:./dep-relative-to-main", "@vitejs/test-dep-with-builtin-module-cjs": "file:./dep-with-builtin-module-cjs", @@ -34,14 +36,15 @@ "@vitejs/test-dep-with-optional-peer-dep-submodule": "file:./dep-with-optional-peer-dep-submodule", "@vitejs/test-dep-non-optimized": "file:./dep-non-optimized", "@vitejs/test-added-in-entries": "file:./added-in-entries", + "@vitejs/test-dep-cjs-external-package-omit-js-suffix": "file:./dep-cjs-external-package-omit-js-suffix", "lodash-es": "^4.17.21", "@vitejs/test-nested-exclude": "file:./nested-exclude", - "phoenix": "^1.7.9", - "react": "^18.2.0", - "react-dom": "^18.2.0", + "phoenix": "^1.7.14", + "react": "^18.3.1", + "react-dom": "^18.3.1", "@vitejs/test-resolve-linked": "workspace:0.0.0", "url": "^0.11.3", - "vue": "^3.3.7", + "vue": "^3.4.31", "vuex": "^4.1.0", "lodash": "^4.17.21", "lodash.clonedeep": "^4.5.0" diff --git a/playground/optimize-deps/vite.config.js b/playground/optimize-deps/vite.config.js index 8947bb66020b7c..e42aa3234119e7 100644 --- a/playground/optimize-deps/vite.config.js +++ b/playground/optimize-deps/vite.config.js @@ -3,9 +3,6 @@ import module from 'node:module' import { defineConfig } from 'vite' const require = module.createRequire(import.meta.url) -// Overriding the NODE_ENV set by vitest -process.env.NODE_ENV = '' - export default defineConfig({ resolve: { dedupe: ['react'], @@ -17,28 +14,42 @@ export default defineConfig({ }, }, optimizeDeps: { - disabled: false, include: [ '@vitejs/test-dep-linked-include', '@vitejs/test-nested-exclude > @vitejs/test-nested-include', + '@vitejs/test-dep-cjs-external-package-omit-js-suffix', // will throw if optimized (should log warning instead) '@vitejs/test-non-optimizable-include', '@vitejs/test-dep-optimize-exports-with-glob/**/*', + '@vitejs/test-dep-optimize-exports-with-root-glob/**/*.js', '@vitejs/test-dep-optimize-with-glob/**/*.js', ], exclude: ['@vitejs/test-nested-exclude', '@vitejs/test-dep-non-optimized'], - esbuildOptions: { + // esbuildOptions: { + // plugins: [ + // { + // name: 'replace-a-file', + // setup(build) { + // build.onLoad( + // { filter: /dep-esbuild-plugin-transform(\\|\/)index\.js$/ }, + // () => ({ + // contents: `export const hello = () => 'Hello from an esbuild plugin'`, + // loader: 'js', + // }), + // ) + // }, + // }, + // ], + // }, + rollupOptions: { plugins: [ { name: 'replace-a-file', - setup(build) { - build.onLoad( - { filter: /dep-esbuild-plugin-transform(\\|\/)index\.js$/ }, - () => ({ - contents: `export const hello = () => 'Hello from an esbuild plugin'`, - loader: 'js', - }), - ) + load: (id) => { + // eslint-disable-next-line regexp/no-unused-capturing-group + if (/dep-esbuild-plugin-transform(\\|\/)index\.js$/.test(id)) { + return `export const hello = () => 'Hello from an esbuild plugin'` + } }, }, ], @@ -49,10 +60,6 @@ export default defineConfig({ build: { // to make tests faster minify: false, - // Avoid @rollup/plugin-commonjs - commonjsOptions: { - include: [], - }, rollupOptions: { onwarn(msg, warn) { // filter `"Buffer" is not exported by "__vite-browser-external"` warning @@ -142,18 +149,37 @@ function notjs() { return { optimizeDeps: { extensions: ['.notjs'], - esbuildOptions: { + // esbuildOptions: { + // plugins: [ + // { + // name: 'esbuild-notjs', + // setup(build) { + // build.onLoad({ filter: /\.notjs$/ }, ({ path }) => { + // let contents = fs.readFileSync(path, 'utf-8') + // contents = contents + // .replace('', '') + // .replace('', '') + // return { contents, loader: 'js' } + // }) + // }, + // }, + // ], + // }, + rollupOptions: { + // moduleTypes: { + // '.notjs': 'js', + // }, plugins: [ { name: 'esbuild-notjs', - setup(build) { - build.onLoad({ filter: /\.notjs$/ }, ({ path }) => { - let contents = fs.readFileSync(path, 'utf-8') + load: (id) => { + if (id.endsWith('.notjs')) { + let contents = fs.readFileSync(id, 'utf-8') contents = contents .replace('', '') .replace('', '') - return { contents, loader: 'js' } - }) + return contents + } }, }, ], diff --git a/playground/optimize-missing-deps/package.json b/playground/optimize-missing-deps/package.json index 21281f45bd66f8..bcbd49586f6e2c 100644 --- a/playground/optimize-missing-deps/package.json +++ b/playground/optimize-missing-deps/package.json @@ -10,6 +10,6 @@ "@vitejs/test-missing-dep": "file:./missing-dep" }, "devDependencies": { - "express": "^4.18.2" + "express": "^4.19.2" } } diff --git a/playground/optimize-missing-deps/server.js b/playground/optimize-missing-deps/server.js index 411c3342b02d9c..ea50b9adc291e5 100644 --- a/playground/optimize-missing-deps/server.js +++ b/playground/optimize-missing-deps/server.js @@ -1,8 +1,10 @@ // @ts-check import fs from 'node:fs' import path from 'node:path' +import { fileURLToPath } from 'node:url' import express from 'express' +const __dirname = path.dirname(fileURLToPath(import.meta.url)) const isTest = process.env.VITEST export async function createServer(root = process.cwd(), hmrPort) { diff --git a/playground/preload/package.json b/playground/preload/package.json index 790ab57e8c58cb..aca955b2dda999 100644 --- a/playground/preload/package.json +++ b/playground/preload/package.json @@ -18,7 +18,7 @@ "preview:preload-disabled": "vite preview --config vite.config-preload-disabled.ts" }, "devDependencies": { - "terser": "^5.22.0", + "terser": "^5.31.1", "@vitejs/test-dep-a": "file:./dep-a", "@vitejs/test-dep-including-a": "file:./dep-including-a" } diff --git a/playground/proxy-bypass/__tests__/proxy-hmr.spec.ts b/playground/proxy-bypass/__tests__/proxy-bypass.spec.ts similarity index 100% rename from playground/proxy-bypass/__tests__/proxy-hmr.spec.ts rename to playground/proxy-bypass/__tests__/proxy-bypass.spec.ts diff --git a/playground/proxy-bypass/index.html b/playground/proxy-bypass/index.html index f14fde8e428635..366aaebb5651ae 100644 --- a/playground/proxy-bypass/index.html +++ b/playground/proxy-bypass/index.html @@ -1,2 +1,2 @@ root app
- + diff --git a/playground/proxy-bypass/vite.config.js b/playground/proxy-bypass/vite.config.js index 13f7a6b42eaafb..0873583a5bfa56 100644 --- a/playground/proxy-bypass/vite.config.js +++ b/playground/proxy-bypass/vite.config.js @@ -4,7 +4,7 @@ export default defineConfig({ server: { port: 9606, proxy: { - '/anotherApp': { + '/nonExistentApp': { target: 'http://localhost:9607', bypass: () => { return false diff --git a/playground/proxy-hmr/other-app/vite.config.js b/playground/proxy-hmr/other-app/vite.config.js index 6e79541a31d806..9fb3ece5eb9c3c 100644 --- a/playground/proxy-hmr/other-app/vite.config.js +++ b/playground/proxy-hmr/other-app/vite.config.js @@ -3,7 +3,7 @@ import { defineConfig } from 'vite' export default defineConfig({ base: '/anotherApp', server: { - port: 9607, + port: 9617, strictPort: true, }, }) diff --git a/playground/proxy-hmr/vite.config.js b/playground/proxy-hmr/vite.config.js index cd418eacdb5d35..ef57a85757a82a 100644 --- a/playground/proxy-hmr/vite.config.js +++ b/playground/proxy-hmr/vite.config.js @@ -2,10 +2,10 @@ import { defineConfig } from 'vite' export default defineConfig({ server: { - port: 9606, + port: 9616, proxy: { '/anotherApp': { - target: 'http://localhost:9607', + target: 'http://localhost:9617', ws: true, }, }, diff --git a/playground/react/components/DefineVariables.jsx b/playground/react/components/DefineVariables.jsx deleted file mode 100644 index 465baf04d0adbb..00000000000000 --- a/playground/react/components/DefineVariables.jsx +++ /dev/null @@ -1,3 +0,0 @@ -export default function DefineVariable() { - return
import.meta.env
-} diff --git a/playground/resolve-config/__tests__/resolve-config.spec.ts b/playground/resolve-config/__tests__/resolve-config.spec.ts index eeb4f2c79edcbb..12f00aa168f142 100644 --- a/playground/resolve-config/__tests__/resolve-config.spec.ts +++ b/playground/resolve-config/__tests__/resolve-config.spec.ts @@ -13,7 +13,7 @@ const build = (configName: string) => { const getDistFile = (configName: string, extension: string) => { return fs.readFileSync( fromTestDir(`${configName}/dist/index.${extension}`), - 'utf8', + 'utf-8', ) } diff --git a/playground/resolve-config/__tests__/serve.ts b/playground/resolve-config/__tests__/serve.ts index 39e84860cfadfb..76172577f690c3 100644 --- a/playground/resolve-config/__tests__/serve.ts +++ b/playground/resolve-config/__tests__/serve.ts @@ -21,7 +21,7 @@ export async function serve() { await fs.rename(fromTestDir(configName, 'vite.config.ts'), pathToConf) if (['cjs', 'cts'].includes(configName)) { - const conf = await fs.readFile(pathToConf, 'utf8') + const conf = await fs.readFile(pathToConf, 'utf-8') await fs.writeFile( pathToConf, conf.replace('export default', 'module.exports = '), @@ -30,7 +30,7 @@ export async function serve() { // Remove TS annotation for plain JavaScript file. if (configName.endsWith('js')) { - const conf = await fs.readFile(pathToConf, 'utf8') + const conf = await fs.readFile(pathToConf, 'utf-8') await fs.writeFile(pathToConf, conf.replace(': boolean', '')) } diff --git a/playground/resolve/__tests__/mainfields-custom-first/resolve-mainfields-custom-first.spec.ts b/playground/resolve/__tests__/mainfields-custom-first/resolve-mainfields-custom-first.spec.ts new file mode 100644 index 00000000000000..c15f0f56d72375 --- /dev/null +++ b/playground/resolve/__tests__/mainfields-custom-first/resolve-mainfields-custom-first.spec.ts @@ -0,0 +1,8 @@ +import { expect, test } from 'vitest' +import { page } from '~utils' + +test('resolve.mainFields.custom-first', async () => { + expect(await page.textContent('.custom-browser-main-field')).toBe( + 'resolved custom field', + ) +}) diff --git a/playground/resolve/__tests__/resolve.spec.ts b/playground/resolve/__tests__/resolve.spec.ts index d5067698e13713..24715df90a1e29 100644 --- a/playground/resolve/__tests__/resolve.spec.ts +++ b/playground/resolve/__tests__/resolve.spec.ts @@ -167,6 +167,12 @@ test('resolve.mainFields', async () => { expect(await page.textContent('.custom-main-fields')).toMatch('[success]') }) +test('resolve.mainFields.browser-first', async () => { + expect(await page.textContent('.custom-browser-main-field')).toBe( + 'resolved browser field', + ) +}) + test('resolve.conditions', async () => { expect(await page.textContent('.custom-condition')).toMatch('[success]') }) @@ -203,12 +209,23 @@ test('Resolving from other package with imports field', async () => { expect(await page.textContent('.imports-pkg-slash')).toMatch('[success]') }) +test('Resolving with query with imports field', async () => { + // since it is imported with `?url` it should return a URL + expect(await page.textContent('.imports-query')).toMatch( + isBuild ? /base64/ : '/imports-path/query.json', + ) +}) + test('Resolve doesnt interrupt page request with trailing query and .css', async () => { await page.goto(viteTestUrl + '/?test.css') expect(await page.locator('vite-error-overlay').count()).toBe(0) expect(await page.textContent('h1')).toBe('Resolve') }) +test('resolve non-normalized absolute path', async () => { + expect(await page.textContent('.non-normalized')).toMatch('[success]') +}) + test.runIf(!isWindows)( 'Resolve doesnt interrupt page request that clashes with local project package.json', async () => { diff --git a/playground/resolve/browser-field/relative.js b/playground/resolve/browser-field/relative.js index 492be3e96738eb..660d6be578a728 100644 --- a/playground/resolve/browser-field/relative.js +++ b/playground/resolve/browser-field/relative.js @@ -1,4 +1,4 @@ -/* eslint-disable import/no-duplicates */ +/* eslint-disable import-x/no-duplicates */ import ra from './no-ext' import rb from './no-ext.js' // no substitution import rc from './ext' diff --git a/playground/resolve/custom-browser-main-field/index.browser.js b/playground/resolve/custom-browser-main-field/index.browser.js new file mode 100644 index 00000000000000..a12f4a603c068d --- /dev/null +++ b/playground/resolve/custom-browser-main-field/index.browser.js @@ -0,0 +1 @@ +export const msg = 'resolved browser field' diff --git a/playground/resolve/custom-browser-main-field/index.custom.js b/playground/resolve/custom-browser-main-field/index.custom.js new file mode 100644 index 00000000000000..01ea529fad19b5 --- /dev/null +++ b/playground/resolve/custom-browser-main-field/index.custom.js @@ -0,0 +1 @@ +export const msg = 'resolved custom field' diff --git a/playground/resolve/custom-browser-main-field/index.js b/playground/resolve/custom-browser-main-field/index.js new file mode 100644 index 00000000000000..27f9fcfd43658b --- /dev/null +++ b/playground/resolve/custom-browser-main-field/index.js @@ -0,0 +1 @@ +export const msg = '[fail] resolved main field' diff --git a/playground/resolve/custom-browser-main-field/package.json b/playground/resolve/custom-browser-main-field/package.json new file mode 100644 index 00000000000000..0d372bc5eba9fc --- /dev/null +++ b/playground/resolve/custom-browser-main-field/package.json @@ -0,0 +1,8 @@ +{ + "name": "@vitejs/test-resolve-custom-browser-main-field", + "private": true, + "version": "1.0.0", + "main": "index.js", + "browser": "index.browser.js", + "custom": "index.custom.js" +} diff --git a/playground/resolve/exports-with-module-condition/index.js b/playground/resolve/exports-with-module-condition/index.js index 6465a8c55084c3..d38a0e272c457d 100644 --- a/playground/resolve/exports-with-module-condition/index.js +++ b/playground/resolve/exports-with-module-condition/index.js @@ -1,2 +1,2 @@ -/* eslint-disable import/no-commonjs */ +/* eslint-disable import-x/no-commonjs */ module.exports.msg = '[fail] exports with module condition (index.js)' diff --git a/playground/resolve/imports-path/query.json b/playground/resolve/imports-path/query.json new file mode 100644 index 00000000000000..97e19265d6c843 --- /dev/null +++ b/playground/resolve/imports-path/query.json @@ -0,0 +1,3 @@ +{ + "foo": "json" +} diff --git a/playground/resolve/index.html b/playground/resolve/index.html index 3d52ad6722e28c..5badd9bf57bb6e 100644 --- a/playground/resolve/index.html +++ b/playground/resolve/index.html @@ -61,6 +61,9 @@

Resolving slash with imports filed

Resolving from other package with imports field

fail

+

Resolving with query with imports field

+

fail

+

Resolve /index.*

fail

@@ -158,12 +161,18 @@

resolve.extensions

resolve.mainFields

+

resolve.mainFields.custom-browser-main

+

+

resolve.conditions

resolve package that contains # in path

+

resolve non normalized absolute path

+

+