diff --git a/.github/renovate.json5 b/.github/renovate.json5 index 763dfcfc467d9d..06fe0e7ea2aaff 100644 --- a/.github/renovate.json5 +++ b/.github/renovate.json5 @@ -28,6 +28,7 @@ "react-router", // `react-router:v6.0.0+` has breaking changes "react-router-dom", // `react-router-dom:v6.0.0+` has breaking changes "source-map", // `source-map:v0.7.0+` needs more investigation + "dotenv-expand", // `dotenv-expand:6.0.0+` has breaking changes (#6858) // ESM Only => https://gist.github.com/sindresorhus/a39789f98801d908bbc7ff3ecc99d99c#how-can-i-move-my-commonjs-project-to-esm "node-fetch", diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 3954ca49c11479..dfccbea368c449 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -36,7 +36,7 @@ jobs: name: "Build&Test: node-${{ matrix.node_version }}, ${{ matrix.os }}" steps: - name: Checkout - uses: actions/checkout@v2 + uses: actions/checkout@v3 - name: Install pnpm uses: pnpm/action-setup@v2 @@ -44,7 +44,7 @@ jobs: version: 6 - name: Set node version to ${{ matrix.node_version }} - uses: actions/setup-node@v2 + uses: actions/setup-node@v3 with: node-version: ${{ matrix.node_version }} cache: "pnpm" @@ -72,7 +72,7 @@ jobs: runs-on: ubuntu-latest name: "Lint: node-16, ubuntu-latest" steps: - - uses: actions/checkout@v2 + - uses: actions/checkout@v3 with: fetch-depth: 0 @@ -82,7 +82,7 @@ jobs: version: 6 - name: Set node version to 16 - uses: actions/setup-node@v2 + uses: actions/setup-node@v3 with: node-version: 16 cache: "pnpm" diff --git a/.github/workflows/issue-close-require.yml b/.github/workflows/issue-close-require.yml index 02ac374b06c426..97f9dd3a449c3c 100644 --- a/.github/workflows/issue-close-require.yml +++ b/.github/workflows/issue-close-require.yml @@ -9,7 +9,7 @@ jobs: runs-on: ubuntu-latest steps: - name: need reproduction - uses: actions-cool/issues-helper@v2 + uses: actions-cool/issues-helper@v3 with: actions: "close-issues" token: ${{ secrets.GITHUB_TOKEN }} diff --git a/.github/workflows/issue-labeled.yml b/.github/workflows/issue-labeled.yml index a63021952fd73d..b6f3919dfa63a2 100644 --- a/.github/workflows/issue-labeled.yml +++ b/.github/workflows/issue-labeled.yml @@ -10,7 +10,7 @@ jobs: steps: - name: contribution welcome if: github.event.label.name == 'contribution welcome' || github.event.label.name == 'help wanted' - uses: actions-cool/issues-helper@v2 + uses: actions-cool/issues-helper@v3 with: actions: "create-comment, remove-labels" token: ${{ secrets.GITHUB_TOKEN }} @@ -21,7 +21,7 @@ jobs: - name: remove pending if: github.event.label.name == 'enhancement' || github.event.label.name == 'bug' || (contains(github.event.label.name, 'pending triage') == false && startsWith(github.event.label.name, 'bug:') == true) - uses: actions-cool/issues-helper@v2 + uses: actions-cool/issues-helper@v3 with: actions: "remove-labels" token: ${{ secrets.GITHUB_TOKEN }} @@ -30,7 +30,7 @@ jobs: - name: need reproduction if: github.event.label.name == 'need reproduction' - uses: actions-cool/issues-helper@v2 + uses: actions-cool/issues-helper@v3 with: actions: "create-comment, remove-labels" token: ${{ secrets.GITHUB_TOKEN }} diff --git a/.github/workflows/lock-closed-issues.yml b/.github/workflows/lock-closed-issues.yml index e20c3e3c8e8138..1f24f316952ea0 100644 --- a/.github/workflows/lock-closed-issues.yml +++ b/.github/workflows/lock-closed-issues.yml @@ -11,11 +11,11 @@ jobs: action: runs-on: ubuntu-latest steps: - - uses: dessant/lock-threads@v2 + - uses: dessant/lock-threads@v3 with: github-token: ${{ secrets.GITHUB_TOKEN }} - issue-lock-inactive-days: "14" - #issue-lock-comment: | + issue-inactive-days: "14" + #issue-comment: | # This issue has been locked since it has been closed for more than 14 days. # # If you have found a concrete bug or regression related to it, please open a new [bug report](https://github.com/vitejs/vite/issues/new/choose) with a reproduction against the latest Vite version. If you have any other comments you should join the chat at [Vite Land](https://chat.vitejs.dev) or create a new [discussion](https://github.com/vitejs/vite/discussions). diff --git a/.github/workflows/publish.yml b/.github/workflows/publish.yml index eb8f4513707872..c58c28adac7b40 100644 --- a/.github/workflows/publish.yml +++ b/.github/workflows/publish.yml @@ -15,7 +15,7 @@ jobs: environment: Release steps: - name: Checkout - uses: actions/checkout@v2 + uses: actions/checkout@v3 - name: Install pnpm uses: pnpm/action-setup@v2 @@ -23,7 +23,7 @@ jobs: version: 6 - name: Set node version to 16.x - uses: actions/setup-node@v2 + uses: actions/setup-node@v3 with: node-version: 16.x registry-url: https://registry.npmjs.org/ diff --git a/.github/workflows/release-tag.yml b/.github/workflows/release-tag.yml index f9d5b8de30bec9..21af4792d4741a 100644 --- a/.github/workflows/release-tag.yml +++ b/.github/workflows/release-tag.yml @@ -13,7 +13,7 @@ jobs: release: runs-on: ubuntu-latest steps: - - uses: actions/checkout@v2 + - uses: actions/checkout@v3 - name: Get pkgName for tag id: tag diff --git a/.npmrc b/.npmrc new file mode 100644 index 00000000000000..4abbb42e5ef159 --- /dev/null +++ b/.npmrc @@ -0,0 +1,7 @@ +hoist-pattern[]=*eslint* +hoist-pattern[]=*babel* +hoist-pattern[]=*jest* +hoist-pattern[]=@emotion/* +hoist-pattern[]=postcss +hoist-pattern[]=pug +hoist-pattern[]=source-map-support diff --git a/.prettierignore b/.prettierignore index 1692b9d26cfa20..c624a3a21eecfe 100644 --- a/.prettierignore +++ b/.prettierignore @@ -10,3 +10,4 @@ pnpm-lock.yaml pnpm-workspace.yaml packages/playground/tsconfig-json-load-error/has-error/tsconfig.json packages/playground/html/invalid.html +packages/playground/worker/classic-worker.js diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 5b8f1fed7192c3..fdae54c968c964 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -10,10 +10,42 @@ To develop and test the core `vite` package: 1. Run `pnpm i` in Vite's root folder -2. Go to `packages/vite` and run `pnpm run dev`. This starts `rollup` in watch mode. +2. Run `pnpm run build` in Vite's root folder. + +3. If you are developing Vite itself, you can go to `packages/vite` and run `pnpm run dev` to automatically rebuild Vite whenever you change its code. You can alternatively use [Vite.js Docker Dev](https://github.com/nystudio107/vitejs-docker-dev) for a containerized Docker setup for Vite.js development. +## Debugging + +If you want to use break point and explore code execution you can use the ["Run and debug"](https://code.visualstudio.com/docs/editor/debugging) feature from vscode. + +1. Add a `debugger` statement where you want to stop the code execution. + +2. Click on the "Run and Debug" icon in the activity bar of the editor. + +3. Click on the "Javascript Debug Terminal" button. + +4. It will open a terminal, then go to `packages/playground/xxx` and run `pnpm run dev`. + +5. The execution will stop and you'll use the [Debug toolbar](https://code.visualstudio.com/docs/editor/debugging#_debug-actions) to continue, step over, restart the process... + +### Debugging errors in Jest tests using Playwright (Chromium) + +Some errors are masked and hidden away because of the layers of abstraction and sandboxed nature added by Jest, Playwright, and Chromium. In order to see what's actually going wrong and the contents of the devtools console in those instances, follow this setup: + +1. Add a `debugger` statement to the `scripts/jestPerTestSetup.ts` -> `afterAll` hook. This will pause execution before the tests quit and the Playwright browser instance exits. + +1. Run the tests with the `debug-serve` script command which will enable remote debugging: `pnpm run debug-serve -- --runInBand resolve`. + +1. Wait for inspector devtools to open in your browser and the debugger to attach. + +1. In the sources panel in the right column, click the play button to resume execution and allow the tests to run which will open a Chromium instance. + +1. Focusing the Chomium instance, you can open the browser devtools and inspect the console there to find the underlying problems. + +1. To close everything, just stop the test process back in your terminal. + ## Testing Vite against external packages You may wish to test your locally-modified copy of Vite against another package that is built with Vite. For pnpm, after building Vite, you can use [`pnpm.overrides`](https://pnpm.io/package_json#pnpmoverrides). Please note that `pnpm.overrides` must be specified in the root `package.json` and you must first list the package as a dependency in the root `package.json`: @@ -37,7 +69,7 @@ And re-run `pnpm install` to link the package. Each package under `packages/playground/` contains a `__tests__` directory. The tests are run using [Jest](https://jestjs.io/) + [Playwright](https://playwright.dev/) with custom integrations to make writing tests simple. The detailed setup is inside `jest.config.js` and `scripts/jest*` files. -Each test can be run under either dev server mode or build mode. +Each test can be run under either dev server mode or build mode. Make sure that [Vite has been built](#repo-setup). - `pnpm test` by default runs every test in both serve and build mode. diff --git a/docs/config/index.md b/docs/config/index.md index ba84c49b2f5820..905ffae7a89cea 100644 --- a/docs/config/index.md +++ b/docs/config/index.md @@ -27,7 +27,8 @@ You can also explicitly specify a config file to use with the `--config` CLI opt vite --config my-config.js ``` -Note that Vite will replace `__filename`, `__dirname`, and `import.meta.url`. Using these as variable names will result in an error: +::: tip NOTE +Vite will replace `__filename`, `__dirname`, and `import.meta.url` in **CommonJS** and **TypeScript** config files. Using these as variable names will result in an error: ```js const __filename = "value" @@ -35,6 +36,8 @@ const __filename = "value" const "path/vite.config.js" = "value" ``` +::: + ### Config Intellisense Since Vite ships with TypeScript typings, you can leverage your IDE's intellisense with jsdoc type hints: @@ -96,6 +99,22 @@ export default defineConfig(async ({ command, mode }) => { }) ``` +### Environment Variables + +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. + +```js +import { defineConfig, loadEnv } from 'vite' + +export default defineConfig(({ command, mode }) => { + // Load env file based on `mode` in the current working directory + const env = loadEnv(mode, process.cwd()) + return { + // build specific config + } +}) +``` + ## Shared Options ### root @@ -139,9 +158,11 @@ export default defineConfig(async ({ command, mode }) => { - Replacements are performed only when the match is surrounded by word boundaries (`\b`). + ::: warning Because it's implemented as straightforward text replacements without any syntax analysis, we recommend using `define` for CONSTANTS only. For example, `process.env.FOO` and `__APP_VERSION__` are good fits. But `process` or `global` should not be put into this option. Variables can be shimmed or polyfilled instead. + ::: ::: tip NOTE For TypeScript users, make sure to add the type declarations in the `env.d.ts` or `vite-env.d.ts` file to get type checks and Intellisense. @@ -223,6 +244,10 @@ export default defineConfig(async ({ command, mode }) => { Vite has a list of "allowed conditions" and will match the first condition that is in the allowed list. The default allowed conditions are: `import`, `module`, `browser`, `default`, and `production/development` based on current mode. The `resolve.conditions` config option allows specifying additional allowed conditions. + :::warning Resolving subpath exports + Export keys ending with "/" is deprecated by Node and may not work well. Please contact the package author to use [`*` subpath patterns](https://nodejs.org/api/packages.html#package-entry-points) instead. + ::: + ### resolve.mainFields - **Type:** `string[]` @@ -277,7 +302,7 @@ export default defineConfig(async ({ command, mode }) => { - **Type:** `string | (postcss.ProcessOptions & { plugins?: postcss.Plugin[] })` - Inline PostCSS config (expects the same format as `postcss.config.js`), or a custom path to search PostCSS config from (default is project root). The search is done using [postcss-load-config](https://github.com/postcss/postcss-load-config). + Inline PostCSS config (expects the same format as `postcss.config.js`), or a custom directory to search PostCSS config from (default is project root). The search is done using [postcss-load-config](https://github.com/postcss/postcss-load-config) and only the supported config file names are loaded. Note if an inline config is provided, Vite will not search for other PostCSS config sources. @@ -285,7 +310,7 @@ export default defineConfig(async ({ command, mode }) => { - **Type:** `Record` - Specify options to pass to CSS pre-processors. Example: + Specify options to pass to CSS pre-processors. The file extensions are used as keys for the options. Example: ```js export default defineConfig({ @@ -293,6 +318,9 @@ export default defineConfig(async ({ command, mode }) => { preprocessorOptions: { scss: { additionalData: `$injectedColor: orange;` + }, + styl: { + additionalData: `$injectedColor ?= orange` } } } @@ -330,7 +358,7 @@ export default defineConfig(async ({ command, mode }) => { }) ``` - By default, ESBuild is applied to `ts`, `jsx` and `tsx` files. You can customize this with `esbuild.include` and `esbuild.exclude`, both of which expect type of `string | RegExp | (string | RegExp)[]`. + By default, ESBuild is applied to `ts`, `jsx` and `tsx` files. You can customize this with `esbuild.include` and `esbuild.exclude`, which can be a regex, a [picomatch](https://github.com/micromatch/picomatch#globbing-features) pattern, or an array of either. In addition, you can also use `esbuild.jsxInject` to automatically inject JSX helper imports for every file transformed by ESBuild: @@ -349,7 +377,7 @@ export default defineConfig(async ({ command, mode }) => { - **Type:** `string | RegExp | (string | RegExp)[]` - **Related:** [Static Asset Handling](/guide/assets) - Specify additional [picomatch patterns](https://github.com/micromatch/picomatch) to be treated as static assets so that: + Specify additional [picomatch patterns](https://github.com/micromatch/picomatch#globbing-features) to be treated as static assets so that: - They will be excluded from the plugin transform pipeline when referenced from HTML or directly requested over `fetch` or XHR. @@ -499,6 +527,12 @@ export default defineConfig(async ({ command, mode }) => { 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. +### server.headers + +- **Type:** `OutgoingHttpHeaders` + + Specify server response headers. + ### server.force - **Type:** `boolean` @@ -508,15 +542,17 @@ export default defineConfig(async ({ command, mode }) => { ### server.hmr -- **Type:** `boolean | { protocol?: string, host?: string, port?: number, path?: string, timeout?: number, overlay?: boolean, clientPort?: number, server?: Server }` +- **Type:** `boolean | { protocol?: string, host?: string, port?: number | false, path?: string, timeout?: number, overlay?: boolean, clientPort?: number, server?: Server }` Disable or configure HMR connection (in cases where the HMR websocket must use a different address from the http server). Set `server.hmr.overlay` to `false` to disable the server error overlay. + Set `server.hmr.port` to `false` when connecting to a domain without a port. + `clientPort` is an advanced option that overrides the port only on the client side, allowing you to serve the websocket on a different port than the client code looks for it on. Useful if you're using an SSL proxy in front of your dev server. - When using `server.middlewareMode` or `server.https`, assigning `server.hmr.server` to your HTTP(S) server will process HMR connection requests through your server. This can be helpful when using self-signed certificates or when you want to expose Vite over a network on a single port. + If specifying `server.hmr.server`, Vite will process HMR connection requests through the provided server. If not in middleware mode, Vite will attempt to process HMR connection requests through the existing server. This can be helpful when using self-signed certificates or when you want to expose Vite over a network on a single port. ### server.watch @@ -580,6 +616,12 @@ async function createServer() { createServer() ``` +### server.base + +- **Type:** `string | undefined` + + Prepend this folder to http requests, for use when proxying vite as a subfolder. Should start and end with the `/` character. + ### server.fs.strict - **Type:** `boolean` @@ -923,7 +965,7 @@ export default defineConfig({ - **Type:** `string | string[]` - By default, Vite will crawl your index.html to detect dependencies that need to be pre-bundled. If build.rollupOptions.input is specified, Vite will crawl those entry points instead. + By default, Vite will crawl your `index.html` to detect dependencies that need to be pre-bundled. 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. diff --git a/docs/guide/api-javascript.md b/docs/guide/api-javascript.md index 1651923c06cdcb..ddaa04279737f5 100644 --- a/docs/guide/api-javascript.md +++ b/docs/guide/api-javascript.md @@ -94,7 +94,7 @@ interface ViteDevServer { */ ssrLoadModule( url: string, - options?: { isolated?: boolean } + options?: { fixStacktrace?: boolean } ): Promise> /** * Fix ssr error stacktrace. diff --git a/docs/guide/api-plugin.md b/docs/guide/api-plugin.md index 01b42980197473..b3888b0fd7009b 100644 --- a/docs/guide/api-plugin.md +++ b/docs/guide/api-plugin.md @@ -458,7 +458,7 @@ In general, as long as a Rollup plugin fits the following criteria then it shoul - It doesn't use the [`moduleParsed`](https://rollupjs.org/guide/en/#moduleparsed) hook. - It doesn't have strong coupling between bundle-phase hooks and output-phase hooks. -If a Rollup plugin only makes sense for the build phase, then it can be specified under `build.rollupOptions.plugins` instead. +If a Rollup plugin only makes sense for the build phase, then it can be specified under `build.rollupOptions.plugins` instead. It will work the same as a Vite plugin with `enforce: 'post'` and `apply: 'build'`. You can also augment an existing Rollup plugin with Vite-only properties: diff --git a/docs/guide/backend-integration.md b/docs/guide/backend-integration.md index a30831e735fff7..55a4b87f1ac481 100644 --- a/docs/guide/backend-integration.md +++ b/docs/guide/backend-integration.md @@ -33,7 +33,6 @@ If you need a custom integration, you can follow the steps in this guide to conf ```html - ``` diff --git a/docs/guide/build.md b/docs/guide/build.md index aac86a237b6819..8216bcbfbac060 100644 --- a/docs/guide/build.md +++ b/docs/guide/build.md @@ -43,6 +43,20 @@ module.exports = defineConfig({ For example, you can specify multiple Rollup outputs with plugins that are only applied during build. +## Chunking Strategy + +You can configure how chunks are split using `build.rollupOptions.output.manualChunks` (see [Rollup docs](https://rollupjs.org/guide/en/#outputmanualchunks)). Until Vite 2.8, the default chunking strategy divided the chunks into `index` and `vendor`. It is a good strategy for some SPAs, but it is hard to provide a general solution for every Vite target use case. From Vite 2.9, `manualChunks` is no longer modified by default. You can continue to use the Split Vendor Chunk strategy by adding the `splitVendorChunkPlugin` in your config file: + +```js +// vite.config.js +import { splitVendorChunkPlugin } from 'vite' +module.exports = defineConfig({ + plugins: [splitVendorChunkPlugin()] +}) +``` + +This strategy is also provided as a `splitVendorChunk({ cache: SplitVendorChunkCache })` factory, in case composition with custom logic is needed. `cache.reset()` needs to be called at `buildStart` for build watch mode to work correctly in this case. + ## Rebuild on files changes You can enable rollup watcher with `vite build --watch`. Or, you can directly adjust the underlying [`WatcherOptions`](https://rollupjs.org/guide/en/#watch-options) via `build.watch`: @@ -58,6 +72,8 @@ module.exports = defineConfig({ }) ``` +With the `--watch` flag enabled, changes to the `vite.config.js`, as well as any files to be bundled, will trigger a rebuild. + ## Multi-Page App Suppose you have the following source code structure: @@ -129,6 +145,15 @@ module.exports = defineConfig({ }) ``` +The entry file would contain exports that can be imported by users of your package: + +```js +// lib/main.js +import Foo from './Foo.vue' +import Bar from './Bar.vue' +export { Foo, Bar } +``` + Running `vite build` with this config uses a Rollup preset that is oriented towards shipping libraries and produces two bundle formats: `es` and `umd` (configurable via `build.lib`): ``` diff --git a/docs/guide/dep-pre-bundling.md b/docs/guide/dep-pre-bundling.md index 6864ef55899eba..0afce072e9fcc3 100644 --- a/docs/guide/dep-pre-bundling.md +++ b/docs/guide/dep-pre-bundling.md @@ -3,10 +3,10 @@ When you run `vite` for the first time, you may notice this message: ``` -Optimizable dependencies detected: -react, react-dom -Pre-bundling them to speed up dev server page load... -(this will be run only when your dependencies have changed) +Pre-bundling dependencies: + react + react-dom +(this will be run only when your dependencies or config have changed) ``` ## The Why @@ -28,6 +28,10 @@ This is Vite performing what we call "dependency pre-bundling". This process ser By pre-bundling `lodash-es` into a single module, we now only need one HTTP request instead! +::: tip NOTE +Dependency pre-bundling only applies in development mode, and uses `esbuild` to convert dependencies to ESM. In production builds, `@rollup/plugin-commonjs` is used instead. +::: + ## Automatic Dependency Discovery If an existing cache is not found, Vite will crawl your source code and automatically discover dependency imports (i.e. "bare imports" that expect to be resolved from `node_modules`) and use these found imports as entry points for the pre-bundle. The pre-bundling is performed with `esbuild` so it's typically very fast. @@ -36,11 +40,27 @@ After the server has already started, if a new dependency import is encountered ## Monorepos and Linked Dependencies -In a monorepo setup, a dependency may be a linked package from the same repo. Vite automatically detects dependencies that are not resolved from `node_modules` and treats the linked dep as source code. It will not attempt to bundle the linked dep, and instead will analyze the linked dep's dependency list instead. +In a monorepo setup, a dependency may be a linked package from the same repo. Vite automatically detects dependencies that are not resolved from `node_modules` and treats the linked dep as source code. It will not attempt to bundle the linked dep, and will analyze the linked dep's dependency list instead. + +However, this requires the linked dep to be exported as ESM. If not, you can add the dependency to [`optimizeDeps.include`](/config/#optimizedeps-include) and [`build.commonjsOptions.include`](/config/#build-commonjsoptions) in your config. + +```js +export default defineConfig({ + optimizeDeps: { + include: ['linked-dep'] + }, + build: { + commonjsOptions: { + include: [/linked-dep/, /node_modules/] + } + } +}) +``` + +When making changes to the linked dep, restart the dev server with the `--force` command line option for the changes to take effect. -::: warning Note -Linked dependencies might not work properly in the final build due to differences in dependency resolution. -Use `npm package` instead for all local dependencies to avoid issues in the final bundle. +::: 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. ::: ## Customizing the Behavior @@ -57,7 +77,7 @@ Both `include` and `exclude` can be used to deal with this. If the dependency is Vite caches the pre-bundled dependencies in `node_modules/.vite`. It determines whether it needs to re-run the pre-bundling step based on a few sources: -- The `dependencies` list in your `package.json` +- The `dependencies` list in your `package.json`. - Package manager lockfiles, e.g. `package-lock.json`, `yarn.lock`, or `pnpm-lock.yaml`. - Relevant fields in your `vite.config.js`, if present. diff --git a/docs/guide/features.md b/docs/guide/features.md index da8e3b1de092b2..ebed85cd2d529d 100644 --- a/docs/guide/features.md +++ b/docs/guide/features.md @@ -298,10 +298,26 @@ const modules = { } ``` +`import.meta.glob` and `import.meta.globEager` also support importing files as strings (similar to [Importing Asset as String](https://vitejs.dev/guide/assets.html#importing-asset-as-string)) with the [Import Reflection](https://github.com/tc39/proposal-import-reflection) syntax: + +```js +const modules = import.meta.glob('./dir/*.js', { as: 'raw' }) +``` + +The above will be transformed into the following: + +```js +// code produced by vite +const modules = { + './dir/foo.js': '{\n "msg": "foo"\n}\n', + './dir/bar.js': '{\n "msg": "bar"\n}\n' +} +``` + Note that: - This is a Vite-only feature and is not a web or ES standard. -- The glob patterns are treated like import specifiers: they must be either relative (start with `./`) or absolute (start with `/`, resolved relative to project root). +- The glob patterns are treated like import specifiers: they must be either relative (start with `./`) or absolute (start with `/`, resolved relative to project root) or an alias path (see [`resolve.alias` option](/config/#resolve-alias)). - The glob matching is done via `fast-glob` - check out its documentation for [supported glob patterns](https://github.com/mrmlnc/fast-glob#pattern-syntax). - You should also be aware that glob imports do not accept variables, you need to directly pass the string pattern. - The glob patterns cannot contain the same quote string (i.e. `'`, `"`, `` ` ``) as outer quotes, e.g. `'/Tom\'s files/**'`, use `"/Tom's files/**"` instead. @@ -336,6 +352,24 @@ In the production build, `.wasm` files smaller than `assetInlineLimit` will be i ## Web Workers +### Import with Constructors + +A web worker script can be imported using [`new Worker()`](https://developer.mozilla.org/en-US/docs/Web/API/Worker/Worker) and [`new SharedWorker()`](https://developer.mozilla.org/en-US/docs/Web/API/SharedWorker/SharedWorker). Compared to the worker suffixes, this syntax leans closer to the standards and is the **recommended** way to create workers. + +```ts +const worker = new Worker(new URL('./worker.js', import.meta.url)) +``` + +The worker constructor also accepts options, which can be used to create "module" workers: + +```ts +const worker = new Worker(new URL('./worker.js', import.meta.url), { + type: 'module' +}) +``` + +### Import with Query Suffixes + A web worker script can be directly imported by appending `?worker` or `?sharedworker` to the import request. The default export will be a custom worker constructor: ```js @@ -352,6 +386,8 @@ By default, the worker script will be emitted as a separate chunk in the product import MyWorker from './worker?worker&inline' ``` +See [Worker Options](/config/#worker-options) for details on configuring the bundling of all workers. + ## Build Optimizations > Features listed below are automatically applied as part of the build process and there is no need for explicit configuration unless you want to disable them. diff --git a/docs/guide/why.md b/docs/guide/why.md index 59e9c138eb7dca..89ca7698394246 100644 --- a/docs/guide/why.md +++ b/docs/guide/why.md @@ -32,7 +32,7 @@ Vite improves the dev server start time by first dividing the modules in an appl When a file is edited in a bundler-based build setup, it is inefficient to rebuild the whole bundle for obvious reasons: the update speed will degrade linearly with the size of the app. -Some bundler dev server runs the bundling in memory so that it only needs to invalidate part of its module graph when a file changes, but it still needs to re-construct the entire bundle and reload the web page. Reconstructing the bundle can be expensive, and reloading the page blows away the current state of the application. This is why some bundlers support Hot Module Replacement (HMR): allowing a module to "hot replace" itself without affecting the rest of the page. This greatly improves DX - however, in practice we've found that even HMR update speed deteriorates significantly as the size of the application grows. +In some bundlers, the dev server runs the bundling in memory so that it only needs to invalidate part of its module graph when a file changes, but it still needs to re-construct the entire bundle and reload the web page. Reconstructing the bundle can be expensive, and reloading the page blows away the current state of the application. This is why some bundlers support Hot Module Replacement (HMR): allowing a module to "hot replace" itself without affecting the rest of the page. This greatly improves DX - however, in practice we've found that even HMR update speed deteriorates significantly as the size of the application grows. In Vite, HMR is performed over native ESM. When a file is edited, Vite only needs to precisely invalidate the chain between the edited module and its closest HMR boundary (most of the time only the module itself), making HMR updates consistently fast regardless of the size of your application. diff --git a/jest.config.ts b/jest.config.ts index 7d4831524d01c3..11663af4e08107 100644 --- a/jest.config.ts +++ b/jest.config.ts @@ -6,7 +6,7 @@ const config: Config.InitialOptions = { testMatch: process.env.VITE_TEST_BUILD ? ['**/playground/**/*.spec.[jt]s?(x)'] : ['**/*.spec.[jt]s?(x)'], - testTimeout: process.env.CI ? 30000 : 10000, + testTimeout: process.env.CI ? 50000 : 20000, globalSetup: './scripts/jestGlobalSetup.cjs', globalTeardown: './scripts/jestGlobalTeardown.cjs', testEnvironment: './scripts/jestEnv.cjs', diff --git a/package.json b/package.json index 72b7076ee65d01..858e7502d53324 100644 --- a/package.json +++ b/package.json @@ -34,30 +34,30 @@ "ci-docs": "run-s build-vite build-plugin-vue build-docs" }, "devDependencies": { - "@microsoft/api-extractor": "^7.19.4", + "@microsoft/api-extractor": "^7.19.5", "@types/fs-extra": "^9.0.13", - "@types/jest": "^27.4.0", - "@types/node": "^16.11.22", + "@types/jest": "^27.4.1", + "@types/node": "^16.11.26", "@types/prompts": "^2.0.14", "@types/semver": "^7.3.9", - "@typescript-eslint/eslint-plugin": "^5.11.0", - "@typescript-eslint/parser": "^5.11.0", + "@typescript-eslint/eslint-plugin": "^5.16.0", + "@typescript-eslint/parser": "^5.16.0", "conventional-changelog-cli": "^2.2.2", "cross-env": "^7.0.3", - "esbuild": "^0.14.14", - "eslint": "^8.8.0", - "eslint-define-config": "^1.2.4", + "esbuild": "^0.14.27", + "eslint": "^8.11.0", + "eslint-define-config": "^1.3.0", "eslint-plugin-node": "^11.1.0", "execa": "^5.1.1", - "fs-extra": "^10.0.0", + "fs-extra": "^10.0.1", "jest": "^27.5.1", - "lint-staged": "^12.3.3", + "lint-staged": "^12.3.7", "minimist": "^1.2.5", "node-fetch": "^2.6.6", "npm-run-all": "^4.1.5", "picocolors": "^1.0.0", - "playwright-chromium": "^1.18.1", - "prettier": "2.5.1", + "playwright-chromium": "^1.20.0", + "prettier": "2.6.0", "prompts": "^2.4.2", "rimraf": "^3.0.2", "rollup": "^2.59.0", @@ -68,7 +68,7 @@ "ts-node": "^10.4.0", "typescript": "~4.5.4", "vite": "workspace:*", - "vitepress": "^0.21.6" + "vitepress": "^0.22.3" }, "simple-git-hooks": { "pre-commit": "pnpm exec lint-staged --concurrent false", @@ -85,7 +85,7 @@ "eslint --ext .ts" ] }, - "packageManager": "pnpm@6.30.0", + "packageManager": "pnpm@6.32.3", "pnpm": { "overrides": { "vite": "workspace:*", diff --git a/packages/create-vite/template-vue-ts/README.md b/packages/create-vite/template-vue-ts/README.md index f5342b7d8f66dd..b53dcfb1a715e6 100644 --- a/packages/create-vite/template-vue-ts/README.md +++ b/packages/create-vite/template-vue-ts/README.md @@ -8,4 +8,9 @@ This template should help get you started developing with Vue 3 and Typescript i ## Type Support For `.vue` Imports in TS -Since TypeScript cannot handle type information for `.vue` imports, they are shimmed to be a generic Vue component type by default. In most cases this is fine if you don't really care about component prop types outside of templates. However, if you wish to get actual prop types in `.vue` imports (for example to get props validation when using manual `h(...)` calls), you can enable Volar's `.vue` type support plugin by running `Volar: Switch TS Plugin on/off` from VSCode command palette. +Since TypeScript cannot handle type information for `.vue` imports, they are shimmed to be a generic Vue component type by default. In most cases this is fine if you don't really care about component prop types outside of templates. However, if you wish to get actual prop types in `.vue` imports (for example to get props validation when using manual `h(...)` calls), you can enable Volar's Take Over mode by following these steps: + +1. Run `Extensions: Show Built-in Extensions` from VSCode's command palette, look for `TypeScript and JavaScript Language Features`, then right click and select `Disable (Workspace)`. By default, Take Over mode will enable itself if the default TypeScript extension is disabled. +2. Reload the VSCode window by running `Developer: Reload Window` from the command palette. + +You can learn more about Take Over mode [here](https://github.com/johnsoncodehk/volar/discussions/471). diff --git a/packages/create-vite/template-vue-ts/tsconfig.json b/packages/create-vite/template-vue-ts/tsconfig.json index af31eb8152d3cb..52205ea0029c99 100644 --- a/packages/create-vite/template-vue-ts/tsconfig.json +++ b/packages/create-vite/template-vue-ts/tsconfig.json @@ -8,6 +8,7 @@ "jsx": "preserve", "sourceMap": true, "resolveJsonModule": true, + "isolatedModules": true, "esModuleInterop": true, "lib": ["esnext", "dom"] }, diff --git a/packages/playground/assets/__tests__/assets.spec.ts b/packages/playground/assets/__tests__/assets.spec.ts index 8c66588d50716d..f53c783c52b606 100644 --- a/packages/playground/assets/__tests__/assets.spec.ts +++ b/packages/playground/assets/__tests__/assets.spec.ts @@ -8,7 +8,8 @@ import { readManifest, readFile, editFile, - notifyRebuildComplete + notifyRebuildComplete, + untilUpdated } from '../../testUtils' const assetMatch = isBuild @@ -37,7 +38,7 @@ describe('injected scripts', () => { test('html-proxy', async () => { const hasHtmlProxy = await page.$( - 'script[type="module"][src="/foo/index.html?html-proxy&index=0.js"]' + 'script[type="module"][src^="/foo/index.html?html-proxy"]' ) if (isBuild) { expect(hasHtmlProxy).toBeFalsy() @@ -120,6 +121,10 @@ describe('css url() references', () => { const match = isBuild ? `data:image/png;base64` : `/foo/nested/icon.png` expect(await getBg('.css-url-base64-inline')).toMatch(match) expect(await getBg('.css-url-quotes-base64-inline')).toMatch(match) + const icoMatch = isBuild ? `data:image/x-icon;base64` : `favicon.ico` + const el = await page.$(`link.ico`) + const herf = await el.getAttribute('href') + expect(herf).toMatch(icoMatch) }) test('multiple urls on the same line', async () => { @@ -238,6 +243,12 @@ test('new URL(`${dynamic}`, import.meta.url)', async () => { ) }) +test('new URL(`non-existent`, import.meta.url)', async () => { + expect(await page.textContent('.non-existent-import-meta-url')).toMatch( + '/foo/non-existent' + ) +}) + if (isBuild) { test('manifest', async () => { const manifest = readManifest('foo') @@ -264,3 +275,15 @@ describe('css and assets in css in build watch', () => { }) } }) + +if (!isBuild) { + test('@import in html style tag hmr', async () => { + await untilUpdated(() => getColor('.import-css'), 'rgb(0, 136, 255)') + editFile( + './css/import.css', + (code) => code.replace('#0088ff', '#00ff88'), + true + ) + await untilUpdated(() => getColor('.import-css'), 'rgb(0, 255, 136)') + }) +} diff --git a/packages/playground/assets/favicon.ico b/packages/playground/assets/favicon.ico new file mode 100644 index 00000000000000..d75d248ef0b150 Binary files /dev/null and b/packages/playground/assets/favicon.ico differ diff --git a/packages/playground/assets/index.html b/packages/playground/assets/index.html index 66753240c8a54e..7534ecbe1677bf 100644 --- a/packages/playground/assets/index.html +++ b/packages/playground/assets/index.html @@ -2,6 +2,7 @@ + @@ -140,6 +141,14 @@

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

