diff --git a/.eslintignore b/.eslintignore index 3e795e7a98280b..2974196847e467 100644 --- a/.eslintignore +++ b/.eslintignore @@ -1,4 +1,3 @@ dist playground-temp temp - diff --git a/.eslintrc.cjs b/.eslintrc.cjs index ba8d46f3c26f97..6333ec251973c0 100644 --- a/.eslintrc.cjs +++ b/.eslintrc.cjs @@ -1,4 +1,5 @@ // @ts-check +const { builtinModules } = require('node:module') const { defineConfig } = require('eslint-define-config') module.exports = defineConfig({ @@ -42,18 +43,6 @@ module.exports = defineConfig({ tryExtensions: ['.ts', '.js', '.jsx', '.tsx', '.d.ts'] } ], - 'node/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.` - }) - ) - ], 'node/no-extraneous-import': [ 'error', { @@ -93,6 +82,10 @@ module.exports = defineConfig({ { prefer: 'type-imports' } ], + 'import/no-nodejs-modules': [ + 'error', + { allow: builtinModules.map((mod) => `node:${mod}`) } + ], 'import/no-duplicates': 'error', 'import/order': 'error', 'sort-imports': [ @@ -107,6 +100,30 @@ module.exports = defineConfig({ ] }, overrides: [ + { + files: ['packages/**'], + excludedFiles: '**/__tests__/**', + rules: { + 'no-restricted-globals': ['error', 'require', '__dirname', '__filename'] + } + }, + { + files: 'packages/vite/**/*.*', + rules: { + 'node/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: { @@ -114,15 +131,17 @@ module.exports = defineConfig({ } }, { - files: ['packages/vite/types/**', '*.spec.ts'], + files: ['packages/vite/src/types/**', '*.spec.ts'], rules: { 'node/no-extraneous-import': 'off' } }, { - files: ['packages/plugin-*/**/*'], + files: ['packages/create-vite/template-*/**', '**/build.config.ts'], rules: { - 'no-restricted-globals': ['error', 'require', '__dirname', '__filename'] + 'no-undef': 'off', + 'node/no-missing-import': 'off', + '@typescript-eslint/explicit-module-boundary-types': 'off' } }, { @@ -132,7 +151,6 @@ module.exports = defineConfig({ 'node/no-extraneous-require': 'off', 'node/no-missing-import': 'off', 'node/no-missing-require': 'off', - 'no-undef': 'off', // engine field doesn't exist in playgrounds 'node/no-unsupported-features/es-builtins': [ 'error', @@ -145,17 +163,22 @@ module.exports = defineConfig({ { version: '^14.18.0 || >=16.0.0' } - ] + ], + '@typescript-eslint/explicit-module-boundary-types': 'off' } }, { - files: ['packages/create-vite/template-*/**', '**/build.config.ts'], + files: ['playground/**'], + excludedFiles: '**/__tests__/**', rules: { - 'node/no-missing-import': 'off' + 'no-undef': 'off', + 'no-empty': 'off', + 'no-constant-condition': 'off', + '@typescript-eslint/no-empty-function': 'off' } }, { - files: ['playground/**', '*.js'], + files: ['*.js', '*.mjs', '*.cjs'], rules: { '@typescript-eslint/explicit-module-boundary-types': 'off' } @@ -165,12 +188,6 @@ module.exports = defineConfig({ rules: { '@typescript-eslint/triple-slash-reference': 'off' } - }, - { - files: 'packages/vite/**/*.*', - rules: { - 'no-restricted-globals': ['error', 'require', '__dirname', '__filename'] - } } ], reportUnusedDisableDirectives: true diff --git a/.github/ISSUE_TEMPLATE/bug_report.yml b/.github/ISSUE_TEMPLATE/bug_report.yml index 8c3987ca835d2d..c330f6c2926bf3 100644 --- a/.github/ISSUE_TEMPLATE/bug_report.yml +++ b/.github/ISSUE_TEMPLATE/bug_report.yml @@ -19,9 +19,15 @@ body: 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. - placeholder: Reproduction URL and steps + placeholder: Reproduction URL validations: required: true + - type: textarea + id: reproduction-steps + attributes: + label: Steps to reproduce + description: Please provide any reproduction steps that may need to be described. E.g. if it happens only when running the dev or build script make sure it's clear which one to use. + placeholder: Run `npm install` followed by `npm run dev` - type: textarea id: system-info attributes: diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 973b78df00591d..565c108ac94b9c 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -6,6 +6,8 @@ env: NODE_OPTIONS: --max-old-space-size=6144 # install playwright binary manually (because pnpm only runs install script once) PLAYWRIGHT_SKIP_BROWSER_DOWNLOAD: "1" + # Vitest auto retry on flaky segfault + VITEST_SEGFAULT_RETRY: 3 on: push: @@ -17,6 +19,8 @@ on: - perf/* - v1 - v2 + - v2.* + - v3.* pull_request: workflow_dispatch: @@ -46,7 +50,7 @@ jobs: uses: actions/checkout@v3 - name: Install pnpm - uses: pnpm/action-setup@v2.2.2 + uses: pnpm/action-setup@v2.2.4 - name: Set node version to ${{ matrix.node_version }} uses: actions/setup-node@v3 @@ -102,7 +106,7 @@ jobs: fetch-depth: 0 - name: Install pnpm - uses: pnpm/action-setup@v2.2.2 + uses: pnpm/action-setup@v2.2.4 - name: Set node version to 16 uses: actions/setup-node@v3 diff --git a/.github/workflows/ecosystem-ci-trigger.yml b/.github/workflows/ecosystem-ci-trigger.yml new file mode 100644 index 00000000000000..bc87ef57f5ff8b --- /dev/null +++ b/.github/workflows/ecosystem-ci-trigger.yml @@ -0,0 +1,95 @@ +name: ecosystem-ci trigger + +on: + issue_comment: + types: [created] + +jobs: + trigger: + runs-on: ubuntu-latest + if: github.event.issue.pull_request && startsWith(github.event.comment.body, '/ecosystem-ci run') + steps: + - uses: actions/github-script@v6 + with: + script: | + const user = context.payload.sender.login + console.log(`Validate user: ${user}`) + + const allowedUsers = new Set([ + 'yyx990803', + 'patak-dev', + 'antfu', + 'sodatea', + 'Shinigami92', + 'aleclarson', + 'bluwy', + 'poyoho', + 'sapphi-red', + 'ygj6', + 'Niputi' + ]) + + if (allowedUsers.has(user)) { + 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') + } + - uses: actions/github-script@v6 + id: get-pr-data + with: + script: | + console.log(`Get PR info: ${context.repo.owner}/${context.repo.repo}#${context.issue.number}`) + const { data: pr } = await github.rest.pulls.get({ + owner: context.repo.owner, + repo: context.repo.repo, + pull_number: context.issue.number + }) + return { + num: context.issue.number, + branchName: pr.head.ref, + repo: pr.head.repo.full_name + } + - id: generate-token + uses: tibdex/github-app-token@v1 + with: + app_id: ${{ secrets.ECOSYSTEM_CI_GITHUB_APP_ID }} + private_key: ${{ secrets.ECOSYSTEM_CI_GITHUB_APP_PRIVATE_KEY }} + repository: "${{ github.repository_owner }}/vite-ecosystem-ci" + - uses: actions/github-script@v6 + id: trigger + env: + COMMENT: ${{ github.event.comment.body }} + with: + github-token: ${{ steps.generate-token.outputs.token }} + result-encoding: string + script: | + const comment = process.env.COMMENT.trim() + const prData = ${{ steps.get-pr-data.outputs.result }} + + const suite = comment.replace(/^\/ecosystem-ci run/, '').trim() + + await github.rest.actions.createWorkflowDispatch({ + owner: context.repo.owner, + repo: 'vite-ecosystem-ci', + workflow_id: 'ecosystem-ci-from-pr.yml', + ref: 'main', + inputs: { + prNumber: '' + prData.num, + branchName: prData.branchName, + repo: prData.repo, + suite: suite === '' ? '-' : suite + } + }) diff --git a/.github/workflows/publish.yml b/.github/workflows/publish.yml index 5b26ca1a4697ce..d167d77f6442a8 100644 --- a/.github/workflows/publish.yml +++ b/.github/workflows/publish.yml @@ -18,7 +18,7 @@ jobs: uses: actions/checkout@v3 - name: Install pnpm - uses: pnpm/action-setup@v2.2.2 + uses: pnpm/action-setup@v2.2.4 - name: Set node version to 16.x uses: actions/setup-node@v3 diff --git a/.github/workflows/semantic-pull-request.yml b/.github/workflows/semantic-pull-request.yml index df554b38e75246..5c7e8ec4d6e58d 100644 --- a/.github/workflows/semantic-pull-request.yml +++ b/.github/workflows/semantic-pull-request.yml @@ -13,6 +13,6 @@ jobs: name: Semantic Pull Request steps: - name: Validate PR title - uses: amannn/action-semantic-pull-request@v4 + uses: amannn/action-semantic-pull-request@v5 env: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} diff --git a/.npmrc b/.npmrc index 6d64ed708da7d4..4d97f291fd1e49 100644 --- a/.npmrc +++ b/.npmrc @@ -6,3 +6,5 @@ hoist-pattern[]=pug hoist-pattern[]=source-map-support hoist-pattern[]=ts-node strict-peer-dependencies=false +shell-emulator=true +auto-install-peers=false diff --git a/.prettierignore b/.prettierignore index 0d5487354a8c98..b1ea458b9bb9d8 100644 --- a/.prettierignore +++ b/.prettierignore @@ -7,4 +7,5 @@ pnpm-lock.yaml 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 diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 350f36b0f25351..82d291f6ad1d31 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -170,9 +170,9 @@ You can set the `DEBUG` environment variable to turn on debugging logs (e.g. `DE - Make sure tests pass! -- Commit messages must follow the [commit message convention](./.github/commit-convention.md) so that changelogs can be automatically generated. Commit messages are automatically validated before commit (by invoking [Git Hooks](https://git-scm.com/docs/githooks) via [yorkie](https://github.com/yyx990803/yorkie)). +- Commit messages must follow the [commit message convention](./.github/commit-convention.md) so that changelogs can be automatically generated. Commit messages are automatically validated before commit (by invoking [Git Hooks](https://git-scm.com/docs/githooks) via [simple-git-hooks](https://github.com/toplenboren/simple-git-hooks)). -- No need to worry about code style as long as you have installed the dev dependencies. Modified files are automatically formatted with Prettier on commit (by invoking [Git Hooks](https://git-scm.com/docs/githooks) via [yorkie](https://github.com/yyx990803/yorkie)). +- No need to worry about code style as long as you have installed the dev dependencies. Modified files are automatically formatted with Prettier on commit (by invoking [Git Hooks](https://git-scm.com/docs/githooks) via [simple-git-hooks](https://github.com/toplenboren/simple-git-hooks)). ## Maintenance Guidelines @@ -218,10 +218,12 @@ Avoid deps with large transitive dependencies that result in bloated size compar Vite aims to be fully usable as a dependency in a TypeScript project (e.g. it should provide proper typings for VitePress), and also in `vite.config.ts`. This means technically a dependency whose types are exposed needs to be part of `dependencies` instead of `devDependencies`. However, this also means we won't be able to bundle it. -To get around this, we inline some of these dependencies' types in `packages/vite/types`. This way, we can still expose the typing but bundle the dependency's source code. +To get around this, we inline some of these dependencies' types in `packages/vite/src/types`. This way, we can still expose the typing but bundle the dependency's source code. Use `pnpm run check-dist-types` to check that the bundled types do not rely on types in `devDependencies`. If you are adding `dependencies`, make sure to configure `tsconfig.check.json`. +For types shared between client and node, they should be added into `packages/vite/types`. These types are not bundled and are published as is (though they are still considered internal). Dependency types within this directory (e.g. `packages/vite/types/chokidar.d.ts`) are deprecated and should be added to `packages/vite/src/types` instead. + ### Think Before Adding Yet Another Option We already have many config options, and we should avoid fixing an issue by adding yet another one. Before adding an option, consider whether the problem: diff --git a/docs/.vitepress/config.ts b/docs/.vitepress/config.ts index e61c9cc00d6262..678ca2790841d1 100644 --- a/docs/.vitepress/config.ts +++ b/docs/.vitepress/config.ts @@ -65,7 +65,17 @@ export default defineConfig({ ['meta', { property: 'og:url', content: ogUrl }], ['meta', { property: 'og:description', content: ogDescription }], ['meta', { name: 'twitter:card', content: 'summary_large_image' }], - ['meta', { name: 'twitter:site', content: '@vite_js' }] + ['meta', { name: 'twitter:site', content: '@vite_js' }], + ['meta', { name: 'theme-color', content: '#646cff' }], + [ + 'script', + { + src: 'https://cdn.usefathom.com/script.js', + 'data-site': 'CBDFBSLI', + 'data-spa': 'auto', + defer: '' + } + ] ], vue: { diff --git a/docs/blog/announcing-vite3.md b/docs/blog/announcing-vite3.md index 4a1ff3144e26ad..7f727f7307d767 100644 --- a/docs/blog/announcing-vite3.md +++ b/docs/blog/announcing-vite3.md @@ -254,7 +254,7 @@ We want to thank everyone that have implemented features, and fixes, given feedb - Vite team members [@youyuxi](https://twitter.com/youyuxi), [@patak_dev](https://twitter.com/patak_dev), [@antfu7](https://twitter.com/antfu7), [@bluwyoo](https://twitter.com/bluwyoo), [@sapphi_red](https://twitter.com/sapphi_red), [@haoqunjiang](https://twitter.com/haoqunjiang), [@poyoho](https://github.com/poyoho), [@Shini_92](https://twitter.com/Shini_92), and [@retropragma](https://twitter.com/retropragma). - [@benmccann](https://github.com/benmccann), [@danielcroe](https://twitter.com/danielcroe), [@brillout](https://twitter.com/brillout), [@sheremet_va](https://twitter.com/sheremet_va), [@userquin](https://twitter.com/userquin), [@enzoinnocenzi](https://twitter.com/enzoinnocenzi), [@maximomussini](https://twitter.com/maximomussini), [@IanVanSchooten](https://twitter.com/IanVanSchooten), the [Astro team](https://astro.build/), and all other maintainers of frameworks and plugins in the ecosystem in that helped shape v3. - [@dominikg](https://github.com/dominikg) for his work on vite-ecosystem-ci. -- [@ZoltanKochan](https://twitter.com/ZoltanKochan) for his work on [pnpm](https://pnpm.io/), and for his responsivness when we needed support with it. +- [@ZoltanKochan](https://twitter.com/ZoltanKochan) for his work on [pnpm](https://pnpm.io/), and for his responsiveness when we needed support with it. - [@rixo](https://github.com/rixo) for HMR Partial Accept support. - [@KiaKing85](https://twitter.com/KiaKing85) for getting the theme ready for the Vite 3 release, and [@\_brc_dd](https://twitter.com/_brc_dd) for working on the VitePress internals. - [@CodingWithCego](https://twitter.com/CodingWithCego) for the new Spanish translation, and [@ShenQingchuan](https://twitter.com/ShenQingchuan), [@hiro-lapis](https://github.com/hiro-lapis) and others in the Chinese and Japanese translations teams for keeping the translated docs up to date. diff --git a/docs/config/build-options.md b/docs/config/build-options.md index 586f84d6b24fd0..2c60c838fa54ee 100644 --- a/docs/config/build-options.md +++ b/docs/config/build-options.md @@ -13,18 +13,16 @@ Another special value is `'esnext'` - which assumes native dynamic imports suppo - If the [`build.minify`](#build-minify) option is `'terser'`, `'esnext'` will be forced down to `'es2021'`. - In other cases, it will perform no transpilation at all. -The transform is performed with esbuild and the value should be a valid [esbuild target option](https://esbuild.github.io/api/#target). Custom targets can either be a ES version (e.g. `es2015`), a browser with version (e.g. `chrome58`), or an array of multiple target strings. +The transform is performed with esbuild and the value should be a valid [esbuild target option](https://esbuild.github.io/api/#target). Custom targets can either be an ES version (e.g. `es2015`), a browser with version (e.g. `chrome58`), or an array of multiple target strings. Note the build will fail if the code contains features that cannot be safely transpiled by esbuild. See [esbuild docs](https://esbuild.github.io/content-types/#javascript) for more details. -## build.polyfillModulePreload +## build.modulePreload -- **Type:** `boolean` +- **Type:** `boolean | { polyfill?: boolean, resolveDependencies?: ResolveModulePreloadDependenciesFn }` - **Default:** `true` -Whether to automatically inject [module preload polyfill](https://guybedford.com/es-module-preloading-integrity#modulepreload-polyfill). - -If set to `true`, the polyfill is auto injected into the proxy module of each `index.html` entry. If the build is configured to use a non-html custom entry via `build.rollupOptions.input`, then it is necessary to manually import the polyfill in your custom entry: +By default, a [module preload polyfill](https://guybedford.com/es-module-preloading-integrity#modulepreload-polyfill) is automatically injected. The polyfill is auto injected into the proxy module of each `index.html` entry. If the build is configured to use a non-HTML custom entry via `build.rollupOptions.input`, then it is necessary to manually import the polyfill in your custom entry: ```js import 'vite/modulepreload-polyfill' @@ -32,6 +30,42 @@ import 'vite/modulepreload-polyfill' Note: the polyfill does **not** apply to [Library Mode](/guide/build#library-mode). If you need to support browsers without native dynamic import, you should probably avoid using it in your library. +The polyfill can be disabled using `{ polyfill: false }`. + +The list of chunks to preload for each dynamic import is computed by Vite. By default, an absolute path including the `base` will be used when loading these dependencies. If the `base` is relative (`''` or `'./'`), `import.meta.url` is used at runtime to avoid absolute paths that depend on the final deployed base. + +There is experimental support for fine grained control over the dependencies list and their paths using the `resolveDependencies` function. It expects a function of type `ResolveModulePreloadDependenciesFn`: + +```ts +type ResolveModulePreloadDependenciesFn = ( + url: string, + deps: string[], + context: { + importer: string + } +) => (string | { runtime?: string })[] +``` + +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 +modulePreload: { + resolveDependencies: (filename, deps, { hostId, hostType }) => { + return deps.filter(condition) + } +} +``` + +The resolved dependency paths can be further modified using [`experimental.renderBuiltUrl`](../guide/build.md#advanced-base-options). + +## build.polyfillModulePreload + +- **Type:** `boolean` +- **Default:** `true` +- **Deprecated** use `build.modulePreload.polyfill` instead + +Whether to automatically inject a [module preload polyfill](https://guybedford.com/es-module-preloading-integrity#modulepreload-polyfill). + ## build.outDir - **Type:** `string` @@ -53,8 +87,10 @@ Specify the directory to nest generated assets under (relative to `build.outDir` 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. +Git LFS placeholders are automatically excluded from inlining because they do not contain the content of the file they represent. + ::: tip Note -If you specify `build.lib`, `build.assetsInlineLimit` will be ignored and assets will always be inlined, regardless of file size. +If you specify `build.lib`, `build.assetsInlineLimit` will be ignored and assets will always be inlined, regardless of file size or being a Git LFS placeholder. ::: ## build.cssCodeSplit @@ -75,7 +111,7 @@ If you specify `build.lib`, `build.cssCodeSplit` will be `false` as default. - **Type:** `string | string[]` - **Default:** the same as [`build.target`](#build-target) -This options allows users to set a different browser target for CSS minification from the one used for JavaScript transpilation. +This option allows users to set a different browser target for CSS minification from the one used for JavaScript transpilation. It should only be used when you are targeting a non-mainstream browser. One example is Android WeChat WebView, which supports most modern JavaScript features but not the [`#RGBA` hexadecimal color notation in CSS](https://developer.mozilla.org/en-US/docs/Web/CSS/color_value#rgb_colors). @@ -109,10 +145,10 @@ Options to pass on to [@rollup/plugin-dynamic-import-vars](https://github.com/ro ## build.lib -- **Type:** `{ entry: string, name?: string, formats?: ('es' | 'cjs' | 'umd' | 'iife')[], fileName?: string | ((format: ModuleFormat) => string) }` +- **Type:** `{ entry: string | string[] | { [entryAlias: string]: string }, name?: string, formats?: ('es' | 'cjs' | 'umd' | 'iife')[], fileName?: string | ((format: ModuleFormat, entryName: string) => string) }` - **Related:** [Library Mode](/guide/build#library-mode) -Build as a library. `entry` is required since the library cannot use HTML as entry. `name` is the exposed global variable and is required when `formats` includes `'umd'` or `'iife'`. Default `formats` are `['es', 'umd']`. `fileName` is the name of the package file output, default `fileName` is the name option of package.json, it can also be defined as function taking the `format` as an argument. +Build as a library. `entry` is required since the library cannot use HTML as entry. `name` is the exposed global variable and is required when `formats` includes `'umd'` or `'iife'`. Default `formats` are `['es', 'umd']`, or `['es', 'cjs']`, if multiple entries are used. `fileName` is the name of the package file output, default `fileName` is the name option of package.json, it can also be defined as function taking the `format` and `entryAlias` as arguments. ## build.manifest @@ -128,7 +164,7 @@ When set to `true`, the build will also generate a `manifest.json` file that con - **Default:** `false` - **Related:** [Server-Side Rendering](/guide/ssr) -When set to `true`, the build will also generate a SSR manifest for determining style links and asset preload directives in production. When the value is a string, it will be used as the manifest file name. +When set to `true`, the build will also generate an SSR manifest for determining style links and asset preload directives in production. When the value is a string, it will be used as the manifest file name. ## build.ssr @@ -145,7 +181,7 @@ Produce SSR-oriented build. The value can be a string to directly specify the SS 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) -Note the `build.minify` option does not minify whitespaces when using the `'es'` format in lib mode, as it removes pure annotations and break tree-shaking. +Note the `build.minify` option does not minify whitespaces when using the `'es'` format in lib mode, as it removes pure annotations and breaks tree-shaking. Terser must be installed when it is set to `'terser'`. @@ -173,6 +209,14 @@ Set to `false` to disable writing the bundle to disk. This is mostly used in [pr By default, Vite will empty the `outDir` on build if it is inside project root. It will emit a warning if `outDir` is outside of root to avoid accidentally removing important files. You can explicitly set this option to suppress the warning. This is also available via command line as `--emptyOutDir`. +## build.copyPublicDir + +- **Experimental** +- **Type:** `boolean` +- **Default:** `true` + +By default, Vite will copy files from the `publicDir` into the `outDir` on build. Set to `false` to disable this. + ## build.reportCompressedSize - **Type:** `boolean` @@ -193,3 +237,10 @@ Limit for chunk size warnings (in kbs). - **Default:** `null` Set to `{}` to enable rollup watcher. This is mostly used in cases that involve build-only plugins or integrations processes. + +::: warning Using Vite on Windows Subsystem for Linux (WSL) 2 + +There are cases that file system watching does not work with WSL2. +See [`server.watch`](./server-options.md#server-watch) for more details. + +::: diff --git a/docs/config/dep-optimization-options.md b/docs/config/dep-optimization-options.md index 413452d2a23d71..729b6df7cac3f9 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 needs 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 `!`. ## optimizeDeps.exclude diff --git a/docs/config/index.md b/docs/config/index.md index 48254361328e1e..965d5bdf60133b 100644 --- a/docs/config/index.md +++ b/docs/config/index.md @@ -86,7 +86,7 @@ export default defineConfig(async ({ command, mode }) => { 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 affects the loading behaviour. However, you can use the exported `loadEnv` helper to load the specific `.env` file if needed. +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 import { defineConfig, loadEnv } from 'vite' diff --git a/docs/config/preview-options.md b/docs/config/preview-options.md index 10ec82f38e3848..3604afb9576d42 100644 --- a/docs/config/preview-options.md +++ b/docs/config/preview-options.md @@ -65,7 +65,7 @@ Automatically open the app in the browser on server start. When the value is a s - **Type:** `Record` - **Default:** [`server.proxy`](./server-options#server-proxy) -Configure custom proxy rules for the dev server. Expects an object of `{ key: options }` pairs. If the key starts with `^`, it will be interpreted as a `RegExp`. The `configure` option can be used to access the proxy instance. +Configure custom proxy rules for the preview server. Expects an object of `{ key: options }` pairs. If the key starts with `^`, it will be interpreted as a `RegExp`. The `configure` option can be used to access the proxy instance. Uses [`http-proxy`](https://github.com/http-party/node-http-proxy). Full options [here](https://github.com/http-party/node-http-proxy#options). @@ -74,4 +74,10 @@ Uses [`http-proxy`](https://github.com/http-party/node-http-proxy). Full options - **Type:** `boolean | CorsOptions` - **Default:** [`server.cors`](./server-options#server-cors) -Configure CORS for the dev server. This is enabled by default and allows any origin. Pass an [options object](https://github.com/expressjs/cors) to fine tune the behavior or `false` to disable. +Configure CORS for the preview server. This is enabled by default and allows any origin. Pass an [options object](https://github.com/expressjs/cors) to fine tune the behavior or `false` to disable. + +## preview.headers + +- **Type:** `OutgoingHttpHeaders` + +Specify server response headers. diff --git a/docs/config/server-options.md b/docs/config/server-options.md index 29a3582665c7a6..7afd8e19d25486 100644 --- a/docs/config/server-options.md +++ b/docs/config/server-options.md @@ -14,7 +14,7 @@ This can be set via the CLI using `--host 0.0.0.0` or `--host`. There are cases when other servers might respond instead of Vite. -The first case is when `localhost` is used. Node.js under v17 reorders the result of DNS-resolved address by default. When accessing `localhost`, browsers use DNS to resolve the address and that address might differ from the address which Vite is listening. Vite prints the resolved address when it differs. +The first case is when `localhost` is used. Node.js under v17 reorders the result of DNS-resolved addresses by default. When accessing `localhost`, browsers use DNS to resolve the address and that address might differ from the address which Vite is listening to. Vite prints the resolved address when it differs. 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`. @@ -30,7 +30,7 @@ export default defineConfig({ }) ``` -The second case is when wildcard hosts (e.g. `0.0.0.0`) is used. This is because servers listening on non-wildcard hosts take priority over those listening on wildcard hosts. +The second case is when wildcard hosts (e.g. `0.0.0.0`) are used. This is because servers listening on non-wildcard hosts take priority over those listening on wildcard hosts. ::: @@ -149,7 +149,7 @@ Check out [`vite-setup-catalogue`](https://github.com/sapphi-red/vite-setup-cata ::: tip NOTE -With the default configuration, reverse proxies in front of Vite are expected to support proxying WebSocket. If the Vite HMR client fails to connect WebSocket, the client will fallback to connecting the WebSocket directly to the Vite HMR server bypassing the reverse proxies: +With the default configuration, reverse proxies in front of Vite are expected to support proxying WebSocket. If the Vite HMR client fails to connect WebSocket, the client will fall back to connecting the WebSocket directly to the Vite HMR server bypassing the reverse proxies: ``` Direct websocket connection fallback. Check out https://vitejs.dev/config/server-options.html#server-hmr to remove the previous connection error. @@ -169,8 +169,6 @@ The error that appears in the Browser when the fallback happens can be ignored. File system watcher options to pass on to [chokidar](https://github.com/paulmillr/chokidar#api). -When running Vite on Windows Subsystem for Linux (WSL) 2, if the project folder resides in a Windows filesystem, you'll need to set this option to `{ usePolling: true }`. This is due to [a WSL2 limitation](https://github.com/microsoft/WSL/issues/4739) with the Windows filesystem. - The Vite server watcher skips `.git/` and `node_modules/` directories by default. If you want to watch a package inside `node_modules/`, you can pass a negated glob pattern to `server.watch.ignored`. That is: ```js @@ -188,6 +186,19 @@ export default defineConfig({ }) ``` +::: warning Using Vite on Windows Subsystem for Linux (WSL) 2 + +When running Vite on WSL2, file system watching does not work when a file is edited by Windows applications (non-WSL2 process). This is due to [a WSL2 limitation](https://github.com/microsoft/WSL/issues/4739). This also applies to running on Docker with a WSL2 backend. + +To fix it, you could either: + +- **Recommended**: Use WSL2 applications to edit your files. + - It is also recommended to move the project folder outside of a Windows filesystem. Accessing Windows filesystem from WSL2 is slow. Removing that overhead will improve performance. +- Set `{ usePolling: true }`. + - Note that [`usePolling` leads to high CPU utilization](https://github.com/paulmillr/chokidar#performance). + +::: + ## server.middlewareMode - **Type:** `boolean` @@ -244,7 +255,7 @@ Restrict serving files outside of workspace root. Restrict files that could be served via `/@fs/`. When `server.fs.strict` is set to `true`, accessing files outside this directory list that aren't imported from an allowed file will result in a 403. -Vite will search for the root of the potential workspace and use it as default. A valid workspace met the following conditions, otherwise will fallback to the [project root](/guide/#index-html-and-project-root). +Vite will search for the root of the potential workspace and use it as default. A valid workspace met the following conditions, otherwise will fall back to the [project root](/guide/#index-html-and-project-root). - contains `workspaces` field in `package.json` - contains one of the following file @@ -286,10 +297,9 @@ export default defineConfig({ ## server.fs.deny - **Type:** `string[]` +- **Default:** `['.env', '.env.*', '*.{pem,crt}']` -Blocklist for sensitive files being restricted to be served by Vite dev server. - -Default to `['.env', '.env.*', '*.{pem,crt}']`. +Blocklist for sensitive files being restricted to be served by Vite dev server. This will have higher priority than [`server.fs.allow`](#server-fs-allow). [picomatch patterns](https://github.com/micromatch/picomatch#globbing-features) are supported. ## server.origin diff --git a/docs/config/shared-options.md b/docs/config/shared-options.md index 8f7d151499e906..0ebddfd988bb04 100644 --- a/docs/config/shared-options.md +++ b/docs/config/shared-options.md @@ -158,6 +158,16 @@ Export keys ending with "/" is deprecated by Node and may not work well. Please List of fields in `package.json` to try when resolving a package's entry point. Note this takes lower precedence than conditional exports resolved from the `exports` field: if an entry point is successfully resolved from `exports`, the main field will be ignored. +## resolve.browserField + +- **Type:** `boolean` +- **Default:** `true` +- **Deprecated** + +Whether to enable resolving to `browser` field. + +In future, `resolve.mainFields`'s default value will be `['browser', 'module', 'jsnext:main', 'jsnext']` and this option will be removed. + ## resolve.extensions - **Type:** `string[]` @@ -336,10 +346,10 @@ See [here](/guide/env-and-mode#env-files) for more about environment files. - **Type:** `string | string[]` - **Default:** `VITE_` -Env variables starts with `envPrefix` will be exposed to your client source code via import.meta.env. +Env variables starting with `envPrefix` will be exposed to your client source code via import.meta.env. :::warning SECURITY NOTES -`envPrefix` should not be set as `''`, which will expose all your env variables and cause unexpected leaking of of sensitive information. Vite will throw error when detecting `''`. +`envPrefix` should not be set as `''`, which will expose all your env variables and cause unexpected leaking of sensitive information. Vite will throw an error when detecting `''`. ::: ## appType diff --git a/docs/config/ssr-options.md b/docs/config/ssr-options.md index a9a2fa9fdd8a4d..886b5f4bb4fca3 100644 --- a/docs/config/ssr-options.md +++ b/docs/config/ssr-options.md @@ -27,4 +27,4 @@ Build target for the SSR server. - **Type:** `'esm' | 'cjs'` - **Default:** `esm` -Build format for the SSR server. Since Vite v3 the SSR build generates ESM by default. `'cjs'` can be selected to generate a CJS build, but it isn't recommended. The option is left marked as experimental to give users more time to update to ESM. CJS builds requires complex externalization heuristics that aren't present in the ESM format. +Build format for the SSR server. Since Vite v3 the SSR build generates ESM by default. `'cjs'` can be selected to generate a CJS build, but it isn't recommended. The option is left marked as experimental to give users more time to update to ESM. CJS builds require complex externalization heuristics that aren't present in the ESM format. diff --git a/docs/guide/api-hmr.md b/docs/guide/api-hmr.md index 3b416169f5231a..7733c8e91ba477 100644 --- a/docs/guide/api-hmr.md +++ b/docs/guide/api-hmr.md @@ -125,7 +125,18 @@ Calling `import.meta.hot.decline()` indicates this module is not hot-updatable, ## `hot.invalidate()` -For now, calling `import.meta.hot.invalidate()` simply reloads the page. +A self-accepting module may realize during runtime that it can't handle a HMR update, and so the update needs to be forcefully propagated to importers. By calling `import.meta.hot.invalidate()`, the HMR server will invalidate the importers of the caller, as if the caller wasn't self-accepting. + +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 +import.meta.hot.accept((module) => { + // You may use the new module instance to decide whether to invalidate. + if (cannotHandleUpdate(module)) { + import.meta.hot.invalidate() + } +}) +``` ## `hot.on(event, cb)` @@ -136,6 +147,7 @@ The following HMR events are dispatched by Vite automatically: - `'vite:beforeUpdate'` when an update is about to be applied (e.g. a module will be replaced) - `'vite:beforeFullReload'` when a full reload is about to occur - `'vite:beforePrune'` when modules that are no longer needed are about to be pruned +- `'vite:invalidate'` when a module is invalidated with `import.meta.hot.invalidate()` - `'vite:error'` when an error occurs (e.g. syntax error) Custom HMR events can also be sent from plugins. See [handleHotUpdate](./api-plugin#handlehotupdate) for more details. diff --git a/docs/guide/api-javascript.md b/docs/guide/api-javascript.md index 205d32a6070cda..199507bc85b29e 100644 --- a/docs/guide/api-javascript.md +++ b/docs/guide/api-javascript.md @@ -91,6 +91,11 @@ interface ViteDevServer { * and hmr state. */ moduleGraph: ModuleGraph + /** + * The resolved urls Vite prints on the CLI. null in middleware mode or + * before `server.listen` is called. + */ + resolvedUrls: ResolvedServerUrls | null /** * Programmatically resolve, load and transform a URL and get the result * without going through the http request pipeline. @@ -114,6 +119,11 @@ interface ViteDevServer { * Fix ssr error stacktrace. */ ssrFixStacktrace(e: Error): void + /** + * Triggers HMR for a module in the module graph. You can use the `server.moduleGraph` + * API to retrieve the module to be reloaded. If `hmr` is false, this is a no-op. + */ + reloadModule(module: ModuleNode): Promise /** * Start the server. */ @@ -250,7 +260,7 @@ function loadEnv( **Related:** [`.env` Files](./env-and-mode.md#env-files) -Load `.env` files within the `envDir`. By default only env variables prefixed with `VITE_` are loaded, unless `prefixes` is changed. +Load `.env` files within the `envDir`. By default, only env variables prefixed with `VITE_` are loaded, unless `prefixes` is changed. ## `normalizePath` @@ -277,7 +287,7 @@ async function transformWithEsbuild( ): Promise ``` -Transform JavaScript or TypeScript with esbuild. Useful for plugins that prefers matching Vite's internal esbuild transform. +Transform JavaScript or TypeScript with esbuild. Useful for plugins that prefer matching Vite's internal esbuild transform. ## `loadConfigFromFile` diff --git a/docs/guide/api-plugin.md b/docs/guide/api-plugin.md index 89c71cfdeecee7..c18c6daca3263f 100644 --- a/docs/guide/api-plugin.md +++ b/docs/guide/api-plugin.md @@ -54,7 +54,7 @@ export default defineConfig({ Falsy plugins will be ignored, which can be used to easily activate or deactivate plugins. -`plugins` also accept presets including several plugins as a single element. This is useful for complex features (like framework integration) that are implemented using several plugins. The array will be flattened internally. +`plugins` also accepts presets including several plugins as a single element. This is useful for complex features (like framework integration) that are implemented using several plugins. The array will be flattened internally. ```js // framework-plugin @@ -214,7 +214,7 @@ Vite plugins can also provide hooks that serve Vite-specific purposes. These hoo - **Type:** `(config: ResolvedConfig) => void | Promise` - **Kind:** `async`, `parallel` - Called after the Vite config is resolved. Use this hook to read and store the final resolved config. It is also useful when the plugin needs to do something different based the command is being run. + Called after the Vite config is resolved. Use this hook to read and store the final resolved config. It is also useful when the plugin needs to do something different based on the command being run. **Example:** diff --git a/docs/guide/assets.md b/docs/guide/assets.md index 4ba6e417bd3d40..c0dba49a604f80 100644 --- a/docs/guide/assets.md +++ b/docs/guide/assets.md @@ -26,9 +26,11 @@ The behavior is similar to webpack's `file-loader`. The difference is that the i - Assets smaller in bytes than the [`assetsInlineLimit` option](/config/build-options.md#build-assetsinlinelimit) will be inlined as base64 data URLs. +- Git LFS placeholders are automatically excluded from inlining because they do not contain the content of the file they represent. To get inlining, make sure to download the file contents via Git LFS before building. + ### Explicit URL Imports -Assets that are not included in the internal list or in `assetsInclude`, can be explicitly imported as an URL using the `?url` suffix. This is useful, for example, to import [Houdini Paint Worklets](https://houdini.how/usage). +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 import workletURL from 'extra-scalloped-border/worklet.js?url' diff --git a/docs/guide/backend-integration.md b/docs/guide/backend-integration.md index 92bb4d084d23e6..535ccbb3e6127b 100644 --- a/docs/guide/backend-integration.md +++ b/docs/guide/backend-integration.md @@ -83,7 +83,7 @@ If you need a custom integration, you can follow the steps in this guide to conf - The manifest has a `Record` structure - For entry or dynamic entry chunks, the key is the relative src path from project root. - 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 maps to the corresponding chunk in the manifest), and also its corresponding CSS and asset files (if any). + - 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): diff --git a/docs/guide/build.md b/docs/guide/build.md index 72a4562a25ce2e..41376b46e5f2f8 100644 --- a/docs/guide/build.md +++ b/docs/guide/build.md @@ -128,6 +128,7 @@ import { defineConfig } from 'vite' export default defineConfig({ build: { lib: { + // Could also be a dictionary or array of multiple entry points entry: resolve(__dirname, 'lib/main.js'), name: 'MyLib', // the proper extensions will be added @@ -185,6 +186,28 @@ Recommended `package.json` for your lib: } ``` +Or, if exposing multiple entry points: + +```json +{ + "name": "my-lib", + "type": "module", + "files": ["dist"], + "main": "./dist/my-lib.cjs", + "module": "./dist/my-lib.mjs", + "exports": { + ".": { + "import": "./dist/my-lib.mjs", + "require": "./dist/my-lib.cjs" + }, + "./secondary": { + "import": "./dist/secondary.mjs", + "require": "./dist/secondary.cjs" + } + } +} +``` + ::: tip Note If the `package.json` does not contain `"type": "module"`, Vite will generate different file extensions for Node.js compatibility. `.js` will become `.mjs` and `.cjs` will become `.js`. ::: @@ -208,9 +231,9 @@ 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`. -```js +```ts experimental: { - renderBuiltUrl: (filename: string, { hostType: 'js' | 'css' | 'html' }) => { + renderBuiltUrl(filename: string, { hostType }: { hostType: 'js' | 'css' | 'html' }) { if (hostType === 'js') { return { runtime: `window.__toCdnUrl(${JSON.stringify(filename)})` } } else { @@ -220,15 +243,15 @@ experimental: { } ``` -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 third `context` param given to the function. +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. -```js +```ts experimental: { - renderBuiltUrl(filename: string, { hostType: 'js' | 'css' | 'html', type: 'public' | 'asset' }) { + renderBuiltUrl(filename: string, { hostId, hostType, type }: { hostId: string, hostType: 'js' | 'css' | 'html', type: 'public' | 'asset' }) { if (type === 'public') { return 'https://www.domain.com/' + filename } - else if (path.extname(importer) === '.js') { + else if (path.extname(hostId) === '.js') { return { runtime: `window.__assetsPath(${JSON.stringify(filename)})` } } else { diff --git a/docs/guide/dep-pre-bundling.md b/docs/guide/dep-pre-bundling.md index 562423188e5451..4ea53feac36cf6 100644 --- a/docs/guide/dep-pre-bundling.md +++ b/docs/guide/dep-pre-bundling.md @@ -60,7 +60,7 @@ export default defineConfig({ When making changes to the linked dep, restart the dev server with the `--force` command line option for the changes to take effect. ::: warning Deduping -Due to differences in linked dependency resolution, transitive dependencies can deduplicated incorrectly, causing issues when used in runtime. If you stumble on this issue, use `npm pack` on the linked dependency to fix it. +Due to differences in linked dependency resolution, transitive dependencies can deduplicate incorrectly, causing issues when used in runtime. If you stumble on this issue, use `npm pack` on the linked dependency to fix it. ::: ## Customizing the Behavior diff --git a/docs/guide/env-and-mode.md b/docs/guide/env-and-mode.md index c91a53dc6f1a2b..c69badcda5db2e 100644 --- a/docs/guide/env-and-mode.md +++ b/docs/guide/env-and-mode.md @@ -46,7 +46,7 @@ In addition, environment variables that already exist when Vite is executed have Loaded env variables are also exposed to your client source code via `import.meta.env` as strings. -To prevent accidentally leaking env variables to the client, only variables prefixed with `VITE_` are exposed to your Vite-processed code. e.g. the following file: +To prevent accidentally leaking env variables to the client, only variables prefixed with `VITE_` are exposed to your Vite-processed code. e.g. for the following env variables: ``` VITE_SOME_KEY=123 @@ -60,7 +60,7 @@ console.log(import.meta.env.VITE_SOME_KEY) // 123 console.log(import.meta.env.DB_PASSWORD) // undefined ``` -If you want to customize env variables prefix, see [envPrefix](/config/shared-options.html#envprefix) option. +If you want to customize the env variables prefix, see the [envPrefix](/config/shared-options.html#envprefix) option. :::warning SECURITY NOTES @@ -71,9 +71,9 @@ If you want to customize env variables prefix, see [envPrefix](/config/shared-op ### IntelliSense for TypeScript -By default, Vite provides type definition 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 which prefixed with `VITE_`. +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, you can create an `env.d.ts` in `src` directory, then augment `ImportMetaEnv` like this: +To achieve this, you can create an `env.d.ts` in `src` directory, then augment `ImportMetaEnv` like this: ```typescript /// @@ -98,7 +98,7 @@ If your code relies on types from browser environments such as [DOM](https://git ## Modes -By default, the dev server (`dev` command) runs in `development` mode and the `build` command run in `production` mode. +By default, the dev server (`dev` command) runs in `development` mode and the `build` command runs in `production` mode. This means when running `vite build`, it will load the env variables from `.env.production` if there is one: diff --git a/docs/guide/features.md b/docs/guide/features.md index db1f45b4ddeb99..0f69035a71dea5 100644 --- a/docs/guide/features.md +++ b/docs/guide/features.md @@ -102,14 +102,28 @@ This will provide the following type shims: - Types for the Vite-injected [env variables](./env-and-mode#env-variables) on `import.meta.env` - Types for the [HMR API](./api-hmr) on `import.meta.hot` +::: tip +To override the default typing, declare it before the triple-slash reference. For example, to make the default import of `*.svg` a React component: + +```ts +declare module '*.svg' { + const content: React.FC> + export default content +} + +/// +``` + +::: + ## Vue Vite provides first-class Vue support: - Vue 3 SFC support via [@vitejs/plugin-vue](https://github.com/vitejs/vite/tree/main/packages/plugin-vue) - Vue 3 JSX support via [@vitejs/plugin-vue-jsx](https://github.com/vitejs/vite/tree/main/packages/plugin-vue-jsx) -- Vue 2.7 support via [vitejs/vite-plugin-vue2](https://github.com/vitejs/vite-plugin-vue2) -- Vue <2.7 support via [underfin/vite-plugin-vue2](https://github.com/underfin/vite-plugin-vue2) +- Vue 2.7 support via [@vitejs/plugin-vue2](https://github.com/vitejs/vite-plugin-vue2) +- Vue <2.7 support via [vite-plugin-vue2](https://github.com/underfin/vite-plugin-vue2) ## JSX diff --git a/docs/guide/migration.md b/docs/guide/migration.md index 2b6ae6949c9b19..c47207686af609 100644 --- a/docs/guide/migration.md +++ b/docs/guide/migration.md @@ -113,7 +113,7 @@ can be removed by passing `build.commonjsOptions: { include: [] }` ## Advanced -There are some changes which only affects plugin/tool creators. +There are some changes which only affect plugin/tool creators. - [[#5868] refactor: remove deprecated api for 3.0](https://github.com/vitejs/vite/pull/5868) - `printHttpServerUrls` is removed diff --git a/docs/guide/ssr.md b/docs/guide/ssr.md index 3ff4337aaf4b9f..dc7d976382c207 100644 --- a/docs/guide/ssr.md +++ b/docs/guide/ssr.md @@ -177,7 +177,7 @@ Then, in `server.js` we need to add some production specific logic by checking ` - Move the creation and all usage of the `vite` dev server behind dev-only conditional branches, then add static file serving middlewares to serve files from `dist/client`. -Refer to the [Vue](https://github.com/vitejs/vite/tree/main/playground/ssr-vue) and [React](https://github.com/vitejs/vite/tree/main/playground/ssr-react) demos for working setup. +Refer to the [Vue](https://github.com/vitejs/vite/tree/main/playground/ssr-vue) and [React](https://github.com/vitejs/vite/tree/main/playground/ssr-react) demos for a working setup. ## Generating Preload Directives @@ -201,7 +201,7 @@ const html = await vueServerRenderer.renderToString(app, ctx) // ctx.modules is now a Set of module IDs that were used during the render ``` -In the production branch of `server.js` we need to read and pass the manifest to the `render` function exported by `src/entry-server.js`. This would provide us with enough information to render preload directives for files used by async routes! See [demo source](https://github.com/vitejs/vite/blob/main/playground/ssr-vue/src/entry-server.js) for full example. +In the production branch of `server.js` we need to read and pass the manifest to the `render` function exported by `src/entry-server.js`. This would provide us with enough information to render preload directives for files used by async routes! See [demo source](https://github.com/vitejs/vite/blob/main/playground/ssr-vue/src/entry-server.js) for a full example. ## Pre-Rendering / SSG @@ -211,15 +211,17 @@ If the routes and the data needed for certain routes are known ahead of time, we Dependencies are "externalized" from Vite's SSR transform module system by default when running SSR. This speeds up both dev and build. -If a dependency needs to be transformed by Vite's pipeline, for example, because Vite features are used untranspiled in them, they can be added to [`ssr.noExternal`](../config/ssr-options.md#ssrnoexternal). +If a dependency needs to be transformed by Vite's pipeline, for example, because Vite features are used untranspiled in them, they can be added to [`ssr.noExternal`](../config/ssr-options.md#ssr-noexternal). + +For linked dependencies, they are not externalized by default to take advantage of Vite's HMR. If this isn't desired, for example, to test dependencies as if they aren't linked, you can add it to [`ssr.external`](../config/ssr-options.md#ssr-external). :::warning Working with Aliases -If you have configured aliases that redirects one package to another, you may want to alias the actual `node_modules` packages instead to make it work for SSR externalized dependencies. Both [Yarn](https://classic.yarnpkg.com/en/docs/cli/add/#toc-yarn-add-alias) and [pnpm](https://pnpm.js.org/en/aliases) support aliasing via the `npm:` prefix. +If you have configured aliases that redirect one package to another, you may want to alias the actual `node_modules` packages instead to make it work for SSR externalized dependencies. Both [Yarn](https://classic.yarnpkg.com/en/docs/cli/add/#toc-yarn-add-alias) and [pnpm](https://pnpm.js.org/en/aliases) support aliasing via the `npm:` prefix. ::: ## SSR-specific Plugin Logic -Some frameworks such as Vue or Svelte compiles components into different formats based on client vs. SSR. To support conditional transforms, Vite passes an additional `ssr` property in the `options` object of the following plugin hooks: +Some frameworks such as Vue or Svelte compile components into different formats based on client vs. SSR. To support conditional transforms, Vite passes an additional `ssr` property in the `options` object of the following plugin hooks: - `resolveId` - `load` @@ -267,4 +269,4 @@ Use a post hook so that your SSR middleware runs _after_ Vite's middlewares. ## SSR Format -By default, Vite generates the SSR bundle in ESM. There is experimental support for configuring `ssr.format`, but it isn't recommended. Future efforts around SSR development will be based on ESM, and commonjs remain available for backward compatibility. If using ESM for SSR isn't possible in your project, you can set `legacy.buildSsrCjsExternalHeuristics: true` to generate a CJS bundle using the same [externalization heuristics of Vite v2](https://v2.vitejs.dev/guide/ssr.html#ssr-externals). +By default, Vite generates the SSR bundle in ESM. There is experimental support for configuring `ssr.format`, but it isn't recommended. Future efforts around SSR development will be based on ESM, and CommonJS remains available for backward compatibility. If using ESM for SSR isn't possible in your project, you can set `legacy.buildSsrCjsExternalHeuristics: true` to generate a CJS bundle using the same [externalization heuristics of Vite v2](https://v2.vitejs.dev/guide/ssr.html#ssr-externals). diff --git a/docs/guide/static-deploy.md b/docs/guide/static-deploy.md index 534e77639c5f29..5c2273b36888b5 100644 --- a/docs/guide/static-deploy.md +++ b/docs/guide/static-deploy.md @@ -40,9 +40,9 @@ $ npm run build $ npm run preview ``` -The `vite preview` command will boot up local static web server that serves the files from `dist` at `http://localhost:4173`. It's an easy way to check if the production build looks OK in your local environment. +The `vite preview` command will boot up a local static web server that serves the files from `dist` at `http://localhost:4173`. It's an easy way to check if the production build looks OK in your local environment. -You may configure the port of the server by passing `--port` flag as an argument. +You may configure the port of the server by passing the `--port` flag as an argument. ```json { @@ -64,7 +64,7 @@ Now the `preview` command will launch the server at `http://localhost:8080`. 2. Inside your project, create `deploy.sh` with the following content (with highlighted lines uncommented appropriately), and run it to deploy: - ```bash{13,21,24} + ```bash{16,24,27} #!/usr/bin/env sh # abort on errors @@ -76,11 +76,14 @@ Now the `preview` command will launch the server at `http://localhost:8080`. # navigate into the build output directory cd dist + # place .nojekyll to bypass Jekyll processing + echo > .nojekyll + # if you are deploying to a custom domain # echo 'www.example.com' > CNAME git init - git checkout -b main + git checkout -B main git add -A git commit -m 'deploy' @@ -131,6 +134,8 @@ You can also run the above script in your CI setup to enable automatic deploymen ## Netlify +### Netlify CLI + 1. Install the [Netlify CLI](https://cli.netlify.com/). 2. Create a new site using `ntl init`. 3. Deploy using `ntl deploy`. @@ -153,6 +158,16 @@ The Netlify CLI will share with you a preview URL to inspect. When you are ready $ ntl deploy --prod ``` +### Netlify with Git + +1. Push your code to a git repository (GitHub, GitLab, BitBucket, Azure DevOps). +2. [Import the project](https://app.netlify.com/start) to Netlify. +3. Choose the branch, output directory, and set up environment variables if applicable. +4. Click on **Deploy**. +5. Your Vite app is deployed! + +After your project has been imported and deployed, all subsequent pushes to branches other than the production branch along with pull requests will generate [Preview Deployments](https://docs.netlify.com/site-deploys/deploy-previews/), and all changes made to the Production Branch (commonly “main”) will result in a [Production Deployment](https://docs.netlify.com/site-deploys/overview/#definitions). + ## Vercel ### Vercel CLI @@ -264,70 +279,39 @@ You can also add custom domains and handle custom build settings on Pages. Learn You can also deploy to a [custom domain](http://surge.sh/help/adding-a-custom-domain) by adding `surge dist yourdomain.com`. -## Heroku - -1. Install [Heroku CLI](https://devcenter.heroku.com/articles/heroku-cli). - -2. Create a Heroku account by [signing up](https://signup.heroku.com). - -3. Run `heroku login` and fill in your Heroku credentials: - - ```bash - $ heroku login - ``` - -4. Create a file called `static.json` in the root of your project with the below content: - - `static.json`: +## Azure Static Web Apps - ```json - { - "root": "./dist" - } - ``` +You can quickly deploy your Vite app with Microsoft Azure [Static Web Apps](https://aka.ms/staticwebapps) service. You need: - This is the configuration of your site; read more at [heroku-buildpack-static](https://github.com/heroku/heroku-buildpack-static). +- An Azure account and a subscription key. You can create a [free Azure account here](https://azure.microsoft.com/free). +- Your app code pushed to [GitHub](https://github.com). +- The [SWA Extension](https://marketplace.visualstudio.com/items?itemName=ms-azuretools.vscode-azurestaticwebapps) in [Visual Studio Code](https://code.visualstudio.com). -5. Set up your Heroku git remote: +Install the extension in VS Code and navigate to your app root. Open the Static Web Apps extension, sign in to Azure, and click the '+' sign to create a new Static Web App. You will be prompted to designate which subscription key to use. - ```bash - # version change - $ git init - $ git add . - $ git commit -m "My site ready for deployment." +Follow the wizard started by the extension to give your app a name, choose a framework preset, and designate the app root (usually `/`) and built file location `/dist`. The wizard will run and will create a GitHub action in your repo in a `.github` folder. - # creates a new app with a specified name - $ heroku apps:create example - ``` +The action will work to deploy your app (watch its progress in your repo's Actions tab) and, when successfully completed, you can view your app in the address provided in the extension's progress window by clicking the 'Browse Website' button that appears when the GitHub action has run. -6. Set buildpacks. We use `heroku/nodejs` to build the project and `heroku-buildpack-static` to serve it. +## Render - ```bash - # set buildpacks - $ heroku buildpacks:set heroku/nodejs - $ heroku buildpacks:add https://github.com/heroku/heroku-buildpack-static.git - ``` +You can deploy your Vite app as a Static Site on [Render](https://render.com/). -7. Deploy your site: +1. Create a [Render account](https://dashboard.render.com/register). - ```bash - # publish site - $ git push heroku main +2. In the [Dashboard](https://dashboard.render.com/), click the **New** button and select **Static Site**. - # opens a browser to view the Dashboard version of Heroku CI - $ heroku open - ``` +3. Connect your GitHub/GitLab account or use a public repository. -## Azure Static Web Apps +4. Specify a project name and branch. -You can quickly deploy your Vite app with Microsoft Azure [Static Web Apps](https://aka.ms/staticwebapps) service. You need: + - **Build Command**: `npm run build` + - **Publish Directory**: `dist` -- An Azure account and a subscription key. You can create a [free Azure account here](https://azure.microsoft.com/free). -- Your app code pushed to [GitHub](https://github.com). -- The [SWA Extension](https://marketplace.visualstudio.com/items?itemName=ms-azuretools.vscode-azurestaticwebapps) in [Visual Studio Code](https://code.visualstudio.com). +5. Click **Create Static Site**. -Install the extension in VS Code and navigate to your app root. Open the Static Web Apps extension, sign in to Azure, and click the '+' sign to create a new Static Web App. You will be prompted to designate which subscription key to use. + Your app should be deployed at `https://.onrender.com/`. -Follow the wizard started by the extension to give your app a name, choose a framework preset, and designate the app root (usually `/`) and built file location `/dist`. The wizard will run and will create a GitHub action in your repo in a `.github` folder. +By default, any new commit pushed to the specified branch will automatically trigger a new deployment. [Auto-Deploy](https://render.com/docs/deploys#toggling-auto-deploy-for-a-service) can be configured in the project settings. -The action will work to deploy your app (watch its progress in your repo's Actions tab) and, when successfully completed, you can view your app in the address provided in the extension's progress window by clicking the 'Browse Website' button that appears when the GitHub action has run. +You can also add a [custom domain](https://render.com/docs/custom-domains) to your project. diff --git a/docs/guide/troubleshooting.md b/docs/guide/troubleshooting.md index 1021d9e41f04b1..d57899be3112d3 100644 --- a/docs/guide/troubleshooting.md +++ b/docs/guide/troubleshooting.md @@ -8,12 +8,12 @@ If the suggestions here don't work, please try posting questions on [GitHub Disc ### `Error: Cannot find module 'C:\foo\bar&baz\vite\bin\vite.js'` -The path to your project folder may include `?`, which doesn't work with `npm` on Windows ([npm/cmd-shim#45](https://github.com/npm/cmd-shim/issues/45)). +The path to your project folder may include `&`, which doesn't work with `npm` on Windows ([npm/cmd-shim#45](https://github.com/npm/cmd-shim/issues/45)). You will need to either: - Switch to another package manager (e.g. `pnpm`, `yarn`) -- Remove `?` from the path to your project +- Remove `&` from the path to your project ## Dev Server @@ -44,6 +44,16 @@ To solve this: $ sudo sysctl fs.inotify.max_user_watches=524288 ``` +### 431 Request Header Fields Too Large + +When the server / WebSocket server receives a large HTTP header, the request will be dropped and the following warning will be shown. + +> Server responded with status code 431. See https://vitejs.dev/guide/troubleshooting.html#_431-request-header-fields-too-large. + +This is because Node.js limits request header size to mitigate [CVE-2018-12121](https://www.cve.org/CVERecord?id=CVE-2018-12121). + +To avoid this, try to reduce your request header size. For example, if the cookie is long, delete it. Or you can use [`--max-http-header-size`](https://nodejs.org/api/cli.html#--max-http-header-sizesize) to change max header size. + ## HMR ### Vite detects a file change but the HMR is not working @@ -66,6 +76,20 @@ If HMR is not handled by Vite or a plugin, a full reload will happen. Also if there is a dependency loop, a full reload will happen. To solve this, try removing the loop. +## Build + +### Built file does not work because of CORS error + +If the HTML file output was opened with `file` protocol, the scripts won't run with the following error. + +> Access to script at 'file:///foo/bar.js' from origin 'null' has been blocked by CORS policy: Cross origin requests are only supported for protocol schemes: http, data, isolated-app, chrome-extension, chrome, https, chrome-untrusted. + +> Cross-Origin Request Blocked: The Same Origin Policy disallows reading the remote resource at file:///foo/bar.js. (Reason: CORS request not http). + +See [Reason: CORS request not HTTP - HTTP | MDN](https://developer.mozilla.org/en-US/docs/Web/HTTP/CORS/Errors/CORSRequestNotHttp) for more information about why this happens. + +You will need to access the file with `http` protocol. The easiest way to achieve this is to run `npx vite preview`. + ## Others ### Syntax Error / Type Error happens @@ -78,4 +102,4 @@ For example, you might see these errors. > TypeError: Cannot create property 'foo' on boolean 'false' -If these code are used inside dependecies, you could use [`patch-package`](https://github.com/ds300/patch-package) (or [`yarn patch`](https://yarnpkg.com/cli/patch) or [`pnpm patch`](https://pnpm.io/cli/patch)) for an escape hatch. +If these code are used inside dependencies, you could use [`patch-package`](https://github.com/ds300/patch-package) (or [`yarn patch`](https://yarnpkg.com/cli/patch) or [`pnpm patch`](https://pnpm.io/cli/patch)) for an escape hatch. diff --git a/docs/guide/using-plugins.md b/docs/guide/using-plugins.md index 39c81dc24f709b..b05c8c9f2223d3 100644 --- a/docs/guide/using-plugins.md +++ b/docs/guide/using-plugins.md @@ -24,7 +24,7 @@ export default defineConfig({ }) ``` -`plugins` also accept presets including several plugins as a single element. This is useful for complex features (like framework integration) that are implemented using several plugins. The array will be flattened internally. +`plugins` also accepts presets including several plugins as a single element. This is useful for complex features (like framework integration) that are implemented using several plugins. The array will be flattened internally. Falsy plugins will be ignored, which can be used to easily activate or deactivate plugins. diff --git a/docs/guide/why.md b/docs/guide/why.md index 24b1acfef6358f..6a868587abba19 100644 --- a/docs/guide/why.md +++ b/docs/guide/why.md @@ -51,7 +51,7 @@ Ensuring optimal output and behavioral consistency between the dev server and th ## Why Not Bundle with esbuild? -While `esbuild` is blazing fast and is already a very capable bundler for libraries, some of the important features needed for bundling _applications_ are still work in progress - in particular code-splitting and CSS handling. For the time being, Rollup is more mature and flexible in these regards. That said, we won't rule out the possibility of using `esbuild` for production build when it stabilizes these features in the future. +While `esbuild` is extremely fast and is already a very capable bundler for libraries, some of the important features needed for bundling _applications_ are still work in progress - in particular code-splitting and CSS handling. For the time being, Rollup is more mature and flexible in these regards. That said, we won't rule out the possibility of using `esbuild` for production builds when it stabilizes these features in the future. ## How is Vite Different from X? diff --git a/docs/public/cypress.svg b/docs/public/cypress.svg deleted file mode 100644 index 9bb6b2f647b6ce..00000000000000 --- a/docs/public/cypress.svg +++ /dev/null @@ -1,22 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - diff --git a/docs/public/divriots.png b/docs/public/divriots.png deleted file mode 100644 index b021d0aa965f49..00000000000000 Binary files a/docs/public/divriots.png and /dev/null differ diff --git a/docs/public/mux.svg b/docs/public/mux.svg deleted file mode 100644 index 2e1ad4be40b061..00000000000000 --- a/docs/public/mux.svg +++ /dev/null @@ -1,79 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/docs/public/plaid.svg b/docs/public/plaid.svg deleted file mode 100644 index 78354f007caf10..00000000000000 --- a/docs/public/plaid.svg +++ /dev/null @@ -1 +0,0 @@ -plaid_yoko \ No newline at end of file diff --git a/docs/public/stackblitz.svg b/docs/public/stackblitz.svg deleted file mode 100644 index 2c356bdfb0038a..00000000000000 --- a/docs/public/stackblitz.svg +++ /dev/null @@ -1,31 +0,0 @@ - - - - - - - - - - - - - - - - - \ No newline at end of file diff --git a/docs/public/tailwind-labs.svg b/docs/public/tailwind-labs.svg deleted file mode 100644 index 300411468cbb5f..00000000000000 --- a/docs/public/tailwind-labs.svg +++ /dev/null @@ -1,15 +0,0 @@ - - - - - - - - - - - - - - - diff --git a/docs/public/vuejobs.png b/docs/public/vuejobs.png deleted file mode 100644 index 2d6a52f6f4d373..00000000000000 Binary files a/docs/public/vuejobs.png and /dev/null differ diff --git a/docs/vite.config.ts b/docs/vite.config.ts deleted file mode 100644 index 94f3ed69000771..00000000000000 --- a/docs/vite.config.ts +++ /dev/null @@ -1,15 +0,0 @@ -import { defineConfig } from 'vite' - -export default defineConfig({ - ssr: { - format: 'cjs' - }, - legacy: { - buildSsrCjsExternalHeuristics: true - }, - optimizeDeps: { - // vitepress is aliased with replacement `join(DIST_CLIENT_PATH, '/index')` - // This needs to be excluded from optimization - exclude: ['vitepress'] - } -}) diff --git a/netlify.toml b/netlify.toml index 10bc161218e1cc..b5ea297a34bf29 100644 --- a/netlify.toml +++ b/netlify.toml @@ -1,7 +1,6 @@ [build.environment] NODE_VERSION = "16" - NPM_FLAGS = "--version" # prevent Netlify npm install [build] publish = "docs/.vitepress/dist" - command = "npx pnpm i --store=node_modules/.pnpm-store --frozen-lockfile && npm run ci-docs" + command = "pnpm ci-docs" ignore = "./scripts/docs-check.sh" diff --git a/package.json b/package.json index e9a3576f9b4462..4ae32724899a1c 100644 --- a/package.json +++ b/package.json @@ -16,29 +16,29 @@ "preinstall": "npx only-allow pnpm", "postinstall": "simple-git-hooks", "format": "prettier --write --cache .", - "lint": "eslint --cache packages/*/{src,types,__tests__}/** playground/**/__tests__/**/*.ts scripts/**/*.ts", + "lint": "eslint --cache .", "typecheck": "tsc -p scripts --noEmit && tsc -p playground --noEmit", "test": "run-s test-unit test-serve test-build", "test-serve": "vitest run -c vitest.config.e2e.ts", - "test-build": "cross-env VITE_TEST_BUILD=1 vitest run -c vitest.config.e2e.ts", - "test-build-without-plugin-commonjs": "cross-env VITE_TEST_WITHOUT_PLUGIN_COMMONJS=1 pnpm test-build", + "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": "cross-env VITE_DEBUG_SERVE=1 vitest run -c vitest.config.e2e.ts", - "debug-build": "cross-env VITE_TEST_BUILD=1 VITE_PRESERVE_BUILD_ARTIFACTS=1 vitest run -c vitest.config.e2e.ts", + "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", - "build": "pnpm -r --filter=./packages/* run build", - "dev": "pnpm -r --parallel --filter=./packages/* run dev", + "build": "pnpm -r --filter='./packages/*' run build", + "dev": "pnpm -r --parallel --filter='./packages/*' run dev", "release": "tsx scripts/release.ts", "ci-publish": "tsx scripts/publishCI.ts", "ci-docs": "run-s build docs-build" }, "devDependencies": { - "@babel/types": "^7.18.9", - "@microsoft/api-extractor": "^7.28.6", - "@rollup/plugin-typescript": "^8.3.3", + "@babel/types": "^7.20.0", + "@microsoft/api-extractor": "^7.33.5", + "@rollup/plugin-typescript": "^8.5.0", "@types/babel__core": "^7.1.19", "@types/babel__standalone": "^7.1.4", "@types/convert-source-map": "^1.5.2", @@ -47,52 +47,50 @@ "@types/estree": "^1.0.0", "@types/etag": "^1.8.1", "@types/fs-extra": "^9.0.13", - "@types/hash-sum": "^1.0.0", "@types/less": "^3.0.3", "@types/micromatch": "^4.0.2", - "@types/mime": "^2.0.3", "@types/minimist": "^1.2.2", "@types/node": "^17.0.42", - "@types/prompts": "^2.4.0", + "@types/picomatch": "^2.3.0", + "@types/prompts": "^2.4.1", "@types/resolve": "^1.20.2", "@types/sass": "~1.43.1", - "@types/semver": "^7.3.10", + "@types/semver": "^7.3.13", "@types/stylus": "^0.48.38", "@types/ws": "^8.5.3", - "@typescript-eslint/eslint-plugin": "^5.30.7", - "@typescript-eslint/parser": "^5.30.7", + "@typescript-eslint/eslint-plugin": "^5.41.0", + "@typescript-eslint/parser": "^5.41.0", "conventional-changelog-cli": "^2.2.2", - "cross-env": "^7.0.3", "esbuild": "^0.14.47", - "eslint": "^8.20.0", - "eslint-define-config": "^1.5.1", + "eslint": "^8.26.0", + "eslint-define-config": "^1.11.0", "eslint-plugin-import": "^2.26.0", "eslint-plugin-node": "^11.1.0", "execa": "^6.1.0", + "fast-glob": "^3.2.12", "fs-extra": "^10.1.0", - "kill-port": "^1.6.1", "lint-staged": "^13.0.3", - "minimist": "^1.2.6", - "node-fetch": "^3.2.9", + "minimist": "^1.2.7", "npm-run-all": "^4.1.5", "picocolors": "^1.0.0", - "playwright-chromium": "^1.24.0", - "pnpm": "^7.6.0", + "playwright-chromium": "^1.27.1", + "pnpm": "^7.14.1", "prettier": "2.7.1", "prompts": "^2.4.2", + "resolve": "^1.22.1", "rimraf": "^3.0.2", - "rollup": "^2.75.6", - "semver": "^7.3.7", - "simple-git-hooks": "^2.8.0", - "sirv": "^2.0.2", + "rollup": "^2.79.1", + "rollup-plugin-license": "^2.9.0", + "semver": "^7.3.8", + "simple-git-hooks": "^2.8.1", "tslib": "^2.4.0", - "tsx": "^3.8.0", + "tsx": "^3.11.0", "typescript": "^4.6.4", - "unbuild": "^0.7.6", + "unbuild": "^0.9.4", "vite": "workspace:*", - "vitepress": "^1.0.0-alpha.4", - "vitest": "^0.19.0", - "vue": "^3.2.37" + "vitepress": "^1.0.0-alpha.26", + "vitest": "^0.24.3", + "vue": "^3.2.41" }, "simple-git-hooks": { "pre-commit": "pnpm exec lint-staged --concurrent false", @@ -112,7 +110,7 @@ "eslint --cache --fix" ] }, - "packageManager": "pnpm@7.6.0", + "packageManager": "pnpm@7.14.1", "pnpm": { "overrides": { "vite": "workspace:*", diff --git a/packages/create-vite/CHANGELOG.md b/packages/create-vite/CHANGELOG.md index be26f7b3cce15a..f22d6803f2eb20 100644 --- a/packages/create-vite/CHANGELOG.md +++ b/packages/create-vite/CHANGELOG.md @@ -1,3 +1,52 @@ +## 3.2.0 (2022-10-26) + +* fix(create-vite): remove baseUrl from Svelte configs (#10200) ([9c7a331](https://github.com/vitejs/vite/commit/9c7a331)), closes [#10200](https://github.com/vitejs/vite/issues/10200) +* fix(deps): update all non-major dependencies (#10077) ([caf00c8](https://github.com/vitejs/vite/commit/caf00c8)), closes [#10077](https://github.com/vitejs/vite/issues/10077) +* fix(deps): update all non-major dependencies (#10160) ([6233c83](https://github.com/vitejs/vite/commit/6233c83)), closes [#10160](https://github.com/vitejs/vite/issues/10160) +* fix(deps): update all non-major dependencies (#10316) ([a38b450](https://github.com/vitejs/vite/commit/a38b450)), closes [#10316](https://github.com/vitejs/vite/issues/10316) +* fix(deps): update all non-major dependencies (#10610) ([bb95467](https://github.com/vitejs/vite/commit/bb95467)), closes [#10610](https://github.com/vitejs/vite/issues/10610) +* fix(deps): update all non-major dependencies (#9985) ([855f2f0](https://github.com/vitejs/vite/commit/855f2f0)), closes [#9985](https://github.com/vitejs/vite/issues/9985) +* chore: remove duplicated logic in vanilla template (#10145) ([f29fef5](https://github.com/vitejs/vite/commit/f29fef5)), closes [#10145](https://github.com/vitejs/vite/issues/10145) +* chore(create-vite): remove sourcemap, move --noEmit to tsconfig (#10150) ([414d2ef](https://github.com/vitejs/vite/commit/414d2ef)), closes [#10150](https://github.com/vitejs/vite/issues/10150) +* chore(deps): update all non-major dependencies (#10393) ([f519423](https://github.com/vitejs/vite/commit/f519423)), closes [#10393](https://github.com/vitejs/vite/issues/10393) +* chore(deps): update all non-major dependencies (#10488) ([15aa827](https://github.com/vitejs/vite/commit/15aa827)), closes [#10488](https://github.com/vitejs/vite/issues/10488) +* chore(deps): update dependency vue-tsc to v1 (#10547) ([9d0e0af](https://github.com/vitejs/vite/commit/9d0e0af)), closes [#10547](https://github.com/vitejs/vite/issues/10547) +* feat: Switch to JSX runtime transform in preact-ts template (#10061) ([bf69063](https://github.com/vitejs/vite/commit/bf69063)), closes [#10061](https://github.com/vitejs/vite/issues/10061) +* feat(create-vite): support create-vite-extra (#10214) ([8116cbd](https://github.com/vitejs/vite/commit/8116cbd)), closes [#10214](https://github.com/vitejs/vite/issues/10214) + + + +## 3.1.0 (2022-09-05) + +* refactor(create-vite): migrate to TypeScript (#9941) ([85fa2c8](https://github.com/vitejs/vite/commit/85fa2c8)), closes [#9941](https://github.com/vitejs/vite/issues/9941) +* perf: bundle create-vite (#9034) ([37ac91e](https://github.com/vitejs/vite/commit/37ac91e)), closes [#9034](https://github.com/vitejs/vite/issues/9034) +* fix(deps): update all non-major dependencies (#9888) ([e35a58b](https://github.com/vitejs/vite/commit/e35a58b)), closes [#9888](https://github.com/vitejs/vite/issues/9888) +* test(cli): remove unnecessary generics usage (#9859) ([45d6797](https://github.com/vitejs/vite/commit/45d6797)), closes [#9859](https://github.com/vitejs/vite/issues/9859) +* feat: skip `.git` when emptying dir (#9659) ([07fe65e](https://github.com/vitejs/vite/commit/07fe65e)), closes [#9659](https://github.com/vitejs/vite/issues/9659) +* feat(create-vite): add support for custom init commands (`create-vue`, Nuxt, and SvelteKit) (#9406) ([1673f3d](https://github.com/vitejs/vite/commit/1673f3d)), closes [#9406](https://github.com/vitejs/vite/issues/9406) +* chore(deps): update all non-major dependencies (#9675) ([4e56e87](https://github.com/vitejs/vite/commit/4e56e87)), closes [#9675](https://github.com/vitejs/vite/issues/9675) +* chore(deps): update all non-major dependencies (#9778) ([aceaefc](https://github.com/vitejs/vite/commit/aceaefc)), closes [#9778](https://github.com/vitejs/vite/issues/9778) + + + +## 3.0.2 (2022-08-12) + + + + +## 3.0.1 (2022-08-11) + +* fix: mention that Node.js 13/15 support is dropped (fixes #9113) (#9116) ([2826303](https://github.com/vitejs/vite/commit/2826303)), closes [#9113](https://github.com/vitejs/vite/issues/9113) [#9116](https://github.com/vitejs/vite/issues/9116) +* fix(create-vite): update vanilla-ts brand color (#9254) ([bff3abb](https://github.com/vitejs/vite/commit/bff3abb)), closes [#9254](https://github.com/vitejs/vite/issues/9254) +* fix(deps): update all non-major dependencies (#9176) ([31d3b70](https://github.com/vitejs/vite/commit/31d3b70)), closes [#9176](https://github.com/vitejs/vite/issues/9176) +* fix(deps): update all non-major dependencies (#9575) ([8071325](https://github.com/vitejs/vite/commit/8071325)), closes [#9575](https://github.com/vitejs/vite/issues/9575) +* chore: remove unused `favicon.svg` (#9181) ([33b5b0d](https://github.com/vitejs/vite/commit/33b5b0d)), closes [#9181](https://github.com/vitejs/vite/issues/9181) +* chore: tidy up eslint config (#9468) ([f4addcf](https://github.com/vitejs/vite/commit/f4addcf)), closes [#9468](https://github.com/vitejs/vite/issues/9468) +* chore(deps): update all non-major dependencies (#9347) ([2fcb027](https://github.com/vitejs/vite/commit/2fcb027)), closes [#9347](https://github.com/vitejs/vite/issues/9347) +* chore(deps): update all non-major dependencies (#9478) ([c530d16](https://github.com/vitejs/vite/commit/c530d16)), closes [#9478](https://github.com/vitejs/vite/issues/9478) + + + ## 3.0.0 (2022-07-13) * chore: bump minors and rebuild lock (#8074) ([aeb5b74](https://github.com/vitejs/vite/commit/aeb5b74)), closes [#8074](https://github.com/vitejs/vite/issues/8074) diff --git a/packages/create-vite/LICENSE b/packages/create-vite/LICENSE index 9c1b313d7b1816..79575402808eb1 100644 --- a/packages/create-vite/LICENSE +++ b/packages/create-vite/LICENSE @@ -1,3 +1,6 @@ +# create-vite license +create-vite is released under the MIT license: + MIT License Copyright (c) 2019-present, Yuxi (Evan) You and Vite contributors @@ -19,3 +22,274 @@ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + +# Licenses of bundled dependencies +The published create-vite artifact additionally contains code with the following licenses: +ISC, MIT + +# Bundled dependencies: +## cross-spawn +License: MIT +By: André Cruz +Repository: git@github.com:moxystudio/node-cross-spawn.git + +> The MIT License (MIT) +> +> Copyright (c) 2018 Made With MOXY Lda +> +> Permission is hereby granted, free of charge, to any person obtaining a copy +> of this software and associated documentation files (the "Software"), to deal +> in the Software without restriction, including without limitation the rights +> to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +> copies of the Software, and to permit persons to whom the Software is +> furnished to do so, subject to the following conditions: +> +> The above copyright notice and this permission notice shall be included in +> all copies or substantial portions of the Software. +> +> THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +> IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +> FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +> AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +> LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +> OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +> THE SOFTWARE. + +--------------------------------------- + +## isexe +License: ISC +By: Isaac Z. Schlueter +Repository: git+https://github.com/isaacs/isexe.git + +> The ISC License +> +> Copyright (c) Isaac Z. Schlueter and Contributors +> +> Permission to use, copy, modify, and/or distribute this software for any +> purpose with or without fee is hereby granted, provided that the above +> copyright notice and this permission notice appear in all copies. +> +> THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES +> WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF +> MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR +> ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES +> WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN +> ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR +> IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + +--------------------------------------- + +## kleur +License: MIT +By: Luke Edwards +Repository: lukeed/kleur + +> The MIT License (MIT) +> +> Copyright (c) Luke Edwards (lukeed.com) +> +> Permission is hereby granted, free of charge, to any person obtaining a copy +> of this software and associated documentation files (the "Software"), to deal +> in the Software without restriction, including without limitation the rights +> to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +> copies of the Software, and to permit persons to whom the Software is +> furnished to do so, subject to the following conditions: +> +> The above copyright notice and this permission notice shall be included in +> all copies or substantial portions of the Software. +> +> THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +> IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +> FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +> AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +> LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +> OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +> THE SOFTWARE. + +--------------------------------------- + +## kolorist +License: MIT +By: Marvin Hagemeister +Repository: https://github.com/marvinhagemeister/kolorist.git + +> The MIT License (MIT) +> +> Copyright (c) 2020-present Marvin Hagemeister +> +> Permission is hereby granted, free of charge, to any person obtaining a copy +> of this software and associated documentation files (the "Software"), to deal +> in the Software without restriction, including without limitation the rights +> to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +> copies of the Software, and to permit persons to whom the Software is +> furnished to do so, subject to the following conditions: +> +> The above copyright notice and this permission notice shall be included in all +> copies or substantial portions of the Software. +> +> THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +> IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +> FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +> AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +> LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +> OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +> SOFTWARE. + +--------------------------------------- + +## minimist +License: MIT +By: James Halliday +Repository: git://github.com/minimistjs/minimist.git + +> This software is released under the MIT license: +> +> Permission is hereby granted, free of charge, to any person obtaining a copy of +> this software and associated documentation files (the "Software"), to deal in +> the Software without restriction, including without limitation the rights to +> use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of +> the Software, and to permit persons to whom the Software is furnished to do so, +> subject to the following conditions: +> +> The above copyright notice and this permission notice shall be included in all +> copies or substantial portions of the Software. +> +> THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +> IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS +> FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR +> COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER +> IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN +> CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + +--------------------------------------- + +## path-key +License: MIT +By: Sindre Sorhus +Repository: sindresorhus/path-key + +> MIT License +> +> Copyright (c) Sindre Sorhus (sindresorhus.com) +> +> Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: +> +> The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. +> +> THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + +--------------------------------------- + +## prompts +License: MIT +By: Terkel Gjervig +Repository: terkelg/prompts + +> MIT License +> +> Copyright (c) 2018 Terkel Gjervig Nielsen +> +> Permission is hereby granted, free of charge, to any person obtaining a copy +> of this software and associated documentation files (the "Software"), to deal +> in the Software without restriction, including without limitation the rights +> to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +> copies of the Software, and to permit persons to whom the Software is +> furnished to do so, subject to the following conditions: +> +> The above copyright notice and this permission notice shall be included in all +> copies or substantial portions of the Software. +> +> THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +> IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +> FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +> AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +> LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +> OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +> SOFTWARE. + +--------------------------------------- + +## shebang-command +License: MIT +By: Kevin Mårtensson +Repository: kevva/shebang-command + +> MIT License +> +> Copyright (c) Kevin Mårtensson (github.com/kevva) +> +> Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: +> +> The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. +> +> THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + +--------------------------------------- + +## shebang-regex +License: MIT +By: Sindre Sorhus +Repository: sindresorhus/shebang-regex + +> MIT License +> +> Copyright (c) Sindre Sorhus (sindresorhus.com) +> +> Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: +> +> The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. +> +> THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + +--------------------------------------- + +## sisteransi +License: MIT +By: Terkel Gjervig +Repository: https://github.com/terkelg/sisteransi + +> MIT License +> +> Copyright (c) 2018 Terkel Gjervig Nielsen +> +> Permission is hereby granted, free of charge, to any person obtaining a copy +> of this software and associated documentation files (the "Software"), to deal +> in the Software without restriction, including without limitation the rights +> to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +> copies of the Software, and to permit persons to whom the Software is +> furnished to do so, subject to the following conditions: +> +> The above copyright notice and this permission notice shall be included in all +> copies or substantial portions of the Software. +> +> THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +> IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +> FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +> AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +> LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +> OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +> SOFTWARE. + +--------------------------------------- + +## which +License: ISC +By: Isaac Z. Schlueter +Repository: git://github.com/isaacs/node-which.git + +> The ISC License +> +> Copyright (c) Isaac Z. Schlueter and Contributors +> +> Permission to use, copy, modify, and/or distribute this software for any +> purpose with or without fee is hereby granted, provided that the above +> copyright notice and this permission notice appear in all copies. +> +> THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES +> WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF +> MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR +> ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES +> WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN +> ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR +> IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. diff --git a/packages/create-vite/__tests__/cli.spec.ts b/packages/create-vite/__tests__/cli.spec.ts index 4f5d9aa0dbaa15..f5d666c3577e28 100644 --- a/packages/create-vite/__tests__/cli.spec.ts +++ b/packages/create-vite/__tests__/cli.spec.ts @@ -11,8 +11,8 @@ const genPath = join(__dirname, projectName) const run = ( args: string[], - options: SyncOptions = {} -): ExecaSyncReturnValue => { + options: SyncOptions = {} +): ExecaSyncReturnValue => { return execaCommandSync(`node ${CLI_PATH} ${args.join(' ')}`, options) } diff --git a/packages/create-vite/build.config.ts b/packages/create-vite/build.config.ts new file mode 100644 index 00000000000000..2c39d19d427de6 --- /dev/null +++ b/packages/create-vite/build.config.ts @@ -0,0 +1,35 @@ +import path from 'node:path' +import url from 'node:url' +import { defineBuildConfig } from 'unbuild' +import licensePlugin from '../../scripts/rollupLicensePlugin.mjs' + +const __dirname = path.dirname(url.fileURLToPath(import.meta.url)) + +export default defineBuildConfig({ + entries: ['src/index'], + clean: true, + rollup: { + inlineDependencies: true, + esbuild: { + minify: true + } + }, + alias: { + // we can always use non-transpiled code since we support 14.18.0+ + prompts: 'prompts/lib/index.js' + }, + hooks: { + 'rollup:options'(ctx, options) { + if (!options.plugins) { + options.plugins = [] + } + options.plugins.push( + licensePlugin( + path.resolve(__dirname, './LICENSE'), + 'create-vite license', + 'create-vite' + ) + ) + } + } +}) diff --git a/packages/create-vite/index.js b/packages/create-vite/index.js index 40d0aa583124d3..f5e8e064a68d9f 100755 --- a/packages/create-vite/index.js +++ b/packages/create-vite/index.js @@ -1,378 +1,3 @@ #!/usr/bin/env node -// @ts-check -import fs from 'node:fs' -import path from 'node:path' -import { fileURLToPath } from 'node:url' -import minimist from 'minimist' -import prompts from 'prompts' -import { - blue, - cyan, - green, - lightRed, - magenta, - red, - reset, - yellow -} from 'kolorist' - -// 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(process.argv.slice(2), { string: ['_'] }) -const cwd = process.cwd() - -const FRAMEWORKS = [ - { - name: 'vanilla', - color: yellow, - variants: [ - { - name: 'vanilla', - display: 'JavaScript', - color: yellow - }, - { - name: 'vanilla-ts', - display: 'TypeScript', - color: blue - } - ] - }, - { - name: 'vue', - color: green, - variants: [ - { - name: 'vue', - display: 'JavaScript', - color: yellow - }, - { - name: 'vue-ts', - display: 'TypeScript', - color: blue - } - ] - }, - { - name: 'react', - color: cyan, - variants: [ - { - name: 'react', - display: 'JavaScript', - color: yellow - }, - { - name: 'react-ts', - display: 'TypeScript', - color: blue - } - ] - }, - { - name: 'preact', - color: magenta, - variants: [ - { - name: 'preact', - display: 'JavaScript', - color: yellow - }, - { - name: 'preact-ts', - display: 'TypeScript', - color: blue - } - ] - }, - { - name: 'lit', - color: lightRed, - variants: [ - { - name: 'lit', - display: 'JavaScript', - color: yellow - }, - { - name: 'lit-ts', - display: 'TypeScript', - color: blue - } - ] - }, - { - name: 'svelte', - color: red, - variants: [ - { - name: 'svelte', - display: 'JavaScript', - color: yellow - }, - { - name: 'svelte-ts', - display: 'TypeScript', - color: blue - } - ] - } -] - -const TEMPLATES = FRAMEWORKS.map( - (f) => (f.variants && f.variants.map((v) => v.name)) || [f.name] -).reduce((a, b) => a.concat(b), []) - -const renameFiles = { - _gitignore: '.gitignore' -} - -async function init() { - let targetDir = formatTargetDir(argv._[0]) - let template = argv.template || argv.t - - const defaultTargetDir = 'vite-project' - const getProjectName = () => - targetDir === '.' ? path.basename(path.resolve()) : targetDir - - let result = {} - - try { - result = await prompts( - [ - { - type: targetDir ? null : 'text', - name: 'projectName', - message: reset('Project name:'), - initial: defaultTargetDir, - onState: (state) => { - targetDir = formatTargetDir(state.value) || defaultTargetDir - } - }, - { - type: () => - !fs.existsSync(targetDir) || isEmpty(targetDir) ? null : 'confirm', - name: 'overwrite', - message: () => - (targetDir === '.' - ? 'Current directory' - : `Target directory "${targetDir}"`) + - ` is not empty. Remove existing files and continue?` - }, - { - type: (_, { overwrite } = {}) => { - if (overwrite === false) { - throw new Error(red('✖') + ' Operation cancelled') - } - return null - }, - name: 'overwriteChecker' - }, - { - type: () => (isValidPackageName(getProjectName()) ? null : 'text'), - name: 'packageName', - message: reset('Package name:'), - initial: () => toValidPackageName(getProjectName()), - validate: (dir) => - isValidPackageName(dir) || 'Invalid package.json name' - }, - { - type: template && TEMPLATES.includes(template) ? null : 'select', - name: 'framework', - message: - typeof template === 'string' && !TEMPLATES.includes(template) - ? reset( - `"${template}" isn't a valid template. Please choose from below: ` - ) - : reset('Select a framework:'), - initial: 0, - choices: FRAMEWORKS.map((framework) => { - const frameworkColor = framework.color - return { - title: frameworkColor(framework.name), - value: framework - } - }) - }, - { - type: (framework) => - framework && framework.variants ? 'select' : null, - name: 'variant', - message: reset('Select a variant:'), - // @ts-ignore - choices: (framework) => - framework.variants.map((variant) => { - const variantColor = variant.color - return { - title: variantColor(variant.name), - value: variant.name - } - }) - } - ], - { - onCancel: () => { - throw new Error(red('✖') + ' Operation cancelled') - } - } - ) - } catch (cancelled) { - console.log(cancelled.message) - return - } - - // user choice associated with prompts - const { framework, overwrite, packageName, variant } = result - - const root = path.join(cwd, targetDir) - - if (overwrite) { - emptyDir(root) - } else if (!fs.existsSync(root)) { - fs.mkdirSync(root, { recursive: true }) - } - - // determine template - template = variant || framework || template - - console.log(`\nScaffolding project in ${root}...`) - - const templateDir = path.resolve( - fileURLToPath(import.meta.url), - '..', - `template-${template}` - ) - - const write = (file, content) => { - const targetPath = renameFiles[file] - ? path.join(root, renameFiles[file]) - : path.join(root, file) - if (content) { - fs.writeFileSync(targetPath, content) - } else { - copy(path.join(templateDir, file), targetPath) - } - } - - const files = fs.readdirSync(templateDir) - for (const file of files.filter((f) => f !== 'package.json')) { - write(file) - } - - const pkg = JSON.parse( - fs.readFileSync(path.join(templateDir, `package.json`), 'utf-8') - ) - - pkg.name = packageName || getProjectName() - - write('package.json', JSON.stringify(pkg, null, 2)) - - const pkgInfo = pkgFromUserAgent(process.env.npm_config_user_agent) - const pkgManager = pkgInfo ? pkgInfo.name : 'npm' - - console.log(`\nDone. Now run:\n`) - if (root !== cwd) { - console.log(` cd ${path.relative(cwd, root)}`) - } - switch (pkgManager) { - case 'yarn': - console.log(' yarn') - console.log(' yarn dev') - break - default: - console.log(` ${pkgManager} install`) - console.log(` ${pkgManager} run dev`) - break - } - console.log() -} - -/** - * @param {string | undefined} targetDir - */ -function formatTargetDir(targetDir) { - return targetDir?.trim().replace(/\/+$/g, '') -} - -function copy(src, dest) { - const stat = fs.statSync(src) - if (stat.isDirectory()) { - copyDir(src, dest) - } else { - fs.copyFileSync(src, dest) - } -} - -/** - * @param {string} projectName - */ -function isValidPackageName(projectName) { - return /^(?:@[a-z0-9-*~][a-z0-9-*._~]*\/)?[a-z0-9-~][a-z0-9-._~]*$/.test( - projectName - ) -} - -/** - * @param {string} projectName - */ -function toValidPackageName(projectName) { - return projectName - .trim() - .toLowerCase() - .replace(/\s+/g, '-') - .replace(/^[._]/, '') - .replace(/[^a-z0-9-~]+/g, '-') -} - -/** - * @param {string} srcDir - * @param {string} destDir - */ -function copyDir(srcDir, destDir) { - fs.mkdirSync(destDir, { recursive: true }) - for (const file of fs.readdirSync(srcDir)) { - const srcFile = path.resolve(srcDir, file) - const destFile = path.resolve(destDir, file) - copy(srcFile, destFile) - } -} - -/** - * @param {string} path - */ -function isEmpty(path) { - const files = fs.readdirSync(path) - return files.length === 0 || (files.length === 1 && files[0] === '.git') -} - -/** - * @param {string} dir - */ -function emptyDir(dir) { - if (!fs.existsSync(dir)) { - return - } - for (const file of fs.readdirSync(dir)) { - fs.rmSync(path.resolve(dir, file), { recursive: true, force: true }) - } -} - -/** - * @param {string | undefined} userAgent process.env.npm_config_user_agent - * @returns object | undefined - */ -function pkgFromUserAgent(userAgent) { - if (!userAgent) return undefined - const pkgSpec = userAgent.split(' ')[0] - const pkgSpecArr = pkgSpec.split('/') - return { - name: pkgSpecArr[0], - version: pkgSpecArr[1] - } -} - -init().catch((e) => { - console.error(e) -}) +import './dist/index.mjs' diff --git a/packages/create-vite/package.json b/packages/create-vite/package.json index 77ae57a92c60fd..7914ad23aa2b64 100644 --- a/packages/create-vite/package.json +++ b/packages/create-vite/package.json @@ -1,6 +1,6 @@ { "name": "create-vite", - "version": "3.0.0", + "version": "3.2.0", "type": "module", "license": "MIT", "author": "Evan You", @@ -10,9 +10,15 @@ }, "files": [ "index.js", - "template-*" + "template-*", + "dist" ], "main": "index.js", + "scripts": { + "dev": "unbuild --stub", + "build": "unbuild", + "prepublishOnly": "npm run build" + }, "engines": { "node": "^14.18.0 || >=16.0.0" }, @@ -25,9 +31,10 @@ "url": "https://github.com/vitejs/vite/issues" }, "homepage": "https://github.com/vitejs/vite/tree/main/packages/create-vite#readme", - "dependencies": { - "kolorist": "^1.5.1", - "minimist": "^1.2.6", + "devDependencies": { + "cross-spawn": "^7.0.3", + "kolorist": "^1.6.0", + "minimist": "^1.2.7", "prompts": "^2.4.2" } } diff --git a/packages/create-vite/src/index.ts b/packages/create-vite/src/index.ts new file mode 100755 index 00000000000000..d6407b8b3b4c17 --- /dev/null +++ b/packages/create-vite/src/index.ts @@ -0,0 +1,443 @@ +import fs from 'node:fs' +import path from 'node:path' +import { fileURLToPath } from 'node:url' +import spawn from 'cross-spawn' +import minimist from 'minimist' +import prompts from 'prompts' +import { + blue, + cyan, + green, + lightGreen, + lightRed, + magenta, + red, + reset, + yellow +} from 'kolorist' + +// 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: ['_'] }) +const cwd = process.cwd() + +type ColorFunc = (str: string | number) => string +type Framework = { + name: string + display: string + color: ColorFunc + variants: FrameworkVariant[] +} +type FrameworkVariant = { + name: string + display: string + color: ColorFunc + customCommand?: string +} + +const FRAMEWORKS: Framework[] = [ + { + name: 'vanilla', + display: 'Vanilla', + color: yellow, + variants: [ + { + name: 'vanilla', + display: 'JavaScript', + color: yellow + }, + { + name: 'vanilla-ts', + display: 'TypeScript', + color: blue + } + ] + }, + { + name: 'vue', + display: 'Vue', + color: green, + variants: [ + { + name: 'vue', + display: 'JavaScript', + color: yellow + }, + { + name: 'vue-ts', + display: 'TypeScript', + color: blue + }, + { + name: 'custom-create-vue', + display: 'Customize with create-vue ↗', + color: green, + customCommand: 'npm create vue@latest TARGET_DIR' + }, + { + name: 'custom-nuxt', + display: 'Nuxt ↗', + color: lightGreen, + customCommand: 'npm exec nuxi init TARGET_DIR' + } + ] + }, + { + name: 'react', + display: 'React', + color: cyan, + variants: [ + { + name: 'react', + display: 'JavaScript', + color: yellow + }, + { + name: 'react-ts', + display: 'TypeScript', + color: blue + } + ] + }, + { + name: 'preact', + display: 'Preact', + color: magenta, + variants: [ + { + name: 'preact', + display: 'JavaScript', + color: yellow + }, + { + name: 'preact-ts', + display: 'TypeScript', + color: blue + } + ] + }, + { + name: 'lit', + display: 'Lit', + color: lightRed, + variants: [ + { + name: 'lit', + display: 'JavaScript', + color: yellow + }, + { + name: 'lit-ts', + display: 'TypeScript', + color: blue + } + ] + }, + { + name: 'svelte', + display: 'Svelte', + color: red, + variants: [ + { + name: 'svelte', + display: 'JavaScript', + color: yellow + }, + { + name: 'svelte-ts', + display: 'TypeScript', + color: blue + }, + { + name: 'custom-svelte-kit', + display: 'SvelteKit ↗', + color: red, + customCommand: 'npm create svelte@latest TARGET_DIR' + } + ] + }, + { + name: 'others', + display: 'Others', + color: reset, + variants: [ + { + name: 'create-vite-extra', + display: 'create-vite-extra ↗', + color: reset, + customCommand: 'npm create vite-extra@latest TARGET_DIR' + } + ] + } +] + +const TEMPLATES = FRAMEWORKS.map( + (f) => (f.variants && f.variants.map((v) => v.name)) || [f.name] +).reduce((a, b) => a.concat(b), []) + +const renameFiles: Record = { + _gitignore: '.gitignore' +} + +const defaultTargetDir = 'vite-project' + +async function init() { + const argTargetDir = formatTargetDir(argv._[0]) + const argTemplate = argv.template || argv.t + + let targetDir = argTargetDir || defaultTargetDir + const getProjectName = () => + targetDir === '.' ? path.basename(path.resolve()) : targetDir + + let result: prompts.Answers< + 'projectName' | 'overwrite' | 'packageName' | 'framework' | 'variant' + > + + try { + result = await prompts( + [ + { + type: argTargetDir ? null : 'text', + name: 'projectName', + message: reset('Project name:'), + initial: defaultTargetDir, + onState: (state) => { + targetDir = formatTargetDir(state.value) || defaultTargetDir + } + }, + { + type: () => + !fs.existsSync(targetDir) || isEmpty(targetDir) ? null : 'confirm', + name: 'overwrite', + message: () => + (targetDir === '.' + ? 'Current directory' + : `Target directory "${targetDir}"`) + + ` is not empty. Remove existing files and continue?` + }, + { + type: (_, { overwrite }: { overwrite?: boolean }) => { + if (overwrite === false) { + throw new Error(red('✖') + ' Operation cancelled') + } + return null + }, + name: 'overwriteChecker' + }, + { + type: () => (isValidPackageName(getProjectName()) ? null : 'text'), + name: 'packageName', + message: reset('Package name:'), + initial: () => toValidPackageName(getProjectName()), + validate: (dir) => + isValidPackageName(dir) || 'Invalid package.json name' + }, + { + type: + argTemplate && TEMPLATES.includes(argTemplate) ? null : 'select', + name: 'framework', + message: + typeof argTemplate === 'string' && !TEMPLATES.includes(argTemplate) + ? reset( + `"${argTemplate}" isn't a valid template. Please choose from below: ` + ) + : reset('Select a framework:'), + initial: 0, + choices: FRAMEWORKS.map((framework) => { + const frameworkColor = framework.color + return { + title: frameworkColor(framework.display || framework.name), + value: framework + } + }) + }, + { + type: (framework: Framework) => + framework && framework.variants ? 'select' : null, + name: 'variant', + message: reset('Select a variant:'), + choices: (framework: Framework) => + framework.variants.map((variant) => { + const variantColor = variant.color + return { + title: variantColor(variant.display || variant.name), + value: variant.name + } + }) + } + ], + { + onCancel: () => { + throw new Error(red('✖') + ' Operation cancelled') + } + } + ) + } catch (cancelled: any) { + console.log(cancelled.message) + return + } + + // user choice associated with prompts + const { framework, overwrite, packageName, variant } = result + + const root = path.join(cwd, targetDir) + + if (overwrite) { + emptyDir(root) + } else if (!fs.existsSync(root)) { + fs.mkdirSync(root, { recursive: true }) + } + + // determine template + const template: string = variant || framework?.name || argTemplate + + const pkgInfo = pkgFromUserAgent(process.env.npm_config_user_agent) + const pkgManager = pkgInfo ? pkgInfo.name : 'npm' + const isYarn1 = pkgManager === 'yarn' && pkgInfo?.version.startsWith('1.') + + const { customCommand } = + FRAMEWORKS.flatMap((f) => f.variants).find((v) => v.name === template) ?? {} + + if (customCommand) { + const fullCustomCommand = customCommand + .replace('TARGET_DIR', targetDir) + .replace(/^npm create/, `${pkgManager} create`) + // Only Yarn 1.x doesn't support `@version` in the `create` command + .replace('@latest', () => (isYarn1 ? '' : '@latest')) + .replace(/^npm exec/, () => { + // Prefer `pnpm dlx` or `yarn dlx` + if (pkgManager === 'pnpm') { + return 'pnpm dlx' + } + if (pkgManager === 'yarn' && !isYarn1) { + return 'yarn dlx' + } + // Use `npm exec` in all other cases, + // including Yarn 1.x and other custom npm clients. + return 'npm exec' + }) + + const [command, ...args] = fullCustomCommand.split(' ') + const { status } = spawn.sync(command, args, { + stdio: 'inherit' + }) + process.exit(status ?? 0) + } + + console.log(`\nScaffolding project in ${root}...`) + + const templateDir = path.resolve( + fileURLToPath(import.meta.url), + '../..', + `template-${template}` + ) + + const write = (file: string, content?: string) => { + const targetPath = path.join(root, renameFiles[file] ?? file) + if (content) { + fs.writeFileSync(targetPath, content) + } else { + copy(path.join(templateDir, file), targetPath) + } + } + + const files = fs.readdirSync(templateDir) + for (const file of files.filter((f) => f !== 'package.json')) { + write(file) + } + + const pkg = JSON.parse( + fs.readFileSync(path.join(templateDir, `package.json`), 'utf-8') + ) + + pkg.name = packageName || getProjectName() + + write('package.json', JSON.stringify(pkg, null, 2)) + + console.log(`\nDone. Now run:\n`) + if (root !== cwd) { + console.log(` cd ${path.relative(cwd, root)}`) + } + switch (pkgManager) { + case 'yarn': + console.log(' yarn') + console.log(' yarn dev') + break + default: + console.log(` ${pkgManager} install`) + console.log(` ${pkgManager} run dev`) + break + } + console.log() +} + +function formatTargetDir(targetDir: string | undefined) { + return targetDir?.trim().replace(/\/+$/g, '') +} + +function copy(src: string, dest: string) { + const stat = fs.statSync(src) + if (stat.isDirectory()) { + copyDir(src, dest) + } else { + fs.copyFileSync(src, dest) + } +} + +function isValidPackageName(projectName: string) { + return /^(?:@[a-z0-9-*~][a-z0-9-*._~]*\/)?[a-z0-9-~][a-z0-9-._~]*$/.test( + projectName + ) +} + +function toValidPackageName(projectName: string) { + return projectName + .trim() + .toLowerCase() + .replace(/\s+/g, '-') + .replace(/^[._]/, '') + .replace(/[^a-z0-9-~]+/g, '-') +} + +function copyDir(srcDir: string, destDir: string) { + fs.mkdirSync(destDir, { recursive: true }) + for (const file of fs.readdirSync(srcDir)) { + const srcFile = path.resolve(srcDir, file) + const destFile = path.resolve(destDir, file) + copy(srcFile, destFile) + } +} + +function isEmpty(path: string) { + const files = fs.readdirSync(path) + return files.length === 0 || (files.length === 1 && files[0] === '.git') +} + +function emptyDir(dir: string) { + if (!fs.existsSync(dir)) { + return + } + for (const file of fs.readdirSync(dir)) { + if (file === '.git') { + continue + } + fs.rmSync(path.resolve(dir, file), { recursive: true, force: true }) + } +} + +function pkgFromUserAgent(userAgent: string | undefined) { + if (!userAgent) return undefined + const pkgSpec = userAgent.split(' ')[0] + const pkgSpecArr = pkgSpec.split('/') + return { + name: pkgSpecArr[0], + version: pkgSpecArr[1] + } +} + +init().catch((e) => { + console.error(e) +}) diff --git a/packages/create-vite/template-lit-ts/package.json b/packages/create-vite/template-lit-ts/package.json index ad26e218ecde13..77f3617924e240 100644 --- a/packages/create-vite/template-lit-ts/package.json +++ b/packages/create-vite/template-lit-ts/package.json @@ -17,10 +17,10 @@ "build": "tsc && vite build" }, "dependencies": { - "lit": "^2.2.8" + "lit": "^2.4.0" }, "devDependencies": { "typescript": "^4.6.4", - "vite": "^3.0.2" + "vite": "^3.2.1" } } diff --git a/packages/create-vite/template-lit-ts/src/my-element.ts b/packages/create-vite/template-lit-ts/src/my-element.ts index da680eb1d0f19c..dde5d8cd50de89 100644 --- a/packages/create-vite/template-lit-ts/src/my-element.ts +++ b/packages/create-vite/template-lit-ts/src/my-element.ts @@ -1,4 +1,4 @@ -import { html, css, LitElement } from 'lit' +import { LitElement, css, html } from 'lit' import { customElement, property } from 'lit/decorators.js' import litLogo from './assets/lit.svg' diff --git a/packages/create-vite/template-lit/package.json b/packages/create-vite/template-lit/package.json index 125f1a4c00f527..107e184e7a7a97 100644 --- a/packages/create-vite/template-lit/package.json +++ b/packages/create-vite/template-lit/package.json @@ -15,9 +15,9 @@ "build": "vite build" }, "dependencies": { - "lit": "^2.2.8" + "lit": "^2.4.0" }, "devDependencies": { - "vite": "^3.0.2" + "vite": "^3.2.1" } } diff --git a/packages/create-vite/template-lit/src/my-element.js b/packages/create-vite/template-lit/src/my-element.js index 411701454b89de..7c0c0426e49116 100644 --- a/packages/create-vite/template-lit/src/my-element.js +++ b/packages/create-vite/template-lit/src/my-element.js @@ -1,4 +1,4 @@ -import { html, css, LitElement } from 'lit' +import { LitElement, css, html } from 'lit' import litLogo from './assets/lit.svg' /** diff --git a/packages/create-vite/template-preact-ts/package.json b/packages/create-vite/template-preact-ts/package.json index 08cc2ee8862a30..cf062d1007e9b3 100644 --- a/packages/create-vite/template-preact-ts/package.json +++ b/packages/create-vite/template-preact-ts/package.json @@ -9,11 +9,11 @@ "preview": "vite preview" }, "dependencies": { - "preact": "^10.10.0" + "preact": "^10.11.2" }, "devDependencies": { - "@preact/preset-vite": "^2.3.0", + "@preact/preset-vite": "^2.4.0", "typescript": "^4.6.4", - "vite": "^3.0.2" + "vite": "^3.2.1" } } diff --git a/packages/create-vite/template-preact-ts/src/preact.d.ts b/packages/create-vite/template-preact-ts/src/preact.d.ts deleted file mode 100644 index ac79d62a49b4ac..00000000000000 --- a/packages/create-vite/template-preact-ts/src/preact.d.ts +++ /dev/null @@ -1 +0,0 @@ -import JSX = preact.JSX diff --git a/packages/create-vite/template-preact-ts/tsconfig.json b/packages/create-vite/template-preact-ts/tsconfig.json index 0a24dec18dd4f8..9c1b1e0aa6f1df 100644 --- a/packages/create-vite/template-preact-ts/tsconfig.json +++ b/packages/create-vite/template-preact-ts/tsconfig.json @@ -14,9 +14,8 @@ "resolveJsonModule": true, "isolatedModules": true, "noEmit": true, - "jsx": "preserve", - "jsxFactory": "h", - "jsxFragmentFactory": "Fragment" + "jsx": "react-jsx", + "jsxImportSource": "preact" }, "include": ["src"], "references": [{ "path": "./tsconfig.node.json" }] diff --git a/packages/create-vite/template-preact/package.json b/packages/create-vite/template-preact/package.json index 305161e05cd1de..1572fcfd0b626a 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.10.0" + "preact": "^10.11.2" }, "devDependencies": { - "@preact/preset-vite": "^2.3.0", - "vite": "^3.0.2" + "@preact/preset-vite": "^2.4.0", + "vite": "^3.2.1" } } diff --git a/packages/create-vite/template-react-ts/package.json b/packages/create-vite/template-react-ts/package.json index 5f5431e58c7150..c389d9a6fbcb6d 100644 --- a/packages/create-vite/template-react-ts/package.json +++ b/packages/create-vite/template-react-ts/package.json @@ -13,10 +13,10 @@ "react-dom": "^18.2.0" }, "devDependencies": { - "@types/react": "^18.0.15", - "@types/react-dom": "^18.0.6", - "@vitejs/plugin-react": "^2.0.0", + "@types/react": "^18.0.24", + "@types/react-dom": "^18.0.8", + "@vitejs/plugin-react": "^2.2.0", "typescript": "^4.6.4", - "vite": "^3.0.2" + "vite": "^3.2.1" } } diff --git a/packages/create-vite/template-react/package.json b/packages/create-vite/template-react/package.json index 06f85bc71db5e5..35173e53650a4e 100644 --- a/packages/create-vite/template-react/package.json +++ b/packages/create-vite/template-react/package.json @@ -13,9 +13,9 @@ "react-dom": "^18.2.0" }, "devDependencies": { - "@types/react": "^18.0.15", - "@types/react-dom": "^18.0.6", - "@vitejs/plugin-react": "^2.0.0", - "vite": "^3.0.2" + "@types/react": "^18.0.24", + "@types/react-dom": "^18.0.8", + "@vitejs/plugin-react": "^2.2.0", + "vite": "^3.2.1" } } diff --git a/packages/create-vite/template-svelte-ts/README.md b/packages/create-vite/template-svelte-ts/README.md index 4ef762ffec4df3..e6cd94fce77dca 100644 --- a/packages/create-vite/template-svelte-ts/README.md +++ b/packages/create-vite/template-svelte-ts/README.md @@ -16,7 +16,6 @@ Check out [SvelteKit](https://github.com/sveltejs/kit#readme), which is also pow - It brings its own routing solution which might not be preferable for some users. - It is first and foremost a framework that just happens to use Vite under the hood, not a Vite app. - `vite dev` and `vite build` wouldn't work in a SvelteKit environment, for example. This template contains as little as possible to get started with Vite + TypeScript + Svelte, while taking into account the developer experience with regards to HMR and intellisense. It demonstrates capabilities on par with the other `create-vite` templates and is a good starting point for beginners dipping their toes into a Vite + Svelte project. diff --git a/packages/create-vite/template-svelte-ts/package.json b/packages/create-vite/template-svelte-ts/package.json index cdb7ed7c39f0ae..6e80ff44130df1 100644 --- a/packages/create-vite/template-svelte-ts/package.json +++ b/packages/create-vite/template-svelte-ts/package.json @@ -10,13 +10,13 @@ "check": "svelte-check --tsconfig ./tsconfig.json" }, "devDependencies": { - "@sveltejs/vite-plugin-svelte": "^1.0.1", + "@sveltejs/vite-plugin-svelte": "^1.1.0", "@tsconfig/svelte": "^3.0.0", - "svelte": "^3.49.0", - "svelte-check": "^2.8.0", + "svelte": "^3.52.0", + "svelte-check": "^2.9.2", "svelte-preprocess": "^4.10.7", "tslib": "^2.4.0", "typescript": "^4.6.4", - "vite": "^3.0.2" + "vite": "^3.2.1" } } diff --git a/packages/create-vite/template-svelte-ts/src/App.svelte b/packages/create-vite/template-svelte-ts/src/App.svelte index d657a537e4829a..1e9dc039494ef8 100644 --- a/packages/create-vite/template-svelte-ts/src/App.svelte +++ b/packages/create-vite/template-svelte-ts/src/App.svelte @@ -5,10 +5,10 @@
@@ -19,7 +19,7 @@

- Check out SvelteKit, the official Svelte app framework powered by Vite! + Check out SvelteKit, the official Svelte app framework powered by Vite!

diff --git a/packages/create-vite/template-svelte-ts/tsconfig.json b/packages/create-vite/template-svelte-ts/tsconfig.json index d38303196ae8dc..c4e1c5fe6e73a4 100644 --- a/packages/create-vite/template-svelte-ts/tsconfig.json +++ b/packages/create-vite/template-svelte-ts/tsconfig.json @@ -5,7 +5,6 @@ "useDefineForClassFields": true, "module": "ESNext", "resolveJsonModule": true, - "baseUrl": ".", /** * Typecheck JS in `.svelte` and `.js` files by default. * Disable checkJs if you'd like to use dynamic types in JS. diff --git a/packages/create-vite/template-svelte/README.md b/packages/create-vite/template-svelte/README.md index 50ea7ed3b9132d..69c2ac55e18166 100644 --- a/packages/create-vite/template-svelte/README.md +++ b/packages/create-vite/template-svelte/README.md @@ -16,7 +16,6 @@ Check out [SvelteKit](https://github.com/sveltejs/kit#readme), which is also pow - It brings its own routing solution which might not be preferable for some users. - It is first and foremost a framework that just happens to use Vite under the hood, not a Vite app. - `vite dev` and `vite build` wouldn't work in a SvelteKit environment, for example. This template contains as little as possible to get started with Vite + Svelte, while taking into account the developer experience with regards to HMR and intellisense. It demonstrates capabilities on par with the other `create-vite` templates and is a good starting point for beginners dipping their toes into a Vite + Svelte project. diff --git a/packages/create-vite/template-svelte/jsconfig.json b/packages/create-vite/template-svelte/jsconfig.json index ee5e92f298e484..e596c582326d98 100644 --- a/packages/create-vite/template-svelte/jsconfig.json +++ b/packages/create-vite/template-svelte/jsconfig.json @@ -19,7 +19,6 @@ "esModuleInterop": true, "skipLibCheck": true, "forceConsistentCasingInFileNames": true, - "baseUrl": ".", /** * Typecheck JS in `.svelte` and `.js` files by default. * Disable this if you'd like to use dynamic types. diff --git a/packages/create-vite/template-svelte/package.json b/packages/create-vite/template-svelte/package.json index 3e401fc3cd673d..0270ae51af6021 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": "^1.0.1", - "svelte": "^3.49.0", - "vite": "^3.0.2" + "@sveltejs/vite-plugin-svelte": "^1.1.0", + "svelte": "^3.52.0", + "vite": "^3.2.1" } } diff --git a/packages/create-vite/template-svelte/src/App.svelte b/packages/create-vite/template-svelte/src/App.svelte index 704ad09e9ea536..7d6f4ed4339f95 100644 --- a/packages/create-vite/template-svelte/src/App.svelte +++ b/packages/create-vite/template-svelte/src/App.svelte @@ -5,10 +5,10 @@

@@ -19,7 +19,7 @@

- Check out SvelteKit, the official Svelte app framework powered by Vite! + Check out SvelteKit, the official Svelte app framework powered by Vite!

diff --git a/packages/create-vite/template-vanilla-ts/package.json b/packages/create-vite/template-vanilla-ts/package.json index 89881abdc58079..607301c0314882 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": "^4.6.4", - "vite": "^3.0.2" + "vite": "^3.2.1" } } diff --git a/packages/create-vite/template-vanilla-ts/src/counter.ts b/packages/create-vite/template-vanilla-ts/src/counter.ts index a3529e1f26bfc8..09e5afd2d8ad2b 100644 --- a/packages/create-vite/template-vanilla-ts/src/counter.ts +++ b/packages/create-vite/template-vanilla-ts/src/counter.ts @@ -4,6 +4,6 @@ export function setupCounter(element: HTMLButtonElement) { counter = count element.innerHTML = `count is ${counter}` } - element.addEventListener('click', () => setCounter(++counter)) + element.addEventListener('click', () => setCounter(counter + 1)) setCounter(0) } diff --git a/packages/create-vite/template-vanilla-ts/tsconfig.json b/packages/create-vite/template-vanilla-ts/tsconfig.json index fbd022532d3096..eac16d14a6f2e2 100644 --- a/packages/create-vite/template-vanilla-ts/tsconfig.json +++ b/packages/create-vite/template-vanilla-ts/tsconfig.json @@ -6,7 +6,6 @@ "lib": ["ESNext", "DOM"], "moduleResolution": "Node", "strict": true, - "sourceMap": true, "resolveJsonModule": true, "isolatedModules": true, "esModuleInterop": true, diff --git a/packages/create-vite/template-vanilla/counter.js b/packages/create-vite/template-vanilla/counter.js index 12ae65abfaea09..881e2d7adf529f 100644 --- a/packages/create-vite/template-vanilla/counter.js +++ b/packages/create-vite/template-vanilla/counter.js @@ -4,6 +4,6 @@ export function setupCounter(element) { counter = count element.innerHTML = `count is ${counter}` } - element.addEventListener('click', () => setCounter(++counter)) + element.addEventListener('click', () => setCounter(counter + 1)) setCounter(0) } diff --git a/packages/create-vite/template-vanilla/package.json b/packages/create-vite/template-vanilla/package.json index e3053d7fe79533..2aa2f582aa9968 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": "^3.0.2" + "vite": "^3.2.1" } } diff --git a/packages/create-vite/template-vue-ts/package.json b/packages/create-vite/template-vue-ts/package.json index 35526e650e6bc3..7ac6cc2cf45ba3 100644 --- a/packages/create-vite/template-vue-ts/package.json +++ b/packages/create-vite/template-vue-ts/package.json @@ -5,16 +5,16 @@ "type": "module", "scripts": { "dev": "vite", - "build": "vue-tsc --noEmit && vite build", + "build": "vue-tsc && vite build", "preview": "vite preview" }, "dependencies": { - "vue": "^3.2.37" + "vue": "^3.2.41" }, "devDependencies": { - "@vitejs/plugin-vue": "^3.0.1", + "@vitejs/plugin-vue": "^3.2.0", "typescript": "^4.6.4", - "vite": "^3.0.2", - "vue-tsc": "^0.39.0" + "vite": "^3.2.1", + "vue-tsc": "^1.0.9" } } diff --git a/packages/create-vite/template-vue-ts/tsconfig.json b/packages/create-vite/template-vue-ts/tsconfig.json index d4aefa2c929dc2..b557c4047cac64 100644 --- a/packages/create-vite/template-vue-ts/tsconfig.json +++ b/packages/create-vite/template-vue-ts/tsconfig.json @@ -6,12 +6,12 @@ "moduleResolution": "Node", "strict": true, "jsx": "preserve", - "sourceMap": true, "resolveJsonModule": true, "isolatedModules": true, "esModuleInterop": true, "lib": ["ESNext", "DOM"], - "skipLibCheck": true + "skipLibCheck": true, + "noEmit": true }, "include": ["src/**/*.ts", "src/**/*.d.ts", "src/**/*.tsx", "src/**/*.vue"], "references": [{ "path": "./tsconfig.node.json" }] diff --git a/packages/create-vite/template-vue/package.json b/packages/create-vite/template-vue/package.json index 5bf8423ad99da6..fe6ff2fc21dd75 100644 --- a/packages/create-vite/template-vue/package.json +++ b/packages/create-vite/template-vue/package.json @@ -9,10 +9,10 @@ "preview": "vite preview" }, "dependencies": { - "vue": "^3.2.37" + "vue": "^3.2.41" }, "devDependencies": { - "@vitejs/plugin-vue": "^3.0.1", - "vite": "^3.0.2" + "@vitejs/plugin-vue": "^3.2.0", + "vite": "^3.2.1" } } diff --git a/packages/create-vite/tsconfig.json b/packages/create-vite/tsconfig.json new file mode 100644 index 00000000000000..0ec39bdf6a1404 --- /dev/null +++ b/packages/create-vite/tsconfig.json @@ -0,0 +1,14 @@ +{ + "include": ["src", "__tests__"], + "compilerOptions": { + "outDir": "dist", + "target": "ES2020", + "module": "ES2020", + "moduleResolution": "Node", + "strict": true, + "declaration": false, + "sourceMap": false, + "noUnusedLocals": true, + "esModuleInterop": true + } +} diff --git a/packages/plugin-legacy/CHANGELOG.md b/packages/plugin-legacy/CHANGELOG.md index 11783e3f959c44..77c9684a7e4f1d 100644 --- a/packages/plugin-legacy/CHANGELOG.md +++ b/packages/plugin-legacy/CHANGELOG.md @@ -1,3 +1,58 @@ +## 2.3.0 (2022-10-26) + +* fix(deps): update all non-major dependencies (#10610) ([bb95467](https://github.com/vitejs/vite/commit/bb95467)), closes [#10610](https://github.com/vitejs/vite/issues/10610) +* chore(deps): update all non-major dependencies (#10393) ([f519423](https://github.com/vitejs/vite/commit/f519423)), closes [#10393](https://github.com/vitejs/vite/issues/10393) +* chore(deps): update all non-major dependencies (#10488) ([15aa827](https://github.com/vitejs/vite/commit/15aa827)), closes [#10488](https://github.com/vitejs/vite/issues/10488) + + + +## 2.3.0-beta.0 (2022-10-05) + +* fix(deps): update all non-major dependencies (#10160) ([6233c83](https://github.com/vitejs/vite/commit/6233c83)), closes [#10160](https://github.com/vitejs/vite/issues/10160) +* fix(deps): update all non-major dependencies (#10246) ([81d4d04](https://github.com/vitejs/vite/commit/81d4d04)), closes [#10246](https://github.com/vitejs/vite/issues/10246) +* fix(deps): update all non-major dependencies (#10316) ([a38b450](https://github.com/vitejs/vite/commit/a38b450)), closes [#10316](https://github.com/vitejs/vite/issues/10316) +* fix(legacy): don't force set `build.target` when `renderLegacyChunks=false` (fixes #10201) (#10220) ([7f548e8](https://github.com/vitejs/vite/commit/7f548e8)), closes [#10201](https://github.com/vitejs/vite/issues/10201) [#10220](https://github.com/vitejs/vite/issues/10220) +* refactor(types): bundle client types (#9966) ([da632bf](https://github.com/vitejs/vite/commit/da632bf)), closes [#9966](https://github.com/vitejs/vite/issues/9966) + + + +## 2.2.0 (2022-09-19) + +* docs(plugin-legacy): fix Vite default target (#10158) ([62ff788](https://github.com/vitejs/vite/commit/62ff788)), closes [#10158](https://github.com/vitejs/vite/issues/10158) +* fix(deps): update all non-major dependencies (#10077) ([caf00c8](https://github.com/vitejs/vite/commit/caf00c8)), closes [#10077](https://github.com/vitejs/vite/issues/10077) +* fix(deps): update all non-major dependencies (#9985) ([855f2f0](https://github.com/vitejs/vite/commit/855f2f0)), closes [#9985](https://github.com/vitejs/vite/issues/9985) +* fix(plugin-legacy): force set `build.target` (#10072) ([a13a7eb](https://github.com/vitejs/vite/commit/a13a7eb)), closes [#10072](https://github.com/vitejs/vite/issues/10072) + + + +## 2.1.0 (2022-09-05) + + + + +## 2.1.0-beta.0 (2022-08-29) + +* fix(deps): update all non-major dependencies (#9888) ([e35a58b](https://github.com/vitejs/vite/commit/e35a58b)), closes [#9888](https://github.com/vitejs/vite/issues/9888) +* fix(plugin-legacy): prevent global process.env.NODE_ENV mutation (#9741) ([a8279af](https://github.com/vitejs/vite/commit/a8279af)), closes [#9741](https://github.com/vitejs/vite/issues/9741) +* chore(deps): update all non-major dependencies (#9675) ([4e56e87](https://github.com/vitejs/vite/commit/4e56e87)), closes [#9675](https://github.com/vitejs/vite/issues/9675) +* chore(deps): update all non-major dependencies (#9778) ([aceaefc](https://github.com/vitejs/vite/commit/aceaefc)), closes [#9778](https://github.com/vitejs/vite/issues/9778) +* refactor(legacy): build polyfill chunk (#9639) ([7ba0c9f](https://github.com/vitejs/vite/commit/7ba0c9f)), closes [#9639](https://github.com/vitejs/vite/issues/9639) +* refactor(legacy): remove code for Vite 2 (#9640) ([b1bbc5b](https://github.com/vitejs/vite/commit/b1bbc5b)), closes [#9640](https://github.com/vitejs/vite/issues/9640) + + + +## 2.0.1 (2022-08-11) + +* fix: mention that Node.js 13/15 support is dropped (fixes #9113) (#9116) ([2826303](https://github.com/vitejs/vite/commit/2826303)), closes [#9113](https://github.com/vitejs/vite/issues/9113) [#9116](https://github.com/vitejs/vite/issues/9116) +* fix(deps): update all non-major dependencies (#9176) ([31d3b70](https://github.com/vitejs/vite/commit/31d3b70)), closes [#9176](https://github.com/vitejs/vite/issues/9176) +* fix(deps): update all non-major dependencies (#9575) ([8071325](https://github.com/vitejs/vite/commit/8071325)), closes [#9575](https://github.com/vitejs/vite/issues/9575) +* fix(legacy): skip esbuild transform for systemjs (#9635) ([ac16abd](https://github.com/vitejs/vite/commit/ac16abd)), closes [#9635](https://github.com/vitejs/vite/issues/9635) +* chore: fix code typos (#9033) ([ed02861](https://github.com/vitejs/vite/commit/ed02861)), closes [#9033](https://github.com/vitejs/vite/issues/9033) +* chore(deps): update all non-major dependencies (#9347) ([2fcb027](https://github.com/vitejs/vite/commit/2fcb027)), closes [#9347](https://github.com/vitejs/vite/issues/9347) +* chore(deps): update all non-major dependencies (#9478) ([c530d16](https://github.com/vitejs/vite/commit/c530d16)), closes [#9478](https://github.com/vitejs/vite/issues/9478) + + + ## 2.0.0 (2022-07-13) * chore: 3.0 release notes and bump peer deps (#9072) ([427ba26](https://github.com/vitejs/vite/commit/427ba26)), closes [#9072](https://github.com/vitejs/vite/issues/9072) diff --git a/packages/plugin-legacy/README.md b/packages/plugin-legacy/README.md index b5c3631ab2ac60..708ba6059561f1 100644 --- a/packages/plugin-legacy/README.md +++ b/packages/plugin-legacy/README.md @@ -1,6 +1,6 @@ # @vitejs/plugin-legacy [![npm](https://img.shields.io/npm/v/@vitejs/plugin-legacy.svg)](https://npmjs.com/package/@vitejs/plugin-legacy) -Vite's default browser support baseline is [Native ESM](https://caniuse.com/es6-module). This plugin provides support for legacy browsers that do not support native ESM when building for production. +Vite's default browser support baseline is [Native ESM](https://caniuse.com/es6-module), [native ESM dynamic import](https://caniuse.com/es6-module-dynamic-import), and [`import.meta`](https://caniuse.com/mdn-javascript_operators_import_meta). This plugin provides support for legacy browsers that do not support those features when building for production. By default, this plugin will: diff --git a/packages/plugin-legacy/build.config.ts b/packages/plugin-legacy/build.config.ts index 6dcf1a5a2dc0b3..12afbbc54d1efd 100644 --- a/packages/plugin-legacy/build.config.ts +++ b/packages/plugin-legacy/build.config.ts @@ -5,6 +5,7 @@ export default defineBuildConfig({ clean: true, declaration: true, rollup: { - emitCJS: true + emitCJS: true, + inlineDependencies: true } }) diff --git a/packages/plugin-legacy/package.json b/packages/plugin-legacy/package.json index 6d416fe5e96474..4629625b567e0c 100644 --- a/packages/plugin-legacy/package.json +++ b/packages/plugin-legacy/package.json @@ -1,6 +1,6 @@ { "name": "@vitejs/plugin-legacy", - "version": "2.0.0", + "version": "2.3.0", "license": "MIT", "author": "Evan You", "files": [ @@ -35,18 +35,19 @@ }, "homepage": "https://github.com/vitejs/vite/tree/main/packages/plugin-legacy#readme", "dependencies": { - "@babel/standalone": "^7.18.9", - "core-js": "^3.24.0", - "magic-string": "^0.26.2", - "regenerator-runtime": "^0.13.9", - "systemjs": "^6.12.1" + "@babel/standalone": "^7.20.0", + "core-js": "^3.26.0", + "magic-string": "^0.26.7", + "regenerator-runtime": "^0.13.10", + "systemjs": "^6.13.0" }, "peerDependencies": { "terser": "^5.4.0", "vite": "^3.0.0" }, "devDependencies": { - "@babel/core": "^7.18.9", + "@babel/core": "^7.19.6", + "picocolors": "^1.0.0", "vite": "workspace:*" } } diff --git a/packages/plugin-legacy/src/index.ts b/packages/plugin-legacy/src/index.ts index ca79a56f160146..203cad9dc293c6 100644 --- a/packages/plugin-legacy/src/index.ts +++ b/packages/plugin-legacy/src/index.ts @@ -19,6 +19,7 @@ import type { RenderedChunk } from 'rollup' import type { PluginItem as BabelPlugin } from '@babel/core' +import colors from 'picocolors' import type { Options } from './types' // lazy load babel since it's not used during dev @@ -156,22 +157,56 @@ function viteLegacyPlugin(options: Options = {}): Plugin[] { }) } + let overriddenBuildTarget = false const legacyConfigPlugin: Plugin = { name: 'vite:legacy-config', - apply: 'build', - config(config) { - if (!config.build) { - config.build = {} + config(config, env) { + if (env.command === 'build') { + if (!config.build) { + config.build = {} + } + + if (!config.build.cssTarget) { + // Hint for esbuild that we are targeting legacy browsers when minifying CSS. + // Full CSS compat table available at https://github.com/evanw/esbuild/blob/78e04680228cf989bdd7d471e02bbc2c8d345dc9/internal/compat/css_table.go + // But note that only the `HexRGBA` feature affects the minify outcome. + // HSL & rebeccapurple values will be minified away regardless the target. + // So targeting `chrome61` suffices to fix the compatibility issue. + config.build.cssTarget = 'chrome61' + } + + if (genLegacy) { + // Vite's default target browsers are **not** the same. + // See https://github.com/vitejs/vite/pull/10052#issuecomment-1242076461 + overriddenBuildTarget = config.build.target !== undefined + // browsers supporting ESM + dynamic import + import.meta + config.build.target = [ + 'es2020', + 'edge79', + 'firefox67', + 'chrome64', + 'safari11.1' + ] + } } - if (!config.build.cssTarget) { - // Hint for esbuild that we are targeting legacy browsers when minifying CSS. - // Full CSS compat table available at https://github.com/evanw/esbuild/blob/78e04680228cf989bdd7d471e02bbc2c8d345dc9/internal/compat/css_table.go - // But note that only the `HexRGBA` feature affects the minify outcome. - // HSL & rebeccapurple values will be minified away regardless the target. - // So targeting `chrome61` suffices to fix the compatibility issue. - config.build.cssTarget = 'chrome61' + return { + define: { + 'import.meta.env.LEGACY': + env.command === 'serve' || config.build?.ssr + ? false + : legacyEnvVarMarker + } + } + }, + configResolved(config) { + if (overriddenBuildTarget) { + config.logger.warn( + colors.yellow( + `plugin-legacy overrode 'build.target'. You should pass 'targets' as an option to this plugin with the list of legacy browsers to support instead.` + ) + ) } } } @@ -195,6 +230,7 @@ function viteLegacyPlugin(options: Options = {}): Plugin[] { modernPolyfills ) await buildPolyfillChunk( + config.mode, modernPolyfills, bundle, facadeToModernPolyfillMap, @@ -227,6 +263,7 @@ function viteLegacyPlugin(options: Options = {}): Plugin[] { ) await buildPolyfillChunk( + config.mode, legacyPolyfills, bundle, facadeToLegacyPolyfillMap, @@ -547,41 +584,7 @@ function viteLegacyPlugin(options: Options = {}): Plugin[] { } } - let envInjectionFailed = false - const legacyEnvPlugin: Plugin = { - name: 'vite:legacy-env', - - config(config, env) { - if (env) { - return { - define: { - 'import.meta.env.LEGACY': - env.command === 'serve' || config.build?.ssr - ? false - : legacyEnvVarMarker - } - } - } else { - envInjectionFailed = true - } - }, - - configResolved(config) { - if (envInjectionFailed) { - config.logger.warn( - `[@vitejs/plugin-legacy] import.meta.env.LEGACY was not injected due ` + - `to incompatible vite version (requires vite@^2.0.0-beta.69).` - ) - } - } - } - - return [ - legacyConfigPlugin, - legacyGenerateBundlePlugin, - legacyPostPlugin, - legacyEnvPlugin - ] + return [legacyConfigPlugin, legacyGenerateBundlePlugin, legacyPostPlugin] } export async function detectPolyfills( @@ -639,6 +642,7 @@ function createBabelPresetEnvOptions( } async function buildPolyfillChunk( + mode: string, imports: Set, bundle: OutputBundle, facadeToChunkMap: Map, @@ -650,6 +654,7 @@ async function buildPolyfillChunk( let { minify, assetsDir } = buildOptions minify = minify ? 'terser' : false const res = await build({ + mode, // so that everything is resolved from here root: path.dirname(fileURLToPath(import.meta.url)), configFile: false, @@ -657,8 +662,6 @@ async function buildPolyfillChunk( plugins: [polyfillsPlugin(imports, excludeSystemJS)], build: { write: false, - // if a value above 'es5' is set, esbuild injects helper functions which uses es2015 features - target: 'es5', minify, assetsDir, rollupOptions: { @@ -667,10 +670,21 @@ async function buildPolyfillChunk( }, output: { format, - entryFileNames: rollupOutputOptions.entryFileNames, - manualChunks: undefined + entryFileNames: rollupOutputOptions.entryFileNames } } + }, + // Don't run esbuild for transpilation or minification + // because we don't want to transpile code. + esbuild: false, + optimizeDeps: { + esbuildOptions: { + // If a value above 'es5' is set, esbuild injects helper functions which uses es2015 features. + // This limits the input code not to include es2015+ codes. + // But core-js is the only dependency which includes commonjs code + // and core-js doesn't include es2015+ codes. + target: 'es5' + } } }) const _polyfillChunk = Array.isArray(res) ? res[0] : res diff --git a/packages/plugin-legacy/tsconfig.json b/packages/plugin-legacy/tsconfig.json index ddb60f864b7e6d..bd94458fe2dc28 100644 --- a/packages/plugin-legacy/tsconfig.json +++ b/packages/plugin-legacy/tsconfig.json @@ -12,7 +12,6 @@ "noUnusedLocals": true, "esModuleInterop": true, "paths": { - "types/*": ["../vite/types/*"], "vite": ["../vite/src/node/index.js"] } } diff --git a/packages/plugin-react/CHANGELOG.md b/packages/plugin-react/CHANGELOG.md index 78b6de12fea5f0..fa6f5f3a57a6f3 100644 --- a/packages/plugin-react/CHANGELOG.md +++ b/packages/plugin-react/CHANGELOG.md @@ -1,3 +1,48 @@ +## 2.2.0 (2022-10-26) + +* fix(deps): update all non-major dependencies (#10610) ([bb95467](https://github.com/vitejs/vite/commit/bb95467)), closes [#10610](https://github.com/vitejs/vite/issues/10610) +* fix(plugin-react): update `package.json` (#10479) ([7f45eb5](https://github.com/vitejs/vite/commit/7f45eb5)), closes [#10479](https://github.com/vitejs/vite/issues/10479) +* chore(deps): update all non-major dependencies (#10393) ([f519423](https://github.com/vitejs/vite/commit/f519423)), closes [#10393](https://github.com/vitejs/vite/issues/10393) + + + +## 2.2.0-beta.0 (2022-10-05) + +* fix(deps): update all non-major dependencies (#10077) ([caf00c8](https://github.com/vitejs/vite/commit/caf00c8)), closes [#10077](https://github.com/vitejs/vite/issues/10077) +* fix(deps): update all non-major dependencies (#10160) ([6233c83](https://github.com/vitejs/vite/commit/6233c83)), closes [#10160](https://github.com/vitejs/vite/issues/10160) +* fix(deps): update all non-major dependencies (#10316) ([a38b450](https://github.com/vitejs/vite/commit/a38b450)), closes [#10316](https://github.com/vitejs/vite/issues/10316) +* fix(deps): update all non-major dependencies (#9985) ([855f2f0](https://github.com/vitejs/vite/commit/855f2f0)), closes [#9985](https://github.com/vitejs/vite/issues/9985) +* fix(react): conditionally self-accept fast-refresh HMR (#10239) ([e976b06](https://github.com/vitejs/vite/commit/e976b06)), closes [#10239](https://github.com/vitejs/vite/issues/10239) +* feat: add `throwIfNamespace` option for custom JSX runtime (#9571) ([f842f74](https://github.com/vitejs/vite/commit/f842f74)), closes [#9571](https://github.com/vitejs/vite/issues/9571) +* refactor(types): bundle client types (#9966) ([da632bf](https://github.com/vitejs/vite/commit/da632bf)), closes [#9966](https://github.com/vitejs/vite/issues/9966) + + + +## 2.1.0 (2022-09-05) + +* fix(plugin-react): duplicate __self prop and __source prop (#9387) ([c89de3a](https://github.com/vitejs/vite/commit/c89de3a)), closes [#9387](https://github.com/vitejs/vite/issues/9387) + + + +## 2.1.0-beta.0 (2022-08-29) + +* docs: fix typo (#9855) ([583f185](https://github.com/vitejs/vite/commit/583f185)), closes [#9855](https://github.com/vitejs/vite/issues/9855) +* fix: add `react` to `optimizeDeps` (#9056) ([bc4a627](https://github.com/vitejs/vite/commit/bc4a627)), closes [#9056](https://github.com/vitejs/vite/issues/9056) +* fix(deps): update all non-major dependencies (#9888) ([e35a58b](https://github.com/vitejs/vite/commit/e35a58b)), closes [#9888](https://github.com/vitejs/vite/issues/9888) + + + +## 2.0.1 (2022-08-11) + +* fix: don't count class declarations as react fast refresh boundry (fixes #3675) (#8887) ([5a18284](https://github.com/vitejs/vite/commit/5a18284)), closes [#3675](https://github.com/vitejs/vite/issues/3675) [#8887](https://github.com/vitejs/vite/issues/8887) +* fix: mention that Node.js 13/15 support is dropped (fixes #9113) (#9116) ([2826303](https://github.com/vitejs/vite/commit/2826303)), closes [#9113](https://github.com/vitejs/vite/issues/9113) [#9116](https://github.com/vitejs/vite/issues/9116) +* fix(deps): update all non-major dependencies (#9176) ([31d3b70](https://github.com/vitejs/vite/commit/31d3b70)), closes [#9176](https://github.com/vitejs/vite/issues/9176) +* fix(deps): update all non-major dependencies (#9575) ([8071325](https://github.com/vitejs/vite/commit/8071325)), closes [#9575](https://github.com/vitejs/vite/issues/9575) +* fix(plugin-react): wrong substitution causes `React is not defined` (#9386) ([8a5b575](https://github.com/vitejs/vite/commit/8a5b575)), closes [#9386](https://github.com/vitejs/vite/issues/9386) +* docs: fix server options link (#9242) ([29db3ea](https://github.com/vitejs/vite/commit/29db3ea)), closes [#9242](https://github.com/vitejs/vite/issues/9242) + + + ## 2.0.0 (2022-07-13) * chore: 3.0 release notes and bump peer deps (#9072) ([427ba26](https://github.com/vitejs/vite/commit/427ba26)), closes [#9072](https://github.com/vitejs/vite/issues/9072) diff --git a/packages/plugin-react/package.json b/packages/plugin-react/package.json index 47a97b5ef612a9..273f8c49405782 100644 --- a/packages/plugin-react/package.json +++ b/packages/plugin-react/package.json @@ -1,14 +1,13 @@ { "name": "@vitejs/plugin-react", - "version": "2.0.0", + "version": "2.2.0", "license": "MIT", "author": "Evan You", "contributors": [ "Alec Larson" ], "files": [ - "dist", - "src" + "dist" ], "main": "./dist/index.cjs", "module": "./dist/index.mjs", @@ -39,12 +38,12 @@ }, "homepage": "https://github.com/vitejs/vite/tree/main/packages/plugin-react#readme", "dependencies": { - "@babel/core": "^7.18.9", - "@babel/plugin-transform-react-jsx": "^7.18.6", + "@babel/core": "^7.19.6", + "@babel/plugin-transform-react-jsx": "^7.19.0", "@babel/plugin-transform-react-jsx-development": "^7.18.6", "@babel/plugin-transform-react-jsx-self": "^7.18.6", - "@babel/plugin-transform-react-jsx-source": "^7.18.6", - "magic-string": "^0.26.2", + "@babel/plugin-transform-react-jsx-source": "^7.19.6", + "magic-string": "^0.26.7", "react-refresh": "^0.14.0" }, "peerDependencies": { diff --git a/packages/plugin-react/src/fast-refresh.ts b/packages/plugin-react/src/fast-refresh.ts index 6e5019d1e059df..b0b38a8cafb94e 100644 --- a/packages/plugin-react/src/fast-refresh.ts +++ b/packages/plugin-react/src/fast-refresh.ts @@ -58,20 +58,57 @@ if (import.meta.hot) { window.$RefreshSig$ = RefreshRuntime.createSignatureFunctionForTransform; }`.replace(/[\n]+/gm, '') -const footer = ` -if (import.meta.hot) { - window.$RefreshReg$ = prevRefreshReg; - window.$RefreshSig$ = prevRefreshSig; - - __ACCEPT__ +const timeout = ` if (!window.__vite_plugin_react_timeout) { window.__vite_plugin_react_timeout = setTimeout(() => { window.__vite_plugin_react_timeout = 0; RefreshRuntime.performReactRefresh(); }, 30); } +` + +const footer = ` +if (import.meta.hot) { + window.$RefreshReg$ = prevRefreshReg; + window.$RefreshSig$ = prevRefreshSig; + + __ACCEPT__ }` +const checkAndAccept = ` +function isReactRefreshBoundary(mod) { + if (mod == null || typeof mod !== 'object') { + return false; + } + let hasExports = false; + let areAllExportsComponents = true; + for (const exportName in mod) { + hasExports = true; + if (exportName === '__esModule') { + continue; + } + const desc = Object.getOwnPropertyDescriptor(mod, exportName); + if (desc && desc.get) { + // Don't invoke getters as they may have side effects. + return false; + } + const exportValue = mod[exportName]; + if (!RefreshRuntime.isLikelyComponentType(exportValue)) { + areAllExportsComponents = false; + } + } + return hasExports && areAllExportsComponents; +} + +import.meta.hot.accept(mod => { + if (isReactRefreshBoundary(mod)) { + ${timeout} + } else { + import.meta.hot.invalidate(); + } +}); +` + export function addRefreshWrapper( code: string, id: string, @@ -80,18 +117,20 @@ export function addRefreshWrapper( return ( header.replace('__SOURCE__', JSON.stringify(id)) + code + - footer.replace('__ACCEPT__', accept ? 'import.meta.hot.accept();' : '') + footer.replace('__ACCEPT__', accept ? checkAndAccept : timeout) ) } export function isRefreshBoundary(ast: t.File): boolean { - // Every export must be a React component. + // Every export must be a potential React component. + // We'll also perform a runtime check that's more robust as well (isLikelyComponentType). return ast.program.body.every((node) => { if (node.type !== 'ExportNamedDeclaration') { return true } const { declaration, specifiers } = node if (declaration) { + if (declaration.type === 'ClassDeclaration') return false if (declaration.type === 'VariableDeclaration') { return declaration.declarations.every((variable) => isComponentLikeIdentifier(variable.id) diff --git a/packages/plugin-react/src/index.ts b/packages/plugin-react/src/index.ts index f1adf81510bcef..3617605afc686d 100644 --- a/packages/plugin-react/src/index.ts +++ b/packages/plugin-react/src/index.ts @@ -40,6 +40,11 @@ export interface Options { * @default true */ jsxPure?: boolean + /** + * Toggles whether or not to throw an error if an XML namespaced tag name is used. + * @default true + */ + jsxThrowIfNamespace?: boolean /** * Babel configuration applied in both dev and prod. */ @@ -248,7 +253,8 @@ export default function viteReact(opts: Options = {}): PluginOption[] { { runtime: 'automatic', importSource: opts.jsxImportSource, - pure: opts.jsxPure !== false + pure: opts.jsxPure !== false, + throwIfNamespace: opts.jsxThrowIfNamespace } ]) @@ -406,7 +412,10 @@ export default function viteReact(opts: Options = {}): PluginOption[] { config() { return { optimizeDeps: { - include: [reactJsxRuntimeId, reactJsxDevRuntimeId] + // We can't add `react-dom` because the dependency is `react-dom/client` + // for React 18 while it's `react-dom` for React 17. We'd need to detect + // what React version the user has installed. + include: [reactJsxRuntimeId, reactJsxDevRuntimeId, 'react'] } } }, diff --git a/packages/plugin-react/src/jsx-runtime/babel-restore-jsx.spec.ts b/packages/plugin-react/src/jsx-runtime/babel-restore-jsx.spec.ts index b37ef698e5f215..5590bfb9d7fa5f 100644 --- a/packages/plugin-react/src/jsx-runtime/babel-restore-jsx.spec.ts +++ b/packages/plugin-react/src/jsx-runtime/babel-restore-jsx.spec.ts @@ -115,4 +115,18 @@ describe('babel-restore-jsx', () => { `"React.createElement(aaa);"` ) }) + + it('should not handle contains __self prop', () => { + expect( + jsx('React.createElement(Provider, { __self: this })') + ).toMatchInlineSnapshot('";"') + }) + + it('should not handle contains __source prop', () => { + expect( + jsx( + 'React.createElement(Provider, { __source: { fileName: _jsxFileName, lineNumber: 133 }})' + ) + ).toMatchInlineSnapshot('";"') + }) }) diff --git a/packages/plugin-react/src/jsx-runtime/babel-restore-jsx.ts b/packages/plugin-react/src/jsx-runtime/babel-restore-jsx.ts index 669a0aeeced207..e5ee9ce454b555 100644 --- a/packages/plugin-react/src/jsx-runtime/babel-restore-jsx.ts +++ b/packages/plugin-react/src/jsx-runtime/babel-restore-jsx.ts @@ -4,6 +4,10 @@ */ import type * as babel from '@babel/core' +interface PluginOptions { + reactAlias: string +} + /** * Visitor factory for babel, converting React.createElement(...) to ... * @@ -17,7 +21,10 @@ import type * as babel from '@babel/core' * * Any of those arguments might also be missing (undefined) and/or invalid. */ -export default function ({ types: t }: typeof babel): babel.PluginObj { +export default function ( + { types: t }: typeof babel, + { reactAlias = 'React' }: PluginOptions +): babel.PluginObj { /** * Get a `JSXElement` from a `CallExpression`. * Returns `null` if this impossible. @@ -48,7 +55,7 @@ export default function ({ types: t }: typeof babel): babel.PluginObj { if ( t.isJSXMemberExpression(name) && t.isJSXIdentifier(name.object) && - name.object.name === 'React' && + name.object.name === reactAlias && name.property.name === 'Fragment' ) { return t.jsxFragment( @@ -69,7 +76,7 @@ export default function ({ types: t }: typeof babel): babel.PluginObj { /** * Get a JSXIdentifier or JSXMemberExpression from a Node of known type. - * Returns null if a unknown node type, null or undefined is passed. + * Returns null if an unknown node type, null or undefined is passed. */ function getJSXName(node: any): any { if (node == null) { @@ -93,7 +100,7 @@ export default function ({ types: t }: typeof babel): babel.PluginObj { } /** - * Get a array of JSX(Spread)Attribute from a props ObjectExpression. + * Get an array of JSX(Spread)Attribute from a props ObjectExpression. * Handles the _extends Expression babel creates from SpreadElement nodes. * Returns null if a validation error occurs. */ @@ -119,14 +126,20 @@ export default function ({ types: t }: typeof babel): babel.PluginObj { if (!isPlainObjectExpression(node)) { return null } - return node.properties.map((prop: any) => - t.isObjectProperty(prop) - ? t.jsxAttribute( - getJSXIdentifier(prop.key)!, - getJSXAttributeValue(prop.value) - ) - : t.jsxSpreadAttribute(prop.argument) - ) + return node.properties + .map((prop: any) => + t.isObjectProperty(prop) + ? t.jsxAttribute( + getJSXIdentifier(prop.key)!, + getJSXAttributeValue(prop.value) + ) + : t.jsxSpreadAttribute(prop.argument) + ) + .filter((prop: any) => + t.isJSXIdentifier(prop.name) + ? prop.name.name !== '__self' && prop.name.name !== '__source' + : true + ) } function getJSXChild(node: any) { @@ -182,7 +195,7 @@ export default function ({ types: t }: typeof babel): babel.PluginObj { const isReactCreateElement = (node: any) => t.isCallExpression(node) && t.isMemberExpression(node.callee) && - t.isIdentifier(node.callee.object, { name: 'React' }) && + t.isIdentifier(node.callee.object, { name: reactAlias }) && t.isIdentifier(node.callee.property, { name: 'createElement' }) && !node.callee.computed diff --git a/packages/plugin-react/src/jsx-runtime/restore-jsx.spec.ts b/packages/plugin-react/src/jsx-runtime/restore-jsx.spec.ts index 7d5b14bfc9cfd4..00ea39673ec415 100644 --- a/packages/plugin-react/src/jsx-runtime/restore-jsx.spec.ts +++ b/packages/plugin-react/src/jsx-runtime/restore-jsx.spec.ts @@ -81,7 +81,10 @@ describe('restore-jsx', () => { expect( await jsx(`import React__default, { PureComponent, Component, forwardRef, memo, createElement } from 'react'; React__default.createElement(foo)`) - ).toBeNull() + ).toMatchInlineSnapshot(` + "import React__default, { PureComponent, Component, forwardRef, memo, createElement } from 'react'; + React__default.createElement(foo);" + `) expect( await jsx(`import React__default, { PureComponent, Component, forwardRef, memo, createElement } from 'react'; React__default.createElement("h1")`) @@ -104,7 +107,12 @@ describe('restore-jsx', () => { expect( await jsx(`import React__default, { PureComponent, Component, forwardRef, memo, createElement } from 'react'; React__default.createElement(foo, {hi: there})`) - ).toBeNull() + ).toMatchInlineSnapshot(` + "import React__default, { PureComponent, Component, forwardRef, memo, createElement } from 'react'; + React__default.createElement(foo, { + hi: there + });" + `) expect( await jsx(`import React__default, { PureComponent, Component, forwardRef, memo, createElement } from 'react'; React__default.createElement("h1", {hi: there})`) @@ -114,4 +122,26 @@ describe('restore-jsx', () => { React__default.createElement(Foo, {hi: there})`) ).toMatch(`;`) }) + + it('should handle Fragment', async () => { + expect( + await jsx(`import R, { Fragment } from 'react'; + R.createElement(Fragment) + `) + ).toMatchInlineSnapshot(` + "import R, { Fragment } from 'react'; + ;" + `) + }) + + it('should handle Fragment alias', async () => { + expect( + await jsx(`import RA, { Fragment as F } from 'react'; + RA.createElement(F, null, RA.createElement(RA.Fragment)) + `) + ).toMatchInlineSnapshot(` + "import RA, { Fragment as F } from 'react'; + <>;" + `) + }) }) diff --git a/packages/plugin-react/src/jsx-runtime/restore-jsx.ts b/packages/plugin-react/src/jsx-runtime/restore-jsx.ts index d67a54e8f08aad..ddc3c1530e0d9f 100644 --- a/packages/plugin-react/src/jsx-runtime/restore-jsx.ts +++ b/packages/plugin-react/src/jsx-runtime/restore-jsx.ts @@ -33,34 +33,12 @@ export async function restoreJSX( return jsxNotFound } - let hasCompiledJsx = false - - const fragmentPattern = `\\b${reactAlias}\\.Fragment\\b` - const createElementPattern = `\\b${reactAlias}\\.createElement\\(\\s*([A-Z"'][\\w$.]*["']?)` - - // Replace the alias with "React" so JSX can be reverse compiled. - code = code - .replace(new RegExp(fragmentPattern, 'g'), () => { - hasCompiledJsx = true - return 'React.Fragment' - }) - .replace(new RegExp(createElementPattern, 'g'), (original, component) => { - if (/^[a-z][\w$]*$/.test(component)) { - // Take care not to replace the alias for `createElement` calls whose - // component is a lowercased variable, since the `restoreJSX` Babel - // plugin leaves them untouched. - return original - } - hasCompiledJsx = true - return ( - 'React.createElement(' + - // Assume `Fragment` is equivalent to `React.Fragment` so modules - // that use `import {Fragment} from 'react'` are reverse compiled. - (component === 'Fragment' ? 'React.Fragment' : component) - ) - }) + const reactJsxRE = new RegExp( + `\\b${reactAlias}\\.(createElement|Fragment)\\b`, + 'g' + ) - if (!hasCompiledJsx) { + if (!reactJsxRE.test(code)) { return jsxNotFound } @@ -73,7 +51,7 @@ export async function restoreJSX( parserOpts: { plugins: ['jsx'] }, - plugins: [await getBabelRestoreJSX()] + plugins: [[await getBabelRestoreJSX(), { reactAlias }]] }) return [result?.ast, isCommonJS] diff --git a/packages/plugin-react/tsconfig.json b/packages/plugin-react/tsconfig.json index ddb60f864b7e6d..bd94458fe2dc28 100644 --- a/packages/plugin-react/tsconfig.json +++ b/packages/plugin-react/tsconfig.json @@ -12,7 +12,6 @@ "noUnusedLocals": true, "esModuleInterop": true, "paths": { - "types/*": ["../vite/types/*"], "vite": ["../vite/src/node/index.js"] } } diff --git a/packages/plugin-vue-jsx/CHANGELOG.md b/packages/plugin-vue-jsx/CHANGELOG.md index d5c54726f3e012..fe4418c05025ec 100644 --- a/packages/plugin-vue-jsx/CHANGELOG.md +++ b/packages/plugin-vue-jsx/CHANGELOG.md @@ -1,3 +1,29 @@ +## 2.1.0 (2022-10-26) + +* fix(deps): update all non-major dependencies (#10610) ([bb95467](https://github.com/vitejs/vite/commit/bb95467)), closes [#10610](https://github.com/vitejs/vite/issues/10610) + + + +## 2.1.0-beta.0 (2022-10-05) + +* fix(deps): update all non-major dependencies (#10077) ([caf00c8](https://github.com/vitejs/vite/commit/caf00c8)), closes [#10077](https://github.com/vitejs/vite/issues/10077) +* fix(deps): update all non-major dependencies (#10160) ([6233c83](https://github.com/vitejs/vite/commit/6233c83)), closes [#10160](https://github.com/vitejs/vite/issues/10160) +* fix(deps): update all non-major dependencies (#10316) ([a38b450](https://github.com/vitejs/vite/commit/a38b450)), closes [#10316](https://github.com/vitejs/vite/issues/10316) +* refactor(types): bundle client types (#9966) ([da632bf](https://github.com/vitejs/vite/commit/da632bf)), closes [#9966](https://github.com/vitejs/vite/issues/9966) +* refactor(vue-jsx): remove `@babel/plugin-syntax-import-meta` (#10233) ([1bac86a](https://github.com/vitejs/vite/commit/1bac86a)), closes [#10233](https://github.com/vitejs/vite/issues/10233) + + + +## 2.0.1 (2022-08-29) + +* fix: mention that Node.js 13/15 support is dropped (fixes #9113) (#9116) ([2826303](https://github.com/vitejs/vite/commit/2826303)), closes [#9113](https://github.com/vitejs/vite/issues/9113) [#9116](https://github.com/vitejs/vite/issues/9116) +* fix(deps): update all non-major dependencies (#9176) ([31d3b70](https://github.com/vitejs/vite/commit/31d3b70)), closes [#9176](https://github.com/vitejs/vite/issues/9176) +* fix(deps): update all non-major dependencies (#9575) ([8071325](https://github.com/vitejs/vite/commit/8071325)), closes [#9575](https://github.com/vitejs/vite/issues/9575) +* fix(deps): update all non-major dependencies (#9888) ([e35a58b](https://github.com/vitejs/vite/commit/e35a58b)), closes [#9888](https://github.com/vitejs/vite/issues/9888) +* perf(plugin-vue-jsx): hoist variables (#9687) ([d9eb6b9](https://github.com/vitejs/vite/commit/d9eb6b9)), closes [#9687](https://github.com/vitejs/vite/issues/9687) + + + ## 2.0.0 (2022-07-13) * chore: 3.0 release notes and bump peer deps (#9072) ([427ba26](https://github.com/vitejs/vite/commit/427ba26)), closes [#9072](https://github.com/vitejs/vite/issues/9072) diff --git a/packages/plugin-vue-jsx/package.json b/packages/plugin-vue-jsx/package.json index aadc2f5aa25e7c..762d421855c4cb 100644 --- a/packages/plugin-vue-jsx/package.json +++ b/packages/plugin-vue-jsx/package.json @@ -1,6 +1,6 @@ { "name": "@vitejs/plugin-vue-jsx", - "version": "2.0.0", + "version": "2.1.0", "license": "MIT", "author": "Evan You", "files": [ @@ -35,9 +35,8 @@ }, "homepage": "https://github.com/vitejs/vite/tree/main/packages/plugin-vue-jsx#readme", "dependencies": { - "@babel/core": "^7.18.9", - "@babel/plugin-syntax-import-meta": "^7.10.4", - "@babel/plugin-transform-typescript": "^7.18.8", + "@babel/core": "^7.19.6", + "@babel/plugin-transform-typescript": "^7.20.0", "@vue/babel-plugin-jsx": "^1.1.1" }, "devDependencies": { diff --git a/packages/plugin-vue-jsx/src/index.ts b/packages/plugin-vue-jsx/src/index.ts index 803c7fd7fd1a97..cb2888728a45b5 100644 --- a/packages/plugin-vue-jsx/src/index.ts +++ b/packages/plugin-vue-jsx/src/index.ts @@ -3,8 +3,6 @@ import path from 'node:path' import type { types } from '@babel/core' import * as babel from '@babel/core' import jsx from '@vue/babel-plugin-jsx' -// @ts-expect-error missing type -import importMeta from '@babel/plugin-syntax-import-meta' import { createFilter, normalizePath } from 'vite' import type { ComponentOptions } from 'vue' import type { Plugin } from 'vite' @@ -38,6 +36,9 @@ function vueJsxPlugin(options: Options = {}): Plugin { let needHmr = false let needSourceMap = true + const { include, exclude, babelPlugins = [], ...babelPluginOptions } = options + const filter = createFilter(include || /\.[jt]sx$/, exclude) + return { name: 'vite:vue-jsx', @@ -75,20 +76,12 @@ function vueJsxPlugin(options: Options = {}): Plugin { async transform(code, id, opt) { const ssr = opt?.ssr === true - const { - include, - exclude, - babelPlugins = [], - ...babelPluginOptions - } = options - - const filter = createFilter(include || /\.[jt]sx$/, exclude) const [filepath] = id.split('?') // use id for script blocks in Vue SFCs (e.g. `App.vue?vue&type=script&lang.jsx`) // use filepath for plain jsx files (e.g. App.jsx) if (filter(id) || filter(filepath)) { - const plugins = [importMeta, [jsx, babelPluginOptions], ...babelPlugins] + const plugins = [[jsx, babelPluginOptions], ...babelPlugins] if (id.endsWith('.tsx') || filepath.endsWith('.tsx')) { plugins.push([ // @ts-ignore missing type diff --git a/packages/plugin-vue-jsx/tsconfig.json b/packages/plugin-vue-jsx/tsconfig.json index ddb60f864b7e6d..bd94458fe2dc28 100644 --- a/packages/plugin-vue-jsx/tsconfig.json +++ b/packages/plugin-vue-jsx/tsconfig.json @@ -12,7 +12,6 @@ "noUnusedLocals": true, "esModuleInterop": true, "paths": { - "types/*": ["../vite/types/*"], "vite": ["../vite/src/node/index.js"] } } diff --git a/packages/plugin-vue/CHANGELOG.md b/packages/plugin-vue/CHANGELOG.md index 2bc0752d6fc351..bccb81d2e3f4f9 100644 --- a/packages/plugin-vue/CHANGELOG.md +++ b/packages/plugin-vue/CHANGELOG.md @@ -1,3 +1,51 @@ +## 3.2.0 (2022-10-26) + +* chore(deps): update all non-major dependencies (#10393) ([f519423](https://github.com/vitejs/vite/commit/f519423)), closes [#10393](https://github.com/vitejs/vite/issues/10393) +* chore(deps): update all non-major dependencies (#10488) ([15aa827](https://github.com/vitejs/vite/commit/15aa827)), closes [#10488](https://github.com/vitejs/vite/issues/10488) +* fix(plugin-vue): enable ts in template when using tsx in dev mode (#10180) ([a9f9d31](https://github.com/vitejs/vite/commit/a9f9d31)), closes [#10180](https://github.com/vitejs/vite/issues/10180) +* docs: add missing binding in asset import example (#10414) ([d7ac96d](https://github.com/vitejs/vite/commit/d7ac96d)), closes [#10414](https://github.com/vitejs/vite/issues/10414) + + + +## 3.2.0-beta.0 (2022-10-05) + +* fix(deps): update all non-major dependencies (#10077) ([caf00c8](https://github.com/vitejs/vite/commit/caf00c8)), closes [#10077](https://github.com/vitejs/vite/issues/10077) +* fix(deps): update all non-major dependencies (#10316) ([a38b450](https://github.com/vitejs/vite/commit/a38b450)), closes [#10316](https://github.com/vitejs/vite/issues/10316) +* fix(deps): update all non-major dependencies (#9985) ([855f2f0](https://github.com/vitejs/vite/commit/855f2f0)), closes [#9985](https://github.com/vitejs/vite/issues/9985) +* fix(deps): update rollup to `^2.79.1` (#10298) ([2266d83](https://github.com/vitejs/vite/commit/2266d83)), closes [#10298](https://github.com/vitejs/vite/issues/10298) +* fix(esbuild): transpile with esnext in dev (#10207) ([43b7b78](https://github.com/vitejs/vite/commit/43b7b78)), closes [#10207](https://github.com/vitejs/vite/issues/10207) +* chore(deps): update dependency slash to v5 (#10317) ([9d87c11](https://github.com/vitejs/vite/commit/9d87c11)), closes [#10317](https://github.com/vitejs/vite/issues/10317) +* refactor(types): bundle client types (#9966) ([da632bf](https://github.com/vitejs/vite/commit/da632bf)), closes [#9966](https://github.com/vitejs/vite/issues/9966) + + + +## 3.1.0 (2022-09-05) + + + + +## 3.1.0-beta.0 (2022-08-29) + +* docs: fix typo (#9855) ([583f185](https://github.com/vitejs/vite/commit/583f185)), closes [#9855](https://github.com/vitejs/vite/issues/9855) +* feat: support object style hooks (#9634) ([757a92f](https://github.com/vitejs/vite/commit/757a92f)), closes [#9634](https://github.com/vitejs/vite/issues/9634) +* chore: fix typo (#9684) ([d30f881](https://github.com/vitejs/vite/commit/d30f881)), closes [#9684](https://github.com/vitejs/vite/issues/9684) +* chore(deps): update all non-major dependencies (#9675) ([4e56e87](https://github.com/vitejs/vite/commit/4e56e87)), closes [#9675](https://github.com/vitejs/vite/issues/9675) +* chore(plugin-vue): update reactivityTransform comment docs [ci skip] ([d04784b](https://github.com/vitejs/vite/commit/d04784b)) + + + +## 3.0.3 (2022-08-12) + + + + +## 3.0.2 (2022-08-11) + +* chore: fix code typos (#9033) ([ed02861](https://github.com/vitejs/vite/commit/ed02861)), closes [#9033](https://github.com/vitejs/vite/issues/9033) +* chore: narrow down rollup version (#9637) ([fcf4d98](https://github.com/vitejs/vite/commit/fcf4d98)), closes [#9637](https://github.com/vitejs/vite/issues/9637) + + + ## 3.0.1 (2022-07-18) * fix: mention that Node.js 13/15 support is dropped (fixes #9113) (#9116) ([2826303](https://github.com/vitejs/vite/commit/2826303)), closes [#9113](https://github.com/vitejs/vite/issues/9113) [#9116](https://github.com/vitejs/vite/issues/9116) diff --git a/packages/plugin-vue/README.md b/packages/plugin-vue/README.md index be02ab7ae4ab36..2718e7e3360e70 100644 --- a/packages/plugin-vue/README.md +++ b/packages/plugin-vue/README.md @@ -77,7 +77,7 @@ Is the same as: import _imports_0 from '../image.png' - + ``` By default the following tag/attribute combinations are transformed, and can be configured using the `template.transformAssetUrls` option. diff --git a/packages/plugin-vue/package.json b/packages/plugin-vue/package.json index 255e1936174e30..49d2d9fca5e6c7 100644 --- a/packages/plugin-vue/package.json +++ b/packages/plugin-vue/package.json @@ -1,6 +1,6 @@ { "name": "@vitejs/plugin-vue", - "version": "3.0.1", + "version": "3.2.0", "license": "MIT", "author": "Evan You", "files": [ @@ -40,12 +40,12 @@ }, "devDependencies": { "@jridgewell/gen-mapping": "^0.3.2", - "@jridgewell/trace-mapping": "^0.3.14", + "@jridgewell/trace-mapping": "^0.3.17", "debug": "^4.3.4", - "rollup": "^2.75.6", - "slash": "^4.0.0", + "rollup": "^2.79.1", + "slash": "^5.0.0", "source-map": "^0.6.1", "vite": "workspace:*", - "vue": "^3.2.37" + "vue": "^3.2.41" } } diff --git a/packages/plugin-vue/src/index.ts b/packages/plugin-vue/src/index.ts index 5a7a6947929be4..e8763443d032c0 100644 --- a/packages/plugin-vue/src/index.ts +++ b/packages/plugin-vue/src/index.ts @@ -54,11 +54,11 @@ export interface Options { /** * Enable Vue reactivity transform (experimental). - * https://github.com/vuejs/core/tree/master/packages/reactivity-transform + * https://vuejs.org/guide/extras/reactivity-transform.html * - `true`: transform will be enabled for all vue,js(x),ts(x) files except * those inside node_modules * - `string | RegExp`: apply to vue + only matched files (will include - * node_modules, so specify directories in necessary) + * node_modules, so specify directories if necessary) * - `false`: disable in all cases * * @default false @@ -186,7 +186,7 @@ export default function vuePlugin(rawOptions: Options = {}): Plugin { const descriptor = getDescriptor(filename, options)! let block: SFCBlock | null | undefined if (query.type === 'script') { - // handle + const filePath = id.replace(normalizePath(config.root), '') addToHTMLProxyCache(config, filePath, inlineModuleIndex, { @@ -324,9 +390,10 @@ export function buildHtmlPlugin(config: ResolvedConfig): Plugin { ` asset for (const { start, end, url } of scriptUrls) { if (!isExcludedUrl(url)) { - s.overwrite( - start, - end, - await urlToBuiltUrl(url, id, config, this), - { contentOnly: true } - ) + s.update(start, end, await urlToBuiltUrl(url, id, config, this)) } else if (checkPublicFile(url, config)) { - s.overwrite(start, end, toOutputPublicFilePath(url), { - contentOnly: true - }) + s.update(start, end, toOutputPublicFilePath(url)) } } @@ -523,8 +582,10 @@ export function buildHtmlPlugin(config: ResolvedConfig): Plugin { processedHtml.set(id, s.toString()) // inject module preload polyfill only when configured and needed + const { modulePreload } = config.build if ( - config.build.polyfillModulePreload && + (modulePreload === true || + (typeof modulePreload === 'object' && modulePreload.polyfill)) && (someScriptsAreAsync || someScriptsAreDefer) ) { js = `import "${modulePreloadPolyfillId}";\n${js}` @@ -569,14 +630,14 @@ export function buildHtmlPlugin(config: ResolvedConfig): Plugin { }) const toPreloadTag = ( - chunk: OutputChunk, + filename: string, toOutputPath: (filename: string) => string ): HtmlTagDescriptor => ({ tag: 'link', attrs: { rel: 'modulepreload', crossorigin: true, - href: toOutputPath(chunk.fileName) + href: toOutputPath(filename) } }) @@ -613,7 +674,10 @@ export function buildHtmlPlugin(config: ResolvedConfig): Plugin { } for (const [id, html] of processedHtml) { - const relativeUrlPath = path.posix.relative(config.root, id) + const relativeUrlPath = path.posix.relative( + config.root, + normalizePath(id) + ) const assetsBase = getBaseInHTML(relativeUrlPath, config) const toOutputFilePath = ( filename: string, @@ -665,15 +729,28 @@ export function buildHtmlPlugin(config: ResolvedConfig): Plugin { // when not inlined, inject `, - { contentOnly: true } + s.update( + node.sourceCodeLocation!.startOffset, + node.sourceCodeLocation!.endOffset, + `` ) } await traverseHtml(html, htmlPath, (node) => { - if (node.type !== NodeTypes.ELEMENT) { + if (!nodeIsElement(node)) { return } // script tags - if (node.tag === 'script') { - const { src, isModule } = getScriptInfo(node) + if (node.nodeName === 'script') { + const { src, sourceCodeLocation, isModule } = getScriptInfo(node) if (src) { - processNodeUrl(src, s, config, htmlPath, originalUrl, moduleGraph) - } else if (isModule && node.children.length) { + processNodeUrl( + src, + sourceCodeLocation!, + s, + config, + htmlPath, + originalUrl, + moduleGraph + ) + } else if (isModule && node.childNodes.length) { addInlineModule(node, 'js') } } - if (node.tag === 'style' && node.children.length) { - const children = node.children[0] as TextNode + if (node.nodeName === 'style' && node.childNodes.length) { + const children = node.childNodes[0] as DefaultTreeAdapterMap['textNode'] styleUrl.push({ - start: children.loc.start.offset, - end: children.loc.end.offset, - code: children.content + start: children.sourceCodeLocation!.startOffset, + end: children.sourceCodeLocation!.endOffset, + code: children.value }) } // elements with [href/src] attrs - const assetAttrs = assetAttrsConfig[node.tag] + const assetAttrs = assetAttrsConfig[node.nodeName] if (assetAttrs) { - for (const p of node.props) { - if ( - p.type === NodeTypes.ATTRIBUTE && - p.value && - assetAttrs.includes(p.name) - ) { - processNodeUrl(p, s, config, htmlPath, originalUrl) + for (const p of node.attrs) { + const attrKey = getAttrKey(p) + if (p.value && assetAttrs.includes(attrKey)) { + processNodeUrl( + p, + node.sourceCodeLocation!.attrs![attrKey], + s, + config, + htmlPath, + originalUrl + ) } } } @@ -262,7 +286,7 @@ export function indexHtmlMiddleware( } const url = req.url && cleanUrl(req.url) - // spa-fallback always redirects to /index.html + // htmlFallbackMiddleware appends '.html' to URLs if (url?.endsWith('.html') && req.headers['sec-fetch-dest'] !== 'script') { const filename = getHtmlFilename(url, server) if (fs.existsSync(filename)) { diff --git a/packages/vite/src/node/server/middlewares/proxy.ts b/packages/vite/src/node/server/middlewares/proxy.ts index 9f07a3c6e7bc24..fcd89707fe0204 100644 --- a/packages/vite/src/node/server/middlewares/proxy.ts +++ b/packages/vite/src/node/server/middlewares/proxy.ts @@ -1,8 +1,8 @@ import type * as http from 'node:http' import type * as net from 'node:net' import httpProxy from 'http-proxy' -import type { Connect } from 'types/connect' -import type { HttpProxy } from 'types/http-proxy' +import type { Connect } from 'dep-types/connect' +import type { HttpProxy } from 'dep-types/http-proxy' import colors from 'picocolors' import { HMR_HEADER } from '../ws' import { createDebugger, isObject } from '../../utils' @@ -39,6 +39,9 @@ export function proxyMiddleware( Object.keys(options).forEach((context) => { let opts = options[context] + if (!opts) { + return + } if (typeof opts === 'string') { opts = { target: opts, changeOrigin: true } as ProxyOptions } @@ -49,7 +52,9 @@ export function proxyMiddleware( const res = originalRes as http.ServerResponse | net.Socket if ('req' in res) { config.logger.error( - `${colors.red(`http proxy error:`)}\n${err.stack}`, + `${colors.red(`http proxy error at ${originalRes.req.url}:`)}\n${ + err.stack + }`, { timestamp: true, error: err @@ -85,7 +90,9 @@ export function proxyMiddleware( if (doesProxyContextMatchUrl(context, url)) { const [proxy, opts] = proxies[context] if ( - (opts.ws || opts.target?.toString().startsWith('ws:')) && + (opts.ws || + opts.target?.toString().startsWith('ws:') || + opts.target?.toString().startsWith('wss:')) && req.headers['sec-websocket-protocol'] !== HMR_HEADER ) { if (opts.rewrite) { diff --git a/packages/vite/src/node/server/middlewares/static.ts b/packages/vite/src/node/server/middlewares/static.ts index 1c71529ef72e79..c877022a63cde7 100644 --- a/packages/vite/src/node/server/middlewares/static.ts +++ b/packages/vite/src/node/server/middlewares/static.ts @@ -2,8 +2,7 @@ import path from 'node:path' import type { OutgoingHttpHeaders, ServerResponse } from 'node:http' import type { Options } from 'sirv' import sirv from 'sirv' -import type { Connect } from 'types/connect' -import micromatch from 'micromatch' +import type { Connect } from 'dep-types/connect' import type { ViteDevServer } from '../..' import { FS_PREFIX } from '../../constants' import { @@ -18,8 +17,6 @@ import { slash } from '../../utils' -const { isMatch } = micromatch - const sirvOptions = (headers?: OutgoingHttpHeaders): Options => { return { dev: true, @@ -158,8 +155,6 @@ export function serveRawFsMiddleware( } } -const _matchOptions = { matchBase: true } - export function isFileServingAllowed( url: string, server: ViteDevServer @@ -168,8 +163,7 @@ export function isFileServingAllowed( const file = fsPathFromUrl(url) - if (server.config.server.fs.deny.some((i) => isMatch(file, i, _matchOptions))) - return false + if (server._fsDenyGlob(file)) return false if (server.moduleGraph.safeModulesPath.has(file)) return true diff --git a/packages/vite/src/node/server/middlewares/time.ts b/packages/vite/src/node/server/middlewares/time.ts index 41a42f63270e50..7fdc7172f1ed58 100644 --- a/packages/vite/src/node/server/middlewares/time.ts +++ b/packages/vite/src/node/server/middlewares/time.ts @@ -1,5 +1,5 @@ import { performance } from 'node:perf_hooks' -import type { Connect } from 'types/connect' +import type { Connect } from 'dep-types/connect' import { createDebugger, prettifyUrl, timeFrom } from '../../utils' const logTime = createDebugger('vite:time') diff --git a/packages/vite/src/node/server/middlewares/transform.ts b/packages/vite/src/node/server/middlewares/transform.ts index 1bba1711e1b326..929c6856d34748 100644 --- a/packages/vite/src/node/server/middlewares/transform.ts +++ b/packages/vite/src/node/server/middlewares/transform.ts @@ -1,6 +1,6 @@ import { promises as fs } from 'node:fs' import path from 'node:path' -import type { Connect } from 'types/connect' +import type { Connect } from 'dep-types/connect' import colors from 'picocolors' import type { ViteDevServer } from '..' import { diff --git a/packages/vite/src/node/server/moduleGraph.ts b/packages/vite/src/node/server/moduleGraph.ts index 4bbd79cd5cc2f6..b9d928b589e00b 100644 --- a/packages/vite/src/node/server/moduleGraph.ts +++ b/packages/vite/src/node/server/moduleGraph.ts @@ -185,7 +185,7 @@ export class ModuleGraph { setIsSelfAccepting = true ): Promise { const [url, resolvedId, meta] = await this.resolveUrl(rawUrl, ssr) - let mod = this.urlToModuleMap.get(url) + let mod = this.idToModuleMap.get(resolvedId) if (!mod) { mod = new ModuleNode(url, setIsSelfAccepting) if (meta) mod.meta = meta @@ -200,6 +200,11 @@ export class ModuleGraph { } fileMappedModules.add(mod) } + // multiple urls can map to the same module and id, make sure we register + // the url to the existing module in that case + else if (!this.urlToModuleMap.has(url)) { + this.urlToModuleMap.set(url, mod) + } return mod } diff --git a/packages/vite/src/node/server/openBrowser.ts b/packages/vite/src/node/server/openBrowser.ts index 319cb4431f4583..72fe339937a624 100644 --- a/packages/vite/src/node/server/openBrowser.ts +++ b/packages/vite/src/node/server/openBrowser.ts @@ -16,9 +16,6 @@ import colors from 'picocolors' import type { Logger } from '../logger' import { VITE_PACKAGE_DIR } from '../constants' -// https://github.com/sindresorhus/open#app -const OSX_CHROME = 'google chrome' - /** * Reads the BROWSER environment variable and decides what to do with it. * Returns true if it opened a browser or ran a node.js script, otherwise false. @@ -59,23 +56,46 @@ function executeNodeScript(scriptPath: string, url: string, logger: Logger) { return true } +const supportedChromiumBrowsers = [ + 'Google Chrome Canary', + 'Google Chrome Dev', + 'Google Chrome Beta', + 'Google Chrome', + 'Microsoft Edge', + 'Brave Browser', + 'Vivaldi', + 'Chromium' +] + function startBrowserProcess(browser: string | undefined, url: string) { // If we're on OS X, the user hasn't specifically // requested a different browser, we can try opening - // Chrome with AppleScript. This lets us reuse an + // a Chromium browser with AppleScript. This lets us reuse an // existing tab when possible instead of creating a new one. + const preferredOSXBrowser = + browser === 'google chrome' ? 'Google Chrome' : browser const shouldTryOpenChromeWithAppleScript = - process.platform === 'darwin' && (browser === '' || browser === OSX_CHROME) + process.platform === 'darwin' && + (!preferredOSXBrowser || + supportedChromiumBrowsers.includes(preferredOSXBrowser)) if (shouldTryOpenChromeWithAppleScript) { try { - // Try our best to reuse existing tab - // on OS X Google Chrome with AppleScript - execSync('ps cax | grep "Google Chrome"') - execSync('osascript openChrome.applescript "' + encodeURI(url) + '"', { - cwd: join(VITE_PACKAGE_DIR, 'bin'), - stdio: 'ignore' - }) + const ps = execSync('ps cax').toString() + const openedBrowser = + preferredOSXBrowser && ps.includes(preferredOSXBrowser) + ? preferredOSXBrowser + : supportedChromiumBrowsers.find((b) => ps.includes(b)) + // Try our best to reuse existing tab with AppleScript + execSync( + `osascript openChrome.applescript "${encodeURI( + url + )}" "${openedBrowser}"`, + { + cwd: join(VITE_PACKAGE_DIR, 'bin'), + stdio: 'ignore' + } + ) return true } catch (err) { // Ignore errors diff --git a/packages/vite/src/node/server/pluginContainer.ts b/packages/vite/src/node/server/pluginContainer.ts index fc1523fc7888a0..cd297d1e0826f2 100644 --- a/packages/vite/src/node/server/pluginContainer.ts +++ b/packages/vite/src/node/server/pluginContainer.ts @@ -34,14 +34,17 @@ import { join, resolve } from 'node:path' import { performance } from 'node:perf_hooks' import { createRequire } from 'node:module' import type { + AsyncPluginHooks, CustomPluginOptions, EmittedFile, + FunctionPluginHooks, InputOptions, LoadResult, MinimalPluginContext, ModuleInfo, NormalizedInputOptions, OutputOptions, + ParallelPluginHooks, PartialResolvedId, ResolvedId, RollupError, @@ -74,6 +77,7 @@ import { } from '../utils' import { FS_PREFIX } from '../constants' import type { ResolvedConfig } from '../config' +import { createPluginHookUtils } from '../plugins' import { buildErrorMessage } from './middlewares/error' import type { ModuleGraph } from './moduleGraph' @@ -137,11 +141,19 @@ type PluginContext = Omit< export let parser = acorn.Parser export async function createPluginContainer( - { plugins, logger, root, build: { rollupOptions } }: ResolvedConfig, + config: ResolvedConfig, moduleGraph?: ModuleGraph, watcher?: FSWatcher ): Promise { const isDebug = process.env.DEBUG + const { + plugins, + logger, + root, + build: { rollupOptions } + } = config + const { getSortedPluginHooks, getSortedPlugins } = + createPluginHookUtils(plugins) const seenResolves: Record = {} const debugResolve = createDebugger('vite:resolve') @@ -192,6 +204,28 @@ export async function createPluginContainer( ) } + // parallel, ignores returns + async function hookParallel( + hookName: H, + context: (plugin: Plugin) => ThisType, + args: (plugin: Plugin) => Parameters + ): Promise { + const parallelPromises: Promise[] = [] + for (const plugin of getSortedPlugins(hookName)) { + const hook = plugin[hookName] + if (!hook) continue + const handler: Function = 'handler' in hook ? hook.handler : hook + if ((hook as { sequential?: boolean }).sequential) { + await Promise.all(parallelPromises) + parallelPromises.length = 0 + await handler.apply(context(plugin), args(plugin)) + } else { + parallelPromises.push(handler.apply(context(plugin), args(plugin))) + } + } + await Promise.all(parallelPromises) + } + // throw when an unsupported ModuleInfo property is accessed, // so that incompatible plugins fail in a non-cryptic way. const ModuleInfoProxy: ProxyHandler = { @@ -438,6 +472,10 @@ export async function createPluginContainer( this.filename = filename this.originalCode = code if (inMap) { + if (isDebugSourcemapCombineFocused) { + // @ts-expect-error inject name for debug purpose + inMap.name = '$inMap' + } this.sourcemapChain.push(inMap) } } @@ -500,10 +538,8 @@ export async function createPluginContainer( const container: PluginContainer = { options: await (async () => { let options = rollupOptions - for (const plugin of plugins) { - if (!plugin.options) continue - options = - (await plugin.options.call(minimalContext, options)) || options + for (const optionsHook of getSortedPluginHooks('options')) { + options = (await optionsHook.call(minimalContext, options)) || options } if (options.acornInjectPlugins) { parser = acorn.Parser.extend( @@ -520,15 +556,10 @@ export async function createPluginContainer( getModuleInfo, async buildStart() { - await Promise.all( - plugins.map((plugin) => { - if (plugin.buildStart) { - return plugin.buildStart.call( - new Context(plugin) as any, - container.options as NormalizedInputOptions - ) - } - }) + await hookParallel( + 'buildStart', + (plugin) => new Context(plugin), + () => [container.options as NormalizedInputOptions] ) }, @@ -544,24 +575,23 @@ export async function createPluginContainer( let id: string | null = null const partial: Partial = {} - for (const plugin of plugins) { + for (const plugin of getSortedPlugins('resolveId')) { if (!plugin.resolveId) continue if (skip?.has(plugin)) continue ctx._activePlugin = plugin const pluginResolveStart = isDebug ? performance.now() : 0 - const result = await plugin.resolveId.call( - ctx as any, - rawId, - importer, - { - custom: options?.custom, - isEntry: !!options?.isEntry, - ssr, - scan - } - ) + const handler = + 'handler' in plugin.resolveId + ? plugin.resolveId.handler + : plugin.resolveId + const result = await handler.call(ctx as any, rawId, importer, { + custom: options?.custom, + isEntry: !!options?.isEntry, + ssr, + scan + }) if (!result) continue if (typeof result === 'string') { @@ -607,10 +637,12 @@ export async function createPluginContainer( const ssr = options?.ssr const ctx = new Context() ctx.ssr = !!ssr - for (const plugin of plugins) { + for (const plugin of getSortedPlugins('load')) { if (!plugin.load) continue ctx._activePlugin = plugin - const result = await plugin.load.call(ctx as any, id, { ssr }) + const handler = + 'handler' in plugin.load ? plugin.load.handler : plugin.load + const result = await handler.call(ctx as any, id, { ssr }) if (result != null) { if (isObject(result)) { updateModuleInfo(id, result) @@ -626,15 +658,19 @@ export async function createPluginContainer( const ssr = options?.ssr const ctx = new TransformContext(id, code, inMap as SourceMap) ctx.ssr = !!ssr - for (const plugin of plugins) { + for (const plugin of getSortedPlugins('transform')) { if (!plugin.transform) continue ctx._activePlugin = plugin ctx._activeId = id ctx._activeCode = code const start = isDebug ? performance.now() : 0 let result: TransformResult | string | undefined + const handler = + 'handler' in plugin.transform + ? plugin.transform.handler + : plugin.transform try { - result = await plugin.transform.call(ctx as any, code, id, { ssr }) + result = await handler.call(ctx as any, code, id, { ssr }) } catch (e) { ctx.error(e) } @@ -670,11 +706,15 @@ export async function createPluginContainer( async close() { if (closed) return const ctx = new Context() - await Promise.all( - plugins.map((p) => p.buildEnd && p.buildEnd.call(ctx as any)) + await hookParallel( + 'buildEnd', + () => ctx, + () => [] ) - await Promise.all( - plugins.map((p) => p.closeBundle && p.closeBundle.call(ctx as any)) + await hookParallel( + 'closeBundle', + () => ctx, + () => [] ) closed = true } diff --git a/packages/vite/src/node/server/transformRequest.ts b/packages/vite/src/node/server/transformRequest.ts index b7f3667d536b78..252551e4f38a42 100644 --- a/packages/vite/src/node/server/transformRequest.ts +++ b/packages/vite/src/node/server/transformRequest.ts @@ -2,12 +2,13 @@ import { promises as fs } from 'node:fs' import path from 'node:path' import { performance } from 'node:perf_hooks' import getEtag from 'etag' -import * as convertSourceMap from 'convert-source-map' +import convertSourceMap from 'convert-source-map' import type { SourceDescription, SourceMap } from 'rollup' import colors from 'picocolors' import MagicString from 'magic-string' import type { ViteDevServer } from '..' import { + blankReplacer, cleanUrl, createDebugger, ensureWatchedFile, @@ -17,7 +18,6 @@ import { timeFrom } from '../utils' import { checkPublicFile } from '../plugins/asset' -import { ssrTransform } from '../ssr/ssrTransform' import { getDepsOptimizer } from '../optimizer' import { injectSourcesContent } from './sourcemap' import { isFileServingAllowed } from './middlewares/static' @@ -198,6 +198,8 @@ async function loadAndTransform( convertSourceMap.fromSource(code) || convertSourceMap.fromMapFileSource(code, path.dirname(file)) )?.toObject() + + code = code.replace(convertSourceMap.mapFileCommentRegex, blankReplacer) } catch (e) { logger.warn(`Failed to load source map for ${url}.`, { timestamp: true @@ -269,9 +271,7 @@ async function loadAndTransform( } const result = ssr - ? await ssrTransform(code, map as SourceMap, url, originalCode, { - json: { stringify: !!server.config.json?.stringify } - }) + ? await server.ssrTransform(code, map as SourceMap, url, originalCode) : ({ code, map, diff --git a/packages/vite/src/node/server/ws.ts b/packages/vite/src/node/server/ws.ts index 0d787a79157d6b..cb8555122b6840 100644 --- a/packages/vite/src/node/server/ws.ts +++ b/packages/vite/src/node/server/ws.ts @@ -6,7 +6,7 @@ import type { Socket } from 'node:net' import colors from 'picocolors' import type { ServerOptions, WebSocket as WebSocketRaw } from 'ws' import { WebSocketServer as WebSocketServerRaw } from 'ws' -import type { WebSocket as WebSocketTypes } from 'types/ws' +import type { WebSocket as WebSocketTypes } from 'dep-types/ws' import type { CustomPayload, ErrorPayload, HMRPayload } from 'types/hmrPayload' import type { InferCustomEventPayload } from 'types/customEvent' import type { ResolvedConfig } from '..' @@ -25,7 +25,7 @@ export interface WebSocketServer { */ clients: Set /** - * Boardcast events to all clients + * Broadcast events to all clients */ send(payload: HMRPayload): void /** diff --git a/packages/vite/src/node/ssr/__tests__/ssrTransform.spec.ts b/packages/vite/src/node/ssr/__tests__/ssrTransform.spec.ts index cc5b19522ef9a5..33d5071a054a99 100644 --- a/packages/vite/src/node/ssr/__tests__/ssrTransform.spec.ts +++ b/packages/vite/src/node/ssr/__tests__/ssrTransform.spec.ts @@ -1,6 +1,5 @@ import { expect, test } from 'vitest' import { transformWithEsbuild } from '../../plugins/esbuild' -import { traverseHtml } from '../../plugins/html' import { ssrTransform } from '../ssrTransform' const ssrTransformSimple = async (code: string, url = '') => @@ -125,6 +124,18 @@ test('export default', async () => { ).toMatchInlineSnapshot(`"__vite_ssr_exports__.default = {}"`) }) +test('export then import minified', async () => { + expect( + await ssrTransformSimpleCode( + `export * from 'vue';import {createApp} from 'vue';` + ) + ).toMatchInlineSnapshot(` + "const __vite_ssr_import_1__ = await __vite_ssr_import__(\\"vue\\"); + __vite_ssr_exportAll__(__vite_ssr_import_1__);const __vite_ssr_import_0__ = await __vite_ssr_import__(\\"vue\\"); + " + `) +}) + test('import.meta', async () => { expect( await ssrTransformSimpleCode(`console.log(import.meta.url)`) @@ -388,6 +399,30 @@ const a = () => { } " `) + + // #9585 + expect( + await ssrTransformSimpleCode( + ` +import { n, m } from 'foo' +const foo = {} + +{ + const { [n]: m } = foo +} +` + ) + ).toMatchInlineSnapshot(` + " + const __vite_ssr_import_0__ = await __vite_ssr_import__(\\"foo\\"); + + const foo = {} + + { + const { [__vite_ssr_import_0__.n]: m } = foo + } + " + `) }) test('nested object destructure alias', async () => { @@ -451,6 +486,45 @@ objRest() `) }) +test('object props and methods', async () => { + expect( + await ssrTransformSimpleCode( + ` +import foo from 'foo' + +const bar = 'bar' + +const obj = { + foo() {}, + [foo]() {}, + [bar]() {}, + foo: () => {}, + [foo]: () => {}, + [bar]: () => {}, + bar(foo) {} +} +` + ) + ).toMatchInlineSnapshot(` + " + const __vite_ssr_import_0__ = await __vite_ssr_import__(\\"foo\\"); + + + const bar = 'bar' + + const obj = { + foo() {}, + [__vite_ssr_import_0__.default]() {}, + [bar]() {}, + foo: () => {}, + [__vite_ssr_import_0__.default]: () => {}, + [bar]: () => {}, + bar(foo) {} + } + " + `) +}) + test('class props', async () => { expect( await ssrTransformSimpleCode( @@ -653,3 +727,87 @@ console.log("it can parse the hashbang")` console.log(\\"it can parse the hashbang\\")" `) }) + +// #10289 +test('track scope by class, function, condition blocks', async () => { + const code = ` +import { foo, bar } from 'foobar' +if (false) { + const foo = 'foo' + console.log(foo) +} else if (false) { + const [bar] = ['bar'] + console.log(bar) +} else { + console.log(foo) + console.log(bar) +} +export class Test { + constructor() { + if (false) { + const foo = 'foo' + console.log(foo) + } else if (false) { + const [bar] = ['bar'] + console.log(bar) + } else { + console.log(foo) + console.log(bar) + } + } +};`.trim() + + expect(await ssrTransformSimpleCode(code)).toMatchInlineSnapshot(` + "const __vite_ssr_import_0__ = await __vite_ssr_import__(\\"foobar\\"); + + if (false) { + const foo = 'foo' + console.log(foo) + } else if (false) { + const [bar] = ['bar'] + console.log(bar) + } else { + console.log(__vite_ssr_import_0__.foo) + console.log(__vite_ssr_import_0__.bar) + } + class Test { + constructor() { + if (false) { + const foo = 'foo' + console.log(foo) + } else if (false) { + const [bar] = ['bar'] + console.log(bar) + } else { + console.log(__vite_ssr_import_0__.foo) + console.log(__vite_ssr_import_0__.bar) + } + } + } + Object.defineProperty(__vite_ssr_exports__, \\"Test\\", { enumerable: true, configurable: true, get(){ return Test }});;" + `) +}) + +// #10386 +test('track var scope by function', async () => { + expect( + await ssrTransformSimpleCode(` +import { foo, bar } from 'foobar' +function test() { + if (true) { + var foo = () => { var why = 'would' }, bar = 'someone' + } + return [foo, bar] +}`) + ).toMatchInlineSnapshot(` + " + const __vite_ssr_import_0__ = await __vite_ssr_import__(\\"foobar\\"); + + function test() { + if (true) { + var foo = () => { var why = 'would' }, bar = 'someone' + } + return [foo, bar] + }" + `) +}) diff --git a/packages/vite/src/node/ssr/index.ts b/packages/vite/src/node/ssr/index.ts index 7d2e4724f98b44..d23e78b18cae5f 100644 --- a/packages/vite/src/node/ssr/index.ts +++ b/packages/vite/src/node/ssr/index.ts @@ -41,8 +41,8 @@ export interface ResolvedSSROptions extends SSROptions { export function resolveSSROptions( ssr: SSROptions | undefined, - buildSsrCjsExternalHeuristics?: boolean, - preserveSymlinks?: boolean + preserveSymlinks: boolean, + buildSsrCjsExternalHeuristics?: boolean ): ResolvedSSROptions { ssr ??= {} const optimizeDeps = ssr.optimizeDeps ?? {} diff --git a/packages/vite/src/node/ssr/ssrExternal.ts b/packages/vite/src/node/ssr/ssrExternal.ts index d73d17c1d7c3c8..7c48c84db48743 100644 --- a/packages/vite/src/node/ssr/ssrExternal.ts +++ b/packages/vite/src/node/ssr/ssrExternal.ts @@ -1,7 +1,7 @@ import fs from 'node:fs' import path from 'node:path' import { createRequire } from 'node:module' -import type { InternalResolveOptions } from '../plugins/resolve' +import type { InternalResolveOptions, ResolveOptions } from '../plugins/resolve' import { tryNodeResolve } from '../plugins/resolve' import { bareImportRE, @@ -53,7 +53,7 @@ export function cjsSsrResolveExternals( cjsSsrCollectExternals( config.root, - config.resolve.preserveSymlinks, + config.resolve, ssrExternals, seen, config.logger @@ -116,8 +116,8 @@ export function createIsConfiguredAsSsrExternal( createFilter(undefined, noExternal, { resolve: false }) const resolveOptions: InternalResolveOptions = { + ...config.resolve, root, - preserveSymlinks: config.resolve.preserveSymlinks, isProduction: false, isBuild: true } @@ -211,7 +211,7 @@ function createIsSsrExternal( // is used reverting to the Vite 2.9 SSR externalization heuristics function cjsSsrCollectExternals( root: string, - preserveSymlinks: boolean | undefined, + resolveOptions: Required, ssrExternals: Set, seen: Set, logger: Logger @@ -227,9 +227,9 @@ function cjsSsrCollectExternals( ...rootPkg.dependencies } - const resolveOptions: InternalResolveOptions = { + const internalResolveOptions: InternalResolveOptions = { + ...resolveOptions, root, - preserveSymlinks, isProduction: false, isBuild: true } @@ -247,7 +247,7 @@ function cjsSsrCollectExternals( esmEntry = tryNodeResolve( id, undefined, - resolveOptions, + internalResolveOptions, true, // we set `targetWeb` to `true` to get the ESM entry undefined, true @@ -314,13 +314,7 @@ function cjsSsrCollectExternals( } for (const depRoot of depsToTrace) { - cjsSsrCollectExternals( - depRoot, - preserveSymlinks, - ssrExternals, - seen, - logger - ) + cjsSsrCollectExternals(depRoot, resolveOptions, ssrExternals, seen, logger) } } diff --git a/packages/vite/src/node/ssr/ssrManifestPlugin.ts b/packages/vite/src/node/ssr/ssrManifestPlugin.ts index 11aafb4f329a74..9a68b5ea22afe5 100644 --- a/packages/vite/src/node/ssr/ssrManifestPlugin.ts +++ b/packages/vite/src/node/ssr/ssrManifestPlugin.ts @@ -5,7 +5,7 @@ import type { OutputChunk } from 'rollup' import type { ResolvedConfig } from '..' import type { Plugin } from '../plugin' import { preloadMethod } from '../plugins/importAnalysisBuild' -import { normalizePath } from '../utils' +import { joinUrlSegments, normalizePath } from '../utils' export function ssrManifestPlugin(config: ResolvedConfig): Plugin { // module id => preload assets mapping @@ -23,15 +23,15 @@ export function ssrManifestPlugin(config: ResolvedConfig): Plugin { const mappedChunks = ssrManifest[normalizedId] ?? (ssrManifest[normalizedId] = []) if (!chunk.isEntry) { - mappedChunks.push(base + chunk.fileName) + mappedChunks.push(joinUrlSegments(base, chunk.fileName)) // tags for entry chunks are already generated in static HTML, // so we only need to record info for non-entry chunks. chunk.viteMetadata.importedCss.forEach((file) => { - mappedChunks.push(base + file) + mappedChunks.push(joinUrlSegments(base, file)) }) } chunk.viteMetadata.importedAssets.forEach((file) => { - mappedChunks.push(base + file) + mappedChunks.push(joinUrlSegments(base, file)) }) } if (chunk.code.includes(preloadMethod)) { @@ -39,7 +39,7 @@ export function ssrManifestPlugin(config: ResolvedConfig): Plugin { const code = chunk.code let imports: ImportSpecifier[] try { - imports = parseImports(code)[0].filter((i) => i.d > -1) + imports = parseImports(code)[0].filter((i) => i.n && i.d > -1) } catch (e: any) { this.error(e, e.idx) } @@ -59,7 +59,7 @@ export function ssrManifestPlugin(config: ResolvedConfig): Plugin { const chunk = bundle[filename] as OutputChunk | undefined if (chunk) { chunk.viteMetadata.importedCss.forEach((file) => { - deps.push(join(base, file)) // TODO:base + deps.push(joinUrlSegments(base, file)) // TODO:base }) chunk.imports.forEach(addDeps) } diff --git a/packages/vite/src/node/ssr/ssrModuleLoader.ts b/packages/vite/src/node/ssr/ssrModuleLoader.ts index 8f61125c134d8c..0e4e163570a575 100644 --- a/packages/vite/src/node/ssr/ssrModuleLoader.ts +++ b/packages/vite/src/node/ssr/ssrModuleLoader.ts @@ -12,7 +12,6 @@ import { transformRequest } from '../server/transformRequest' import type { InternalResolveOptions } from '../plugins/resolve' import { tryNodeResolve } from '../plugins/resolve' import { hookNodeResolve } from '../plugins/ssrRequireHook' -import { NULL_BYTE_PLACEHOLDER } from '../constants' import { ssrDynamicImportKey, ssrExportAllKey, @@ -38,7 +37,7 @@ export async function ssrLoadModule( urlStack: string[] = [], fixStacktrace?: boolean ): Promise { - url = unwrapId(url).replace(NULL_BYTE_PLACEHOLDER, '\0') + url = unwrapId(url) // when we instantiate multiple dependency modules in parallel, they may // point to shared modules. We need to avoid duplicate instantiation attempts @@ -119,13 +118,15 @@ async function instantiateModule( // CommonJS modules are preferred. We want to avoid ESM->ESM imports // whenever possible, because `hookNodeResolve` can't intercept them. const resolveOptions: InternalResolveOptions = { - dedupe, + mainFields: ['main'], + browserField: true, + conditions: [], extensions: ['.js', '.cjs', '.json'], + dedupe, + preserveSymlinks, isBuild: true, isProduction, isRequire: true, - mainFields: ['main'], - preserveSymlinks, root } @@ -138,7 +139,7 @@ async function instantiateModule( return nodeImport(dep, mod.file!, resolveOptions) } // convert to rollup URL because `pendingImports`, `moduleGraph.urlToModuleMap` requires that - dep = unwrapId(dep).replace(NULL_BYTE_PLACEHOLDER, '\0') + dep = unwrapId(dep) if (!isCircular(dep) && !pendingImports.get(dep)?.some(isCircular)) { pendingDeps.push(dep) if (pendingDeps.length === 1) { @@ -225,6 +226,12 @@ async function instantiateModule( return Object.freeze(ssrModule) } +// `nodeImport` may run in parallel on multiple `ssrLoadModule` calls. +// We keep track of the current importing count so that the first import +// would `hookNodeResolve`, and the last import would `unhookNodeResolve`. +let importingCount = 0 +let unhookNodeResolve: ReturnType | undefined + // In node@12+ we can use dynamic import to load CJS and ESM async function nodeImport( id: string, @@ -249,34 +256,36 @@ async function nodeImport( return resolved.id } - // When an ESM module imports an ESM dependency, this hook is *not* used. - const unhookNodeResolve = hookNodeResolve( - (nodeResolve) => (id, parent, isMain, options) => { - // Use the Vite resolver only for bare imports while skipping - // any absolute paths, built-in modules and binary modules. - if ( - !bareImportRE.test(id) || - path.isAbsolute(id) || - isBuiltin(id) || - id.endsWith('.node') - ) { - return nodeResolve(id, parent, isMain, options) - } - if (parent) { - let resolved = viteResolve(id, parent.id) - if (resolved) { - // hookNodeResolve must use platform-specific path.normalize - // to be compatible with dynamicImport (#6080) - resolved = path.normalize(resolved) + if (importingCount === 0) { + // When an ESM module imports an ESM dependency, this hook is *not* used. + unhookNodeResolve = hookNodeResolve( + (nodeResolve) => (id, parent, isMain, options) => { + // Use the Vite resolver only for bare imports while skipping + // any absolute paths, built-in modules and binary modules. + if ( + !bareImportRE.test(id) || + path.isAbsolute(id) || + isBuiltin(id) || + id.endsWith('.node') + ) { + return nodeResolve(id, parent, isMain, options) } - return resolved + if (parent) { + let resolved = viteResolve(id, parent.id) + if (resolved) { + // hookNodeResolve must use platform-specific path.normalize + // to be compatible with dynamicImport (#6080) + resolved = path.normalize(resolved) + } + return resolved + } + // Importing a CJS module from an ESM module. In this case, the import + // specifier is already an absolute path, so this is a no-op. + // Options like `resolve.dedupe` and `mode` are not respected. + return id } - // Importing a CJS module from an ESM module. In this case, the import - // specifier is already an absolute path, so this is a no-op. - // Options like `resolve.dedupe` and `mode` are not respected. - return id - } - ) + ) + } let url: string if (id.startsWith('node:') || isBuiltin(id)) { @@ -298,10 +307,14 @@ async function nodeImport( } try { + importingCount++ const mod = await dynamicImport(url) return proxyESM(mod) } finally { - unhookNodeResolve() + importingCount-- + if (importingCount === 0) { + unhookNodeResolve?.() + } } } diff --git a/packages/vite/src/node/ssr/ssrTransform.ts b/packages/vite/src/node/ssr/ssrTransform.ts index 2c38c53e74714e..eefdf29f70d7a1 100644 --- a/packages/vite/src/node/ssr/ssrTransform.ts +++ b/packages/vite/src/node/ssr/ssrTransform.ts @@ -5,6 +5,7 @@ import type { Identifier, Pattern, Property, + VariableDeclaration, Node as _Node } from 'estree' import { extract_names as extractNames } from 'periscopic' @@ -94,7 +95,7 @@ async function ssrTransformScript( function defineImport(node: Node, source: string) { deps.add(source) const importId = `__vite_ssr_import_${uid++}__` - s.appendLeft( + s.appendRight( node.start, `const ${importId} = await ${ssrImportKey}(${JSON.stringify(source)});\n` ) @@ -115,6 +116,7 @@ async function ssrTransformScript( // import { baz } from 'foo' --> baz -> __import_foo__.baz // import * as ok from 'foo' --> ok -> __import_foo__ if (node.type === 'ImportDeclaration') { + s.remove(node.start, node.end) const importId = defineImport(node, node.source.value as string) for (const spec of node.specifiers) { if (spec.type === 'ImportSpecifier') { @@ -129,7 +131,6 @@ async function ssrTransformScript( idToImportMap.set(spec.local.name, importId) } } - s.remove(node.start, node.end) } } @@ -196,24 +197,21 @@ async function ssrTransformScript( ) } else { // anonymous default exports - s.overwrite( + s.update( node.start, node.start + 14 /* 'export default'.length */, - `${ssrModuleExportsKey}.default =`, - { contentOnly: true } + `${ssrModuleExportsKey}.default =` ) } } // export * from './foo' if (node.type === 'ExportAllDeclaration') { + s.remove(node.start, node.end) + const importId = defineImport(node, node.source.value as string) if (node.exported) { - const importId = defineImport(node, node.source.value as string) - s.remove(node.start, node.end) defineExport(node.end, node.exported.name, `${importId}`) } else { - const importId = defineImport(node, node.source.value as string) - s.remove(node.start, node.end) s.appendLeft(node.end, `${ssrExportAllKey}(${importId});`) } } @@ -232,7 +230,7 @@ async function ssrTransformScript( // { foo } -> { foo: __import_x__.foo } // skip for destructuring patterns if ( - !isNodeInPatternWeakMap.get(parent) || + !isNodeInPattern(parent) || isInDestructuringAssignment(parent, parentStack) ) { s.appendLeft(id.end, `: ${binding}`) @@ -249,16 +247,14 @@ async function ssrTransformScript( s.prependRight(topNode.start, `const ${id.name} = ${binding};\n`) } } else { - s.overwrite(id.start, id.end, binding, { contentOnly: true }) + s.update(id.start, id.end, binding) } }, onImportMeta(node) { - s.overwrite(node.start, node.end, ssrImportMetaKey, { contentOnly: true }) + s.update(node.start, node.end, ssrImportMetaKey) }, onDynamicImport(node) { - s.overwrite(node.start, node.start + 6, ssrDynamicImportKey, { - contentOnly: true - }) + s.update(node.start, node.start + 6, ssrDynamicImportKey) if (node.type === 'ImportExpression' && node.source.type === 'Literal') { dynamicDeps.add(node.source.value as string) } @@ -307,7 +303,10 @@ interface Visitors { onDynamicImport: (node: Node) => void } -const isNodeInPatternWeakMap = new WeakMap<_Node, boolean>() +const isNodeInPatternWeakSet = new WeakSet<_Node>() +const setIsNodeInPattern = (node: Property) => isNodeInPatternWeakSet.add(node) +const isNodeInPattern = (node: _Node): node is Property => + isNodeInPatternWeakSet.has(node) /** * Same logic from \@vue/compiler-core & \@vue/compiler-sfc @@ -318,10 +317,11 @@ function walk( { onIdentifier, onImportMeta, onDynamicImport }: Visitors ) { const parentStack: Node[] = [] + const varKindStack: VariableDeclaration['kind'][] = [] const scopeMap = new WeakMap<_Node, Set>() const identifiers: [id: any, stack: Node[]][] = [] - const setScope = (node: FunctionNode, name: string) => { + const setScope = (node: _Node, name: string) => { let scopeIds = scopeMap.get(node) if (scopeIds && scopeIds.has(name)) { return @@ -336,29 +336,29 @@ function walk( function isInScope(name: string, parents: Node[]) { return parents.some((node) => node && scopeMap.get(node)?.has(name)) } - function handlePattern(p: Pattern, parentFunction: FunctionNode) { + function handlePattern(p: Pattern, parentScope: _Node) { if (p.type === 'Identifier') { - setScope(parentFunction, p.name) + setScope(parentScope, p.name) } else if (p.type === 'RestElement') { - handlePattern(p.argument, parentFunction) + handlePattern(p.argument, parentScope) } else if (p.type === 'ObjectPattern') { p.properties.forEach((property) => { if (property.type === 'RestElement') { - setScope(parentFunction, (property.argument as Identifier).name) + setScope(parentScope, (property.argument as Identifier).name) } else { - handlePattern(property.value, parentFunction) + handlePattern(property.value, parentScope) } }) } else if (p.type === 'ArrayPattern') { p.elements.forEach((element) => { if (element) { - handlePattern(element, parentFunction) + handlePattern(element, parentScope) } }) } else if (p.type === 'AssignmentPattern') { - handlePattern(p.left, parentFunction) + handlePattern(p.left, parentScope) } else { - setScope(parentFunction, (p as any).name) + setScope(parentScope, (p as any).name) } } @@ -368,7 +368,19 @@ function walk( return this.skip() } - parent && parentStack.unshift(parent) + // track parent stack, skip for "else-if"/"else" branches as acorn nests + // the ast within "if" nodes instead of flattening them + if ( + parent && + !(parent.type === 'IfStatement' && node === parent.alternate) + ) { + parentStack.unshift(parent) + } + + // track variable declaration kind stack used by VariableDeclarator + if (node.type === 'VariableDeclaration') { + varKindStack.unshift(node.kind) + } if (node.type === 'MetaProperty' && node.meta.name === 'import') { onImportMeta(node) @@ -388,9 +400,9 @@ function walk( // If it is a function declaration, it could be shadowing an import // Add its name to the scope so it won't get replaced if (node.type === 'FunctionDeclaration') { - const parentFunction = findParentFunction(parentStack) - if (parentFunction) { - setScope(parentFunction, node.id!.name) + const parentScope = findParentScope(parentStack) + if (parentScope) { + setScope(parentScope, node.id!.name) } } // walk function expressions and add its arguments to known identifiers @@ -427,9 +439,12 @@ function walk( }) } else if (node.type === 'Property' && parent!.type === 'ObjectPattern') { // mark property in destructuring pattern - isNodeInPatternWeakMap.set(node, true) + setIsNodeInPattern(node) } else if (node.type === 'VariableDeclarator') { - const parentFunction = findParentFunction(parentStack) + const parentFunction = findParentScope( + parentStack, + varKindStack[0] === 'var' + ) if (parentFunction) { handlePattern(node.id, parentFunction) } @@ -437,7 +452,17 @@ function walk( }, leave(node: Node, parent: Node | null) { - parent && parentStack.shift() + // untrack parent stack from above + if ( + parent && + !(parent.type === 'IfStatement' && node === parent.alternate) + ) { + parentStack.shift() + } + + if (node.type === 'VariableDeclaration') { + varKindStack.shift() + } } }) @@ -476,8 +501,12 @@ function isRefIdentifier(id: Identifier, parent: _Node, parentStack: _Node[]) { } // property key - // this also covers object destructuring pattern - if (isStaticPropertyKey(id, parent) || isNodeInPatternWeakMap.get(parent)) { + if (isStaticPropertyKey(id, parent)) { + return false + } + + // object destructuring pattern + if (isNodeInPattern(parent) && parent.value === id) { return false } @@ -516,12 +545,19 @@ const isStaticProperty = (node: _Node): node is Property => const isStaticPropertyKey = (node: _Node, parent: _Node) => isStaticProperty(parent) && parent.key === node +const functionNodeTypeRE = /Function(?:Expression|Declaration)$|Method$/ function isFunction(node: _Node): node is FunctionNode { - return /Function(?:Expression|Declaration)$|Method$/.test(node.type) + return functionNodeTypeRE.test(node.type) } -function findParentFunction(parentStack: _Node[]): FunctionNode | undefined { - return parentStack.find((i) => isFunction(i)) as FunctionNode +const scopeNodeTypeRE = + /(?:Function|Class)(?:Expression|Declaration)$|Method$|^IfStatement$/ +function findParentScope( + parentStack: _Node[], + isVar = false +): _Node | undefined { + const regex = isVar ? functionNodeTypeRE : scopeNodeTypeRE + return parentStack.find((i) => regex.test(i.type)) } function isInDestructuringAssignment( diff --git a/packages/vite/src/node/tsconfig.json b/packages/vite/src/node/tsconfig.json index 270b34189571e1..bfaac1db6cbf00 100644 --- a/packages/vite/src/node/tsconfig.json +++ b/packages/vite/src/node/tsconfig.json @@ -1,6 +1,6 @@ { "extends": "../../tsconfig.base.json", - "include": ["./", "../../types"], + "include": ["./", "../dep-types", "../types"], "exclude": ["**/__tests__"], "compilerOptions": { "lib": ["ESNext", "DOM"] diff --git a/packages/vite/src/node/utils.ts b/packages/vite/src/node/utils.ts index 85f85adf3cf550..d307b5089b9d45 100644 --- a/packages/vite/src/node/utils.ts +++ b/packages/vite/src/node/utils.ts @@ -3,7 +3,7 @@ import os from 'node:os' import path from 'node:path' import { createHash } from 'node:crypto' import { promisify } from 'node:util' -import { URL, URLSearchParams, pathToFileURL } from 'node:url' +import { URL, URLSearchParams } from 'node:url' import { builtinModules, createRequire } from 'node:module' import { promises as dns } from 'node:dns' import { performance } from 'node:perf_hooks' @@ -14,7 +14,7 @@ import remapping from '@ampproject/remapping' import type { DecodedSourceMap, RawSourceMap } from '@ampproject/remapping' import colors from 'picocolors' import debug from 'debug' -import type { Alias, AliasOptions } from 'types/alias' +import type { Alias, AliasOptions } from 'dep-types/alias' import type MagicString from 'magic-string' import type { TransformResult } from 'rollup' @@ -25,6 +25,7 @@ import { DEFAULT_EXTENSIONS, ENV_PUBLIC_PATH, FS_PREFIX, + NULL_BYTE_PLACEHOLDER, OPTIMIZABLE_ENTRY_RE, VALID_ID_PREFIX, loopbackHosts, @@ -53,10 +54,24 @@ export function slash(p: string): string { return p.replace(/\\/g, '/') } -// Strip valid id prefix. This is prepended to resolved Ids that are -// not valid browser import specifiers by the importAnalysis plugin. +/** + * Prepend `/@id/` and replace null byte so the id is URL-safe. + * This is prepended to resolved ids that are not valid browser + * import specifiers by the importAnalysis plugin. + */ +export function wrapId(id: string): string { + return id.startsWith(VALID_ID_PREFIX) + ? id + : VALID_ID_PREFIX + id.replace('\0', NULL_BYTE_PLACEHOLDER) +} + +/** + * Undo {@link wrapId}'s `/@id/` and null byte replacements. + */ export function unwrapId(id: string): string { - return id.startsWith(VALID_ID_PREFIX) ? id.slice(VALID_ID_PREFIX.length) : id + return id.startsWith(VALID_ID_PREFIX) + ? id.slice(VALID_ID_PREFIX.length).replace(NULL_BYTE_PLACEHOLDER, '\0') + : id } export const flattenId = (id: string): string => @@ -261,7 +276,7 @@ export const isDataUrl = (url: string): boolean => dataUrlRE.test(url) export const virtualModuleRE = /^virtual-module:.*/ export const virtualModulePrefix = 'virtual-module:' -const knownJsSrcRE = /\.((j|t)sx?|m[jt]s|vue|marko|svelte|astro)($|\?)/ +const knownJsSrcRE = /\.((j|t)sx?|m[jt]s|vue|marko|svelte|astro|imba)($|\?)/ export const isJSRequest = (url: string): boolean => { url = cleanUrl(url) if (knownJsSrcRE.test(url)) { @@ -288,6 +303,7 @@ export function getPotentialTsSrcPaths(filePath: string): string[] { } const importQueryRE = /(\?|&)import=?(?:&|$)/ +const directRequestRE = /(\?|&)direct=?(?:&|$)/ const internalPrefixes = [ FS_PREFIX, VALID_ID_PREFIX, @@ -303,19 +319,17 @@ export const isInternalRequest = (url: string): boolean => export function removeImportQuery(url: string): string { return url.replace(importQueryRE, '$1').replace(trailingSeparatorRE, '') } +export function removeDirectQuery(url: string): string { + return url.replace(directRequestRE, '$1').replace(trailingSeparatorRE, '') +} export function injectQuery(url: string, queryToInject: string): string { // encode percents for consistent behavior with pathToFileURL // see #2614 for details - let resolvedUrl = new URL(url.replace(/%/g, '%25'), 'relative:///') - if (resolvedUrl.protocol !== 'relative:') { - resolvedUrl = pathToFileURL(url) - } - let { protocol, pathname, search, hash } = resolvedUrl - if (protocol === 'file:') { - pathname = pathname.slice(1) - } - pathname = decodeURIComponent(pathname) + const resolvedUrl = new URL(url.replace(/%/g, '%25'), 'relative:///') + const { search, hash } = resolvedUrl + let pathname = cleanUrl(url) + pathname = isWindows ? slash(pathname) : pathname return `${pathname}?${queryToInject}${search ? `&` + search.slice(1) : ''}${ hash ?? '' }` @@ -390,6 +404,7 @@ export function isDefined(value: T | undefined | null): value is T { interface LookupFileOptions { pathOnly?: boolean rootDir?: string + predicate?: (file: string) => boolean } export function lookupFile( @@ -400,7 +415,12 @@ export function lookupFile( for (const format of formats) { const fullPath = path.join(dir, format) if (fs.existsSync(fullPath) && fs.statSync(fullPath).isFile()) { - return options?.pathOnly ? fullPath : fs.readFileSync(fullPath, 'utf-8') + const result = options?.pathOnly + ? fullPath + : fs.readFileSync(fullPath, 'utf-8') + if (!options?.predicate || options.predicate(result)) { + return result + } } } const parentDir = path.dirname(dir) @@ -429,10 +449,8 @@ export function posToNumber( const lines = source.split(splitRE) const { line, column } = pos let start = 0 - for (let i = 0; i < line - 1; i++) { - if (lines[i]) { - start += lines[i].length + 1 - } + for (let i = 0; i < line - 1 && i < lines.length; i++) { + start += lines[i].length + 1 } return start + column } @@ -532,16 +550,45 @@ export function isFileReadable(filename: string): boolean { } } +const splitFirstDirRE = /(.+?)[\\/](.+)/ + /** * Delete every file and subdirectory. **The given directory must exist.** - * Pass an optional `skip` array to preserve files in the root directory. + * Pass an optional `skip` array to preserve files under the root directory. */ export function emptyDir(dir: string, skip?: string[]): void { + const skipInDir: string[] = [] + let nested: Map | null = null + if (skip?.length) { + for (const file of skip) { + if (path.dirname(file) !== '.') { + const matched = file.match(splitFirstDirRE) + if (matched) { + nested ??= new Map() + const [, nestedDir, skipPath] = matched + let nestedSkip = nested.get(nestedDir) + if (!nestedSkip) { + nestedSkip = [] + nested.set(nestedDir, nestedSkip) + } + if (!nestedSkip.includes(skipPath)) { + nestedSkip.push(skipPath) + } + } + } else { + skipInDir.push(file) + } + } + } for (const file of fs.readdirSync(dir)) { - if (skip?.includes(file)) { + if (skipInDir.includes(file)) { continue } - fs.rmSync(path.resolve(dir, file), { recursive: true, force: true }) + if (nested?.has(file)) { + emptyDir(path.resolve(dir, file), nested.get(file)) + } else { + fs.rmSync(path.resolve(dir, file), { recursive: true, force: true }) + } } } @@ -1144,3 +1191,16 @@ export const isNonDriveRelativeAbsolutePath = (p: string): boolean => { if (!isWindows) return p.startsWith('/') return windowsDrivePathPrefixRE.test(p) } + +export function joinUrlSegments(a: string, b: string): string { + if (!a || !b) { + return a || b || '' + } + if (a.endsWith('/')) { + a = a.substring(0, a.length - 1) + } + if (!b.startsWith('/')) { + b = '/' + b + } + return a + b +} diff --git a/packages/vite/src/node/watch.ts b/packages/vite/src/node/watch.ts new file mode 100644 index 00000000000000..47a455a0cd0e76 --- /dev/null +++ b/packages/vite/src/node/watch.ts @@ -0,0 +1,21 @@ +import type { WatchOptions } from 'dep-types/chokidar' + +export function resolveChokidarOptions( + options: WatchOptions | undefined +): WatchOptions { + const { ignored = [], ...otherOptions } = options ?? {} + + const resolvedWatchOptions: WatchOptions = { + ignored: [ + '**/.git/**', + '**/node_modules/**', + '**/test-results/**', // Playwright + ...(Array.isArray(ignored) ? ignored : [ignored]) + ], + ignoreInitial: true, + ignorePermissionErrors: true, + ...otherOptions + } + + return resolvedWatchOptions +} diff --git a/packages/vite/src/types/alias.d.ts b/packages/vite/src/types/alias.d.ts new file mode 100644 index 00000000000000..5752c25c04fc96 --- /dev/null +++ b/packages/vite/src/types/alias.d.ts @@ -0,0 +1,61 @@ +/** +Types from https://github.com/rollup/plugins/blob/master/packages/alias/types/index.d.ts +Inlined because the plugin is bundled. + +https://github.com/rollup/plugins/blob/master/LICENSE + +The MIT License (MIT) + +Copyright (c) 2019 RollupJS Plugin Contributors (https://github.com/rollup/plugins/graphs/contributors) + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. +*/ + +import type { PluginHooks } from 'rollup' + +export interface Alias { + find: string | RegExp + replacement: string + /** + * Instructs the plugin to use an alternative resolving algorithm, + * rather than the Rollup's resolver. + * @default null + */ + customResolver?: ResolverFunction | ResolverObject | null +} + +export type MapToFunction = T extends Function ? T : never + +export type ResolverFunction = MapToFunction + +export interface ResolverObject { + buildStart?: PluginHooks['buildStart'] + resolveId: ResolverFunction +} + +/** + * Specifies an `Object`, or an `Array` of `Object`, + * which defines aliases used to replace values in `import` or `require` statements. + * With either format, the order of the entries is important, + * in that the first defined rules are applied first. + * + * This is passed to \@rollup/plugin-alias as the "entries" field + * https://github.com/rollup/plugins/tree/master/packages/alias#entries + */ +export type AliasOptions = readonly Alias[] | { [find: string]: string } diff --git a/packages/vite/src/types/anymatch.d.ts b/packages/vite/src/types/anymatch.d.ts new file mode 100644 index 00000000000000..9204588583046d --- /dev/null +++ b/packages/vite/src/types/anymatch.d.ts @@ -0,0 +1,5 @@ +export type AnymatchFn = (testString: string) => boolean +export type AnymatchPattern = string | RegExp | AnymatchFn +type AnymatchMatcher = AnymatchPattern | AnymatchPattern[] + +export { AnymatchMatcher as Matcher } diff --git a/packages/vite/src/types/chokidar.d.ts b/packages/vite/src/types/chokidar.d.ts new file mode 100644 index 00000000000000..0dc4bec1013643 --- /dev/null +++ b/packages/vite/src/types/chokidar.d.ts @@ -0,0 +1,229 @@ +// Inlined to avoid extra dependency (chokidar is bundled in the published build) + +// https://github.com/paulmillr/chokidar/blob/master/types/index.d.ts +// MIT Licensed https://github.com/paulmillr/chokidar/blob/master/LICENSE + +/** +The MIT License (MIT) + +Copyright (c) 2012-2019 Paul Miller (https://paulmillr.com), Elan Shanker + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the “Software”), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. +*/ +/// + +import type * as fs from 'node:fs' +import { EventEmitter } from 'node:events' +import type { Matcher } from './anymatch' + +export class FSWatcher extends EventEmitter implements fs.FSWatcher { + options: WatchOptions + + /** + * Constructs a new FSWatcher instance with optional WatchOptions parameter. + */ + constructor(options?: WatchOptions) + + /** + * Add files, directories, or glob patterns for tracking. Takes an array of strings or just one + * string. + */ + add(paths: string | ReadonlyArray): this + + /** + * Stop watching files, directories, or glob patterns. Takes an array of strings or just one + * string. + */ + unwatch(paths: string | ReadonlyArray): this + + /** + * Returns an object representing all the paths on the file system being watched by this + * `FSWatcher` instance. The object's keys are all the directories (using absolute paths unless + * the `cwd` option was used), and the values are arrays of the names of the items contained in + * each directory. + */ + getWatched(): { + [directory: string]: string[] + } + + /** + * Removes all listeners from watched files. + */ + close(): Promise + + on( + event: 'add' | 'addDir' | 'change', + listener: (path: string, stats?: fs.Stats) => void + ): this + + on( + event: 'all', + listener: ( + eventName: 'add' | 'addDir' | 'change' | 'unlink' | 'unlinkDir', + path: string, + stats?: fs.Stats + ) => void + ): this + + /** + * Error occurred + */ + on(event: 'error', listener: (error: Error) => void): this + + /** + * Exposes the native Node `fs.FSWatcher events` + */ + on( + event: 'raw', + listener: (eventName: string, path: string, details: any) => void + ): this + + /** + * Fires when the initial scan is complete + */ + on(event: 'ready', listener: () => void): this + + on(event: 'unlink' | 'unlinkDir', listener: (path: string) => void): this + + on(event: string, listener: (...args: any[]) => void): this +} + +export interface WatchOptions { + /** + * Indicates whether the process should continue to run as long as files are being watched. If + * set to `false` when using `fsevents` to watch, no more events will be emitted after `ready`, + * even if the process continues to run. + */ + persistent?: boolean + + /** + * ([anymatch](https://github.com/micromatch/anymatch)-compatible definition) Defines files/paths to + * be ignored. The whole relative or absolute path is tested, not just filename. If a function + * with two arguments is provided, it gets called twice per path - once with a single argument + * (the path), second time with two arguments (the path and the + * [`fs.Stats`](https://nodejs.org/api/fs.html#fs_class_fs_stats) object of that path). + */ + ignored?: Matcher + + /** + * If set to `false` then `add`/`addDir` events are also emitted for matching paths while + * instantiating the watching as chokidar discovers these file paths (before the `ready` event). + */ + ignoreInitial?: boolean + + /** + * When `false`, only the symlinks themselves will be watched for changes instead of following + * the link references and bubbling events through the link's path. + */ + followSymlinks?: boolean + + /** + * The base directory from which watch `paths` are to be derived. Paths emitted with events will + * be relative to this. + */ + cwd?: string + + /** + * If set to true then the strings passed to .watch() and .add() are treated as literal path + * names, even if they look like globs. + * + * @default false + */ + disableGlobbing?: boolean + + /** + * Whether to use fs.watchFile (backed by polling), or fs.watch. If polling leads to high CPU + * utilization, consider setting this to `false`. It is typically necessary to **set this to + * `true` to successfully watch files over a network**, and it may be necessary to successfully + * watch files in other non-standard situations. Setting to `true` explicitly on OS X overrides + * the `useFsEvents` default. + */ + usePolling?: boolean + + /** + * Whether to use the `fsevents` watching interface if available. When set to `true` explicitly + * and `fsevents` is available this supercedes the `usePolling` setting. When set to `false` on + * OS X, `usePolling: true` becomes the default. + */ + useFsEvents?: boolean + + /** + * If relying upon the [`fs.Stats`](https://nodejs.org/api/fs.html#fs_class_fs_stats) object that + * may get passed with `add`, `addDir`, and `change` events, set this to `true` to ensure it is + * provided even in cases where it wasn't already available from the underlying watch events. + */ + alwaysStat?: boolean + + /** + * If set, limits how many levels of subdirectories will be traversed. + */ + depth?: number + + /** + * Interval of file system polling. + */ + interval?: number + + /** + * Interval of file system polling for binary files. ([see list of binary extensions](https://gi + * thub.com/sindresorhus/binary-extensions/blob/master/binary-extensions.json)) + */ + binaryInterval?: number + + /** + * Indicates whether to watch files that don't have read permissions if possible. If watching + * fails due to `EPERM` or `EACCES` with this set to `true`, the errors will be suppressed + * silently. + */ + ignorePermissionErrors?: boolean + + /** + * `true` if `useFsEvents` and `usePolling` are `false`. Automatically filters out artifacts + * that occur when using editors that use "atomic writes" instead of writing directly to the + * source file. If a file is re-added within 100 ms of being deleted, Chokidar emits a `change` + * event rather than `unlink` then `add`. If the default of 100 ms does not work well for you, + * you can override it by setting `atomic` to a custom value, in milliseconds. + */ + atomic?: boolean | number + + /** + * can be set to an object in order to adjust timing params: + */ + awaitWriteFinish?: AwaitWriteFinishOptions | boolean +} + +export interface AwaitWriteFinishOptions { + /** + * Amount of time in milliseconds for a file size to remain constant before emitting its event. + */ + stabilityThreshold?: number + + /** + * File size polling interval. + */ + pollInterval?: number +} + +/** + * produces an instance of `FSWatcher`. + */ +export function watch( + paths: string | ReadonlyArray, + options?: WatchOptions +): FSWatcher diff --git a/packages/vite/src/types/commonjs.d.ts b/packages/vite/src/types/commonjs.d.ts new file mode 100644 index 00000000000000..01948156deb1cd --- /dev/null +++ b/packages/vite/src/types/commonjs.d.ts @@ -0,0 +1,230 @@ +/** + * https://github.com/rollup/plugins/blob/master/packages/commonjs/types/index.d.ts + * + * This source code is licensed under the MIT license found in the + * LICENSE file at + * https://github.com/rollup/plugins/blob/master/LICENSE + */ +export interface RollupCommonJSOptions { + /** + * A minimatch pattern, or array of patterns, which specifies the files in + * the build the plugin should operate on. By default, all files with + * extension `".cjs"` or those in `extensions` are included, but you can + * narrow this list by only including specific files. These files will be + * analyzed and transpiled if either the analysis does not find ES module + * specific statements or `transformMixedEsModules` is `true`. + * @default undefined + */ + include?: string | RegExp | readonly (string | RegExp)[] + /** + * A minimatch pattern, or array of patterns, which specifies the files in + * the build the plugin should _ignore_. By default, all files with + * extensions other than those in `extensions` or `".cjs"` are ignored, but you + * can exclude additional files. See also the `include` option. + * @default undefined + */ + exclude?: string | RegExp | readonly (string | RegExp)[] + /** + * For extensionless imports, search for extensions other than .js in the + * order specified. Note that you need to make sure that non-JavaScript files + * are transpiled by another plugin first. + * @default [ '.js' ] + */ + extensions?: ReadonlyArray + /** + * If true then uses of `global` won't be dealt with by this plugin + * @default false + */ + ignoreGlobal?: boolean + /** + * If false, skips source map generation for CommonJS modules. This will + * improve performance. + * @default true + */ + sourceMap?: boolean + /** + * Some `require` calls cannot be resolved statically to be translated to + * imports. + * When this option is set to `false`, the generated code will either + * directly throw an error when such a call is encountered or, when + * `dynamicRequireTargets` is used, when such a call cannot be resolved with a + * configured dynamic require target. + * Setting this option to `true` will instead leave the `require` call in the + * code or use it as a fallback for `dynamicRequireTargets`. + * @default false + */ + ignoreDynamicRequires?: boolean + /** + * Instructs the plugin whether to enable mixed module transformations. This + * is useful in scenarios with modules that contain a mix of ES `import` + * statements and CommonJS `require` expressions. Set to `true` if `require` + * calls should be transformed to imports in mixed modules, or `false` if the + * `require` expressions should survive the transformation. The latter can be + * important if the code contains environment detection, or you are coding + * for an environment with special treatment for `require` calls such as + * ElectronJS. See also the `ignore` option. + * @default false + */ + transformMixedEsModules?: boolean + /** + * By default, this plugin will try to hoist `require` statements as imports + * to the top of each file. While this works well for many code bases and + * allows for very efficient ESM output, it does not perfectly capture + * CommonJS semantics as the order of side effects like log statements may + * change. But it is especially problematic when there are circular `require` + * calls between CommonJS modules as those often rely on the lazy execution of + * nested `require` calls. + * + * Setting this option to `true` will wrap all CommonJS files in functions + * which are executed when they are required for the first time, preserving + * NodeJS semantics. Note that this can have an impact on the size and + * performance of the generated code. + * + * The default value of `"auto"` will only wrap CommonJS files when they are + * part of a CommonJS dependency cycle, e.g. an index file that is required by + * many of its dependencies. All other CommonJS files are hoisted. This is the + * recommended setting for most code bases. + * + * `false` will entirely prevent wrapping and hoist all files. This may still + * work depending on the nature of cyclic dependencies but will often cause + * problems. + * + * You can also provide a minimatch pattern, or array of patterns, to only + * specify a subset of files which should be wrapped in functions for proper + * `require` semantics. + * + * `"debug"` works like `"auto"` but after bundling, it will display a warning + * containing a list of ids that have been wrapped which can be used as + * minimatch pattern for fine-tuning. + * @default "auto" + */ + strictRequires?: boolean | string | RegExp | readonly (string | RegExp)[] + /** + * Sometimes you have to leave require statements unconverted. Pass an array + * containing the IDs or a `id => boolean` function. + * @default [] + */ + ignore?: ReadonlyArray | ((id: string) => boolean) + /** + * In most cases, where `require` calls are inside a `try-catch` clause, + * they should be left unconverted as it requires an optional dependency + * that may or may not be installed beside the rolled up package. + * Due to the conversion of `require` to a static `import` - the call is + * hoisted to the top of the file, outside of the `try-catch` clause. + * + * - `true`: All `require` calls inside a `try` will be left unconverted. + * - `false`: All `require` calls inside a `try` will be converted as if the + * `try-catch` clause is not there. + * - `remove`: Remove all `require` calls from inside any `try` block. + * - `string[]`: Pass an array containing the IDs to left unconverted. + * - `((id: string) => boolean|'remove')`: Pass a function that control + * individual IDs. + * + * @default false + */ + ignoreTryCatch?: + | boolean + | 'remove' + | ReadonlyArray + | ((id: string) => boolean | 'remove') + /** + * Controls how to render imports from external dependencies. By default, + * this plugin assumes that all external dependencies are CommonJS. This + * means they are rendered as default imports to be compatible with e.g. + * NodeJS where ES modules can only import a default export from a CommonJS + * dependency. + * + * If you set `esmExternals` to `true`, this plugins assumes that all + * external dependencies are ES modules and respect the + * `requireReturnsDefault` option. If that option is not set, they will be + * rendered as namespace imports. + * + * You can also supply an array of ids to be treated as ES modules, or a + * function that will be passed each external id to determine if it is an ES + * module. + * @default false + */ + esmExternals?: boolean | ReadonlyArray | ((id: string) => boolean) + /** + * Controls what is returned when requiring an ES module from a CommonJS file. + * When using the `esmExternals` option, this will also apply to external + * modules. By default, this plugin will render those imports as namespace + * imports i.e. + * + * ```js + * // input + * const foo = require('foo'); + * + * // output + * import * as foo from 'foo'; + * ``` + * + * However there are some situations where this may not be desired. + * For these situations, you can change Rollup's behaviour either globally or + * per module. To change it globally, set the `requireReturnsDefault` option + * to one of the following values: + * + * - `false`: This is the default, requiring an ES module returns its + * namespace. This is the only option that will also add a marker + * `__esModule: true` to the namespace to support interop patterns in + * CommonJS modules that are transpiled ES modules. + * - `"namespace"`: Like `false`, requiring an ES module returns its + * namespace, but the plugin does not add the `__esModule` marker and thus + * creates more efficient code. For external dependencies when using + * `esmExternals: true`, no additional interop code is generated. + * - `"auto"`: This is complementary to how `output.exports: "auto"` works in + * Rollup: If a module has a default export and no named exports, requiring + * that module returns the default export. In all other cases, the namespace + * is returned. For external dependencies when using `esmExternals: true`, a + * corresponding interop helper is added. + * - `"preferred"`: If a module has a default export, requiring that module + * always returns the default export, no matter whether additional named + * exports exist. This is similar to how previous versions of this plugin + * worked. Again for external dependencies when using `esmExternals: true`, + * an interop helper is added. + * - `true`: This will always try to return the default export on require + * without checking if it actually exists. This can throw at build time if + * there is no default export. This is how external dependencies are handled + * when `esmExternals` is not used. The advantage over the other options is + * that, like `false`, this does not add an interop helper for external + * dependencies, keeping the code lean. + * + * To change this for individual modules, you can supply a function for + * `requireReturnsDefault` instead. This function will then be called once for + * each required ES module or external dependency with the corresponding id + * and allows you to return different values for different modules. + * @default false + */ + requireReturnsDefault?: + | boolean + | 'auto' + | 'preferred' + | 'namespace' + | ((id: string) => boolean | 'auto' | 'preferred' | 'namespace') + + /** + * @default "auto" + */ + defaultIsModuleExports?: boolean | 'auto' | ((id: string) => boolean | 'auto') + /** + * Some modules contain dynamic `require` calls, or require modules that + * contain circular dependencies, which are not handled well by static + * imports. Including those modules as `dynamicRequireTargets` will simulate a + * CommonJS (NodeJS-like) environment for them with support for dynamic + * dependencies. It also enables `strictRequires` for those modules. + * + * Note: In extreme cases, this feature may result in some paths being + * rendered as absolute in the final bundle. The plugin tries to avoid + * exposing paths from the local machine, but if you are `dynamicRequirePaths` + * with paths that are far away from your project's folder, that may require + * replacing strings like `"/Users/John/Desktop/foo-project/"` -\> `"/"`. + */ + dynamicRequireTargets?: string | ReadonlyArray + /** + * To avoid long paths when using the `dynamicRequireTargets` option, you can use this option to specify a directory + * that is a common parent for all files that use dynamic require statements. Using a directory higher up such as `/` + * may lead to unnecessarily long paths in the generated code and may expose directory names on your machine like your + * home directory name. By default it uses the current working directory. + */ + dynamicRequireRoot?: string +} diff --git a/packages/vite/src/types/connect.d.ts b/packages/vite/src/types/connect.d.ts new file mode 100644 index 00000000000000..74c559b6a436f5 --- /dev/null +++ b/packages/vite/src/types/connect.d.ts @@ -0,0 +1,111 @@ +// Inlined to avoid extra dependency +// MIT Licensed https://github.com/DefinitelyTyped/DefinitelyTyped/blob/master/LICENSE + +// Type definitions for connect v3.4.0 +// Project: https://github.com/senchalabs/connect +// Definitions by: Maxime LUCE +// Evan Hahn +// Definitions: https://github.com/DefinitelyTyped/DefinitelyTyped + +/// +import * as http from 'node:http' + +export namespace Connect { + export type ServerHandle = HandleFunction | http.Server + + export class IncomingMessage extends http.IncomingMessage { + originalUrl?: http.IncomingMessage['url'] | undefined + } + + export type NextFunction = (err?: any) => void + + export type SimpleHandleFunction = ( + req: IncomingMessage, + res: http.ServerResponse + ) => void + export type NextHandleFunction = ( + req: IncomingMessage, + res: http.ServerResponse, + next: NextFunction + ) => void + export type ErrorHandleFunction = ( + err: any, + req: IncomingMessage, + res: http.ServerResponse, + next: NextFunction + ) => void + export type HandleFunction = + | SimpleHandleFunction + | NextHandleFunction + | ErrorHandleFunction + + export interface ServerStackItem { + route: string + handle: ServerHandle + } + + export interface Server extends NodeJS.EventEmitter { + (req: http.IncomingMessage, res: http.ServerResponse, next?: Function): void + + route: string + stack: ServerStackItem[] + + /** + * Utilize the given middleware `handle` to the given `route`, + * defaulting to _/_. This "route" is the mount-point for the + * middleware, when given a value other than _/_ the middleware + * is only effective when that segment is present in the request's + * pathname. + * + * For example if we were to mount a function at _/admin_, it would + * be invoked on _/admin_, and _/admin/settings_, however it would + * not be invoked for _/_, or _/posts_. + */ + use(fn: NextHandleFunction): Server + use(fn: HandleFunction): Server + use(route: string, fn: NextHandleFunction): Server + use(route: string, fn: HandleFunction): Server + + /** + * Handle server requests, punting them down + * the middleware stack. + */ + handle( + req: http.IncomingMessage, + res: http.ServerResponse, + next: Function + ): void + + /** + * Listen for connections. + * + * This method takes the same arguments + * as node's `http.Server#listen()`. + * + * HTTP and HTTPS: + * + * If you run your application both as HTTP + * and HTTPS you may wrap them individually, + * since your Connect "server" is really just + * a JavaScript `Function`. + * + * var connect = require('connect') + * , http = require('http') + * , https = require('https'); + * + * var app = connect(); + * + * http.createServer(app).listen(80); + * https.createServer(options, app).listen(443); + */ + listen( + port: number, + hostname?: string, + backlog?: number, + callback?: Function + ): http.Server + listen(port: number, hostname?: string, callback?: Function): http.Server + listen(path: string, callback?: Function): http.Server + listen(handle: any, listeningListener?: Function): http.Server + } +} diff --git a/packages/vite/src/types/dynamicImportVars.d.ts b/packages/vite/src/types/dynamicImportVars.d.ts new file mode 100644 index 00000000000000..99f1b5c453ba97 --- /dev/null +++ b/packages/vite/src/types/dynamicImportVars.d.ts @@ -0,0 +1,17 @@ +export interface RollupDynamicImportVarsOptions { + /** + * Files to include in this plugin (default all). + * @default [] + */ + include?: string | RegExp | (string | RegExp)[] + /** + * Files to exclude in this plugin (default none). + * @default [] + */ + exclude?: string | RegExp | (string | RegExp)[] + /** + * By default, the plugin quits the build process when it encounters an error. If you set this option to true, it will throw a warning instead and leave the code untouched. + * @default false + */ + warnOnError?: boolean +} diff --git a/packages/vite/src/types/http-proxy.d.ts b/packages/vite/src/types/http-proxy.d.ts new file mode 100644 index 00000000000000..1cae820dcdfa8f --- /dev/null +++ b/packages/vite/src/types/http-proxy.d.ts @@ -0,0 +1,250 @@ +// Inlined to avoid extra dependency +// MIT Licensed https://github.com/DefinitelyTyped/DefinitelyTyped/blob/master/LICENSE + +// Type definitions for node-http-proxy 1.17 +// Project: https://github.com/nodejitsu/node-http-proxy +// Definitions by: Maxime LUCE +// Florian Oellerich +// Daniel Schmidt +// Jordan Abreu +// Samuel Bodin +// Definitions: https://github.com/DefinitelyTyped/DefinitelyTyped +// TypeScript Version: 2.1 + +/// + +import type * as net from 'node:net' +import type * as http from 'node:http' +import * as events from 'node:events' +import type * as url from 'node:url' +import type * as stream from 'node:stream' + +export namespace HttpProxy { + export type ProxyTarget = ProxyTargetUrl | ProxyTargetDetailed + + export type ProxyTargetUrl = string | Partial + + export interface ProxyTargetDetailed { + host: string + port: number + protocol?: string | undefined + hostname?: string | undefined + socketPath?: string | undefined + key?: string | undefined + passphrase?: string | undefined + pfx?: Buffer | string | undefined + cert?: string | undefined + ca?: string | undefined + ciphers?: string | undefined + secureProtocol?: string | undefined + } + + export type ErrorCallback = ( + err: Error, + req: http.IncomingMessage, + res: http.ServerResponse, + target?: ProxyTargetUrl + ) => void + + export class Server extends events.EventEmitter { + /** + * Creates the proxy server with specified options. + * @param options - Config object passed to the proxy + */ + constructor(options?: ServerOptions) + + /** + * Used for proxying regular HTTP(S) requests + * @param req - Client request. + * @param res - Client response. + * @param options - Additional options. + */ + web( + req: http.IncomingMessage, + res: http.ServerResponse, + options?: ServerOptions, + callback?: ErrorCallback + ): void + + /** + * Used for proxying regular HTTP(S) requests + * @param req - Client request. + * @param socket - Client socket. + * @param head - Client head. + * @param options - Additional options. + */ + ws( + req: http.IncomingMessage, + socket: unknown, + head: unknown, + options?: ServerOptions, + callback?: ErrorCallback + ): void + + /** + * A function that wraps the object in a webserver, for your convenience + * @param port - Port to listen on + */ + listen(port: number): Server + + /** + * A function that closes the inner webserver and stops listening on given port + */ + close(callback?: () => void): void + + /** + * Creates the proxy server with specified options. + * @param options - Config object passed to the proxy + * @returns Proxy object with handlers for `ws` and `web` requests + */ + static createProxyServer(options?: ServerOptions): Server + + /** + * Creates the proxy server with specified options. + * @param options - Config object passed to the proxy + * @returns Proxy object with handlers for `ws` and `web` requests + */ + static createServer(options?: ServerOptions): Server + + /** + * Creates the proxy server with specified options. + * @param options - Config object passed to the proxy + * @returns Proxy object with handlers for `ws` and `web` requests + */ + static createProxy(options?: ServerOptions): Server + + addListener(event: string, listener: () => void): this + on(event: string, listener: () => void): this + on(event: 'error', listener: ErrorCallback): this + on( + event: 'start', + listener: ( + req: http.IncomingMessage, + res: http.ServerResponse, + target: ProxyTargetUrl + ) => void + ): this + on( + event: 'proxyReq', + listener: ( + proxyReq: http.ClientRequest, + req: http.IncomingMessage, + res: http.ServerResponse, + options: ServerOptions + ) => void + ): this + on( + event: 'proxyRes', + listener: ( + proxyRes: http.IncomingMessage, + req: http.IncomingMessage, + res: http.ServerResponse + ) => void + ): this + on( + event: 'proxyReqWs', + listener: ( + proxyReq: http.ClientRequest, + req: http.IncomingMessage, + socket: net.Socket, + options: ServerOptions, + head: any + ) => void + ): this + on( + event: 'econnreset', + listener: ( + err: Error, + req: http.IncomingMessage, + res: http.ServerResponse, + target: ProxyTargetUrl + ) => void + ): this + on( + event: 'end', + listener: ( + req: http.IncomingMessage, + res: http.ServerResponse, + proxyRes: http.IncomingMessage + ) => void + ): this + on( + event: 'close', + listener: ( + proxyRes: http.IncomingMessage, + proxySocket: net.Socket, + proxyHead: any + ) => void + ): this + + once(event: string, listener: () => void): this + removeListener(event: string, listener: () => void): this + removeAllListeners(event?: string): this + getMaxListeners(): number + setMaxListeners(n: number): this + listeners(event: string): Array<() => void> + emit(event: string, ...args: any[]): boolean + listenerCount(type: string): number + } + + export interface ServerOptions { + /** URL string to be parsed with the url module. */ + target?: ProxyTarget | undefined + /** URL string to be parsed with the url module. */ + forward?: ProxyTargetUrl | undefined + /** Object to be passed to http(s).request. */ + agent?: any + /** Object to be passed to https.createServer(). */ + ssl?: any + /** If you want to proxy websockets. */ + ws?: boolean | undefined + /** Adds x- forward headers. */ + xfwd?: boolean | undefined + /** Verify SSL certificate. */ + secure?: boolean | undefined + /** Explicitly specify if we are proxying to another proxy. */ + toProxy?: boolean | undefined + /** Specify whether you want to prepend the target's path to the proxy path. */ + prependPath?: boolean | undefined + /** Specify whether you want to ignore the proxy path of the incoming request. */ + ignorePath?: boolean | undefined + /** Local interface string to bind for outgoing connections. */ + localAddress?: string | undefined + /** Changes the origin of the host header to the target URL. */ + changeOrigin?: boolean | undefined + /** specify whether you want to keep letter case of response header key */ + preserveHeaderKeyCase?: boolean | undefined + /** Basic authentication i.e. 'user:password' to compute an Authorization header. */ + auth?: string | undefined + /** Rewrites the location hostname on (301 / 302 / 307 / 308) redirects, Default: null. */ + hostRewrite?: string | undefined + /** Rewrites the location host/ port on (301 / 302 / 307 / 308) redirects based on requested host/ port.Default: false. */ + autoRewrite?: boolean | undefined + /** Rewrites the location protocol on (301 / 302 / 307 / 308) redirects to 'http' or 'https'.Default: null. */ + protocolRewrite?: string | undefined + /** rewrites domain of set-cookie headers. */ + cookieDomainRewrite?: + | false + | string + | { [oldDomain: string]: string } + | undefined + /** rewrites path of set-cookie headers. Default: false */ + cookiePathRewrite?: + | false + | string + | { [oldPath: string]: string } + | undefined + /** object with extra headers to be added to target requests. */ + headers?: { [header: string]: string } | undefined + /** Timeout (in milliseconds) when proxy receives no response from target. Default: 120000 (2 minutes) */ + proxyTimeout?: number | undefined + /** Timeout (in milliseconds) for incoming requests */ + timeout?: number | undefined + /** Specify whether you want to follow redirects. Default: false */ + followRedirects?: boolean | undefined + /** If set to true, none of the webOutgoing passes are called and it's your responsibility to appropriately return the response by listening and acting on the proxyRes event */ + selfHandleResponse?: boolean | undefined + /** Buffer */ + buffer?: stream.Stream | undefined + } +} diff --git a/packages/vite/src/types/package.json b/packages/vite/src/types/package.json new file mode 100644 index 00000000000000..3d6a75c81455a0 --- /dev/null +++ b/packages/vite/src/types/package.json @@ -0,0 +1,3 @@ +{ + "//": "this file is just here to make pnpm happy with --frozen-lockfile" +} diff --git a/packages/vite/src/types/shims.d.ts b/packages/vite/src/types/shims.d.ts new file mode 100644 index 00000000000000..db5ce9bc1dfb37 --- /dev/null +++ b/packages/vite/src/types/shims.d.ts @@ -0,0 +1,41 @@ +declare module 'connect' { + const connect: () => any + export = connect +} + +declare module 'cors' { + function cors(options: any): any + export = cors +} + +declare module 'http-proxy' { + const proxy: any + export = proxy +} + +declare module 'connect-history-api-fallback' { + const plugin: any + export = plugin +} + +declare module 'launch-editor-middleware' { + const plugin: any + export = plugin +} + +declare module 'postcss-import' { + import type { Plugin } from 'postcss' + const plugin: (options: { + resolve: ( + id: string, + basedir: string, + importOptions: any + ) => string | string[] | Promise + nameLayer: (index: number, rootFilename: string) => string + }) => Plugin + export = plugin +} + +// LESS' types somewhat references this which doesn't make sense in Node, +// so we have to shim it +declare interface HTMLLinkElement {} diff --git a/packages/vite/src/types/terser.d.ts b/packages/vite/src/types/terser.d.ts new file mode 100644 index 00000000000000..a704a20cdc71ae --- /dev/null +++ b/packages/vite/src/types/terser.d.ts @@ -0,0 +1,250 @@ +// Modified and inlined to avoid extra dependency +// Source: https://github.com/terser/terser/blob/master/tools/terser.d.ts +// BSD Licensed https://github.com/terser/terser/blob/master/LICENSE + +/* +Terser is released under the BSD license: + +Copyright 2012-2018 (c) Mihai Bazon + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions +are met: + + * Redistributions of source code must retain the above + copyright notice, this list of conditions and the following + disclaimer. + + * Redistributions in binary form must reproduce the above + copyright notice, this list of conditions and the following + disclaimer in the documentation and/or other materials + provided with the distribution. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDER “AS IS” AND ANY +EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER BE +LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, +OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR +TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF +THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF +SUCH DAMAGE. +*/ + +export namespace Terser { + export type ECMA = 5 | 2015 | 2016 | 2017 | 2018 | 2019 | 2020 + + export interface ParseOptions { + bare_returns?: boolean + /** @deprecated legacy option. Currently, all supported EcmaScript is valid to parse. */ + ecma?: ECMA + html5_comments?: boolean + shebang?: boolean + } + + export interface CompressOptions { + arguments?: boolean + arrows?: boolean + booleans_as_integers?: boolean + booleans?: boolean + collapse_vars?: boolean + comparisons?: boolean + computed_props?: boolean + conditionals?: boolean + dead_code?: boolean + defaults?: boolean + directives?: boolean + drop_console?: boolean + drop_debugger?: boolean + ecma?: ECMA + evaluate?: boolean + expression?: boolean + global_defs?: object + hoist_funs?: boolean + hoist_props?: boolean + hoist_vars?: boolean + ie8?: boolean + if_return?: boolean + inline?: boolean | InlineFunctions + join_vars?: boolean + keep_classnames?: boolean | RegExp + keep_fargs?: boolean + keep_fnames?: boolean | RegExp + keep_infinity?: boolean + loops?: boolean + module?: boolean + negate_iife?: boolean + passes?: number + properties?: boolean + pure_funcs?: string[] + pure_getters?: boolean | 'strict' + reduce_funcs?: boolean + reduce_vars?: boolean + sequences?: boolean | number + side_effects?: boolean + switches?: boolean + toplevel?: boolean + top_retain?: null | string | string[] | RegExp + typeofs?: boolean + unsafe_arrows?: boolean + unsafe?: boolean + unsafe_comps?: boolean + unsafe_Function?: boolean + unsafe_math?: boolean + unsafe_symbols?: boolean + unsafe_methods?: boolean + unsafe_proto?: boolean + unsafe_regexp?: boolean + unsafe_undefined?: boolean + unused?: boolean + } + + export enum InlineFunctions { + Disabled = 0, + SimpleFunctions = 1, + WithArguments = 2, + WithArgumentsAndVariables = 3 + } + + export interface MangleOptions { + eval?: boolean + keep_classnames?: boolean | RegExp + keep_fnames?: boolean | RegExp + module?: boolean + nth_identifier?: SimpleIdentifierMangler | WeightedIdentifierMangler + properties?: boolean | ManglePropertiesOptions + reserved?: string[] + safari10?: boolean + toplevel?: boolean + } + + /** + * An identifier mangler for which the output is invariant with respect to the source code. + */ + export interface SimpleIdentifierMangler { + /** + * Obtains the nth most favored (usually shortest) identifier to rename a variable to. + * The mangler will increment n and retry until the return value is not in use in scope, and is not a reserved word. + * This function is expected to be stable; Evaluating get(n) === get(n) should always return true. + * @param n - The ordinal of the identifier. + */ + get(n: number): string + } + + /** + * An identifier mangler that leverages character frequency analysis to determine identifier precedence. + */ + export interface WeightedIdentifierMangler extends SimpleIdentifierMangler { + /** + * Modifies the internal weighting of the input characters by the specified delta. + * Will be invoked on the entire printed AST, and then deduct mangleable identifiers. + * @param chars - The characters to modify the weighting of. + * @param delta - The numeric weight to add to the characters. + */ + consider(chars: string, delta: number): number + /** + * Resets character weights. + */ + reset(): void + /** + * Sorts identifiers by character frequency, in preparation for calls to get(n). + */ + sort(): void + } + + export interface ManglePropertiesOptions { + builtins?: boolean + debug?: boolean + keep_quoted?: boolean | 'strict' + nth_identifier?: SimpleIdentifierMangler | WeightedIdentifierMangler + regex?: RegExp | string + reserved?: string[] + } + + export interface FormatOptions { + ascii_only?: boolean + /** @deprecated Not implemented anymore */ + beautify?: boolean + braces?: boolean + comments?: + | boolean + | 'all' + | 'some' + | RegExp + | (( + node: any, + comment: { + value: string + type: 'comment1' | 'comment2' | 'comment3' | 'comment4' + pos: number + line: number + col: number + } + ) => boolean) + ecma?: ECMA + ie8?: boolean + keep_numbers?: boolean + indent_level?: number + indent_start?: number + inline_script?: boolean + keep_quoted_props?: boolean + max_line_len?: number | false + preamble?: string + preserve_annotations?: boolean + quote_keys?: boolean + quote_style?: OutputQuoteStyle + safari10?: boolean + semicolons?: boolean + shebang?: boolean + shorthand?: boolean + source_map?: SourceMapOptions + webkit?: boolean + width?: number + wrap_iife?: boolean + wrap_func_args?: boolean + } + + export enum OutputQuoteStyle { + PreferDouble = 0, + AlwaysSingle = 1, + AlwaysDouble = 2, + AlwaysOriginal = 3 + } + + export interface MinifyOptions { + compress?: boolean | CompressOptions + ecma?: ECMA + enclose?: boolean | string + ie8?: boolean + keep_classnames?: boolean | RegExp + keep_fnames?: boolean | RegExp + mangle?: boolean | MangleOptions + module?: boolean + nameCache?: object + format?: FormatOptions + /** @deprecated deprecated */ + output?: FormatOptions + parse?: ParseOptions + safari10?: boolean + sourceMap?: boolean | SourceMapOptions + toplevel?: boolean + } + + export interface MinifyOutput { + code?: string + map?: object | string + decoded_map?: object | null + } + + export interface SourceMapOptions { + /** Source map object, 'inline' or source map file content */ + content?: object | string + includeSources?: boolean + filename?: string + root?: string + url?: string | 'inline' + } +} diff --git a/packages/vite/src/types/ws.d.ts b/packages/vite/src/types/ws.d.ts new file mode 100644 index 00000000000000..a06341fca9eeb9 --- /dev/null +++ b/packages/vite/src/types/ws.d.ts @@ -0,0 +1,553 @@ +// Modified and inlined to avoid extra dependency +// Source: https://github.com/DefinitelyTyped/DefinitelyTyped/blob/master/types/ws/index.d.ts + +// Type definitions for ws 8.5 +// Project: https://github.com/websockets/ws +// Definitions by: Paul Loyd +// Margus Lamp +// Philippe D'Alva +// reduckted +// teidesu +// Bartosz Wojtkowiak +// Kyle Hensel +// Samuel Skeen +// Definitions: https://github.com/DefinitelyTyped/DefinitelyTyped + +/// + +import { EventEmitter } from 'node:events' +import type { + Agent, + ClientRequest, + ClientRequestArgs, + Server as HTTPServer, + IncomingMessage, + OutgoingHttpHeaders +} from 'node:http' +import type { Server as HTTPSServer } from 'node:https' +import type { Duplex, DuplexOptions } from 'node:stream' +import type { SecureContextOptions } from 'node:tls' +import type { URL } from 'node:url' +import type { ZlibOptions } from 'node:zlib' + +// WebSocket socket. +declare class WebSocket extends EventEmitter { + /** The connection is not yet open. */ + static readonly CONNECTING: 0 + /** The connection is open and ready to communicate. */ + static readonly OPEN: 1 + /** The connection is in the process of closing. */ + static readonly CLOSING: 2 + /** The connection is closed. */ + static readonly CLOSED: 3 + + binaryType: 'nodebuffer' | 'arraybuffer' | 'fragments' + readonly bufferedAmount: number + readonly extensions: string + /** Indicates whether the websocket is paused */ + readonly isPaused: boolean + readonly protocol: string + /** The current state of the connection */ + readonly readyState: + | typeof WebSocket.CONNECTING + | typeof WebSocket.OPEN + | typeof WebSocket.CLOSING + | typeof WebSocket.CLOSED + readonly url: string + + /** The connection is not yet open. */ + readonly CONNECTING: 0 + /** The connection is open and ready to communicate. */ + readonly OPEN: 1 + /** The connection is in the process of closing. */ + readonly CLOSING: 2 + /** The connection is closed. */ + readonly CLOSED: 3 + + onopen: ((event: WebSocket.Event) => void) | null + onerror: ((event: WebSocket.ErrorEvent) => void) | null + onclose: ((event: WebSocket.CloseEvent) => void) | null + onmessage: ((event: WebSocket.MessageEvent) => void) | null + + constructor(address: null) + constructor( + address: string | URL, + options?: WebSocket.ClientOptions | ClientRequestArgs + ) + constructor( + address: string | URL, + protocols?: string | string[], + options?: WebSocket.ClientOptions | ClientRequestArgs + ) + + close(code?: number, data?: string | Buffer): void + ping(data?: any, mask?: boolean, cb?: (err: Error) => void): void + pong(data?: any, mask?: boolean, cb?: (err: Error) => void): void + send(data: any, cb?: (err?: Error) => void): void + send( + data: any, + options: { + mask?: boolean | undefined + binary?: boolean | undefined + compress?: boolean | undefined + fin?: boolean | undefined + }, + cb?: (err?: Error) => void + ): void + terminate(): void + + /** + * Pause the websocket causing it to stop emitting events. Some events can still be + * emitted after this is called, until all buffered data is consumed. This method + * is a noop if the ready state is `CONNECTING` or `CLOSED`. + */ + pause(): void + /** + * Make a paused socket resume emitting events. This method is a noop if the ready + * state is `CONNECTING` or `CLOSED`. + */ + resume(): void + + // HTML5 WebSocket events + addEventListener( + method: 'message', + cb: (event: WebSocket.MessageEvent) => void, + options?: WebSocket.EventListenerOptions + ): void + addEventListener( + method: 'close', + cb: (event: WebSocket.CloseEvent) => void, + options?: WebSocket.EventListenerOptions + ): void + addEventListener( + method: 'error', + cb: (event: WebSocket.ErrorEvent) => void, + options?: WebSocket.EventListenerOptions + ): void + addEventListener( + method: 'open', + cb: (event: WebSocket.Event) => void, + options?: WebSocket.EventListenerOptions + ): void + + removeEventListener( + method: 'message', + cb: (event: WebSocket.MessageEvent) => void + ): void + removeEventListener( + method: 'close', + cb: (event: WebSocket.CloseEvent) => void + ): void + removeEventListener( + method: 'error', + cb: (event: WebSocket.ErrorEvent) => void + ): void + removeEventListener( + method: 'open', + cb: (event: WebSocket.Event) => void + ): void + + // Events + on( + event: 'close', + listener: (this: WebSocket, code: number, reason: Buffer) => void + ): this + on(event: 'error', listener: (this: WebSocket, err: Error) => void): this + on( + event: 'upgrade', + listener: (this: WebSocket, request: IncomingMessage) => void + ): this + on( + event: 'message', + listener: ( + this: WebSocket, + data: WebSocket.RawData, + isBinary: boolean + ) => void + ): this + on(event: 'open', listener: (this: WebSocket) => void): this + on( + event: 'ping' | 'pong', + listener: (this: WebSocket, data: Buffer) => void + ): this + on( + event: 'unexpected-response', + listener: ( + this: WebSocket, + request: ClientRequest, + response: IncomingMessage + ) => void + ): this + on( + event: string | symbol, + listener: (this: WebSocket, ...args: any[]) => void + ): this + + once( + event: 'close', + listener: (this: WebSocket, code: number, reason: Buffer) => void + ): this + once(event: 'error', listener: (this: WebSocket, err: Error) => void): this + once( + event: 'upgrade', + listener: (this: WebSocket, request: IncomingMessage) => void + ): this + once( + event: 'message', + listener: ( + this: WebSocket, + data: WebSocket.RawData, + isBinary: boolean + ) => void + ): this + once(event: 'open', listener: (this: WebSocket) => void): this + once( + event: 'ping' | 'pong', + listener: (this: WebSocket, data: Buffer) => void + ): this + once( + event: 'unexpected-response', + listener: ( + this: WebSocket, + request: ClientRequest, + response: IncomingMessage + ) => void + ): this + once( + event: string | symbol, + listener: (this: WebSocket, ...args: any[]) => void + ): this + + off( + event: 'close', + listener: (this: WebSocket, code: number, reason: Buffer) => void + ): this + off(event: 'error', listener: (this: WebSocket, err: Error) => void): this + off( + event: 'upgrade', + listener: (this: WebSocket, request: IncomingMessage) => void + ): this + off( + event: 'message', + listener: ( + this: WebSocket, + data: WebSocket.RawData, + isBinary: boolean + ) => void + ): this + off(event: 'open', listener: (this: WebSocket) => void): this + off( + event: 'ping' | 'pong', + listener: (this: WebSocket, data: Buffer) => void + ): this + off( + event: 'unexpected-response', + listener: ( + this: WebSocket, + request: ClientRequest, + response: IncomingMessage + ) => void + ): this + off( + event: string | symbol, + listener: (this: WebSocket, ...args: any[]) => void + ): this + + addListener( + event: 'close', + listener: (code: number, reason: Buffer) => void + ): this + addListener(event: 'error', listener: (err: Error) => void): this + addListener( + event: 'upgrade', + listener: (request: IncomingMessage) => void + ): this + addListener( + event: 'message', + listener: (data: WebSocket.RawData, isBinary: boolean) => void + ): this + addListener(event: 'open', listener: () => void): this + addListener(event: 'ping' | 'pong', listener: (data: Buffer) => void): this + addListener( + event: 'unexpected-response', + listener: (request: ClientRequest, response: IncomingMessage) => void + ): this + addListener(event: string | symbol, listener: (...args: any[]) => void): this + + removeListener( + event: 'close', + listener: (code: number, reason: Buffer) => void + ): this + removeListener(event: 'error', listener: (err: Error) => void): this + removeListener( + event: 'upgrade', + listener: (request: IncomingMessage) => void + ): this + removeListener( + event: 'message', + listener: (data: WebSocket.RawData, isBinary: boolean) => void + ): this + removeListener(event: 'open', listener: () => void): this + removeListener(event: 'ping' | 'pong', listener: (data: Buffer) => void): this + removeListener( + event: 'unexpected-response', + listener: (request: ClientRequest, response: IncomingMessage) => void + ): this + removeListener( + event: string | symbol, + listener: (...args: any[]) => void + ): this +} + +declare const WebSocketAlias: typeof WebSocket +interface WebSocketAlias extends WebSocket {} // tslint:disable-line no-empty-interface + +declare namespace WebSocket { + /** + * Data represents the raw message payload received over the WebSocket. + */ + type RawData = Buffer | ArrayBuffer | Buffer[] + + /** + * Data represents the message payload received over the WebSocket. + */ + type Data = string | Buffer | ArrayBuffer | Buffer[] + + /** + * CertMeta represents the accepted types for certificate & key data. + */ + type CertMeta = string | string[] | Buffer | Buffer[] + + /** + * VerifyClientCallbackSync is a synchronous callback used to inspect the + * incoming message. The return value (boolean) of the function determines + * whether or not to accept the handshake. + */ + type VerifyClientCallbackSync = (info: { + origin: string + secure: boolean + req: IncomingMessage + }) => boolean + + /** + * VerifyClientCallbackAsync is an asynchronous callback used to inspect the + * incoming message. The return value (boolean) of the function determines + * whether or not to accept the handshake. + */ + type VerifyClientCallbackAsync = ( + info: { origin: string; secure: boolean; req: IncomingMessage }, + callback: ( + res: boolean, + code?: number, + message?: string, + headers?: OutgoingHttpHeaders + ) => void + ) => void + + interface ClientOptions extends SecureContextOptions { + protocol?: string | undefined + followRedirects?: boolean | undefined + generateMask?(mask: Buffer): void + handshakeTimeout?: number | undefined + maxRedirects?: number | undefined + perMessageDeflate?: boolean | PerMessageDeflateOptions | undefined + localAddress?: string | undefined + protocolVersion?: number | undefined + headers?: { [key: string]: string } | undefined + origin?: string | undefined + agent?: Agent | undefined + host?: string | undefined + family?: number | undefined + checkServerIdentity?(servername: string, cert: CertMeta): boolean + rejectUnauthorized?: boolean | undefined + maxPayload?: number | undefined + skipUTF8Validation?: boolean | undefined + } + + interface PerMessageDeflateOptions { + serverNoContextTakeover?: boolean | undefined + clientNoContextTakeover?: boolean | undefined + serverMaxWindowBits?: number | undefined + clientMaxWindowBits?: number | undefined + zlibDeflateOptions?: + | { + flush?: number | undefined + finishFlush?: number | undefined + chunkSize?: number | undefined + windowBits?: number | undefined + level?: number | undefined + memLevel?: number | undefined + strategy?: number | undefined + dictionary?: Buffer | Buffer[] | DataView | undefined + info?: boolean | undefined + } + | undefined + zlibInflateOptions?: ZlibOptions | undefined + threshold?: number | undefined + concurrencyLimit?: number | undefined + } + + interface Event { + type: string + target: WebSocket + } + + interface ErrorEvent { + error: any + message: string + type: string + target: WebSocket + } + + interface CloseEvent { + wasClean: boolean + code: number + reason: string + type: string + target: WebSocket + } + + interface MessageEvent { + data: Data + type: string + target: WebSocket + } + + interface EventListenerOptions { + once?: boolean | undefined + } + + interface ServerOptions { + host?: string | undefined + port?: number | undefined + backlog?: number | undefined + server?: HTTPServer | HTTPSServer | undefined + verifyClient?: + | VerifyClientCallbackAsync + | VerifyClientCallbackSync + | undefined + handleProtocols?: ( + protocols: Set, + request: IncomingMessage + ) => string | false + path?: string | undefined + noServer?: boolean | undefined + clientTracking?: boolean | undefined + perMessageDeflate?: boolean | PerMessageDeflateOptions | undefined + maxPayload?: number | undefined + skipUTF8Validation?: boolean | undefined + WebSocket?: typeof WebSocket.WebSocket | undefined + } + + interface AddressInfo { + address: string + family: string + port: number + } + + // WebSocket Server + class Server extends EventEmitter { + options: ServerOptions + path: string + clients: Set + + constructor(options?: ServerOptions, callback?: () => void) + + address(): AddressInfo | string + close(cb?: (err?: Error) => void): void + handleUpgrade( + request: IncomingMessage, + socket: Duplex, + upgradeHead: Buffer, + callback: (client: T, request: IncomingMessage) => void + ): void + shouldHandle(request: IncomingMessage): boolean | Promise + + // Events + on( + event: 'connection', + cb: (this: Server, socket: T, request: IncomingMessage) => void + ): this + on(event: 'error', cb: (this: Server, error: Error) => void): this + on( + event: 'headers', + cb: (this: Server, headers: string[], request: IncomingMessage) => void + ): this + on(event: 'close' | 'listening', cb: (this: Server) => void): this + on( + event: string | symbol, + listener: (this: Server, ...args: any[]) => void + ): this + + once( + event: 'connection', + cb: (this: Server, socket: T, request: IncomingMessage) => void + ): this + once(event: 'error', cb: (this: Server, error: Error) => void): this + once( + event: 'headers', + cb: (this: Server, headers: string[], request: IncomingMessage) => void + ): this + once(event: 'close' | 'listening', cb: (this: Server) => void): this + once( + event: string | symbol, + listener: (this: Server, ...args: any[]) => void + ): this + + off( + event: 'connection', + cb: (this: Server, socket: T, request: IncomingMessage) => void + ): this + off(event: 'error', cb: (this: Server, error: Error) => void): this + off( + event: 'headers', + cb: (this: Server, headers: string[], request: IncomingMessage) => void + ): this + off(event: 'close' | 'listening', cb: (this: Server) => void): this + off( + event: string | symbol, + listener: (this: Server, ...args: any[]) => void + ): this + + addListener( + event: 'connection', + cb: (client: T, request: IncomingMessage) => void + ): this + addListener(event: 'error', cb: (err: Error) => void): this + addListener( + event: 'headers', + cb: (headers: string[], request: IncomingMessage) => void + ): this + addListener(event: 'close' | 'listening', cb: () => void): this + addListener( + event: string | symbol, + listener: (...args: any[]) => void + ): this + + removeListener(event: 'connection', cb: (client: T) => void): this + removeListener(event: 'error', cb: (err: Error) => void): this + removeListener( + event: 'headers', + cb: (headers: string[], request: IncomingMessage) => void + ): this + removeListener(event: 'close' | 'listening', cb: () => void): this + removeListener( + event: string | symbol, + listener: (...args: any[]) => void + ): this + } + + const WebSocketServer: typeof Server + interface WebSocketServer extends Server {} // tslint:disable-line no-empty-interface + const WebSocket: typeof WebSocketAlias + interface WebSocket extends WebSocketAlias {} // tslint:disable-line no-empty-interface + + // WebSocket stream + function createWebSocketStream( + websocket: WebSocket, + options?: DuplexOptions + ): Duplex +} + +// export = WebSocket +export { WebSocket, WebSocketAlias } diff --git a/packages/vite/tsconfig.check.json b/packages/vite/tsconfig.check.json index d1e9f059c9409c..47d8cd0e54aded 100644 --- a/packages/vite/tsconfig.check.json +++ b/packages/vite/tsconfig.check.json @@ -2,6 +2,8 @@ "compilerOptions": { "noEmit": true, "moduleResolution": "classic", + "noResolve": true, + "typeRoots": [], // Only add entries to `paths` when you are adding/updating dependencies (not devDependencies) // See CONTRIBUTING.md "Ensure type support" for more details "paths": { @@ -14,9 +16,22 @@ // indirect: postcss depends on it "source-map-js": ["./node_modules/source-map-js/source-map.d.ts"] }, - "typeRoots": [], "strict": true, "exactOptionalPropertyTypes": true }, - "include": ["dist/**/*.d.ts"] + "include": [ + "../../node_modules/@types/node/**/*", + // dependencies + "./node_modules/rollup/**/*", + "./node_modules/esbuild/**/*", + "./node_modules/postcss/**/*", + "./node_modules/source-map-js/**/*", + // dist + "dist/**/*", + "types/customEvent.d.ts", + "types/hmrPayload.d.ts", + "types/importGlob.d.ts", + "types/importMeta.d.ts", + "types/hot.d.ts" + ] } diff --git a/packages/vite/types/alias.d.ts b/packages/vite/types/alias.d.ts index 3f4393586f1299..91f73635584ffb 100644 --- a/packages/vite/types/alias.d.ts +++ b/packages/vite/types/alias.d.ts @@ -1,59 +1,6 @@ -/** -Types from https://github.com/rollup/plugins/blob/master/packages/alias/types/index.d.ts -Inlined because the plugin is bundled. - -https://github.com/rollup/plugins/blob/master/LICENSE - -The MIT License (MIT) - -Copyright (c) 2019 RollupJS Plugin Contributors (https://github.com/rollup/plugins/graphs/contributors) - -Permission is hereby granted, free of charge, to any person obtaining a copy -of this software and associated documentation files (the "Software"), to deal -in the Software without restriction, including without limitation the rights -to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -copies of the Software, and to permit persons to whom the Software is -furnished to do so, subject to the following conditions: - -The above copyright notice and this permission notice shall be included in -all copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN -THE SOFTWARE. -*/ - -import type { PluginHooks } from 'rollup' - -export interface Alias { - find: string | RegExp - replacement: string - /** - * Instructs the plugin to use an alternative resolving algorithm, - * rather than the Rollup's resolver. - * @default null - */ - customResolver?: ResolverFunction | ResolverObject | null -} - -export type ResolverFunction = PluginHooks['resolveId'] - -export interface ResolverObject { - buildStart?: PluginHooks['buildStart'] - resolveId: ResolverFunction -} - -/** - * Specifies an `Object`, or an `Array` of `Object`, - * which defines aliases used to replace values in `import` or `require` statements. - * With either format, the order of the entries is important, - * in that the first defined rules are applied first. - * - * This is passed to \@rollup/plugin-alias as the "entries" field - * https://github.com/rollup/plugins/tree/master/packages/alias#entries - */ -export type AliasOptions = readonly Alias[] | { [find: string]: string } +export type { + Alias, + ResolverFunction, + ResolverObject, + AliasOptions +} from '../dist/node' diff --git a/packages/vite/types/anymatch.d.ts b/packages/vite/types/anymatch.d.ts index 9204588583046d..c98e9505f3f90f 100644 --- a/packages/vite/types/anymatch.d.ts +++ b/packages/vite/types/anymatch.d.ts @@ -1,5 +1 @@ -export type AnymatchFn = (testString: string) => boolean -export type AnymatchPattern = string | RegExp | AnymatchFn -type AnymatchMatcher = AnymatchPattern | AnymatchPattern[] - -export { AnymatchMatcher as Matcher } +export type { AnymatchFn, AnymatchPattern, Matcher } from '../dist/node' diff --git a/packages/vite/types/chokidar.d.ts b/packages/vite/types/chokidar.d.ts index 51ac89b8e98d1f..eae22093bf9d7d 100644 --- a/packages/vite/types/chokidar.d.ts +++ b/packages/vite/types/chokidar.d.ts @@ -1,228 +1,10 @@ -// Inlined to avoid extra dependency (chokidar is bundled in the published build) +export type { + FSWatcher, + WatchOptions, + AwaitWriteFinishOptions +} from '../dist/node' -// https://github.com/paulmillr/chokidar/blob/master/types/index.d.ts -// MIT Licensed https://github.com/paulmillr/chokidar/blob/master/LICENSE - -/** -The MIT License (MIT) - -Copyright (c) 2012-2019 Paul Miller (https://paulmillr.com), Elan Shanker - -Permission is hereby granted, free of charge, to any person obtaining a copy -of this software and associated documentation files (the “Software”), to deal -in the Software without restriction, including without limitation the rights -to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -copies of the Software, and to permit persons to whom the Software is -furnished to do so, subject to the following conditions: - -The above copyright notice and this permission notice shall be included in -all copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN -THE SOFTWARE. -*/ -/// - -import type * as fs from 'node:fs' -import { EventEmitter } from 'node:events' -import type { Matcher } from './anymatch' - -export class FSWatcher extends EventEmitter implements fs.FSWatcher { - options: WatchOptions - - /** - * Constructs a new FSWatcher instance with optional WatchOptions parameter. - */ - constructor(options?: WatchOptions) - - /** - * Add files, directories, or glob patterns for tracking. Takes an array of strings or just one - * string. - */ - add(paths: string | ReadonlyArray): this - - /** - * Stop watching files, directories, or glob patterns. Takes an array of strings or just one - * string. - */ - unwatch(paths: string | ReadonlyArray): this - - /** - * Returns an object representing all the paths on the file system being watched by this - * `FSWatcher` instance. The object's keys are all the directories (using absolute paths unless - * the `cwd` option was used), and the values are arrays of the names of the items contained in - * each directory. - */ - getWatched(): { - [directory: string]: string[] - } - - /** - * Removes all listeners from watched files. - */ - close(): Promise - - on( - event: 'add' | 'addDir' | 'change', - listener: (path: string, stats?: fs.Stats) => void - ): this - - on( - event: 'all', - listener: ( - eventName: 'add' | 'addDir' | 'change' | 'unlink' | 'unlinkDir', - path: string, - stats?: fs.Stats - ) => void - ): this - - /** - * Error occurred - */ - on(event: 'error', listener: (error: Error) => void): this - - /** - * Exposes the native Node `fs.FSWatcher events` - */ - on( - event: 'raw', - listener: (eventName: string, path: string, details: any) => void - ): this - - /** - * Fires when the initial scan is complete - */ - on(event: 'ready', listener: () => void): this - - on(event: 'unlink' | 'unlinkDir', listener: (path: string) => void): this - - on(event: string, listener: (...args: any[]) => void): this -} - -export interface WatchOptions { - /** - * Indicates whether the process should continue to run as long as files are being watched. If - * set to `false` when using `fsevents` to watch, no more events will be emitted after `ready`, - * even if the process continues to run. - */ - persistent?: boolean - - /** - * ([anymatch](https://github.com/micromatch/anymatch)-compatible definition) Defines files/paths to - * be ignored. The whole relative or absolute path is tested, not just filename. If a function - * with two arguments is provided, it gets called twice per path - once with a single argument - * (the path), second time with two arguments (the path and the - * [`fs.Stats`](https://nodejs.org/api/fs.html#fs_class_fs_stats) object of that path). - */ - ignored?: Matcher - - /** - * If set to `false` then `add`/`addDir` events are also emitted for matching paths while - * instantiating the watching as chokidar discovers these file paths (before the `ready` event). - */ - ignoreInitial?: boolean - - /** - * When `false`, only the symlinks themselves will be watched for changes instead of following - * the link references and bubbling events through the link's path. - */ - followSymlinks?: boolean - - /** - * The base directory from which watch `paths` are to be derived. Paths emitted with events will - * be relative to this. - */ - cwd?: string - - /** - * If set to true then the strings passed to .watch() and .add() are treated as literal path - * names, even if they look like globs. - * - * @default false - */ - disableGlobbing?: boolean - - /** - * Whether to use fs.watchFile (backed by polling), or fs.watch. If polling leads to high CPU - * utilization, consider setting this to `false`. It is typically necessary to **set this to - * `true` to successfully watch files over a network**, and it may be necessary to successfully - * watch files in other non-standard situations. Setting to `true` explicitly on OS X overrides - * the `useFsEvents` default. - */ - usePolling?: boolean - - /** - * Whether to use the `fsevents` watching interface if available. When set to `true` explicitly - * and `fsevents` is available this supercedes the `usePolling` setting. When set to `false` on - * OS X, `usePolling: true` becomes the default. - */ - useFsEvents?: boolean - - /** - * If relying upon the [`fs.Stats`](https://nodejs.org/api/fs.html#fs_class_fs_stats) object that - * may get passed with `add`, `addDir`, and `change` events, set this to `true` to ensure it is - * provided even in cases where it wasn't already available from the underlying watch events. - */ - alwaysStat?: boolean - - /** - * If set, limits how many levels of subdirectories will be traversed. - */ - depth?: number - - /** - * Interval of file system polling. - */ - interval?: number - - /** - * Interval of file system polling for binary files. ([see list of binary extensions](https://gi - * thub.com/sindresorhus/binary-extensions/blob/master/binary-extensions.json)) - */ - binaryInterval?: number - - /** - * Indicates whether to watch files that don't have read permissions if possible. If watching - * fails due to `EPERM` or `EACCES` with this set to `true`, the errors will be suppressed - * silently. - */ - ignorePermissionErrors?: boolean - - /** - * `true` if `useFsEvents` and `usePolling` are `false`). Automatically filters out artifacts - * that occur when using editors that use "atomic writes" instead of writing directly to the - * source file. If a file is re-added within 100 ms of being deleted, Chokidar emits a `change` - * event rather than `unlink` then `add`. If the default of 100 ms does not work well for you, - * you can override it by setting `atomic` to a custom value, in milliseconds. - */ - atomic?: boolean | number - - /** - * can be set to an object in order to adjust timing params: - */ - awaitWriteFinish?: AwaitWriteFinishOptions | boolean -} - -export interface AwaitWriteFinishOptions { - /** - * Amount of time in milliseconds for a file size to remain constant before emitting its event. - */ - stabilityThreshold?: number - - /** - * File size polling interval. - */ - pollInterval?: number -} - -/** - * produces an instance of `FSWatcher`. - */ +import type { FSWatcher, WatchOptions } from '../dist/node' export function watch( paths: string | ReadonlyArray, options?: WatchOptions diff --git a/packages/vite/types/commonjs.d.ts b/packages/vite/types/commonjs.d.ts index 5891a7573112a5..8b95405a14257e 100644 --- a/packages/vite/types/commonjs.d.ts +++ b/packages/vite/types/commonjs.d.ts @@ -1,182 +1 @@ -/** - * https://github.com/rollup/plugins/blob/master/packages/commonjs/types/index.d.ts - * - * This source code is licensed under the MIT license found in the - * LICENSE file at - * https://github.com/rollup/plugins/blob/master/LICENSE - */ -export interface RollupCommonJSOptions { - /** - * A picomatch pattern, or array of patterns, which specifies the files in - * the build the plugin should operate on. By default, all files with - * extension `".cjs"` or those in `extensions` are included, but you can narrow - * this list by only including specific files. These files will be analyzed - * and transpiled if either the analysis does not find ES module specific - * statements or `transformMixedEsModules` is `true`. - * @default undefined - */ - include?: string | RegExp | readonly (string | RegExp)[] - /** - * A picomatch pattern, or array of patterns, which specifies the files in - * the build the plugin should _ignore_. By default, all files with - * extensions other than those in `extensions` or `".cjs"` are ignored, but you - * can exclude additional files. See also the `include` option. - * @default undefined - */ - exclude?: string | RegExp | readonly (string | RegExp)[] - /** - * For extensionless imports, search for extensions other than .js in the - * order specified. Note that you need to make sure that non-JavaScript files - * are transpiled by another plugin first. - * @default [ '.js' ] - */ - extensions?: ReadonlyArray - /** - * If true then uses of `global` won't be dealt with by this plugin - * @default false - */ - ignoreGlobal?: boolean - /** - * If false, skips source map generation for CommonJS modules. This will improve performance. - * @default true - */ - sourceMap?: boolean - /** - * Some `require` calls cannot be resolved statically to be translated to - * imports. - * When this option is set to `false`, the generated code will either - * directly throw an error when such a call is encountered or, when - * `dynamicRequireTargets` is used, when such a call cannot be resolved with a - * configured dynamic require target. - * Setting this option to `true` will instead leave the `require` call in the - * code or use it as a fallback for `dynamicRequireTargets`. - * @default false - */ - ignoreDynamicRequires?: boolean - /** - * Instructs the plugin whether to enable mixed module transformations. This - * is useful in scenarios with modules that contain a mix of ES `import` - * statements and CommonJS `require` expressions. Set to `true` if `require` - * calls should be transformed to imports in mixed modules, or `false` if the - * `require` expressions should survive the transformation. The latter can be - * important if the code contains environment detection, or you are coding - * for an environment with special treatment for `require` calls such as - * ElectronJS. See also the `ignore` option. - * @default false - */ - transformMixedEsModules?: boolean - /** - * Sometimes you have to leave require statements unconverted. Pass an array - * containing the IDs or a `id => boolean` function. - * @default [] - */ - ignore?: ReadonlyArray | ((id: string) => boolean) - /** - * In most cases, where `require` calls are inside a `try-catch` clause, - * they should be left unconverted as it requires an optional dependency - * that may or may not be installed beside the rolled up package. - * Due to the conversion of `require` to a static `import` - the call is hoisted - * to the top of the file, outside of the `try-catch` clause. - * - * - `true`: All `require` calls inside a `try` will be left unconverted. - * - `false`: All `require` calls inside a `try` will be converted as if the `try-catch` clause is not there. - * - `remove`: Remove all `require` calls from inside any `try` block. - * - `string[]`: Pass an array containing the IDs to left unconverted. - * - `((id: string) => boolean|'remove')`: Pass a function that control individual IDs. - * - * @default false - */ - ignoreTryCatch?: - | boolean - | 'remove' - | ReadonlyArray - | ((id: string) => boolean | 'remove') - /** - * Controls how to render imports from external dependencies. By default, - * this plugin assumes that all external dependencies are CommonJS. This - * means they are rendered as default imports to be compatible with e.g. - * NodeJS where ES modules can only import a default export from a CommonJS - * dependency. - * - * If you set `esmExternals` to `true`, this plugins assumes that all - * external dependencies are ES modules and respect the - * `requireReturnsDefault` option. If that option is not set, they will be - * rendered as namespace imports. - * - * You can also supply an array of ids to be treated as ES modules, or a - * function that will be passed each external id to determine if it is an ES - * module. - * @default false - */ - esmExternals?: boolean | ReadonlyArray | ((id: string) => boolean) - /** - * Controls what is returned when requiring an ES module from a CommonJS file. - * When using the `esmExternals` option, this will also apply to external - * modules. By default, this plugin will render those imports as namespace - * imports i.e. - * - * ```js - * // input - * const foo = require('foo'); - * - * // output - * import * as foo from 'foo'; - * ``` - * - * However there are some situations where this may not be desired. - * For these situations, you can change Rollup's behaviour either globally or - * per module. To change it globally, set the `requireReturnsDefault` option - * to one of the following values: - * - * - `false`: This is the default, requiring an ES module returns its - * namespace. This is the only option that will also add a marker - * `__esModule: true` to the namespace to support interop patterns in - * CommonJS modules that are transpiled ES modules. - * - `"namespace"`: Like `false`, requiring an ES module returns its - * namespace, but the plugin does not add the `__esModule` marker and thus - * creates more efficient code. For external dependencies when using - * `esmExternals: true`, no additional interop code is generated. - * - `"auto"`: This is complementary to how `output.exports: "auto"` works in - * Rollup: If a module has a default export and no named exports, requiring - * that module returns the default export. In all other cases, the namespace - * is returned. For external dependencies when using `esmExternals: true`, a - * corresponding interop helper is added. - * - `"preferred"`: If a module has a default export, requiring that module - * always returns the default export, no matter whether additional named - * exports exist. This is similar to how previous versions of this plugin - * worked. Again for external dependencies when using `esmExternals: true`, - * an interop helper is added. - * - `true`: This will always try to return the default export on require - * without checking if it actually exists. This can throw at build time if - * there is no default export. This is how external dependencies are handled - * when `esmExternals` is not used. The advantage over the other options is - * that, like `false`, this does not add an interop helper for external - * dependencies, keeping the code lean. - * - * To change this for individual modules, you can supply a function for - * `requireReturnsDefault` instead. This function will then be called once for - * each required ES module or external dependency with the corresponding id - * and allows you to return different values for different modules. - * @default false - */ - requireReturnsDefault?: - | boolean - | 'auto' - | 'preferred' - | 'namespace' - | ((id: string) => boolean | 'auto' | 'preferred' | 'namespace') - /** - * Some modules contain dynamic `require` calls, or require modules that - * contain circular dependencies, which are not handled well by static - * imports. Including those modules as `dynamicRequireTargets` will simulate a - * CommonJS (NodeJS-like) environment for them with support for dynamic and - * circular dependencies. - * - * Note: In extreme cases, this feature may result in some paths being - * rendered as absolute in the final bundle. The plugin tries to avoid - * exposing paths from the local machine, but if you are `dynamicRequirePaths` - * with paths that are far away from your project's folder, that may require - * replacing strings like `"/Users/John/Desktop/foo-project/"` -\> `"/"`. - */ - dynamicRequireTargets?: string | ReadonlyArray -} +export type { RollupCommonJSOptions } from '../dist/node' diff --git a/packages/vite/types/connect.d.ts b/packages/vite/types/connect.d.ts index 2fb97ebeb494ab..50502555f0f56c 100644 --- a/packages/vite/types/connect.d.ts +++ b/packages/vite/types/connect.d.ts @@ -1,111 +1 @@ -// Inlined to avoid extra dependency -// MIT Licensed https://github.com/DefinitelyTyped/DefinitelyTyped/blob/master/LICENSE - -// Type definitions for connect v3.4.0 -// Project: https://github.com/senchalabs/connect -// Definitions by: Maxime LUCE -// Evan Hahn -// Definitions: https://github.com/DefinitelyTyped/DefinitelyTyped - -/// -import * as http from 'node:http' - -export namespace Connect { - export type ServerHandle = HandleFunction | http.Server - - export class IncomingMessage extends http.IncomingMessage { - originalUrl?: http.IncomingMessage['url'] - } - - export type NextFunction = (err?: any) => void - - export type SimpleHandleFunction = ( - req: IncomingMessage, - res: http.ServerResponse - ) => void - export type NextHandleFunction = ( - req: IncomingMessage, - res: http.ServerResponse, - next: NextFunction - ) => void - export type ErrorHandleFunction = ( - err: any, - req: IncomingMessage, - res: http.ServerResponse, - next: NextFunction - ) => void - export type HandleFunction = - | SimpleHandleFunction - | NextHandleFunction - | ErrorHandleFunction - - export interface ServerStackItem { - route: string - handle: ServerHandle - } - - export interface Server extends NodeJS.EventEmitter { - (req: http.IncomingMessage, res: http.ServerResponse, next?: Function): void - - route: string - stack: ServerStackItem[] - - /** - * Utilize the given middleware `handle` to the given `route`, - * defaulting to _/_. This "route" is the mount-point for the - * middleware, when given a value other than _/_ the middleware - * is only effective when that segment is present in the request's - * pathname. - * - * For example if we were to mount a function at _/admin_, it would - * be invoked on _/admin_, and _/admin/settings_, however it would - * not be invoked for _/_, or _/posts_. - */ - use(fn: NextHandleFunction): Server - use(fn: HandleFunction): Server - use(route: string, fn: NextHandleFunction): Server - use(route: string, fn: HandleFunction): Server - - /** - * Handle server requests, punting them down - * the middleware stack. - */ - handle( - req: http.IncomingMessage, - res: http.ServerResponse, - next: Function - ): void - - /** - * Listen for connections. - * - * This method takes the same arguments - * as node's `http.Server#listen()`. - * - * HTTP and HTTPS: - * - * If you run your application both as HTTP - * and HTTPS you may wrap them individually, - * since your Connect "server" is really just - * a JavaScript `Function`. - * - * var connect = require('connect') - * , http = require('http') - * , https = require('https'); - * - * var app = connect(); - * - * http.createServer(app).listen(80); - * https.createServer(options, app).listen(443); - */ - listen( - port: number, - hostname?: string, - backlog?: number, - callback?: Function - ): http.Server - listen(port: number, hostname?: string, callback?: Function): http.Server - listen(path: string, callback?: Function): http.Server - listen(handle: any, listeningListener?: Function): http.Server - } -} +export type { Connect } from '../dist/node' diff --git a/packages/vite/types/customEvent.d.ts b/packages/vite/types/customEvent.d.ts index af4db5d14fbe97..839e17dd729eda 100644 --- a/packages/vite/types/customEvent.d.ts +++ b/packages/vite/types/customEvent.d.ts @@ -10,6 +10,11 @@ export interface CustomEventMap { 'vite:beforePrune': PrunePayload 'vite:beforeFullReload': FullReloadPayload 'vite:error': ErrorPayload + 'vite:invalidate': InvalidatePayload +} + +export interface InvalidatePayload { + path: string } export type InferCustomEventPayload = diff --git a/packages/vite/types/dynamicImportVars.d.ts b/packages/vite/types/dynamicImportVars.d.ts index 99f1b5c453ba97..2d46aaf582de8b 100644 --- a/packages/vite/types/dynamicImportVars.d.ts +++ b/packages/vite/types/dynamicImportVars.d.ts @@ -1,17 +1 @@ -export interface RollupDynamicImportVarsOptions { - /** - * Files to include in this plugin (default all). - * @default [] - */ - include?: string | RegExp | (string | RegExp)[] - /** - * Files to exclude in this plugin (default none). - * @default [] - */ - exclude?: string | RegExp | (string | RegExp)[] - /** - * By default, the plugin quits the build process when it encounters an error. If you set this option to true, it will throw a warning instead and leave the code untouched. - * @default false - */ - warnOnError?: boolean -} +export type { RollupDynamicImportVarsOptions } from '../dist/node' diff --git a/packages/vite/types/hmrPayload.d.ts b/packages/vite/types/hmrPayload.d.ts index 2fbed3a821466f..839095009e76fb 100644 --- a/packages/vite/types/hmrPayload.d.ts +++ b/packages/vite/types/hmrPayload.d.ts @@ -20,6 +20,10 @@ export interface Update { path: string acceptedPath: string timestamp: number + /** + * @experimental internal + */ + explicitImportRequired?: boolean | undefined } export interface PrunePayload { diff --git a/packages/vite/types/http-proxy.d.ts b/packages/vite/types/http-proxy.d.ts index 81b9226a4db669..54bbefc05c8c48 100644 --- a/packages/vite/types/http-proxy.d.ts +++ b/packages/vite/types/http-proxy.d.ts @@ -1,242 +1 @@ -// Inlined to avoid extra dependency -// MIT Licensed https://github.com/DefinitelyTyped/DefinitelyTyped/blob/master/LICENSE - -// Type definitions for node-http-proxy 1.17 -// Project: https://github.com/nodejitsu/node-http-proxy -// Definitions by: Maxime LUCE -// Florian Oellerich -// Daniel Schmidt -// Jordan Abreu -// Samuel Bodin -// Definitions: https://github.com/DefinitelyTyped/DefinitelyTyped -// TypeScript Version: 2.1 - -/// - -import type * as net from 'node:net' -import type * as http from 'node:http' -import * as events from 'node:events' -import type * as url from 'node:url' -import type * as stream from 'node:stream' - -export namespace HttpProxy { - export type ProxyTarget = ProxyTargetUrl | ProxyTargetDetailed - - export type ProxyTargetUrl = string | Partial - - export interface ProxyTargetDetailed { - host: string - port: number - protocol?: string - hostname?: string - socketPath?: string - key?: string - passphrase?: string - pfx?: Buffer | string - cert?: string - ca?: string - ciphers?: string - secureProtocol?: string - } - - export type ErrorCallback = ( - err: Error, - req: http.IncomingMessage, - res: http.ServerResponse, - target?: ProxyTargetUrl - ) => void - - export class Server extends events.EventEmitter { - /** - * Creates the proxy server with specified options. - * @param options - Config object passed to the proxy - */ - constructor(options?: ServerOptions) - - /** - * Used for proxying regular HTTP(S) requests - * @param req - Client request. - * @param res - Client response. - * @param options - Additionnal options. - */ - web( - req: http.IncomingMessage, - res: http.ServerResponse, - options?: ServerOptions, - callback?: ErrorCallback - ): void - - /** - * Used for proxying regular HTTP(S) requests - * @param req - Client request. - * @param socket - Client socket. - * @param head - Client head. - * @param options - Additional options. - */ - ws( - req: http.IncomingMessage, - socket: unknown, - head: unknown, - options?: ServerOptions, - callback?: ErrorCallback - ): void - - /** - * A function that wraps the object in a webserver, for your convenience - * @param port - Port to listen on - */ - listen(port: number): Server - - /** - * A function that closes the inner webserver and stops listening on given port - */ - close(callback?: () => void): void - - /** - * Creates the proxy server with specified options. - * @param options - Config object passed to the proxy - * @returns Proxy object with handlers for `ws` and `web` requests - */ - static createProxyServer(options?: ServerOptions): Server - - /** - * Creates the proxy server with specified options. - * @param options - Config object passed to the proxy - * @returns Proxy object with handlers for `ws` and `web` requests - */ - static createServer(options?: ServerOptions): Server - - /** - * Creates the proxy server with specified options. - * @param options - Config object passed to the proxy - * @returns Proxy object with handlers for `ws` and `web` requests - */ - static createProxy(options?: ServerOptions): Server - - addListener(event: string, listener: () => void): this - on(event: string, listener: () => void): this - on(event: 'error', listener: ErrorCallback): this - on( - event: 'start', - listener: ( - req: http.IncomingMessage, - res: http.ServerResponse, - target: ProxyTargetUrl - ) => void - ): this - on( - event: 'proxyReq', - listener: ( - proxyReq: http.ClientRequest, - req: http.IncomingMessage, - res: http.ServerResponse, - options: ServerOptions - ) => void - ): this - on( - event: 'proxyRes', - listener: ( - proxyRes: http.IncomingMessage, - req: http.IncomingMessage, - res: http.ServerResponse - ) => void - ): this - on( - event: 'proxyReqWs', - listener: ( - proxyReq: http.ClientRequest, - req: http.IncomingMessage, - socket: net.Socket, - options: ServerOptions, - head: any - ) => void - ): this - on( - event: 'econnreset', - listener: ( - err: Error, - req: http.IncomingMessage, - res: http.ServerResponse, - target: ProxyTargetUrl - ) => void - ): this - on( - event: 'end', - listener: ( - req: http.IncomingMessage, - res: http.ServerResponse, - proxyRes: http.IncomingMessage - ) => void - ): this - on( - event: 'close', - listener: ( - proxyRes: http.IncomingMessage, - proxySocket: net.Socket, - proxyHead: any - ) => void - ): this - - once(event: string, listener: () => void): this - removeListener(event: string, listener: () => void): this - removeAllListeners(event?: string): this - getMaxListeners(): number - setMaxListeners(n: number): this - listeners(event: string): Array<() => void> - emit(event: string, ...args: any[]): boolean - listenerCount(type: string): number - } - - export interface ServerOptions { - /** URL string to be parsed with the url module. */ - target?: ProxyTarget - /** URL string to be parsed with the url module. */ - forward?: ProxyTargetUrl - /** Object to be passed to http(s).request. */ - agent?: any - /** Object to be passed to https.createServer(). */ - ssl?: any - /** If you want to proxy websockets. */ - ws?: boolean - /** Adds x- forward headers. */ - xfwd?: boolean - /** Verify SSL certificate. */ - secure?: boolean - /** Explicitly specify if we are proxying to another proxy. */ - toProxy?: boolean - /** Specify whether you want to prepend the target's path to the proxy path. */ - prependPath?: boolean - /** Specify whether you want to ignore the proxy path of the incoming request. */ - ignorePath?: boolean - /** Local interface string to bind for outgoing connections. */ - localAddress?: string - /** Changes the origin of the host header to the target URL. */ - changeOrigin?: boolean - /** specify whether you want to keep letter case of response header key */ - preserveHeaderKeyCase?: boolean - /** Basic authentication i.e. 'user:password' to compute an Authorization header. */ - auth?: string - /** Rewrites the location hostname on (301 / 302 / 307 / 308) redirects, Default: null. */ - hostRewrite?: string - /** Rewrites the location host/ port on (301 / 302 / 307 / 308) redirects based on requested host/ port.Default: false. */ - autoRewrite?: boolean - /** Rewrites the location protocol on (301 / 302 / 307 / 308) redirects to 'http' or 'https'.Default: null. */ - protocolRewrite?: string - /** rewrites domain of set-cookie headers. */ - cookieDomainRewrite?: false | string | { [oldDomain: string]: string } - /** rewrites path of set-cookie headers. Default: false */ - cookiePathRewrite?: false | string | { [oldPath: string]: string } - /** object with extra headers to be added to target requests. */ - headers?: { [header: string]: string } - /** Timeout (in milliseconds) when proxy receives no response from target. Default: 120000 (2 minutes) */ - proxyTimeout?: number - /** Timeout (in milliseconds) for incoming requests */ - timeout?: number - /** Specify whether you want to follow redirects. Default: false */ - followRedirects?: boolean - /** If set to true, none of the webOutgoing passes are called and it's your responsibility to appropriately return the response by listening and acting on the proxyRes event */ - selfHandleResponse?: boolean - /** Buffer */ - buffer?: stream.Stream - } -} +export type { HttpProxy } from '../dist/node' diff --git a/packages/vite/types/importGlob.d.ts b/packages/vite/types/importGlob.d.ts index 6bb03fe9943a93..321e3804a7e09a 100644 --- a/packages/vite/types/importGlob.d.ts +++ b/packages/vite/types/importGlob.d.ts @@ -38,7 +38,9 @@ export interface KnownAsTypeMap { export interface ImportGlobFunction { /** - * 1. No generic provided, infer the type from `eager` and `as` + * Import a list of files with a glob pattern. + * + * Overload 1: No generic provided, infer the type from `eager` and `as` */ < Eager extends boolean, @@ -51,14 +53,18 @@ export interface ImportGlobFunction { ? Record : Record Promise> /** - * 2. Module generic provided, infer the type from `eager: false` + * Import a list of files with a glob pattern. + * + * Overload 2: Module generic provided, infer the type from `eager: false` */ ( glob: string | string[], options?: ImportGlobOptions ): Record Promise> /** - * 3. Module generic provided, infer the type from `eager: true` + * Import a list of files with a glob pattern. + * + * Overload 3: Module generic provided, infer the type from `eager: true` */ ( glob: string | string[], @@ -68,7 +74,9 @@ export interface ImportGlobFunction { export interface ImportGlobEagerFunction { /** - * 1. No generic provided, infer the type from `as` + * Eagerly import a list of files with a glob pattern. + * + * Overload 1: No generic provided, infer the type from `as` */ < As extends string, @@ -78,7 +86,9 @@ export interface ImportGlobEagerFunction { options?: Omit, 'eager'> ): Record /** - * 2. Module generic provided + * Eagerly import a list of files with a glob pattern. + * + * Overload 2: Module generic provided */ ( glob: string | string[], diff --git a/packages/vite/types/importMeta.d.ts b/packages/vite/types/importMeta.d.ts index 54eaa9f4c4c140..da338b6833bed6 100644 --- a/packages/vite/types/importMeta.d.ts +++ b/packages/vite/types/importMeta.d.ts @@ -4,11 +4,13 @@ /* eslint-disable @typescript-eslint/consistent-type-imports */ -// Duplicate of import('../src/node/importGlob').GlobOptions in order to -// avoid breaking the production client type. Because this file is referenced -// in vite/client.d.ts and in production src/node/importGlob.ts doesn't exist. -interface GlobOptions { - as?: string +interface ImportMetaEnv { + [key: string]: any + BASE_URL: string + MODE: string + DEV: boolean + PROD: boolean + SSR: boolean } interface ImportMeta { @@ -24,12 +26,3 @@ interface ImportMeta { */ globEager: import('./importGlob').ImportGlobEagerFunction } - -interface ImportMetaEnv { - [key: string]: any - BASE_URL: string - MODE: string - DEV: boolean - PROD: boolean - SSR: boolean -} diff --git a/packages/vite/types/package.json b/packages/vite/types/package.json index 3d6a75c81455a0..5757150f514df6 100644 --- a/packages/vite/types/package.json +++ b/packages/vite/types/package.json @@ -1,3 +1,3 @@ { - "//": "this file is just here to make pnpm happy with --frozen-lockfile" + "//": "this file is here to make typescript happy when moduleResolution=node16+" } diff --git a/packages/vite/types/shims.d.ts b/packages/vite/types/shims.d.ts deleted file mode 100644 index d90f0bf42c7057..00000000000000 --- a/packages/vite/types/shims.d.ts +++ /dev/null @@ -1,95 +0,0 @@ -declare module 'connect' { - const connect: () => any - export = connect -} - -declare module 'cors' { - function cors(options: any): any - export = cors -} - -declare module 'selfsigned' { - export function generate(attrs: any, options: any, done?: any): any -} - -declare module 'http-proxy' { - const proxy: any - export = proxy -} - -declare module 'connect-history-api-fallback' { - const plugin: any - export = plugin -} - -declare module 'launch-editor-middleware' { - const plugin: any - export = plugin -} - -declare module 'postcss-load-config' { - import type { Plugin, ProcessOptions } from 'postcss' - function load( - inline: any, - root: string - ): Promise<{ - options: ProcessOptions - plugins: Plugin[] - }> - export = load -} - -declare module 'postcss-import' { - import type { Plugin } from 'postcss' - const plugin: (options: { - resolve: ( - id: string, - basedir: string, - importOptions: any - ) => string | string[] | Promise - }) => Plugin - export = plugin -} - -declare module 'postcss-modules' { - import type { Plugin } from 'postcss' - const plugin: (options: any) => Plugin - export = plugin -} - -declare module '@rollup/plugin-dynamic-import-vars' { - import type { Plugin } from 'rollup' - import type { BaseNode } from 'estree' - - interface Options { - include?: string | RegExp | (string | RegExp)[] - exclude?: string | RegExp | (string | RegExp)[] - warnOnError?: boolean - } - - const p: (o?: Options) => Plugin - export default p - export function dynamicImportToGlob( - ast: BaseNode, - source: string - ): null | string -} - -declare module 'rollup-plugin-web-worker-loader' { - import type { Plugin } from 'rollup' - - interface Options { - targetPlatform?: string - pattern?: RegExp - extensions?: string[] - sourcemap?: boolean - inline?: boolean - } - - const p: (o?: Options) => Plugin - export default p -} - -// LESS' types somewhat references this which doesn't make sense in Node, -// so we have to shim it -declare interface HTMLLinkElement {} diff --git a/packages/vite/types/terser.d.ts b/packages/vite/types/terser.d.ts index 5c24660eb98781..695795d4df448f 100644 --- a/packages/vite/types/terser.d.ts +++ b/packages/vite/types/terser.d.ts @@ -1,207 +1 @@ -// Modified and inlined to avoid extra dependency -// Source: https://github.com/terser/terser/blob/master/tools/terser.d.ts -// BSD Licensed https://github.com/terser/terser/blob/master/LICENSE - -/* -Terser is released under the BSD license: - -Copyright 2012-2018 (c) Mihai Bazon - -Redistribution and use in source and binary forms, with or without -modification, are permitted provided that the following conditions -are met: - - * Redistributions of source code must retain the above - copyright notice, this list of conditions and the following - disclaimer. - - * Redistributions in binary form must reproduce the above - copyright notice, this list of conditions and the following - disclaimer in the documentation and/or other materials - provided with the distribution. - -THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDER “AS IS” AND ANY -EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE -IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR -PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER BE -LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, -OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, -PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR -PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY -THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR -TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF -THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF -SUCH DAMAGE. -*/ - -export namespace Terser { - export type ECMA = 5 | 2015 | 2016 | 2017 | 2018 | 2019 | 2020 - - export interface ParseOptions { - bare_returns?: boolean - ecma?: ECMA - html5_comments?: boolean - shebang?: boolean - } - - export interface CompressOptions { - arguments?: boolean - arrows?: boolean - booleans_as_integers?: boolean - booleans?: boolean - collapse_vars?: boolean - comparisons?: boolean - computed_props?: boolean - conditionals?: boolean - dead_code?: boolean - defaults?: boolean - directives?: boolean - drop_console?: boolean - drop_debugger?: boolean - ecma?: ECMA - evaluate?: boolean - expression?: boolean - global_defs?: object - hoist_funs?: boolean - hoist_props?: boolean - hoist_vars?: boolean - ie8?: boolean - if_return?: boolean - inline?: boolean | InlineFunctions - join_vars?: boolean - keep_classnames?: boolean | RegExp - keep_fargs?: boolean - keep_fnames?: boolean | RegExp - keep_infinity?: boolean - loops?: boolean - module?: boolean - negate_iife?: boolean - passes?: number - properties?: boolean - pure_funcs?: string[] - pure_getters?: boolean | 'strict' - reduce_funcs?: boolean - reduce_vars?: boolean - sequences?: boolean | number - side_effects?: boolean - switches?: boolean - toplevel?: boolean - top_retain?: null | string | string[] | RegExp - typeofs?: boolean - unsafe_arrows?: boolean - unsafe?: boolean - unsafe_comps?: boolean - unsafe_Function?: boolean - unsafe_math?: boolean - unsafe_symbols?: boolean - unsafe_methods?: boolean - unsafe_proto?: boolean - unsafe_regexp?: boolean - unsafe_undefined?: boolean - unused?: boolean - } - - export enum InlineFunctions { - Disabled = 0, - SimpleFunctions = 1, - WithArguments = 2, - WithArgumentsAndVariables = 3 - } - - export interface MangleOptions { - eval?: boolean - keep_classnames?: boolean | RegExp - keep_fnames?: boolean | RegExp - module?: boolean - properties?: boolean | ManglePropertiesOptions - reserved?: string[] - safari10?: boolean - toplevel?: boolean - } - - export interface ManglePropertiesOptions { - builtins?: boolean - debug?: boolean - keep_quoted?: boolean | 'strict' - regex?: RegExp | string - reserved?: string[] - } - - export interface FormatOptions { - ascii_only?: boolean - beautify?: boolean - braces?: boolean - comments?: - | boolean - | 'all' - | 'some' - | RegExp - | (( - node: any, - comment: { - value: string - type: 'comment1' | 'comment2' | 'comment3' | 'comment4' - pos: number - line: number - col: number - } - ) => boolean) - ecma?: ECMA - ie8?: boolean - indent_level?: number - indent_start?: number - inline_script?: boolean - keep_quoted_props?: boolean - max_line_len?: number | false - preamble?: string - preserve_annotations?: boolean - quote_keys?: boolean - quote_style?: OutputQuoteStyle - safari10?: boolean - semicolons?: boolean - shebang?: boolean - shorthand?: boolean - source_map?: SourceMapOptions - webkit?: boolean - width?: number - wrap_iife?: boolean - wrap_func_args?: boolean - } - - export enum OutputQuoteStyle { - PreferDouble = 0, - AlwaysSingle = 1, - AlwaysDouble = 2, - AlwaysOriginal = 3 - } - - export interface MinifyOptions { - compress?: boolean | CompressOptions - ecma?: ECMA - ie8?: boolean - keep_classnames?: boolean | RegExp - keep_fnames?: boolean | RegExp - mangle?: boolean | MangleOptions - module?: boolean - nameCache?: object - format?: FormatOptions - parse?: ParseOptions - safari10?: boolean - sourceMap?: boolean | SourceMapOptions - toplevel?: boolean - } - - export interface MinifyOutput { - code?: string - map?: object | string - } - - export interface SourceMapOptions { - /** Source map object, 'inline' or source map file content */ - content?: object | string - includeSources?: boolean - filename?: string - root?: string - url?: string | 'inline' - } -} +export type { Terser } from '../dist/node' diff --git a/packages/vite/types/ws.d.ts b/packages/vite/types/ws.d.ts index 4a03058d0eeaa2..3916212d392bff 100644 --- a/packages/vite/types/ws.d.ts +++ b/packages/vite/types/ws.d.ts @@ -1,553 +1 @@ -// Modified and inlined to avoid extra dependency -// Source: https://github.com/DefinitelyTyped/DefinitelyTyped/blob/master/types/ws/index.d.ts - -// Type definitions for ws 8.5 -// Project: https://github.com/websockets/ws -// Definitions by: Paul Loyd -// Margus Lamp -// Philippe D'Alva -// reduckted -// teidesu -// Bartosz Wojtkowiak -// Kyle Hensel -// Samuel Skeen -// Definitions: https://github.com/DefinitelyTyped/DefinitelyTyped - -/// - -import { EventEmitter } from 'node:events' -import type { - Agent, - ClientRequest, - ClientRequestArgs, - Server as HTTPServer, - IncomingMessage, - OutgoingHttpHeaders -} from 'node:http' -import type { Server as HTTPSServer } from 'node:https' -import type { Duplex, DuplexOptions } from 'node:stream' -import type { SecureContextOptions } from 'tls' -import type { URL } from 'node:url' -import type { ZlibOptions } from 'node:zlib' - -// WebSocket socket. -declare class WebSocket extends EventEmitter { - /** The connection is not yet open. */ - static readonly CONNECTING: 0 - /** The connection is open and ready to communicate. */ - static readonly OPEN: 1 - /** The connection is in the process of closing. */ - static readonly CLOSING: 2 - /** The connection is closed. */ - static readonly CLOSED: 3 - - binaryType: 'nodebuffer' | 'arraybuffer' | 'fragments' - readonly bufferedAmount: number - readonly extensions: string - /** Indicates whether the websocket is paused */ - readonly isPaused: boolean - readonly protocol: string - /** The current state of the connection */ - readonly readyState: - | typeof WebSocket.CONNECTING - | typeof WebSocket.OPEN - | typeof WebSocket.CLOSING - | typeof WebSocket.CLOSED - readonly url: string - - /** The connection is not yet open. */ - readonly CONNECTING: 0 - /** The connection is open and ready to communicate. */ - readonly OPEN: 1 - /** The connection is in the process of closing. */ - readonly CLOSING: 2 - /** The connection is closed. */ - readonly CLOSED: 3 - - onopen: ((event: WebSocket.Event) => void) | null - onerror: ((event: WebSocket.ErrorEvent) => void) | null - onclose: ((event: WebSocket.CloseEvent) => void) | null - onmessage: ((event: WebSocket.MessageEvent) => void) | null - - constructor(address: null) - constructor( - address: string | URL, - options?: WebSocket.ClientOptions | ClientRequestArgs - ) - constructor( - address: string | URL, - protocols?: string | string[], - options?: WebSocket.ClientOptions | ClientRequestArgs - ) - - close(code?: number, data?: string | Buffer): void - ping(data?: any, mask?: boolean, cb?: (err: Error) => void): void - pong(data?: any, mask?: boolean, cb?: (err: Error) => void): void - send(data: any, cb?: (err?: Error) => void): void - send( - data: any, - options: { - mask?: boolean | undefined - binary?: boolean | undefined - compress?: boolean | undefined - fin?: boolean | undefined - }, - cb?: (err?: Error) => void - ): void - terminate(): void - - /** - * Pause the websocket causing it to stop emitting events. Some events can still be - * emitted after this is called, until all buffered data is consumed. This method - * is a noop if the ready state is `CONNECTING` or `CLOSED`. - */ - pause(): void - /** - * Make a paused socket resume emitting events. This method is a noop if the ready - * state is `CONNECTING` or `CLOSED`. - */ - resume(): void - - // HTML5 WebSocket events - addEventListener( - method: 'message', - cb: (event: WebSocket.MessageEvent) => void, - options?: WebSocket.EventListenerOptions - ): void - addEventListener( - method: 'close', - cb: (event: WebSocket.CloseEvent) => void, - options?: WebSocket.EventListenerOptions - ): void - addEventListener( - method: 'error', - cb: (event: WebSocket.ErrorEvent) => void, - options?: WebSocket.EventListenerOptions - ): void - addEventListener( - method: 'open', - cb: (event: WebSocket.Event) => void, - options?: WebSocket.EventListenerOptions - ): void - - removeEventListener( - method: 'message', - cb: (event: WebSocket.MessageEvent) => void - ): void - removeEventListener( - method: 'close', - cb: (event: WebSocket.CloseEvent) => void - ): void - removeEventListener( - method: 'error', - cb: (event: WebSocket.ErrorEvent) => void - ): void - removeEventListener( - method: 'open', - cb: (event: WebSocket.Event) => void - ): void - - // Events - on( - event: 'close', - listener: (this: WebSocket, code: number, reason: Buffer) => void - ): this - on(event: 'error', listener: (this: WebSocket, err: Error) => void): this - on( - event: 'upgrade', - listener: (this: WebSocket, request: IncomingMessage) => void - ): this - on( - event: 'message', - listener: ( - this: WebSocket, - data: WebSocket.RawData, - isBinary: boolean - ) => void - ): this - on(event: 'open', listener: (this: WebSocket) => void): this - on( - event: 'ping' | 'pong', - listener: (this: WebSocket, data: Buffer) => void - ): this - on( - event: 'unexpected-response', - listener: ( - this: WebSocket, - request: ClientRequest, - response: IncomingMessage - ) => void - ): this - on( - event: string | symbol, - listener: (this: WebSocket, ...args: any[]) => void - ): this - - once( - event: 'close', - listener: (this: WebSocket, code: number, reason: Buffer) => void - ): this - once(event: 'error', listener: (this: WebSocket, err: Error) => void): this - once( - event: 'upgrade', - listener: (this: WebSocket, request: IncomingMessage) => void - ): this - once( - event: 'message', - listener: ( - this: WebSocket, - data: WebSocket.RawData, - isBinary: boolean - ) => void - ): this - once(event: 'open', listener: (this: WebSocket) => void): this - once( - event: 'ping' | 'pong', - listener: (this: WebSocket, data: Buffer) => void - ): this - once( - event: 'unexpected-response', - listener: ( - this: WebSocket, - request: ClientRequest, - response: IncomingMessage - ) => void - ): this - once( - event: string | symbol, - listener: (this: WebSocket, ...args: any[]) => void - ): this - - off( - event: 'close', - listener: (this: WebSocket, code: number, reason: Buffer) => void - ): this - off(event: 'error', listener: (this: WebSocket, err: Error) => void): this - off( - event: 'upgrade', - listener: (this: WebSocket, request: IncomingMessage) => void - ): this - off( - event: 'message', - listener: ( - this: WebSocket, - data: WebSocket.RawData, - isBinary: boolean - ) => void - ): this - off(event: 'open', listener: (this: WebSocket) => void): this - off( - event: 'ping' | 'pong', - listener: (this: WebSocket, data: Buffer) => void - ): this - off( - event: 'unexpected-response', - listener: ( - this: WebSocket, - request: ClientRequest, - response: IncomingMessage - ) => void - ): this - off( - event: string | symbol, - listener: (this: WebSocket, ...args: any[]) => void - ): this - - addListener( - event: 'close', - listener: (code: number, reason: Buffer) => void - ): this - addListener(event: 'error', listener: (err: Error) => void): this - addListener( - event: 'upgrade', - listener: (request: IncomingMessage) => void - ): this - addListener( - event: 'message', - listener: (data: WebSocket.RawData, isBinary: boolean) => void - ): this - addListener(event: 'open', listener: () => void): this - addListener(event: 'ping' | 'pong', listener: (data: Buffer) => void): this - addListener( - event: 'unexpected-response', - listener: (request: ClientRequest, response: IncomingMessage) => void - ): this - addListener(event: string | symbol, listener: (...args: any[]) => void): this - - removeListener( - event: 'close', - listener: (code: number, reason: Buffer) => void - ): this - removeListener(event: 'error', listener: (err: Error) => void): this - removeListener( - event: 'upgrade', - listener: (request: IncomingMessage) => void - ): this - removeListener( - event: 'message', - listener: (data: WebSocket.RawData, isBinary: boolean) => void - ): this - removeListener(event: 'open', listener: () => void): this - removeListener(event: 'ping' | 'pong', listener: (data: Buffer) => void): this - removeListener( - event: 'unexpected-response', - listener: (request: ClientRequest, response: IncomingMessage) => void - ): this - removeListener( - event: string | symbol, - listener: (...args: any[]) => void - ): this -} - -declare const WebSocketAlias: typeof WebSocket -interface WebSocketAlias extends WebSocket {} // tslint:disable-line no-empty-interface - -declare namespace WebSocket { - /** - * Data represents the raw message payload received over the WebSocket. - */ - type RawData = Buffer | ArrayBuffer | Buffer[] - - /** - * Data represents the message payload received over the WebSocket. - */ - type Data = string | Buffer | ArrayBuffer | Buffer[] - - /** - * CertMeta represents the accepted types for certificate & key data. - */ - type CertMeta = string | string[] | Buffer | Buffer[] - - /** - * VerifyClientCallbackSync is a synchronous callback used to inspect the - * incoming message. The return value (boolean) of the function determines - * whether or not to accept the handshake. - */ - type VerifyClientCallbackSync = (info: { - origin: string - secure: boolean - req: IncomingMessage - }) => boolean - - /** - * VerifyClientCallbackAsync is an asynchronous callback used to inspect the - * incoming message. The return value (boolean) of the function determines - * whether or not to accept the handshake. - */ - type VerifyClientCallbackAsync = ( - info: { origin: string; secure: boolean; req: IncomingMessage }, - callback: ( - res: boolean, - code?: number, - message?: string, - headers?: OutgoingHttpHeaders - ) => void - ) => void - - interface ClientOptions extends SecureContextOptions { - protocol?: string | undefined - followRedirects?: boolean | undefined - generateMask?(mask: Buffer): void - handshakeTimeout?: number | undefined - maxRedirects?: number | undefined - perMessageDeflate?: boolean | PerMessageDeflateOptions | undefined - localAddress?: string | undefined - protocolVersion?: number | undefined - headers?: { [key: string]: string } | undefined - origin?: string | undefined - agent?: Agent | undefined - host?: string | undefined - family?: number | undefined - checkServerIdentity?(servername: string, cert: CertMeta): boolean - rejectUnauthorized?: boolean | undefined - maxPayload?: number | undefined - skipUTF8Validation?: boolean | undefined - } - - interface PerMessageDeflateOptions { - serverNoContextTakeover?: boolean | undefined - clientNoContextTakeover?: boolean | undefined - serverMaxWindowBits?: number | undefined - clientMaxWindowBits?: number | undefined - zlibDeflateOptions?: - | { - flush?: number | undefined - finishFlush?: number | undefined - chunkSize?: number | undefined - windowBits?: number | undefined - level?: number | undefined - memLevel?: number | undefined - strategy?: number | undefined - dictionary?: Buffer | Buffer[] | DataView | undefined - info?: boolean | undefined - } - | undefined - zlibInflateOptions?: ZlibOptions | undefined - threshold?: number | undefined - concurrencyLimit?: number | undefined - } - - interface Event { - type: string - target: WebSocket - } - - interface ErrorEvent { - error: any - message: string - type: string - target: WebSocket - } - - interface CloseEvent { - wasClean: boolean - code: number - reason: string - type: string - target: WebSocket - } - - interface MessageEvent { - data: Data - type: string - target: WebSocket - } - - interface EventListenerOptions { - once?: boolean | undefined - } - - interface ServerOptions { - host?: string | undefined - port?: number | undefined - backlog?: number | undefined - server?: HTTPServer | HTTPSServer | undefined - verifyClient?: - | VerifyClientCallbackAsync - | VerifyClientCallbackSync - | undefined - handleProtocols?: ( - protocols: Set, - request: IncomingMessage - ) => string | false - path?: string | undefined - noServer?: boolean | undefined - clientTracking?: boolean | undefined - perMessageDeflate?: boolean | PerMessageDeflateOptions | undefined - maxPayload?: number | undefined - skipUTF8Validation?: boolean | undefined - WebSocket?: typeof WebSocket.WebSocket | undefined - } - - interface AddressInfo { - address: string - family: string - port: number - } - - // WebSocket Server - class Server extends EventEmitter { - options: ServerOptions - path: string - clients: Set - - constructor(options?: ServerOptions, callback?: () => void) - - address(): AddressInfo | string - close(cb?: (err?: Error) => void): void - handleUpgrade( - request: IncomingMessage, - socket: Duplex, - upgradeHead: Buffer, - callback: (client: T, request: IncomingMessage) => void - ): void - shouldHandle(request: IncomingMessage): boolean | Promise - - // Events - on( - event: 'connection', - cb: (this: Server, socket: T, request: IncomingMessage) => void - ): this - on(event: 'error', cb: (this: Server, error: Error) => void): this - on( - event: 'headers', - cb: (this: Server, headers: string[], request: IncomingMessage) => void - ): this - on(event: 'close' | 'listening', cb: (this: Server) => void): this - on( - event: string | symbol, - listener: (this: Server, ...args: any[]) => void - ): this - - once( - event: 'connection', - cb: (this: Server, socket: T, request: IncomingMessage) => void - ): this - once(event: 'error', cb: (this: Server, error: Error) => void): this - once( - event: 'headers', - cb: (this: Server, headers: string[], request: IncomingMessage) => void - ): this - once(event: 'close' | 'listening', cb: (this: Server) => void): this - once( - event: string | symbol, - listener: (this: Server, ...args: any[]) => void - ): this - - off( - event: 'connection', - cb: (this: Server, socket: T, request: IncomingMessage) => void - ): this - off(event: 'error', cb: (this: Server, error: Error) => void): this - off( - event: 'headers', - cb: (this: Server, headers: string[], request: IncomingMessage) => void - ): this - off(event: 'close' | 'listening', cb: (this: Server) => void): this - off( - event: string | symbol, - listener: (this: Server, ...args: any[]) => void - ): this - - addListener( - event: 'connection', - cb: (client: T, request: IncomingMessage) => void - ): this - addListener(event: 'error', cb: (err: Error) => void): this - addListener( - event: 'headers', - cb: (headers: string[], request: IncomingMessage) => void - ): this - addListener(event: 'close' | 'listening', cb: () => void): this - addListener( - event: string | symbol, - listener: (...args: any[]) => void - ): this - - removeListener(event: 'connection', cb: (client: T) => void): this - removeListener(event: 'error', cb: (err: Error) => void): this - removeListener( - event: 'headers', - cb: (headers: string[], request: IncomingMessage) => void - ): this - removeListener(event: 'close' | 'listening', cb: () => void): this - removeListener( - event: string | symbol, - listener: (...args: any[]) => void - ): this - } - - const WebSocketServer: typeof Server - interface WebSocketServer extends Server {} // tslint:disable-line no-empty-interface - const WebSocket: typeof WebSocketAlias - interface WebSocket extends WebSocketAlias {} // tslint:disable-line no-empty-interface - - // WebSocket stream - function createWebSocketStream( - websocket: WebSocket, - options?: DuplexOptions - ): Duplex -} - -// export = WebSocket -export { WebSocket, WebSocketAlias } +export type { WebSocket, WebSocketAlias } from '../dist/node' diff --git a/playground/alias/package.json b/playground/alias/package.json index 4b49ca8d346ebf..a1bb3913f0f694 100644 --- a/playground/alias/package.json +++ b/playground/alias/package.json @@ -10,8 +10,8 @@ }, "dependencies": { "aliased-module": "file:./dir/module", - "vue": "^3.2.37", - "@vue/shared": "^3.2.37" + "vue": "^3.2.41", + "@vue/shared": "^3.2.41" }, "devDependencies": { "resolve-linked": "workspace:*" diff --git a/playground/alias/vite.config.js b/playground/alias/vite.config.js index 634871877a0f56..16c33dd859aa66 100644 --- a/playground/alias/vite.config.js +++ b/playground/alias/vite.config.js @@ -1,4 +1,4 @@ -const path = require('path') +const path = require('node:path') /** * @type {import('vite').UserConfig} diff --git a/playground/assets-sanitize/+circle.svg b/playground/assets-sanitize/+circle.svg new file mode 100644 index 00000000000000..81ff39ee185e2e --- /dev/null +++ b/playground/assets-sanitize/+circle.svg @@ -0,0 +1,3 @@ + + + diff --git a/playground/assets-sanitize/__tests__/assets-sanitize.spec.ts b/playground/assets-sanitize/__tests__/assets-sanitize.spec.ts new file mode 100644 index 00000000000000..fc9c1ad8c81a7c --- /dev/null +++ b/playground/assets-sanitize/__tests__/assets-sanitize.spec.ts @@ -0,0 +1,27 @@ +import { expect, test } from 'vitest' +import { getBg, isBuild, page, readManifest } from '~utils' + +if (!isBuild) { + test('importing asset with special char in filename works in dev', async () => { + expect(await getBg('.plus-circle')).toContain('+circle.svg') + expect(await page.textContent('.plus-circle')).toMatch('+circle.svg') + expect(await getBg('.underscore-circle')).toContain('_circle.svg') + expect(await page.textContent('.underscore-circle')).toMatch('_circle.svg') + }) +} else { + test('importing asset with special char in filename works in build', async () => { + const manifest = readManifest() + const plusCircleAsset = manifest['+circle.svg'].file + const underscoreCircleAsset = manifest['_circle.svg'].file + expect(await getBg('.plus-circle')).toMatch(plusCircleAsset) + expect(await page.textContent('.plus-circle')).toMatch(plusCircleAsset) + expect(await getBg('.underscore-circle')).toMatch(underscoreCircleAsset) + expect(await page.textContent('.underscore-circle')).toMatch( + underscoreCircleAsset + ) + expect(plusCircleAsset).toMatch('/_circle') + expect(underscoreCircleAsset).toMatch('/_circle') + expect(plusCircleAsset).not.toEqual(underscoreCircleAsset) + expect(Object.keys(manifest).length).toBe(3) // 2 svg, 1 index.js + }) +} diff --git a/playground/assets-sanitize/_circle.svg b/playground/assets-sanitize/_circle.svg new file mode 100644 index 00000000000000..f8e310c6148d42 --- /dev/null +++ b/playground/assets-sanitize/_circle.svg @@ -0,0 +1,3 @@ + + + diff --git a/playground/assets-sanitize/index.html b/playground/assets-sanitize/index.html new file mode 100644 index 00000000000000..e4b4913ca7142c --- /dev/null +++ b/playground/assets-sanitize/index.html @@ -0,0 +1,11 @@ + + +

test elements below should show circles and their url

+
+
diff --git a/playground/assets-sanitize/index.js b/playground/assets-sanitize/index.js new file mode 100644 index 00000000000000..bac3b3b83e6b1d --- /dev/null +++ b/playground/assets-sanitize/index.js @@ -0,0 +1,9 @@ +import plusCircle from './+circle.svg' +import underscoreCircle from './_circle.svg' +function setData(classname, file) { + const el = document.body.querySelector(classname) + el.style.backgroundImage = `url(${file})` + el.textContent = file +} +setData('.plus-circle', plusCircle) +setData('.underscore-circle', underscoreCircle) diff --git a/playground/assets-sanitize/package.json b/playground/assets-sanitize/package.json new file mode 100644 index 00000000000000..3ade78a2bd33fe --- /dev/null +++ b/playground/assets-sanitize/package.json @@ -0,0 +1,11 @@ +{ + "name": "test-assets-sanitize", + "private": true, + "version": "0.0.0", + "scripts": { + "dev": "vite", + "build": "vite build", + "debug": "node --inspect-brk ../../packages/vite/bin/vite", + "preview": "vite preview" + } +} diff --git a/playground/assets-sanitize/vite.config.js b/playground/assets-sanitize/vite.config.js new file mode 100644 index 00000000000000..0e365a95383833 --- /dev/null +++ b/playground/assets-sanitize/vite.config.js @@ -0,0 +1,11 @@ +const { defineConfig } = require('vite') + +module.exports = defineConfig({ + build: { + //speed up build + minify: false, + target: 'esnext', + assetsInlineLimit: 0, + manifest: true + } +}) diff --git a/playground/assets/__tests__/assets.spec.ts b/playground/assets/__tests__/assets.spec.ts index ceb259102d4031..768fadf7f35ce5 100644 --- a/playground/assets/__tests__/assets.spec.ts +++ b/playground/assets/__tests__/assets.spec.ts @@ -272,7 +272,7 @@ describe.runIf(isBuild)('encodeURI', () => { test('img src with encodeURI', async () => { const img = await page.$('.encodeURI') expect( - await (await img.getAttribute('src')).startsWith('data:image/png;base64') + (await img.getAttribute('src')).startsWith('data:image/png;base64') ).toBe(true) }) }) @@ -281,6 +281,16 @@ test('new URL(..., import.meta.url)', async () => { expect(await page.textContent('.import-meta-url')).toMatch(assetMatch) }) +test('new URL("@/...", import.meta.url)', async () => { + expect(await page.textContent('.import-meta-url-dep')).toMatch(assetMatch) +}) + +test('new URL("/...", import.meta.url)', async () => { + expect(await page.textContent('.import-meta-url-base-path')).toMatch( + iconMatch + ) +}) + test('new URL(`${dynamic}`, import.meta.url)', async () => { expect(await page.textContent('.dynamic-import-meta-url-1')).toMatch( isBuild ? 'data:image/png;base64' : '/foo/nested/icon.png' diff --git a/playground/assets/__tests__/relative-base/relative-base-assets.spec.ts b/playground/assets/__tests__/relative-base/relative-base-assets.spec.ts index 99544c35b2cd31..f62b23a2083204 100644 --- a/playground/assets/__tests__/relative-base/relative-base-assets.spec.ts +++ b/playground/assets/__tests__/relative-base/relative-base-assets.spec.ts @@ -1,4 +1,4 @@ -import { describe, expect, test } from 'vitest' +import { beforeAll, describe, expect, test } from 'vitest' import { browserLogs, findAssetFile, diff --git a/playground/assets/__tests__/runtime-base/runtime-base-assets.spec.ts b/playground/assets/__tests__/runtime-base/runtime-base-assets.spec.ts index 7b769c0fa83536..90cd764cafe209 100644 --- a/playground/assets/__tests__/runtime-base/runtime-base-assets.spec.ts +++ b/playground/assets/__tests__/runtime-base/runtime-base-assets.spec.ts @@ -1,4 +1,4 @@ -import { describe, expect, test } from 'vitest' +import { beforeAll, describe, expect, test } from 'vitest' import { browserLogs, findAssetFile, diff --git a/playground/assets/index.html b/playground/assets/index.html index 42fa5498f28b8c..f897d61a355ed0 100644 --- a/playground/assets/index.html +++ b/playground/assets/index.html @@ -182,6 +182,14 @@

new URL('...', import.meta.url)

+

new URL('@/...', import.meta.url)

+ + + +

new URL('/...', import.meta.url)

+ + +

new URL('...', import.meta.url,) (with comma)

@@ -354,6 +362,16 @@

style in svg

text('.import-meta-url', metaUrl) document.querySelector('.import-meta-url-img').src = metaUrl + const metaUrlDep = new URL('@/asset.png', import.meta.url) + text('.import-meta-url-dep', metaUrlDep) + document.querySelector('.import-meta-url-dep-img').src = metaUrlDep + + // testing URLs for public assets served at the public base path + // equivalent to `new URL(`${import.meta.env.BASE_URL}/icon.png`, self.location) + const metaUrlBasePath = new URL('/icon.png', import.meta.url) + text('.import-meta-url-base-path', metaUrlBasePath) + document.querySelector('.import-meta-url-base-path-img').src = metaUrlBasePath + // prettier-ignore const metaUrlWithComma = new URL('./nested/asset.png', import.meta.url,) text('.import-meta-url-comma', metaUrlWithComma) diff --git a/playground/assets/vite.config-runtime-base.js b/playground/assets/vite.config-runtime-base.js index 0d643a6cf6058a..1752e974b8abcd 100644 --- a/playground/assets/vite.config-runtime-base.js +++ b/playground/assets/vite.config-runtime-base.js @@ -1,4 +1,4 @@ -const path = require('path') +const path = require('node:path') const dynamicBaseAssetsCode = ` globalThis.__toAssetUrl = url => '/' + url diff --git a/playground/assets/vite.config.js b/playground/assets/vite.config.js index c9d821ae3d73ee..700896cc13d50b 100644 --- a/playground/assets/vite.config.js +++ b/playground/assets/vite.config.js @@ -1,4 +1,4 @@ -const path = require('path') +const path = require('node:path') /** * @type {import('vite').UserConfig} diff --git a/playground/backend-integration/__tests__/backend-integration.spec.ts b/playground/backend-integration/__tests__/backend-integration.spec.ts index a6b773bf791ddb..bd3ee1655dd976 100644 --- a/playground/backend-integration/__tests__/backend-integration.spec.ts +++ b/playground/backend-integration/__tests__/backend-integration.spec.ts @@ -1,3 +1,4 @@ +import { describe, expect, test } from 'vitest' import { browserErrors, browserLogs, diff --git a/playground/backend-integration/package.json b/playground/backend-integration/package.json index ff6b79bd924158..6f07ed647aef2c 100644 --- a/playground/backend-integration/package.json +++ b/playground/backend-integration/package.json @@ -9,8 +9,8 @@ "preview": "vite preview" }, "devDependencies": { - "sass": "^1.54.0", - "tailwindcss": "^3.1.6", - "fast-glob": "^3.2.11" + "sass": "^1.55.0", + "tailwindcss": "^3.2.1", + "fast-glob": "^3.2.12" } } diff --git a/playground/backend-integration/vite.config.js b/playground/backend-integration/vite.config.js index ce3f3361fc70d1..ff5d2c43c3ed04 100644 --- a/playground/backend-integration/vite.config.js +++ b/playground/backend-integration/vite.config.js @@ -1,4 +1,4 @@ -const path = require('path') +const path = require('node:path') const glob = require('fast-glob') const normalizePath = require('vite').normalizePath diff --git a/playground/build-old/__tests__/build-old.spec.ts b/playground/build-old/__tests__/build-old.spec.ts index 085d1bbe75b986..517b204e7dde82 100644 --- a/playground/build-old/__tests__/build-old.spec.ts +++ b/playground/build-old/__tests__/build-old.spec.ts @@ -1,4 +1,4 @@ -import { describe, test } from 'vitest' +import { describe, expect, test } from 'vitest' import { page } from '~utils' describe('syntax preserve', () => { diff --git a/playground/cli-module/__tests__/cli-module.spec.ts b/playground/cli-module/__tests__/cli-module.spec.ts index a443a66c9810dc..ef109e75cc77cc 100644 --- a/playground/cli-module/__tests__/cli-module.spec.ts +++ b/playground/cli-module/__tests__/cli-module.spec.ts @@ -1,3 +1,4 @@ +import { expect, test } from 'vitest' import { port } from './serve' import { page } from '~utils' diff --git a/playground/cli-module/package.json b/playground/cli-module/package.json index 0f297d4e4d0563..30a94d2bb877ad 100644 --- a/playground/cli-module/package.json +++ b/playground/cli-module/package.json @@ -8,5 +8,8 @@ "build": "vite build", "debug": "node --inspect-brk ../../packages/vite/bin/vite", "serve": "vite preview" + }, + "devDependencies": { + "url": "^0.11.0" } } diff --git a/playground/cli-module/vite.config.js b/playground/cli-module/vite.config.js index 9629e535d58e38..4020d4627917bb 100644 --- a/playground/cli-module/vite.config.js +++ b/playground/cli-module/vite.config.js @@ -1,5 +1,11 @@ +// eslint-disable-next-line import/no-nodejs-modules +import { URL } from 'url' import { defineConfig } from 'vite' +// make sure bundling works even if `url` refers to the locally installed +// `url` package instead of the built-in `url` nodejs module +globalThis.__test_url = URL + export default defineConfig({ server: { host: 'localhost' diff --git a/playground/cli/__tests__/cli.spec.ts b/playground/cli/__tests__/cli.spec.ts index 676d076a1aaeed..4ec4d613d20d9a 100644 --- a/playground/cli/__tests__/cli.spec.ts +++ b/playground/cli/__tests__/cli.spec.ts @@ -1,3 +1,4 @@ +import { expect, test } from 'vitest' import { port } from './serve' import { page } from '~utils' diff --git a/playground/config/__tests__/load.spec.ts b/playground/config/__tests__/load.spec.ts new file mode 100644 index 00000000000000..3205912b8156c3 --- /dev/null +++ b/playground/config/__tests__/load.spec.ts @@ -0,0 +1,26 @@ +import { resolve } from 'node:path' +import { loadConfigFromFile } from 'vite' +import { expect, it } from 'vitest' + +it('loadConfigFromFile', async () => { + const { config } = await loadConfigFromFile( + {} as any, + resolve(__dirname, '../packages/entry/vite.config.ts'), + resolve(__dirname, '../packages/entry') + ) + expect(config).toMatchInlineSnapshot(` + { + "array": [ + [ + 1, + 3, + ], + [ + 2, + 4, + ], + ], + "moduleCondition": "require condition", + } + `) +}) diff --git a/playground/config/__tests__/serve.ts b/playground/config/__tests__/serve.ts new file mode 100644 index 00000000000000..e8959c0a1eda19 --- /dev/null +++ b/playground/config/__tests__/serve.ts @@ -0,0 +1,3 @@ +export function serve() { + return +} diff --git a/playground/config/packages/entry/package.json b/playground/config/packages/entry/package.json new file mode 100644 index 00000000000000..287aa6c698618b --- /dev/null +++ b/playground/config/packages/entry/package.json @@ -0,0 +1,6 @@ +{ + "name": "@vite/test-config-entry", + "dependencies": { + "@vite/test-config-plugin-module-condition": "link:../plugin-module-condition" + } +} diff --git a/playground/config/packages/entry/vite.config.ts b/playground/config/packages/entry/vite.config.ts new file mode 100644 index 00000000000000..9b865e5fa3d66a --- /dev/null +++ b/playground/config/packages/entry/vite.config.ts @@ -0,0 +1,7 @@ +import moduleCondition from '@vite/test-config-plugin-module-condition' +import { array } from '../siblings/foo' + +export default { + array, + moduleCondition +} diff --git a/playground/config/packages/plugin-module-condition/index.cjs b/playground/config/packages/plugin-module-condition/index.cjs new file mode 100644 index 00000000000000..082d40b7f30a06 --- /dev/null +++ b/playground/config/packages/plugin-module-condition/index.cjs @@ -0,0 +1 @@ +module.exports = 'require condition' diff --git a/playground/config/packages/plugin-module-condition/index.d.ts b/playground/config/packages/plugin-module-condition/index.d.ts new file mode 100644 index 00000000000000..e04fab0f58f539 --- /dev/null +++ b/playground/config/packages/plugin-module-condition/index.d.ts @@ -0,0 +1,2 @@ +const str: string +export default str diff --git a/playground/config/packages/plugin-module-condition/index.mjs b/playground/config/packages/plugin-module-condition/index.mjs new file mode 100644 index 00000000000000..a4386771448945 --- /dev/null +++ b/playground/config/packages/plugin-module-condition/index.mjs @@ -0,0 +1 @@ +export default 'import condition' diff --git a/playground/config/packages/plugin-module-condition/module.mjs b/playground/config/packages/plugin-module-condition/module.mjs new file mode 100644 index 00000000000000..6361ab1059bd2d --- /dev/null +++ b/playground/config/packages/plugin-module-condition/module.mjs @@ -0,0 +1 @@ +export default 'module condition' diff --git a/playground/config/packages/plugin-module-condition/package.json b/playground/config/packages/plugin-module-condition/package.json new file mode 100644 index 00000000000000..38df99751d9e3e --- /dev/null +++ b/playground/config/packages/plugin-module-condition/package.json @@ -0,0 +1,11 @@ +{ + "name": "@vite/test-config-plugin-module-condition", + "exports": { + ".": { + "types": "./index.d.ts", + "module": "./module.mjs", + "import": "./index.mjs", + "require": "./index.cjs" + } + } +} diff --git a/playground/config/packages/siblings/foo.ts b/playground/config/packages/siblings/foo.ts new file mode 100644 index 00000000000000..78a8912131faed --- /dev/null +++ b/playground/config/packages/siblings/foo.ts @@ -0,0 +1,3 @@ +import { partition } from 'lodash' + +export const array = partition([1, 2, 3, 4], (n) => n % 2) diff --git a/playground/config/packages/siblings/package.json b/playground/config/packages/siblings/package.json new file mode 100644 index 00000000000000..4cbdc81d2100ea --- /dev/null +++ b/playground/config/packages/siblings/package.json @@ -0,0 +1,7 @@ +{ + "name": "@vite/test-config-sibling", + "devDependencies": { + "@types/lodash": "^4.14.186", + "lodash": "^4.17.21" + } +} diff --git a/playground/css-codesplit-cjs/__tests__/css-codesplit.spec.ts b/playground/css-codesplit-cjs/__tests__/css-codesplit.spec.ts index dc51f0a2e473c1..69001c61bdb82b 100644 --- a/playground/css-codesplit-cjs/__tests__/css-codesplit.spec.ts +++ b/playground/css-codesplit-cjs/__tests__/css-codesplit.spec.ts @@ -1,3 +1,4 @@ +import { describe, expect, test } from 'vitest' import { findAssetFile, getColor, isBuild, readManifest } from '~utils' test('should load both stylesheets', async () => { diff --git a/playground/css-codesplit-cjs/vite.config.js b/playground/css-codesplit-cjs/vite.config.js index 7cd8967121f021..85a7a0f07eff14 100644 --- a/playground/css-codesplit-cjs/vite.config.js +++ b/playground/css-codesplit-cjs/vite.config.js @@ -1,4 +1,4 @@ -const { resolve } = require('path') +const { resolve } = require('node:path') module.exports = { build: { diff --git a/playground/css-codesplit/__tests__/css-codesplit.spec.ts b/playground/css-codesplit/__tests__/css-codesplit.spec.ts index f00dfed1d92453..203316c7565932 100644 --- a/playground/css-codesplit/__tests__/css-codesplit.spec.ts +++ b/playground/css-codesplit/__tests__/css-codesplit.spec.ts @@ -1,3 +1,4 @@ +import { describe, expect, test } from 'vitest' import { findAssetFile, getColor, isBuild, page, readManifest } from '~utils' test('should load all stylesheets', async () => { diff --git a/playground/css-codesplit/vite.config.js b/playground/css-codesplit/vite.config.js index 528a3c7d7f19fe..0bc9cab5d025df 100644 --- a/playground/css-codesplit/vite.config.js +++ b/playground/css-codesplit/vite.config.js @@ -1,4 +1,4 @@ -const { resolve } = require('path') +const { resolve } = require('node:path') module.exports = { build: { diff --git a/playground/css-dynamic-import/__tests__/css-dynamic-import.spec.ts b/playground/css-dynamic-import/__tests__/css-dynamic-import.spec.ts new file mode 100644 index 00000000000000..56757fc293dbba --- /dev/null +++ b/playground/css-dynamic-import/__tests__/css-dynamic-import.spec.ts @@ -0,0 +1,121 @@ +import type { InlineConfig } from 'vite' +import { build, createServer, preview } from 'vite' +import { expect, test } from 'vitest' +import { getColor, isBuild, isServe, page, ports, rootDir } from '~utils' + +const baseOptions = [ + { base: '', label: 'relative' }, + { base: '/', label: 'absolute' } +] + +const getConfig = (base: string): InlineConfig => ({ + base, + root: rootDir, + logLevel: 'silent', + preview: { port: ports['css/dynamic-import'] }, + build: { assetsInlineLimit: 0 } +}) + +async function withBuild(base: string, fn: () => Promise) { + const config = getConfig(base) + await build(config) + const server = await preview(config) + + try { + await page.goto(server.resolvedUrls.local[0]) + await fn() + } finally { + server.httpServer.close() + } +} + +async function withServe(base: string, fn: () => Promise) { + const config = getConfig(base) + const server = await createServer(config) + await server.listen() + await new Promise((r) => setTimeout(r, 500)) + + try { + await page.goto(server.resolvedUrls.local[0]) + await fn() + } finally { + await server.close() + } +} + +async function getLinks() { + const links = await page.$$('link') + return await Promise.all( + links.map((handle) => { + return handle.evaluate((link) => ({ + pathname: new URL(link.href).pathname, + rel: link.rel, + as: link.as + })) + }) + ) +} + +baseOptions.forEach(({ base, label }) => { + test.runIf(isBuild)( + `doesn't duplicate dynamically imported css files when built with ${label} base`, + async () => { + await withBuild(base, async () => { + await page.waitForSelector('.loaded', { state: 'attached' }) + + expect(await getColor('.css-dynamic-import')).toBe('green') + expect(await getLinks()).toEqual([ + { + pathname: expect.stringMatching(/^\/assets\/index\..+\.css$/), + rel: 'stylesheet', + as: '' + }, + { + pathname: expect.stringMatching(/^\/assets\/dynamic\..+\.css$/), + rel: 'preload', + as: 'style' + }, + { + pathname: expect.stringMatching(/^\/assets\/dynamic\..+\.js$/), + rel: 'modulepreload', + as: 'script' + }, + { + pathname: expect.stringMatching(/^\/assets\/dynamic\..+\.css$/), + rel: 'stylesheet', + as: '' + }, + { + pathname: expect.stringMatching(/^\/assets\/static\..+\.js$/), + rel: 'modulepreload', + as: 'script' + }, + { + pathname: expect.stringMatching(/^\/assets\/index\..+\.js$/), + rel: 'modulepreload', + as: 'script' + } + ]) + }) + } + ) + + test.runIf(isServe)( + `doesn't duplicate dynamically imported css files when served with ${label} base`, + async () => { + await withServe(base, async () => { + await page.waitForSelector('.loaded', { state: 'attached' }) + + expect(await getColor('.css-dynamic-import')).toBe('green') + // in serve there is no preloading + expect(await getLinks()).toEqual([ + { + pathname: '/dynamic.css', + rel: 'preload', + as: 'style' + } + ]) + }) + } + ) +}) diff --git a/playground/css-dynamic-import/__tests__/serve.ts b/playground/css-dynamic-import/__tests__/serve.ts new file mode 100644 index 00000000000000..ae33c33a5db107 --- /dev/null +++ b/playground/css-dynamic-import/__tests__/serve.ts @@ -0,0 +1,10 @@ +// this is automatically detected by playground/vitestSetup.ts and will replace +// the default e2e test serve behavior + +// The server is started in the test, so we need to have a custom serve +// function or a default server will be created +export async function serve() { + return { + close: () => Promise.resolve() + } +} diff --git a/playground/css-dynamic-import/dynamic.css b/playground/css-dynamic-import/dynamic.css new file mode 100644 index 00000000000000..6212a63c31fa19 --- /dev/null +++ b/playground/css-dynamic-import/dynamic.css @@ -0,0 +1,3 @@ +.css-dynamic-import { + color: green; +} diff --git a/playground/css-dynamic-import/dynamic.js b/playground/css-dynamic-import/dynamic.js new file mode 100644 index 00000000000000..0d0aeb3aec229c --- /dev/null +++ b/playground/css-dynamic-import/dynamic.js @@ -0,0 +1,6 @@ +import './dynamic.css' + +export const lazyLoad = async () => { + await import('./static.js') + document.body.classList.add('loaded') +} diff --git a/playground/css-dynamic-import/index.html b/playground/css-dynamic-import/index.html new file mode 100644 index 00000000000000..d9f9fedbbda752 --- /dev/null +++ b/playground/css-dynamic-import/index.html @@ -0,0 +1,3 @@ +

This should be green

+ + diff --git a/playground/css-dynamic-import/index.js b/playground/css-dynamic-import/index.js new file mode 100644 index 00000000000000..5a0c724da737db --- /dev/null +++ b/playground/css-dynamic-import/index.js @@ -0,0 +1,10 @@ +import './static.js' + +const link = document.head.appendChild(document.createElement('link')) +link.rel = 'preload' +link.as = 'style' +link.href = new URL('./dynamic.css', import.meta.url).href + +import('./dynamic.js').then(async ({ lazyLoad }) => { + await lazyLoad() +}) diff --git a/playground/css-dynamic-import/static.css b/playground/css-dynamic-import/static.css new file mode 100644 index 00000000000000..4efb84fdfea550 --- /dev/null +++ b/playground/css-dynamic-import/static.css @@ -0,0 +1,3 @@ +.css-dynamic-import { + color: red; +} diff --git a/playground/css-dynamic-import/static.js b/playground/css-dynamic-import/static.js new file mode 100644 index 00000000000000..1688198fba4227 --- /dev/null +++ b/playground/css-dynamic-import/static.js @@ -0,0 +1,3 @@ +import './static.css' + +export const foo = 'foo' diff --git a/playground/css-sourcemap/__tests__/css-sourcemap.spec.ts b/playground/css-sourcemap/__tests__/css-sourcemap.spec.ts index f63ef8946eba40..4b2331e8836d4c 100644 --- a/playground/css-sourcemap/__tests__/css-sourcemap.spec.ts +++ b/playground/css-sourcemap/__tests__/css-sourcemap.spec.ts @@ -1,4 +1,5 @@ import { URL } from 'node:url' +import { describe, expect, test } from 'vitest' import { extractSourcemap, formatSourcemapForSnapshot, @@ -89,6 +90,18 @@ describe.runIf(isServe)('serve', () => { `) }) + test.runIf(isServe)( + 'js .css request does not include sourcemap', + async () => { + const res = await page.request.get( + new URL('./linked-with-import.css', page.url()).href + ) + const content = await res.text() + const lines = content.trim().split('\n') + expect(lines[lines.length - 1]).not.toMatch(/^\/\/#/) + } + ) + test('imported css', async () => { const css = await getStyleTagContentIncluding('.imported ') const map = extractSourcemap(css) @@ -218,210 +231,18 @@ describe.runIf(isServe)('serve', () => { `) }) - test('should not output missing source file warning', () => { - serverLogs.forEach((log) => { - expect(log).not.toMatch(/Sourcemap for .+ points to missing source files/) - }) - }) -}) - -describe.runIf(isServe)('serve', () => { - const getStyleTagContentIncluding = async (content: string) => { - const styles = await page.$$('style') - for (const style of styles) { - const text = await style.textContent() - if (text.includes(content)) { - return text - } - } - throw new Error('Not found') - } - - test('linked css', async () => { - const res = await page.request.get( - new URL('./linked.css', page.url()).href, - { - headers: { - accept: 'text/css' - } - } - ) - const css = await res.text() - const map = extractSourcemap(css) - expect(formatSourcemapForSnapshot(map)).toMatchInlineSnapshot(` - { - "mappings": "AAAA,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC;AACT,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC;AACb,CAAC;", - "sources": [ - "/root/linked.css", - ], - "sourcesContent": [ - ".linked { - color: red; - } - ", - ], - "version": 3, - } - `) - }) - - test('linked css with import', async () => { - const res = await page.request.get( - new URL('./linked-with-import.css', page.url()).href, - { - headers: { - accept: 'text/css' - } - } - ) - const css = await res.text() - const map = extractSourcemap(css) - expect(formatSourcemapForSnapshot(map)).toMatchInlineSnapshot(` - { - "mappings": "AAAA;EACE,UAAU;AACZ;;ACAA;EACE,UAAU;AACZ", - "sources": [ - "/root/be-imported.css", - "/root/linked-with-import.css", - ], - "sourcesContent": [ - ".be-imported { - color: red; - } - ", - "@import '@/be-imported.css'; - - .linked-with-import { - color: red; - } - ", - ], - "version": 3, - } - `) - }) - - test('imported css', async () => { - const css = await getStyleTagContentIncluding('.imported ') - const map = extractSourcemap(css) - expect(formatSourcemapForSnapshot(map)).toMatchInlineSnapshot(` - { - "mappings": "AAAA,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC;AACX,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC;AACb,CAAC;", - "sources": [ - "/root/imported.css", - ], - "sourcesContent": [ - ".imported { - color: red; - } - ", - ], - "version": 3, - } - `) - }) - - test('imported css with import', async () => { - const css = await getStyleTagContentIncluding('.imported-with-import ') - const map = extractSourcemap(css) - expect(formatSourcemapForSnapshot(map)).toMatchInlineSnapshot(` - { - "mappings": "AAAA;EACE,UAAU;AACZ;;ACAA;EACE,UAAU;AACZ", - "sources": [ - "/root/be-imported.css", - "/root/imported-with-import.css", - ], - "sourcesContent": [ - ".be-imported { - color: red; - } - ", - "@import '@/be-imported.css'; - - .imported-with-import { - color: red; - } - ", - ], - "version": 3, - } - `) - }) - - test('imported sass', async () => { - const css = await getStyleTagContentIncluding('.imported-sass ') + test('imported sugarss', async () => { + const css = await getStyleTagContentIncluding('.imported-sugarss ') const map = extractSourcemap(css) expect(formatSourcemapForSnapshot(map)).toMatchInlineSnapshot(` { - "mappings": "AACE;EACE", + "mappings": "AAAA;EACE;AADc", "sources": [ - "/root/imported.sass", + "/root/imported.sss", ], "sourcesContent": [ - ".imported - &-sass - color: red - ", - ], - "version": 3, - } - `) - }) - - test('imported sass module', async () => { - const css = await getStyleTagContentIncluding('._imported-sass-module_') - const map = extractSourcemap(css) - expect(formatSourcemapForSnapshot(map)).toMatchInlineSnapshot(` - { - "mappings": "AACE;EACE", - "sources": [ - "/root/imported.module.sass", - ], - "sourcesContent": [ - ".imported - &-sass-module - color: red - ", - ], - "version": 3, - } - `) - }) - - test('imported less', async () => { - const css = await getStyleTagContentIncluding('.imported-less ') - const map = extractSourcemap(css) - expect(formatSourcemapForSnapshot(map)).toMatchInlineSnapshot(` - { - "mappings": "AACE;EACE", - "sources": [ - "/root/imported.less", - ], - "sourcesContent": [ - ".imported { - &-less { - color: @color; - } - } - ", - ], - "version": 3, - } - `) - }) - - test('imported stylus', async () => { - const css = await getStyleTagContentIncluding('.imported-stylus ') - const map = extractSourcemap(css) - expect(formatSourcemapForSnapshot(map)).toMatchInlineSnapshot(` - { - "mappings": "AACE;EACE,cAAM", - "sources": [ - "/root/imported.styl", - ], - "sourcesContent": [ - ".imported - &-stylus - color blue-red-mixed + ".imported-sugarss + color: red ", ], "version": 3, @@ -435,9 +256,3 @@ describe.runIf(isServe)('serve', () => { }) }) }) - -test.runIf(isBuild)('should not output sourcemap warning (#4939)', () => { - serverLogs.forEach((log) => { - expect(log).not.toMatch('Sourcemap is likely to be incorrect') - }) -}) diff --git a/playground/css-sourcemap/imported.sss b/playground/css-sourcemap/imported.sss new file mode 100644 index 00000000000000..56084992472c47 --- /dev/null +++ b/playground/css-sourcemap/imported.sss @@ -0,0 +1,2 @@ +.imported-sugarss + color: red diff --git a/playground/css-sourcemap/index.html b/playground/css-sourcemap/index.html index a943c1d113a9b4..8260ae75ed65ca 100644 --- a/playground/css-sourcemap/index.html +++ b/playground/css-sourcemap/index.html @@ -24,6 +24,10 @@

CSS Sourcemap

<imported less> with string additionalData

<imported stylus>

+ +

<imported sugarss>

+ +

<input source-map>

diff --git a/playground/css-sourcemap/input-map.css b/playground/css-sourcemap/input-map.css new file mode 100644 index 00000000000000..575a1751c2cbca --- /dev/null +++ b/playground/css-sourcemap/input-map.css @@ -0,0 +1,4 @@ +.input-map { + color: #00f; +} +/*# sourceMappingURL=input-map.css.map */ diff --git a/playground/css-sourcemap/input-map.css.map b/playground/css-sourcemap/input-map.css.map new file mode 100644 index 00000000000000..05502b8ce18685 --- /dev/null +++ b/playground/css-sourcemap/input-map.css.map @@ -0,0 +1,7 @@ +{ + "version": 3, + "sources": ["input-map.src.css"], + "sourcesContent": [".input-map {\n color: blue;\n}"], + "mappings": "AAAA,WACE", + "names": [] +} diff --git a/playground/css-sourcemap/input-map.src.css b/playground/css-sourcemap/input-map.src.css new file mode 100644 index 00000000000000..90b9565e271633 --- /dev/null +++ b/playground/css-sourcemap/input-map.src.css @@ -0,0 +1,3 @@ +.input-map { + color: blue; +} diff --git a/playground/css-sourcemap/package.json b/playground/css-sourcemap/package.json index 6e348c6e1e1e49..624a5c40f6e9d5 100644 --- a/playground/css-sourcemap/package.json +++ b/playground/css-sourcemap/package.json @@ -10,8 +10,9 @@ }, "devDependencies": { "less": "^4.1.3", - "magic-string": "^0.26.2", - "sass": "^1.54.0", - "stylus": "^0.58.1" + "magic-string": "^0.26.7", + "sass": "^1.55.0", + "stylus": "^0.59.0", + "sugarss": "^4.0.1" } } diff --git a/playground/css/__tests__/css.spec.ts b/playground/css/__tests__/css.spec.ts index 31425a19fc2c98..e2f41d893021c3 100644 --- a/playground/css/__tests__/css.spec.ts +++ b/playground/css/__tests__/css.spec.ts @@ -1,4 +1,5 @@ import { readFileSync } from 'node:fs' +import { expect, test } from 'vitest' import { editFile, findAssetFile, @@ -8,7 +9,9 @@ import { page, removeFile, serverLogs, - untilUpdated + untilUpdated, + viteTestUrl, + withRetry } from '~utils' // note: tests should retrieve the element at the beginning of test and reuse it @@ -76,6 +79,7 @@ test('sass', async () => { const imported = await page.$('.sass') 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 partialImport = await page.$('.sass-partial') expect(await getColor(imported)).toBe('orange') @@ -85,6 +89,9 @@ test('sass', async () => { expect(await getBg(atImportAlias)).toMatch( isBuild ? /base64/ : '/nested/icon.png' ) + expect(await getBg(urlStartsWithVariable)).toMatch( + isBuild ? /ok\.\w+\.png/ : `${viteTestUrl}/ok.png` + ) expect(await getColor(partialImport)).toBe('orchid') editFile('sass.scss', (code) => @@ -107,6 +114,7 @@ test('less', async () => { const imported = await page.$('.less') const atImport = await page.$('.less-at-import') const atImportAlias = await page.$('.less-at-import-alias') + const urlStartsWithVariable = await page.$('.less-url-starts-with-variable') expect(await getColor(imported)).toBe('blue') expect(await getColor(atImport)).toBe('darkslateblue') @@ -115,6 +123,9 @@ test('less', async () => { expect(await getBg(atImportAlias)).toMatch( isBuild ? /base64/ : '/nested/icon.png' ) + expect(await getBg(urlStartsWithVariable)).toMatch( + isBuild ? /ok\.\w+\.png/ : `${viteTestUrl}/ok.png` + ) editFile('less.less', (code) => code.replace('@color: blue', '@color: red')) await untilUpdated(() => getColor(imported), 'red') @@ -258,6 +269,11 @@ test.runIf(isBuild)('@charset hoist', async () => { }) }) +test('layers', async () => { + expect(await getColor('.layers-blue')).toMatch('blue') + expect(await getColor('.layers-green')).toMatch('green') +}) + test('@import dependency w/ style entry', async () => { expect(await getColor('.css-dep')).toBe('purple') }) @@ -449,3 +465,39 @@ test.runIf(isBuild)('warning can be suppressed by esbuild.logOverride', () => { expect(log).not.toMatch('unsupported-css-property') }) }) + +test('sugarss', async () => { + const imported = await page.$('.sugarss') + const atImport = await page.$('.sugarss-at-import') + const atImportAlias = await page.$('.sugarss-at-import-alias') + + expect(await getColor(imported)).toBe('blue') + expect(await getColor(atImport)).toBe('darkslateblue') + expect(await getBg(atImport)).toMatch(isBuild ? /base64/ : '/nested/icon.png') + expect(await getColor(atImportAlias)).toBe('darkslateblue') + expect(await getBg(atImportAlias)).toMatch( + isBuild ? /base64/ : '/nested/icon.png' + ) + + editFile('sugarss.sss', (code) => code.replace('color: blue', 'color: coral')) + await untilUpdated(() => getColor(imported), 'coral') + + editFile('nested/nested.sss', (code) => + code.replace('color: darkslateblue', 'color: blue') + ) + await untilUpdated(() => getColor(atImport), 'blue') +}) + +// NOTE: the match inline snapshot should generate by build mode +test('async css order', async () => { + await withRetry(async () => { + expect(await getColor('.async-green')).toMatchInlineSnapshot('"green"') + expect(await getColor('.async-blue')).toMatchInlineSnapshot('"blue"') + }, true) +}) + +test('async css order with css modules', async () => { + await withRetry(async () => { + expect(await getColor('.modules-pink')).toMatchInlineSnapshot('"pink"') + }, true) +}) diff --git a/playground/css/__tests__/postcss-plugins-different-dir/postcss-plugins-different-dir.spec.ts b/playground/css/__tests__/postcss-plugins-different-dir/postcss-plugins-different-dir.spec.ts index 346309f7fcc32d..fabf24da3dae99 100644 --- a/playground/css/__tests__/postcss-plugins-different-dir/postcss-plugins-different-dir.spec.ts +++ b/playground/css/__tests__/postcss-plugins-different-dir/postcss-plugins-different-dir.spec.ts @@ -1,5 +1,6 @@ import path from 'node:path' import { createServer } from 'vite' +import { expect, test } from 'vitest' import { getBgColor, getColor, page, ports } from '~utils' // Regression test for https://github.com/vitejs/vite/issues/4000 diff --git a/playground/css/async/async-1.css b/playground/css/async/async-1.css new file mode 100644 index 00000000000000..9af99eec7843fe --- /dev/null +++ b/playground/css/async/async-1.css @@ -0,0 +1,3 @@ +.async-blue { + color: blue; +} diff --git a/playground/css/async/async-1.js b/playground/css/async/async-1.js new file mode 100644 index 00000000000000..8187dc3b9307e7 --- /dev/null +++ b/playground/css/async/async-1.js @@ -0,0 +1,4 @@ +import { createButton } from './base' +import './async-1.css' + +createButton('async-blue') diff --git a/playground/css/async/async-2.css b/playground/css/async/async-2.css new file mode 100644 index 00000000000000..941e034da37389 --- /dev/null +++ b/playground/css/async/async-2.css @@ -0,0 +1,3 @@ +.async-green { + color: green; +} diff --git a/playground/css/async/async-2.js b/playground/css/async/async-2.js new file mode 100644 index 00000000000000..157eafdc4bff79 --- /dev/null +++ b/playground/css/async/async-2.js @@ -0,0 +1,4 @@ +import { createButton } from './base' +import './async-2.css' + +createButton('async-green') diff --git a/playground/css/async/async-3.js b/playground/css/async/async-3.js new file mode 100644 index 00000000000000..b5dd6da1f326d2 --- /dev/null +++ b/playground/css/async/async-3.js @@ -0,0 +1,4 @@ +import { createButton } from './base' +import styles from './async-3.module.css' + +createButton(`${styles['async-pink']} modules-pink`) diff --git a/playground/css/async/async-3.module.css b/playground/css/async/async-3.module.css new file mode 100644 index 00000000000000..7f43f88d754252 --- /dev/null +++ b/playground/css/async/async-3.module.css @@ -0,0 +1,3 @@ +.async-pink { + color: pink; +} diff --git a/playground/css/async/base.css b/playground/css/async/base.css new file mode 100644 index 00000000000000..cc6f88ddccdf10 --- /dev/null +++ b/playground/css/async/base.css @@ -0,0 +1,3 @@ +.btn { + color: black; +} diff --git a/playground/css/async/base.js b/playground/css/async/base.js new file mode 100644 index 00000000000000..1a409d7e32e4c9 --- /dev/null +++ b/playground/css/async/base.js @@ -0,0 +1,8 @@ +import './base.css' + +export function createButton(className) { + const button = document.createElement('button') + button.className = `btn ${className}` + document.body.appendChild(button) + button.textContent = `button ${getComputedStyle(button).color}` +} diff --git a/playground/css/async/index.js b/playground/css/async/index.js new file mode 100644 index 00000000000000..20d6975ab9d23a --- /dev/null +++ b/playground/css/async/index.js @@ -0,0 +1,3 @@ +import('./async-1.js') +import('./async-2.js') +import('./async-3.js') diff --git a/playground/css/composes-path-resolving.module.css b/playground/css/composes-path-resolving.module.css index 4fe7cdbad42136..a5a5172eb4104c 100644 --- a/playground/css/composes-path-resolving.module.css +++ b/playground/css/composes-path-resolving.module.css @@ -1,11 +1,11 @@ .path-resolving-css { - composes: apply-color from '@/composed.module.css'; + composes: apply-color from '=/composed.module.css'; } .path-resolving-sass { - composes: apply-color from '@/composed.module.scss'; + composes: apply-color from '=/composed.module.scss'; } .path-resolving-less { - composes: apply-color from '@/composed.module.less'; + composes: apply-color from '=/composed.module.less'; } diff --git a/playground/css/index.html b/playground/css/index.html index 39e4305ceda7b8..93f77dfa2eb970 100644 --- a/playground/css/index.html +++ b/playground/css/index.html @@ -32,6 +32,7 @@

CSS

contains alias

@import from SASS _partial: This should be orchid

+

url starts with variable

Imported SASS string:


   

@@ -46,6 +47,7 @@

CSS

@import from Less: This should be darkslateblue and have bg image which url contains alias

+

url starts with variable

Imported Less string:


 
@@ -73,6 +75,17 @@ 

CSS

Imported Stylus string:


 
+  

SugarSS: This should be blue

+

+ @import from SugarSS: This should be darkslateblue and have bg image +

+

+ @import from SugarSS: This should be darkslateblue and have bg image which + url contains alias +

+

Imported SugarSS string:

+

+
   

CSS modules: this should be turquoise

Imported CSS module:


@@ -99,6 +112,12 @@ 

CSS

CSS with @charset:


 
+  

+ @import with layers: + blue + green +

+

@import dependency w/ style entrypoints: this should be purple

diff --git a/playground/css/layered/blue.css b/playground/css/layered/blue.css new file mode 100644 index 00000000000000..faa644dd73ce2d --- /dev/null +++ b/playground/css/layered/blue.css @@ -0,0 +1,5 @@ +@media screen { + .layers-blue { + color: blue; + } +} diff --git a/playground/css/layered/green.css b/playground/css/layered/green.css new file mode 100644 index 00000000000000..15a762b7572e0b --- /dev/null +++ b/playground/css/layered/green.css @@ -0,0 +1,5 @@ +@media screen { + .layers-green { + color: green; + } +} diff --git a/playground/css/layered/index.css b/playground/css/layered/index.css new file mode 100644 index 00000000000000..49756673b674d4 --- /dev/null +++ b/playground/css/layered/index.css @@ -0,0 +1,13 @@ +@layer base; + +@import './blue.css' layer; +@import './green.css' layer; + +@layer base { + .layers-blue { + color: black; + } + .layers-green { + color: black; + } +} diff --git a/playground/css/less.less b/playground/css/less.less index 49cbd3c3bb336e..f5f6fa52b36740 100644 --- a/playground/css/less.less +++ b/playground/css/less.less @@ -1,4 +1,4 @@ -@import '@/nested/nested'; +@import '=/nested/nested'; @import './nested/css-in-less.less'; // Test data-uri calls with relative images. diff --git a/playground/css/linked.css b/playground/css/linked.css index ff38b8bc9345ba..55b11f672fc500 100644 --- a/playground/css/linked.css +++ b/playground/css/linked.css @@ -1,4 +1,4 @@ -@import '@/linked-at-import.css'; +@import '=/linked-at-import.css'; /* test postcss nesting */ .wrapper { diff --git a/playground/css/main.js b/playground/css/main.js index 45e7730b868eb1..68299638b78369 100644 --- a/playground/css/main.js +++ b/playground/css/main.js @@ -3,6 +3,9 @@ import './minify.css' import css from './imported.css' text('.imported-css', css) +import sugarss from './sugarss.sss' +text('.imported-sugarss', sugarss) + import sass from './sass.scss' text('.imported-sass', sass) @@ -44,9 +47,12 @@ text('.modules-inline', inlineMod) import charset from './charset.css' text('.charset-css', charset) +import './layered/index.css' + import './dep.css' import './glob-dep.css' +// eslint-disable-next-line import/order import { barModuleClasses } from 'css-js-dep' document .querySelector('.css-js-dep-module') @@ -106,3 +112,5 @@ document .classList.add(aliasModule.aliasedModule) import './unsupported.css' + +import './async/index' diff --git a/playground/css/nested/_index.scss b/playground/css/nested/_index.scss index 48d630b573ae1b..fee2eb88b168f2 100644 --- a/playground/css/nested/_index.scss +++ b/playground/css/nested/_index.scss @@ -7,5 +7,11 @@ .sass-at-import-alias { color: olive; - background: url(@/nested/icon.png) 10px no-repeat; + background: url(=/nested/icon.png) 10px no-repeat; +} + +$var: '/ok.png'; +.sass-url-starts-with-variable { + background: url($var); + background-position: center; } diff --git a/playground/css/nested/nested.less b/playground/css/nested/nested.less index e4ce110ab73679..25aa1944d32c14 100644 --- a/playground/css/nested/nested.less +++ b/playground/css/nested/nested.less @@ -5,5 +5,11 @@ .less-at-import-alias { color: darkslateblue; - background: url(@/nested/icon.png) 10px no-repeat; + background: url(=/nested/icon.png) 10px no-repeat; +} + +@var: '/ok.png'; +.less-url-starts-with-variable { + background: url('@{var}'); + background-position: center; } diff --git a/playground/css/nested/nested.sss b/playground/css/nested/nested.sss new file mode 100644 index 00000000000000..9dc685cb3e50c3 --- /dev/null +++ b/playground/css/nested/nested.sss @@ -0,0 +1,8 @@ +.sugarss-at-import + color: darkslateblue + background: url(./icon.png) 10px no-repeat + + +.sugarss-at-import-alias + color: darkslateblue + background: url(=/nested/icon.png) 10px no-repeat diff --git a/playground/css/nested/nested.styl b/playground/css/nested/nested.styl index 72e6f7a5074685..8a371948538de0 100644 --- a/playground/css/nested/nested.styl +++ b/playground/css/nested/nested.styl @@ -3,4 +3,4 @@ .stylus-import-alias color darkslateblue - background url('@/nested/icon.png') 10px no-repeat + background url('=/nested/icon.png') 10px no-repeat diff --git a/playground/css/package.json b/playground/css/package.json index 69314f8867c9c8..5081b5462c2fa4 100644 --- a/playground/css/package.json +++ b/playground/css/package.json @@ -14,10 +14,11 @@ "devDependencies": { "css-dep": "link:./css-dep", "css-js-dep": "file:./css-js-dep", - "fast-glob": "^3.2.11", + "fast-glob": "^3.2.12", "less": "^4.1.3", "postcss-nested": "^5.0.6", - "sass": "^1.54.0", - "stylus": "^0.58.1" + "sass": "^1.55.0", + "stylus": "^0.59.0", + "sugarss": "^4.0.1" } } diff --git a/playground/css/postcss-caching/css.spec.ts b/playground/css/postcss-caching/css.spec.ts index cb962f1151c5a8..c32bf6b6251d2d 100644 --- a/playground/css/postcss-caching/css.spec.ts +++ b/playground/css/postcss-caching/css.spec.ts @@ -1,5 +1,6 @@ import path from 'node:path' import { createServer } from 'vite' +import { expect, test } from 'vitest' import { getColor, page, ports } from '~utils' test('postcss config', async () => { diff --git a/playground/css/postcss.config.js b/playground/css/postcss.config.js index b30209bff42097..a9bbd0f2b4e09e 100644 --- a/playground/css/postcss.config.js +++ b/playground/css/postcss.config.js @@ -2,9 +2,9 @@ module.exports = { plugins: [require('postcss-nested'), testDirDep, testSourceInput] } -const fs = require('fs') +const fs = require('node:fs') +const path = require('node:path') const glob = require('fast-glob') -const path = require('path') const { normalizePath } = require('vite') /** diff --git a/playground/css/sass.scss b/playground/css/sass.scss index 3c7095418e01e6..721e6b9bfbca6c 100644 --- a/playground/css/sass.scss +++ b/playground/css/sass.scss @@ -1,8 +1,9 @@ -@import '@/nested'; // alias + custom index resolving -> /nested/_index.scss -@import '@/nested/partial'; // sass convention: omitting leading _ for partials +@import '=/nested'; // alias + custom index resolving -> /nested/_index.scss +@import '=/nested/partial'; // sass convention: omitting leading _ for partials @import 'css-dep'; // package w/ sass entry points @import 'virtual-dep'; // virtual file added through importer -@import '@/pkg-dep'; // package w/out sass field +@import '=/pkg-dep'; // package w/out sass field +@import '=/weapp.wxss'; // wxss file .sass { /* injected via vite.config.js */ diff --git a/playground/css/sugarss.sss b/playground/css/sugarss.sss new file mode 100644 index 00000000000000..80cfc3861b9417 --- /dev/null +++ b/playground/css/sugarss.sss @@ -0,0 +1,4 @@ +@import '=/nested/nested.sss' + +.sugarss + color: blue diff --git a/playground/css/vite.config.js b/playground/css/vite.config.js index 704eb134e25880..40ca1a186e58da 100644 --- a/playground/css/vite.config.js +++ b/playground/css/vite.config.js @@ -1,4 +1,4 @@ -const path = require('path') +const path = require('node:path') /** * @type {import('vite').UserConfig} @@ -14,7 +14,7 @@ module.exports = { }, resolve: { alias: { - '@': __dirname, + '=': __dirname, spacefolder: __dirname + '/folder with space', '#alias': __dirname + '/aliased/foo.css', '#alias-module': __dirname + '/aliased/bar.module.css' @@ -45,9 +45,14 @@ module.exports = { preprocessorOptions: { scss: { additionalData: `$injectedColor: orange;`, - importer(url) { - if (url === 'virtual-dep') return { contents: '' } - } + importer: [ + function (url) { + return url === 'virtual-dep' ? { contents: '' } : null + }, + function (url) { + return url.endsWith('.wxss') ? { contents: '' } : null + } + ] }, styl: { additionalData: `$injectedColor ?= orange`, diff --git a/playground/css/weapp.wxss b/playground/css/weapp.wxss new file mode 100644 index 00000000000000..e69de29bb2d1d6 diff --git a/playground/data-uri/__tests__/data-uri.spec.ts b/playground/data-uri/__tests__/data-uri.spec.ts index 4b7f3d6e1e471b..7d9ab64594c663 100644 --- a/playground/data-uri/__tests__/data-uri.spec.ts +++ b/playground/data-uri/__tests__/data-uri.spec.ts @@ -1,3 +1,4 @@ +import { expect, test } from 'vitest' import { findAssetFile, isBuild, page } from '~utils' test('plain', async () => { diff --git a/playground/define/__tests__/define.spec.ts b/playground/define/__tests__/define.spec.ts index c22a622f3bfc21..43787ef0adb112 100644 --- a/playground/define/__tests__/define.spec.ts +++ b/playground/define/__tests__/define.spec.ts @@ -1,3 +1,4 @@ +import { expect, test } from 'vitest' import viteConfig from '../vite.config' import { page } from '~utils' diff --git a/playground/dynamic-import/__tests__/dynamic-import.spec.ts b/playground/dynamic-import/__tests__/dynamic-import.spec.ts index c8a90a32aeaf75..8b599462470d18 100644 --- a/playground/dynamic-import/__tests__/dynamic-import.spec.ts +++ b/playground/dynamic-import/__tests__/dynamic-import.spec.ts @@ -1,4 +1,5 @@ -import { getColor, page, serverLogs, untilUpdated } from '~utils' +import { expect, test } from 'vitest' +import { getColor, isBuild, page, serverLogs, untilUpdated } from '~utils' test('should load literal dynamic import', async () => { await page.click('.baz') @@ -92,7 +93,39 @@ test('should load dynamic import with vars raw', async () => { ) }) +test('should load dynamic import with vars url', async () => { + await untilUpdated( + () => page.textContent('.dynamic-import-with-vars-url'), + isBuild ? 'data:application/javascript' : '/alias/url.js', + true + ) +}) + +test('should load dynamic import with vars worker', async () => { + await untilUpdated( + () => page.textContent('.dynamic-import-with-vars-worker'), + 'load worker', + true + ) +}) + test('should load dynamic import with css in package', async () => { await page.click('.pkg-css') await untilUpdated(() => getColor('.pkg-css'), 'blue', true) }) + +test('should work with load ../ and itself directory', async () => { + await untilUpdated( + () => page.textContent('.dynamic-import-self'), + 'dynamic-import-self-content', + true + ) +}) + +test('should work with load ../ and contain itself directory', async () => { + await untilUpdated( + () => page.textContent('.dynamic-import-nested-self'), + 'dynamic-import-nested-self-content', + true + ) +}) diff --git a/playground/dynamic-import/alias/url.js b/playground/dynamic-import/alias/url.js new file mode 100644 index 00000000000000..c9b0c79461d91e --- /dev/null +++ b/playground/dynamic-import/alias/url.js @@ -0,0 +1 @@ +export const url = 'load url' diff --git a/playground/dynamic-import/alias/worker.js b/playground/dynamic-import/alias/worker.js new file mode 100644 index 00000000000000..6206a6536b8064 --- /dev/null +++ b/playground/dynamic-import/alias/worker.js @@ -0,0 +1,5 @@ +self.onmessage = (event) => { + self.postMessage({ + msg: 'load worker' + }) +} diff --git a/playground/dynamic-import/index.html b/playground/dynamic-import/index.html index 997ad059ad6821..d13e842804eaa7 100644 --- a/playground/dynamic-import/index.html +++ b/playground/dynamic-import/index.html @@ -19,8 +19,18 @@

dynamic-import-with-vars-raw

todo
+

dynamic-import-with-vars-url

+
todo
+ +

dynamic-import-with-vars-worker

+
todo
+
+
+ +
+ diff --git a/playground/vue-legacy/__tests__/vue-legacy.spec.ts b/playground/vue-legacy/__tests__/vue-legacy.spec.ts new file mode 100644 index 00000000000000..908e04567ca35b --- /dev/null +++ b/playground/vue-legacy/__tests__/vue-legacy.spec.ts @@ -0,0 +1,10 @@ +import { test } from 'vitest' +import { getBg, untilUpdated } from '~utils' + +test('vue legacy assets', async () => { + await untilUpdated(() => getBg('.main'), 'assets/asset', true) +}) + +test('async vue legacy assets', async () => { + await untilUpdated(() => getBg('.module'), 'assets/asset', true) +}) diff --git a/playground/vue-legacy/assets/asset.png b/playground/vue-legacy/assets/asset.png new file mode 100644 index 00000000000000..1b3356a746b8bb Binary files /dev/null and b/playground/vue-legacy/assets/asset.png differ diff --git a/playground/vue-legacy/env.d.ts b/playground/vue-legacy/env.d.ts new file mode 100644 index 00000000000000..31dca6bb40c906 --- /dev/null +++ b/playground/vue-legacy/env.d.ts @@ -0,0 +1 @@ +declare module '*.png' diff --git a/playground/vue-legacy/index.html b/playground/vue-legacy/index.html new file mode 100644 index 00000000000000..0f7b79435ed47d --- /dev/null +++ b/playground/vue-legacy/index.html @@ -0,0 +1,7 @@ +
+ diff --git a/playground/vue-legacy/inline.css b/playground/vue-legacy/inline.css new file mode 100644 index 00000000000000..2207a25763ca6d --- /dev/null +++ b/playground/vue-legacy/inline.css @@ -0,0 +1,3 @@ +.inline-css { + color: #0088ff; +} diff --git a/playground/vue-legacy/module.vue b/playground/vue-legacy/module.vue new file mode 100644 index 00000000000000..10c7b42e4c4215 --- /dev/null +++ b/playground/vue-legacy/module.vue @@ -0,0 +1,13 @@ + + diff --git a/playground/vue-legacy/package.json b/playground/vue-legacy/package.json new file mode 100644 index 00000000000000..77b810bb28a164 --- /dev/null +++ b/playground/vue-legacy/package.json @@ -0,0 +1,18 @@ +{ + "name": "test-vue-legacy", + "private": true, + "version": "0.0.0", + "scripts": { + "dev": "vite", + "build": "vite build", + "debug": "node --inspect-brk ../../packages/vite/bin/vite", + "preview": "vite preview" + }, + "dependencies": { + "vue": "^3.2.41" + }, + "devDependencies": { + "@vitejs/plugin-vue": "workspace:*", + "@vitejs/plugin-legacy": "workspace:*" + } +} diff --git a/playground/vue-legacy/vite.config.ts b/playground/vue-legacy/vite.config.ts new file mode 100644 index 00000000000000..5bb2f0efa06f53 --- /dev/null +++ b/playground/vue-legacy/vite.config.ts @@ -0,0 +1,35 @@ +import path from 'node:path' +import fs from 'node:fs' +import { defineConfig } from 'vite' +import vuePlugin from '@vitejs/plugin-vue' +import legacyPlugin from '@vitejs/plugin-legacy' + +export default defineConfig({ + base: '', + resolve: { + alias: { + '@': __dirname + } + }, + plugins: [ + legacyPlugin({ + targets: ['defaults', 'not IE 11', 'chrome > 48'] + }), + vuePlugin() + ], + build: { + minify: false + }, + // special test only hook + // for tests, remove `