diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 3954ca49c11479..69ba432050fa7e 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -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" @@ -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/publish.yml b/.github/workflows/publish.yml index eb8f4513707872..9e22af5aed6ee8 100644 --- a/.github/workflows/publish.yml +++ b/.github/workflows/publish.yml @@ -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/.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..31c1f34f3a4815 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -14,6 +14,36 @@ To develop and test the core `vite` package: 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`: diff --git a/docs/config/index.md b/docs/config/index.md index d4ee7a96a6fa67..4d42063e30e82c 100644 --- a/docs/config/index.md +++ b/docs/config/index.md @@ -139,9 +139,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. @@ -508,12 +510,14 @@ 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. 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/build.md b/docs/guide/build.md index aac86a237b6819..1f1f149a07f7c9 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.manualChunks` (see [Rollup docs](https://rollupjs.org/guide/en/#outputmanualchunks)). Until Vite 2.7, 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.8, `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`: diff --git a/docs/guide/dep-pre-bundling.md b/docs/guide/dep-pre-bundling.md index 150584b1a6521c..a72e06e6aff944 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,8 @@ 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! +Note that this only applies in development mode. + ## 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 +38,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 pack` instead for all local dependencies to avoid issues in the final bundle. (The `npm pack` is only ever needed when the linked source code or package only exports CJS code. If it exports ESM code, then it is not needed.) +::: 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 +75,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/packages/playground/assets/__tests__/assets.spec.ts b/packages/playground/assets/__tests__/assets.spec.ts index c51bb4094b7b8c..191da897858dd5 100644 --- a/packages/playground/assets/__tests__/assets.spec.ts +++ b/packages/playground/assets/__tests__/assets.spec.ts @@ -120,6 +120,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 () => { 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 f3b9a0b372a608..e33e9a7cdaaf7d 100644 --- a/packages/playground/assets/index.html +++ b/packages/playground/assets/index.html @@ -2,6 +2,7 @@ + @@ -137,6 +138,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)

@@ -147,6 +156,16 @@

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

+

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

+

+ + +

+

+ + +

+

simple script tag import-expression

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/vite.config.ts b/packages/playground/glob-import/vite.config.ts new file mode 100644 index 00000000000000..abc75b51656503 --- /dev/null +++ b/packages/playground/glob-import/vite.config.ts @@ -0,0 +1,10 @@ +import path from 'path' +import { defineConfig } from 'vite' + +export default defineConfig({ + resolve: { + alias: { + '@dir': path.resolve(__dirname, './dir/') + } + } +}) diff --git a/packages/playground/json/index.html b/packages/playground/json/index.html index 4f6e2b6ae57466..cf16636f91cb68 100644 --- a/packages/playground/json/index.html +++ b/packages/playground/json/index.html @@ -19,6 +19,9 @@

Importing as URL

Raw Import


 
+

JSON Module

+

+
 