+

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

+ + + +

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

+ + +

new URL(`./${dynamic}`, import.meta.url)

@@ -150,6 +159,21 @@

new URL(`./${dynamic}`, import.meta.url)

+

new URL(`./${dynamic}`, import.meta.url,) (with comma)

+

+ + +

+

+ + +

+ +

new URL(`non-existent`, import.meta.url)

+

+ +

+

simple script tag import-expression

+ + + ", + ], + "version": 3, + } + `) + }) + + 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 lines = css.split('\n') + expect(lines[lines.length - 1].includes('/*')).toBe(false) // expect no sourcemap + }) + + 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(` + Object { + "mappings": "AAAA;EACE,UAAU;AACZ;;ACAA;EACE,UAAU;AACZ", + "sources": Array [ + "/root/be-imported.css", + "/root/linked-with-import.css", + ], + "sourcesContent": Array [ + ".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(` + Object { + "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": Array [ + "/root/imported.css", + ], + "sourcesContent": Array [ + ".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(` + Object { + "mappings": "AAAA;EACE,UAAU;AACZ;;ACAA;EACE,UAAU;AACZ", + "sources": Array [ + "/root/be-imported.css", + "/root/imported-with-import.css", + ], + "sourcesContent": Array [ + ".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 ') + const map = extractSourcemap(css) + expect(formatSourcemapForSnapshot(map)).toMatchInlineSnapshot(` + Object { + "mappings": "AACE;EACE", + "sources": Array [ + "/root/imported.sass", + ], + "sourcesContent": Array [ + ".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(` + Object { + "mappings": "AACE;EACE", + "sources": Array [ + "/root/imported.module.sass", + ], + "sourcesContent": Array [ + ".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(` + Object { + "mappings": "AACE;EACE", + "sources": Array [ + "/root/imported.less", + ], + "sourcesContent": Array [ + ".imported { + &-less { + color: @color; + } + } + ", + ], + "version": 3, + } + `) + }) + + test('imported stylus', async () => { + const css = await getStyleTagContentIncluding('.imported-stylus ') + const map = extractSourcemap(css) + expect(formatSourcemapForSnapshot(map)).toMatchInlineSnapshot(` + Object { + "mappings": "AACE;EACE,cAAM", + "sources": Array [ + "/root/imported.styl", + ], + "sourcesContent": Array [ + ".imported + &-stylus + color blue-red-mixed + ", + ], + "version": 3, + } + `) + }) + + test('should not output missing source file warning', () => { + serverLogs.forEach((log) => { + expect(log).not.toMatch(/Sourcemap for .+ points to missing source files/) + }) + }) +} else { + test('this file only includes test for serve', () => { + expect(true).toBe(true) + }) +} diff --git a/packages/playground/css-sourcemap/be-imported.css b/packages/playground/css-sourcemap/be-imported.css new file mode 100644 index 00000000000000..a29e5f77e3cb5d --- /dev/null +++ b/packages/playground/css-sourcemap/be-imported.css @@ -0,0 +1,3 @@ +.be-imported { + color: red; +} diff --git a/packages/playground/css-sourcemap/imported-with-import.css b/packages/playground/css-sourcemap/imported-with-import.css new file mode 100644 index 00000000000000..6a1ed3c3772698 --- /dev/null +++ b/packages/playground/css-sourcemap/imported-with-import.css @@ -0,0 +1,5 @@ +@import '@/be-imported.css'; + +.imported-with-import { + color: red; +} diff --git a/packages/playground/css-sourcemap/imported.css b/packages/playground/css-sourcemap/imported.css new file mode 100644 index 00000000000000..9c9b32924962dc --- /dev/null +++ b/packages/playground/css-sourcemap/imported.css @@ -0,0 +1,3 @@ +.imported { + color: red; +} diff --git a/packages/playground/css-sourcemap/imported.less b/packages/playground/css-sourcemap/imported.less new file mode 100644 index 00000000000000..e71b15eb102441 --- /dev/null +++ b/packages/playground/css-sourcemap/imported.less @@ -0,0 +1,5 @@ +.imported { + &-less { + color: @color; + } +} diff --git a/packages/playground/css-sourcemap/imported.module.sass b/packages/playground/css-sourcemap/imported.module.sass new file mode 100644 index 00000000000000..448a5e7e31f75a --- /dev/null +++ b/packages/playground/css-sourcemap/imported.module.sass @@ -0,0 +1,3 @@ +.imported + &-sass-module + color: red diff --git a/packages/playground/css-sourcemap/imported.sass b/packages/playground/css-sourcemap/imported.sass new file mode 100644 index 00000000000000..06fa634d5dd4e9 --- /dev/null +++ b/packages/playground/css-sourcemap/imported.sass @@ -0,0 +1,3 @@ +.imported + &-sass + color: red diff --git a/packages/playground/css-sourcemap/imported.styl b/packages/playground/css-sourcemap/imported.styl new file mode 100644 index 00000000000000..83c7cf517acf4d --- /dev/null +++ b/packages/playground/css-sourcemap/imported.styl @@ -0,0 +1,3 @@ +.imported + &-stylus + color blue-red-mixed diff --git a/packages/playground/css-sourcemap/index.html b/packages/playground/css-sourcemap/index.html new file mode 100644 index 00000000000000..a943c1d113a9b4 --- /dev/null +++ b/packages/playground/css-sourcemap/index.html @@ -0,0 +1,45 @@ + + + + + +
+

CSS Sourcemap

+ +

<inline>

+ +

<linked>: no import

+

<linked>: with import

+ +

<imported>: no import

+

<imported>: with import

+ +

<imported sass>

+

<imported sass> with module

+ +

<imported less> with string additionalData

+ +

<imported stylus>

+
+ + + + diff --git a/packages/playground/css-sourcemap/linked-with-import.css b/packages/playground/css-sourcemap/linked-with-import.css new file mode 100644 index 00000000000000..6f65d92441fa49 --- /dev/null +++ b/packages/playground/css-sourcemap/linked-with-import.css @@ -0,0 +1,5 @@ +@import '@/be-imported.css'; + +.linked-with-import { + color: red; +} diff --git a/packages/playground/css-sourcemap/linked.css b/packages/playground/css-sourcemap/linked.css new file mode 100644 index 00000000000000..e3b67c83872ac0 --- /dev/null +++ b/packages/playground/css-sourcemap/linked.css @@ -0,0 +1,3 @@ +.linked { + color: red; +} diff --git a/packages/playground/css-sourcemap/package.json b/packages/playground/css-sourcemap/package.json new file mode 100644 index 00000000000000..c29f18d4dee0d7 --- /dev/null +++ b/packages/playground/css-sourcemap/package.json @@ -0,0 +1,18 @@ +{ + "name": "test-css-sourcemap", + "private": true, + "version": "0.0.0", + "scripts": { + "dev": "vite", + "build": "vite build", + "debug": "node --inspect-brk ../../vite/bin/vite", + "preview": "vite preview" + }, + "devDependencies": { + "convert-source-map": "^1.8.0", + "less": "^4.1.2", + "magic-string": "^0.25.7", + "sass": "^1.43.4", + "stylus": "^0.55.0" + } +} diff --git a/packages/playground/css-sourcemap/vite.config.js b/packages/playground/css-sourcemap/vite.config.js new file mode 100644 index 00000000000000..f743687ddd9b0e --- /dev/null +++ b/packages/playground/css-sourcemap/vite.config.js @@ -0,0 +1,60 @@ +const MagicString = require('magic-string') + +/** + * @type {import('vite').UserConfig} + */ +module.exports = { + resolve: { + alias: { + '@': __dirname + } + }, + css: { + preprocessorOptions: { + less: { + additionalData: '@color: red;' + }, + styl: { + additionalData: (content, filename) => { + const ms = new MagicString(content, { filename }) + + const willBeReplaced = 'blue-red-mixed' + const start = content.indexOf(willBeReplaced) + ms.overwrite(start, start + willBeReplaced.length, 'purple') + + const map = ms.generateMap({ hires: true }) + map.file = filename + map.sources = [filename] + + return { + content: ms.toString(), + map + } + } + } + } + }, + build: { + sourcemap: true + }, + plugins: [ + { + name: 'virtual-html', + configureServer(server) { + server.middlewares.use(async (req, res, next) => { + if (req.url === '/virtual.html') { + const t = await server.transformIndexHtml( + '/virtual.html', + '

virtual html

' + ) + res.setHeader('Content-Type', 'text/html') + res.statusCode = 200 + res.end(t) + return + } + next() + }) + } + } + ] +} diff --git a/packages/playground/css/__tests__/css.spec.ts b/packages/playground/css/__tests__/css.spec.ts index bc2cbfe6a1b57a..34858ab34bc09d 100644 --- a/packages/playground/css/__tests__/css.spec.ts +++ b/packages/playground/css/__tests__/css.spec.ts @@ -13,6 +13,14 @@ import { // note: tests should retrieve the element at the beginning of test and reuse it // in later assertions to ensure CSS HMR doesn't reload the page +test('imported css', async () => { + const css = await page.textContent('.imported-css') + expect(css).toContain('.imported {') + const glob = await page.textContent('.imported-css-glob') + expect(glob).toContain('.dir-import') + const globEager = await page.textContent('.imported-css-globEager') + expect(globEager).toContain('.dir-import') +}) test('linked css', async () => { const linked = await page.$('.linked') @@ -364,3 +372,8 @@ test('?raw', async () => { require('fs').readFileSync(require.resolve('../raw-imported.css'), 'utf-8') ) }) + +test('import css in less', async () => { + expect(await getColor('.css-in-less')).toBe('yellow') + expect(await getColor('.css-in-less-2')).toBe('blue') +}) diff --git a/packages/playground/css/__tests__/postcss-plugins-different-dir.spec.ts b/packages/playground/css/__tests__/postcss-plugins-different-dir.spec.ts new file mode 100644 index 00000000000000..19e9a43ae4ff6e --- /dev/null +++ b/packages/playground/css/__tests__/postcss-plugins-different-dir.spec.ts @@ -0,0 +1,29 @@ +import { getColor, getBgColor } from '../../testUtils' +import { createServer } from 'vite' +import path from 'path' + +// Regression test for https://github.com/vitejs/vite/issues/4000 +test('postcss plugins in different dir', async () => { + const port = 5006 + const server = await createServer({ + root: path.join(__dirname, '..', '..', 'tailwind'), + logLevel: 'silent', + server: { + port, + strictPort: true + }, + build: { + // skip transpilation during tests to make it faster + target: 'esnext' + } + }) + await server.listen() + try { + await page.goto(`http://localhost:${port}`) + const tailwindStyle = await page.$('.tailwind-style') + expect(await getBgColor(tailwindStyle)).toBe('rgb(254, 226, 226)') + expect(await getColor(tailwindStyle)).toBe('rgb(136, 136, 136)') + } finally { + await server.close() + } +}) diff --git a/packages/playground/css/glob-import/bar.css b/packages/playground/css/glob-import/bar.css new file mode 100644 index 00000000000000..a273f4970dcfa9 --- /dev/null +++ b/packages/playground/css/glob-import/bar.css @@ -0,0 +1,3 @@ +.dir-import-2 { + color: grey; +} diff --git a/packages/playground/css/glob-import/foo.css b/packages/playground/css/glob-import/foo.css new file mode 100644 index 00000000000000..03bd30eef45556 --- /dev/null +++ b/packages/playground/css/glob-import/foo.css @@ -0,0 +1,3 @@ +.dir-import { + color: grey; +} diff --git a/packages/playground/css/index.html b/packages/playground/css/index.html index 427bbc437866a5..a09d8e6e7c46aa 100644 --- a/packages/playground/css/index.html +++ b/packages/playground/css/index.html @@ -12,6 +12,8 @@

CSS

Imported css string:


+  

+  

 
   

PostCSS nesting plugin: this should be pink @@ -103,6 +105,18 @@

CSS

Inlined import - this should NOT be red.

+ +
+ test import css in less, this color will be yellow +
+
+ test for import less in less, this color will be blue +
+ +
+ test import css in scss, this color will be orange +
+

 
   

Raw Support

diff --git a/packages/playground/css/less.less b/packages/playground/css/less.less index f8870e06f3a72c..69ffa830862014 100644 --- a/packages/playground/css/less.less +++ b/packages/playground/css/less.less @@ -1,4 +1,5 @@ @import '@/nested/nested'; +@import './nested/css-in-less.less'; @color: blue; diff --git a/packages/playground/css/main.js b/packages/playground/css/main.js index df3563a9d73906..6edd840a87c5e7 100644 --- a/packages/playground/css/main.js +++ b/packages/playground/css/main.js @@ -71,3 +71,13 @@ if (import.meta.env.DEV) { // inlined import inlined from './inlined.css?inline' text('.inlined-code', inlined) + +// glob +const glob = import.meta.glob('./glob-import/*.css') +Promise.all(Object.keys(glob).map((key) => glob[key]())).then((res) => { + text('.imported-css-glob', JSON.stringify(res, null, 2)) +}) + +// globEager +const globEager = import.meta.globEager('./glob-import/*.css') +text('.imported-css-globEager', JSON.stringify(globEager, null, 2)) diff --git a/packages/playground/css/nested/_index.scss b/packages/playground/css/nested/_index.scss index 6f2103c79fc2c8..48d630b573ae1b 100644 --- a/packages/playground/css/nested/_index.scss +++ b/packages/playground/css/nested/_index.scss @@ -1,3 +1,5 @@ +@import './css-in-scss.css'; + .sass-at-import { color: olive; background: url(./icon.png) 10px no-repeat; diff --git a/packages/playground/css/nested/css-in-less-2.less b/packages/playground/css/nested/css-in-less-2.less new file mode 100644 index 00000000000000..443d17da34c0da --- /dev/null +++ b/packages/playground/css/nested/css-in-less-2.less @@ -0,0 +1,3 @@ +.css-in-less-2 { + color: blue; +} diff --git a/packages/playground/css/nested/css-in-less.css b/packages/playground/css/nested/css-in-less.css new file mode 100644 index 00000000000000..b174a601b1356c --- /dev/null +++ b/packages/playground/css/nested/css-in-less.css @@ -0,0 +1,3 @@ +.css-in-less { + color: yellow; +} diff --git a/packages/playground/css/nested/css-in-less.less b/packages/playground/css/nested/css-in-less.less new file mode 100644 index 00000000000000..abdd904b43016a --- /dev/null +++ b/packages/playground/css/nested/css-in-less.less @@ -0,0 +1,4 @@ +@import url('./css-in-less.css'); +@import './css-in-less.css'; + +@import './css-in-less-2.less'; diff --git a/packages/playground/css/nested/css-in-scss.css b/packages/playground/css/nested/css-in-scss.css new file mode 100644 index 00000000000000..a63e49e4d6a1fd --- /dev/null +++ b/packages/playground/css/nested/css-in-scss.css @@ -0,0 +1,3 @@ +.css-in-scss { + color: orange; +} diff --git a/packages/playground/css/package.json b/packages/playground/css/package.json index 13a58874578c09..b45063100be089 100644 --- a/packages/playground/css/package.json +++ b/packages/playground/css/package.json @@ -10,6 +10,7 @@ }, "devDependencies": { "css-dep": "link:./css-dep", + "fast-glob": "^3.2.11", "less": "^4.1.2", "postcss-nested": "^5.0.6", "sass": "^1.43.4", diff --git a/packages/playground/css/vite.config.js b/packages/playground/css/vite.config.js index e4dc8d5a9f265f..53d001d8387989 100644 --- a/packages/playground/css/vite.config.js +++ b/packages/playground/css/vite.config.js @@ -1,4 +1,5 @@ const path = require('path') + /** * @type {import('vite').UserConfig} */ diff --git a/packages/playground/define/__tests__/define.spec.ts b/packages/playground/define/__tests__/define.spec.ts index f5eb78ea4e2766..709f7a935dc8c1 100644 --- a/packages/playground/define/__tests__/define.spec.ts +++ b/packages/playground/define/__tests__/define.spec.ts @@ -20,4 +20,7 @@ test('string', async () => { expect(await page.textContent('.spread-array')).toBe( JSON.stringify([...defines.__STRING__]) ) + // html would't need to define replacement + expect(await page.textContent('.exp-define')).toBe('__EXP__') + expect(await page.textContent('.import-json')).toBe('__EXP__') }) diff --git a/packages/playground/define/data.json b/packages/playground/define/data.json new file mode 100644 index 00000000000000..491b781c2d97d3 --- /dev/null +++ b/packages/playground/define/data.json @@ -0,0 +1,3 @@ +{ + "foo": "__EXP__" +} diff --git a/packages/playground/define/index.html b/packages/playground/define/index.html index bf6a9c59689396..da78d192216b11 100644 --- a/packages/playground/define/index.html +++ b/packages/playground/define/index.html @@ -9,6 +9,8 @@

Define

process as property:

spread object:

spread array:

+

define variable in html: __EXP__

+

import json:

diff --git a/packages/playground/env-nested/package.json b/packages/playground/env-nested/package.json new file mode 100644 index 00000000000000..8fecc69a41c2f4 --- /dev/null +++ b/packages/playground/env-nested/package.json @@ -0,0 +1,11 @@ +{ + "name": "test-env-nested", + "private": true, + "version": "0.0.0", + "scripts": { + "dev": "vite", + "build": "vite build", + "debug": "node --inspect-brk ../../vite/bin/vite", + "preview": "vite preview" + } +} diff --git a/packages/playground/env-nested/vite.config.js b/packages/playground/env-nested/vite.config.js new file mode 100644 index 00000000000000..0e46100698650d --- /dev/null +++ b/packages/playground/env-nested/vite.config.js @@ -0,0 +1,5 @@ +const { defineConfig } = require('vite') + +module.exports = defineConfig({ + envDir: './envs' +}) diff --git a/packages/playground/fs-serve/root/src/index.html b/packages/playground/fs-serve/root/src/index.html index c8b294e86ab0ea..9e4f728a593a91 100644 --- a/packages/playground/fs-serve/root/src/index.html +++ b/packages/playground/fs-serve/root/src/index.html @@ -8,6 +8,10 @@

Safe Fetch


 

 
+

Safe Fetch Subdirectory

+

+

+
 

Unsafe Fetch


 

@@ -42,6 +46,15 @@ 

Denied

.then((data) => { text('.safe-fetch', JSON.stringify(data)) }) + // inside allowed dir, safe fetch + fetch('/src/subdir/safe.txt') + .then((r) => { + text('.safe-fetch-subdir-status', r.status) + return r.text() + }) + .then((data) => { + text('.safe-fetch-subdir', JSON.stringify(data)) + }) // outside of allowed dir, treated as unsafe fetch('/unsafe.txt') diff --git a/packages/playground/fs-serve/root/src/subdir/safe.txt b/packages/playground/fs-serve/root/src/subdir/safe.txt new file mode 100644 index 00000000000000..3f3d0607101642 --- /dev/null +++ b/packages/playground/fs-serve/root/src/subdir/safe.txt @@ -0,0 +1 @@ +KEY=safe diff --git a/packages/playground/glob-import/__tests__/glob-import.spec.ts b/packages/playground/glob-import/__tests__/glob-import.spec.ts index 3a2425736548ea..fff8d9fe202ebc 100644 --- a/packages/playground/glob-import/__tests__/glob-import.spec.ts +++ b/packages/playground/glob-import/__tests__/glob-import.spec.ts @@ -7,6 +7,9 @@ import { } from '../../testUtils' const filteredResult = { + './alias.js': { + default: 'hi' + }, './foo.js': { msg: 'foo' } @@ -30,11 +33,19 @@ const json = isBuild const allResult = { // JSON file should be properly transformed + '/dir/alias.js': { + default: 'hi' + }, '/dir/baz.json': json, '/dir/foo.js': { msg: 'foo' }, '/dir/index.js': { + globWithAlias: { + './alias.js': { + default: 'hi' + } + }, modules: filteredResult }, '/dir/nested/bar.js': { @@ -80,6 +91,7 @@ if (!isBuild) { '/dir/a.js': {}, ...allResult, '/dir/index.js': { + ...allResult['/dir/index.js'], modules: { './a.js': {}, ...allResult['/dir/index.js'].modules @@ -102,6 +114,7 @@ if (!isBuild) { }, ...allResult, '/dir/index.js': { + ...allResult['/dir/index.js'], modules: { './a.js': { msg: 'a' diff --git a/packages/playground/glob-import/dir/alias.js b/packages/playground/glob-import/dir/alias.js new file mode 100644 index 00000000000000..9c533d93b9a98a --- /dev/null +++ b/packages/playground/glob-import/dir/alias.js @@ -0,0 +1 @@ +export default 'hi' diff --git a/packages/playground/glob-import/dir/index.js b/packages/playground/glob-import/dir/index.js index d13d470e2b2b80..fb87f69f0f3a61 100644 --- a/packages/playground/glob-import/dir/index.js +++ b/packages/playground/glob-import/dir/index.js @@ -1,3 +1,4 @@ const modules = import.meta.globEager('./*.(js|ts)') +const globWithAlias = import.meta.globEager('@dir/al*.js') -export { modules } +export { modules, globWithAlias } diff --git a/packages/playground/glob-import/index.html b/packages/playground/glob-import/index.html index bb5e2f3c18f752..52d41b817a169c 100644 --- a/packages/playground/glob-import/index.html +++ b/packages/playground/glob-import/index.html @@ -40,7 +40,7 @@ + + + + diff --git a/packages/playground/optimize-deps/package.json b/packages/playground/optimize-deps/package.json index 0606343e0dce3c..2752e691da6fb2 100644 --- a/packages/playground/optimize-deps/package.json +++ b/packages/playground/optimize-deps/package.json @@ -17,12 +17,15 @@ "dep-esbuild-plugin-transform": "file:./dep-esbuild-plugin-transform", "dep-linked": "link:./dep-linked", "dep-linked-include": "link:./dep-linked-include", + "dep-not-js": "file:./dep-not-js", + "dep-with-dynamic-import": "file:./dep-with-dynamic-import", "lodash-es": "^4.17.21", "nested-exclude": "file:./nested-exclude", "phoenix": "^1.6.2", "react": "^17.0.2", "react-dom": "^17.0.2", "resolve-linked": "workspace:0.0.0", + "url": "^0.11.0", "vue": "^3.2.25", "vuex": "^4.0.0" }, diff --git a/packages/playground/optimize-deps/vite.config.js b/packages/playground/optimize-deps/vite.config.js index 45a50aaf85ede6..a989cf1961de11 100644 --- a/packages/playground/optimize-deps/vite.config.js +++ b/packages/playground/optimize-deps/vite.config.js @@ -1,3 +1,4 @@ +const fs = require('fs') const vue = require('@vitejs/plugin-vue') /** @@ -5,7 +6,10 @@ const vue = require('@vitejs/plugin-vue') */ module.exports = { resolve: { - dedupe: ['react'] + dedupe: ['react'], + alias: { + 'node:url': 'url' + } }, optimizeDeps: { @@ -36,6 +40,7 @@ module.exports = { plugins: [ vue(), + notjs(), // for axios request test { name: 'mock', @@ -48,3 +53,39 @@ module.exports = { } ] } + +// Handles .notjs file, basically remove wrapping and tags +function notjs() { + return { + name: 'notjs', + config() { + return { + optimizeDeps: { + extensions: ['.notjs'], + esbuildOptions: { + plugins: [ + { + name: 'esbuild-notjs', + setup(build) { + build.onLoad({ filter: /\.notjs$/ }, ({ path }) => { + let contents = fs.readFileSync(path, 'utf-8') + contents = contents + .replace('', '') + .replace('', '') + return { contents, loader: 'js' } + }) + } + } + ] + } + } + } + }, + transform(code, id) { + if (id.endsWith('.notjs')) { + code = code.replace('', '').replace('', '') + return { code } + } + } + } +} diff --git a/packages/playground/resolve/__tests__/resolve.spec.ts b/packages/playground/resolve/__tests__/resolve.spec.ts index 97e4a5dd0add7b..c8c85d8df9b806 100644 --- a/packages/playground/resolve/__tests__/resolve.spec.ts +++ b/packages/playground/resolve/__tests__/resolve.spec.ts @@ -16,6 +16,10 @@ test('deep import with exports field', async () => { expect(await page.textContent('.exports-deep')).toMatch('[success]') }) +test('deep import with query with exports field', async () => { + expect(await page.textContent('.exports-deep-query')).not.toMatch('fail') +}) + test('deep import with exports field + exposed dir', async () => { expect(await page.textContent('.exports-deep-exposed-dir')).toMatch( '[success]' @@ -54,6 +58,12 @@ test('dont add extension to directory name (./dir-with-ext.js/index.js)', async expect(await page.textContent('.dir-with-ext')).toMatch('[success]') }) +test('do not resolve to the `module` field if the importer is a `require` call', async () => { + expect(await page.textContent('.require-pkg-with-module-field')).toMatch( + '[success]' + ) +}) + test('a ts module can import another ts module using its corresponding js file name', async () => { expect(await page.textContent('.ts-extension')).toMatch('[success]') }) diff --git a/packages/playground/resolve/exports-path/deep.json b/packages/playground/resolve/exports-path/deep.json new file mode 100644 index 00000000000000..97e19265d6c843 --- /dev/null +++ b/packages/playground/resolve/exports-path/deep.json @@ -0,0 +1,3 @@ +{ + "foo": "json" +} diff --git a/packages/playground/resolve/exports-path/package.json b/packages/playground/resolve/exports-path/package.json index 045fc85db128d2..7355da2f63f616 100644 --- a/packages/playground/resolve/exports-path/package.json +++ b/packages/playground/resolve/exports-path/package.json @@ -8,6 +8,7 @@ "require": "./cjs.js" }, "./deep.js": "./deep.js", + "./deep.json": "./deep.json", "./dir/": "./dir/", "./dir-mapped/*": { "import": "./dir/*", diff --git a/packages/playground/resolve/index.html b/packages/playground/resolve/index.html index db0a4bc54f1ad7..2c4ed7b9aa760c 100644 --- a/packages/playground/resolve/index.html +++ b/packages/playground/resolve/index.html @@ -12,6 +12,9 @@

Entry resolving with exports field

Deep import with exports field

fail

+

Deep import with query with exports field

+

fail

+

Deep import with exports field + exposed directory

fail

@@ -38,12 +41,26 @@

fail

+

+ A ts module can import another tsx module using its corresponding jsx file + name +

+

fail

+ +

+ A ts module can import another tsx module using its corresponding js file name +

+

fail

+

Resolve file name containing dot

fail

Browser Field

fail

+

Don't resolve to the `module` field if the importer is a `require` call

+

fail

+

CSS Entry

@@ -100,6 +117,10 @@

resolve package that contains # in path

import { msg as deepMsg } from 'resolve-exports-path/deep.js' text('.exports-deep', deepMsg) + // deep import w/ exports w/ query + import deepPath from 'resolve-exports-path/deep.json?url' + text('.exports-deep-query', deepPath) + // deep import w/ exposed dir import { msg as exposedDirMsg } from 'resolve-exports-path/dir/dir' text('.exports-deep-exposed-dir', exposedDirMsg) @@ -130,6 +151,12 @@

resolve package that contains # in path

import { msg as tsExtensionMsg } from './ts-extension' text('.ts-extension', tsExtensionMsg) + import { msgJsx as tsJsxExtensionMsg } from './ts-extension' + text('.jsx-extension', tsJsxExtensionMsg) + + import { msgTsx as tsTsxExtensionMsg } from './ts-extension' + text('.tsx-extension', tsTsxExtensionMsg) + // filename with dot import { bar } from './util/bar.util' text('.dot', bar()) @@ -157,6 +184,9 @@

resolve package that contains # in path

text('.browser', main) } + import { msg as requireButWithModuleFieldMsg } from 'require-pkg-with-module-field' + text('.require-pkg-with-module-field', requireButWithModuleFieldMsg) + import { msg as customExtMsg } from './custom-ext' text('.custom-ext', customExtMsg) diff --git a/packages/playground/resolve/package.json b/packages/playground/resolve/package.json index 5e0f53b4c8468a..dda4476bc6ae82 100644 --- a/packages/playground/resolve/package.json +++ b/packages/playground/resolve/package.json @@ -12,6 +12,7 @@ "@babel/runtime": "^7.16.0", "es5-ext": "0.10.53", "normalize.css": "^8.0.1", + "require-pkg-with-module-field": "link:./require-pkg-with-module-field", "resolve-browser-field": "link:./browser-field", "resolve-custom-condition": "link:./custom-condition", "resolve-custom-main-field": "link:./custom-main-field", diff --git a/packages/playground/resolve/require-pkg-with-module-field/dep.cjs b/packages/playground/resolve/require-pkg-with-module-field/dep.cjs new file mode 100644 index 00000000000000..3fb20b76d48b79 --- /dev/null +++ b/packages/playground/resolve/require-pkg-with-module-field/dep.cjs @@ -0,0 +1,5 @@ +const BigNumber = require('bignumber.js') + +const x = new BigNumber('1111222233334444555566') + +module.exports = x.toString() diff --git a/packages/playground/resolve/require-pkg-with-module-field/index.cjs b/packages/playground/resolve/require-pkg-with-module-field/index.cjs new file mode 100644 index 00000000000000..da215f306d1ac1 --- /dev/null +++ b/packages/playground/resolve/require-pkg-with-module-field/index.cjs @@ -0,0 +1,8 @@ +const dep = require('./dep.cjs') + +const msg = + dep === '1.111222233334444555566e+21' + ? '[success] require-pkg-with-module-field' + : '[failed] require-pkg-with-module-field' + +exports.msg = msg diff --git a/packages/playground/resolve/require-pkg-with-module-field/package.json b/packages/playground/resolve/require-pkg-with-module-field/package.json new file mode 100644 index 00000000000000..e409343a7567d5 --- /dev/null +++ b/packages/playground/resolve/require-pkg-with-module-field/package.json @@ -0,0 +1,9 @@ +{ + "name": "require-pkg-with-module-field", + "private": true, + "version": "1.0.0", + "main": "./index.cjs", + "dependencies": { + "bignumber.js": "9.0.2" + } +} diff --git a/packages/playground/resolve/ts-extension/hellojsx.tsx b/packages/playground/resolve/ts-extension/hellojsx.tsx new file mode 100644 index 00000000000000..a8f610b399b17a --- /dev/null +++ b/packages/playground/resolve/ts-extension/hellojsx.tsx @@ -0,0 +1 @@ +export const msgJsx = '[success] use .jsx extension to import a tsx module' diff --git a/packages/playground/resolve/ts-extension/hellotsx.tsx b/packages/playground/resolve/ts-extension/hellotsx.tsx new file mode 100644 index 00000000000000..b7461ca71ded6c --- /dev/null +++ b/packages/playground/resolve/ts-extension/hellotsx.tsx @@ -0,0 +1 @@ +export const msgTsx = '[success] use .js extension to import a tsx module' diff --git a/packages/playground/resolve/ts-extension/index.ts b/packages/playground/resolve/ts-extension/index.ts index e095619ee4d716..bdb326f8778e64 100644 --- a/packages/playground/resolve/ts-extension/index.ts +++ b/packages/playground/resolve/ts-extension/index.ts @@ -1,3 +1,5 @@ import { msg } from './hello.js' +import { msgJsx } from './hellojsx.jsx' +import { msgTsx } from './hellotsx.js' -export { msg } +export { msg, msgJsx, msgTsx } diff --git a/packages/playground/resolve/vite.config.js b/packages/playground/resolve/vite.config.js index be1b75e431383a..0550d1ecf6f044 100644 --- a/packages/playground/resolve/vite.config.js +++ b/packages/playground/resolve/vite.config.js @@ -40,5 +40,8 @@ module.exports = { } } } - ] + ], + optimizeDeps: { + include: ['require-pkg-with-module-field'] + } } diff --git a/packages/playground/tailwind/__test__/tailwind.spec.ts b/packages/playground/tailwind/__test__/tailwind.spec.ts index 105eb245bf207e..47f6b7ccf49037 100644 --- a/packages/playground/tailwind/__test__/tailwind.spec.ts +++ b/packages/playground/tailwind/__test__/tailwind.spec.ts @@ -5,7 +5,7 @@ test('should render', async () => { }) if (!isBuild) { - test('regenerate CSS and HMR', async () => { + test('regenerate CSS and HMR (glob pattern)', async () => { browserLogs.length = 0 const el = await page.$('#pagetitle') const el2 = await page.$('#helloroot') @@ -37,4 +37,24 @@ if (!isBuild) { browserLogs.length = 0 }) + + test('regenerate CSS and HMR (relative path)', async () => { + browserLogs.length = 0 + const el = await page.$('h1') + + expect(await getColor(el)).toBe('black') + + editFile('src/App.vue', (code) => + code.replace('text-black', 'text-[rgb(11,22,33)]') + ) + + await untilUpdated(() => getColor(el), 'rgb(11, 22, 33)') + + expect(browserLogs).toMatchObject([ + '[vite] css hot updated: /index.css', + '[vite] hot updated: /src/App.vue' + ]) + + browserLogs.length = 0 + }) } diff --git a/packages/playground/tailwind/src/App.vue b/packages/playground/tailwind/src/App.vue index b032d5e0db77e3..25835fc414a06f 100644 --- a/packages/playground/tailwind/src/App.vue +++ b/packages/playground/tailwind/src/App.vue @@ -1,6 +1,6 @@