+
+
diff --git a/packages/playground/vue-jsx/vite.config.js b/packages/playground/vue-jsx/vite.config.js
index 3ec89a003d79f4..d6eb84e05f4e4a 100644
--- a/packages/playground/vue-jsx/vite.config.js
+++ b/packages/playground/vue-jsx/vite.config.js
@@ -9,7 +9,28 @@ module.exports = {
     vueJsxPlugin({
       include: [/\.tesx$/, /\.[jt]sx$/]
     }),
-    vuePlugin()
+    vuePlugin(),
+    {
+      name: 'jsx-query-plugin',
+      transform(code, id) {
+        if (id.includes('?query=true')) {
+          return `
+import { createVNode as _createVNode } from "vue";
+import { defineComponent, ref } from 'vue';
+export default defineComponent(() => {
+  const count = ref(6);
+
+  const inc = () => count.value++;
+
+  return () => _createVNode("button", {
+    "class": "jsx-with-query",
+    "onClick": inc
+  }, [count.value]);
+});
+`
+        }
+      }
+    }
   ],
   build: {
     // to make tests faster
diff --git a/packages/playground/vue/public/favicon.ico b/packages/playground/vue/public/favicon.ico
new file mode 100644
index 00000000000000..df36fcfb72584e
Binary files /dev/null and b/packages/playground/vue/public/favicon.ico differ
diff --git a/packages/playground/vue/vite.config.ts b/packages/playground/vue/vite.config.ts
index 82efdac5e9f876..f99a68ce8b6b10 100644
--- a/packages/playground/vue/vite.config.ts
+++ b/packages/playground/vue/vite.config.ts
@@ -1,4 +1,4 @@
-import { defineConfig } from 'vite'
+import { defineConfig, splitVendorChunkPlugin } from 'vite'
 import vuePlugin from '@vitejs/plugin-vue'
 import { vueI18nPlugin } from './CustomBlockPlugin'
 
@@ -12,11 +12,22 @@ export default defineConfig({
     vuePlugin({
       reactivityTransform: true
     }),
+    splitVendorChunkPlugin(),
     vueI18nPlugin
   ],
   build: {
     // to make tests faster
-    minify: false
+    minify: false,
+    rollupOptions: {
+      output: {
+        // Test splitVendorChunkPlugin composition
+        manualChunks(id) {
+          if (id.includes('src-import')) {
+            return 'src-import'
+          }
+        }
+      }
+    }
   },
   css: {
     modules: {
diff --git a/packages/playground/worker/__tests__/worker.spec.ts b/packages/playground/worker/__tests__/worker.spec.ts
index 5992a37f933f2a..1c33e6ec68daf9 100644
--- a/packages/playground/worker/__tests__/worker.spec.ts
+++ b/packages/playground/worker/__tests__/worker.spec.ts
@@ -56,7 +56,7 @@ if (isBuild) {
   // assert correct files
   test('inlined code generation', async () => {
     const files = fs.readdirSync(assetsDir)
-    expect(files.length).toBe(6)
+    expect(files.length).toBe(8)
     const index = files.find((f) => f.includes('index'))
     const content = fs.readFileSync(path.resolve(assetsDir, index), 'utf-8')
     const worker = files.find((f) => f.includes('my-worker'))
@@ -88,3 +88,8 @@ if (isBuild) {
       })
   })
 }
+
+test('classic worker is run', async () => {
+  expect(await page.textContent('.classic-worker')).toMatch('A classic')
+  expect(await page.textContent('.classic-shared-worker')).toMatch('A classic')
+})
diff --git a/packages/playground/worker/classic-worker.js b/packages/playground/worker/classic-worker.js
new file mode 100644
index 00000000000000..bb6f9c3f49fc84
--- /dev/null
+++ b/packages/playground/worker/classic-worker.js
@@ -0,0 +1,29 @@
+// prettier-ignore
+function text(el, text) {
+  document.querySelector(el).textContent = text
+}
+
+const classicWorker = new Worker(
+  new URL('./newUrl/classic-worker.js', import.meta.url) /* , */  ,
+  // test comment
+
+)
+
+classicWorker.addEventListener('message', ({ data }) => {
+  text('.classic-worker', data)
+})
+classicWorker.postMessage('ping')
+
+const classicSharedWorker = new SharedWorker(
+  new URL('./newUrl/classic-shared-worker.js', import.meta.url),
+  {
+    type: 'classic'
+  }
+)
+classicSharedWorker.port.addEventListener('message', (ev) => {
+  text(
+    '.classic-shared-worker',
+    ev.data
+  )
+})
+classicSharedWorker.port.start()
diff --git a/packages/playground/worker/index.html b/packages/playground/worker/index.html
index be0b3becb5f94f..10207453712aa2 100644
--- a/packages/playground/worker/index.html
+++ b/packages/playground/worker/index.html
@@ -20,18 +20,25 @@
   0
 
 
-

new Worker(new Url('path', import.meta.url))

+

new Worker(new Url('path', import.meta.url), { type: 'module' })

-

new SharedWorker(new Url('path', import.meta.url))

+

new SharedWorker(new Url('path', import.meta.url), { type: 'module' })

+

new Worker(new Url('path', import.meta.url))

+
+ +

new Worker(new Url('path', import.meta.url), { type: 'classic' })

+
+