diff --git a/.eslintrc.cjs b/.eslintrc.cjs index edc202b5e539df..f0b3be9e03a8bb 100644 --- a/.eslintrc.cjs +++ b/.eslintrc.cjs @@ -155,7 +155,7 @@ module.exports = defineConfig({ } }, { - files: ['*.js'], + files: ['playground/**', '*.js'], rules: { '@typescript-eslint/explicit-module-boundary-types': 'off' } diff --git a/.github/renovate.json5 b/.github/renovate.json5 index 05c1c02ab4cb12..0014c768519180 100644 --- a/.github/renovate.json5 +++ b/.github/renovate.json5 @@ -19,15 +19,9 @@ "typescript", // breaking changes - "cac", // `cac:v6.7.10+` has breaking changes - "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", - "periscopic", - "strip-ansi" + "kill-port", // `kill-port:^2.0.0 has perf issues (#8392) + "miniflare" // `miniflare:v2.0.0+` only supports node 16.7 ] } diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 248bb3fbedd101..f190529faa247d 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -4,6 +4,8 @@ env: # 7 GiB by default on GitHub, setting to 6 GiB # https://docs.github.com/en/actions/using-github-hosted-runners/about-github-hosted-runners#supported-runners-and-hardware-resources NODE_OPTIONS: --max-old-space-size=6144 + # install playwright binary manually (because pnpm only runs install script once) + PLAYWRIGHT_SKIP_BROWSER_DOWNLOAD: "1" on: push: @@ -38,10 +40,6 @@ jobs: node_version: 16 fail-fast: false - env: - # Install playwright's binray under node_modules so it will be cached together - PLAYWRIGHT_BROWSERS_PATH: "0" - name: "Build&Test: node-${{ matrix.node_version }}, ${{ matrix.os }}" steps: - name: Checkout @@ -59,8 +57,25 @@ jobs: - name: Install deps run: pnpm install + # Install playwright's binary under custom directory to cache + - name: Set Playwright path (non-windows) + if: runner.os != 'Windows' + run: echo "PLAYWRIGHT_BROWSERS_PATH=$HOME/.cache/playwright-bin" >> $GITHUB_ENV + - name: Set Playwright path (windows) + if: runner.os == 'Windows' + run: echo "PLAYWRIGHT_BROWSERS_PATH=$HOME\.cache\playwright-bin" >> $env:GITHUB_ENV + + - name: Cache Playwright's binary + uses: actions/cache@v3 + with: + # Playwright removes unused browsers automatically + # So does not need to add playwright version to key + key: ${{ runner.os }}-playwright-bin-v1 + path: ${{ env.PLAYWRIGHT_BROWSERS_PATH }} + - name: Install Playwright - run: pnpm playwright install + # does not need to explictly set chromium after https://github.com/microsoft/playwright/issues/14862 is solved + run: pnpm playwright install chromium - name: Build run: pnpm run build @@ -97,8 +112,6 @@ jobs: - name: Install deps run: pnpm install - env: - PLAYWRIGHT_SKIP_BROWSER_DOWNLOAD: "1" - name: Build run: pnpm run build diff --git a/.gitignore b/.gitignore index 9194d44b544c4e..7f1cab0e1f4356 100644 --- a/.gitignore +++ b/.gitignore @@ -13,3 +13,4 @@ node_modules playground-temp temp TODOs.md +.eslintcache diff --git a/docs/.vitepress/config.ts b/docs/.vitepress/config.ts index 9c3150eb1d0c1b..55b4657af24288 100644 --- a/docs/.vitepress/config.ts +++ b/docs/.vitepress/config.ts @@ -14,9 +14,7 @@ export default defineConfig({ logo: '/logo.svg', editLink: { - repo: 'vitejs/vite', - branch: 'main', - dir: 'docs', + pattern: 'https://github.com/vitejs/vite/edit/main/docs/:path', text: 'Suggest changes to this page' }, diff --git a/docs/config/index.md b/docs/config/index.md index 5daf5e7f714275..73a20082295304 100644 --- a/docs/config/index.md +++ b/docs/config/index.md @@ -41,14 +41,10 @@ const func = () => { Since Vite ships with TypeScript typings, you can leverage your IDE's intellisense with jsdoc type hints: ```js -/** - * @type {import('vite').UserConfig} - */ -const config = { +/** @type {import('vite').UserConfig} */ +export default { // ... } - -export default config ``` Alternatively, you can use the `defineConfig` helper which should provide intellisense without the need for jsdoc annotations: diff --git a/docs/config/server-options.md b/docs/config/server-options.md index cf59ab27644656..a3f82aa0f12b79 100644 --- a/docs/config/server-options.md +++ b/docs/config/server-options.md @@ -3,7 +3,7 @@ ## server.host - **Type:** `string | boolean` -- **Default:** `'127.0.0.1'` +- **Default:** `'localhost'` Specify which IP addresses the server should listen on. Set this to `0.0.0.0` or `true` to listen on all addresses, including LAN and public addresses. diff --git a/docs/config/worker-options.md b/docs/config/worker-options.md index c0c13bf4133adb..c506d249a24932 100644 --- a/docs/config/worker-options.md +++ b/docs/config/worker-options.md @@ -11,7 +11,7 @@ Output format for worker bundle. - **Type:** [`(Plugin | Plugin[])[]`](./shared-options#plugins) -Vite plugins that apply to worker bundle +Vite plugins that apply to worker bundle. Note that [config.plugins](./shared-options#plugins) does not apply to workers, it should be configured here instead. ## worker.rollupOptions diff --git a/docs/guide/api-plugin.md b/docs/guide/api-plugin.md index 735a30d3266909..51dc3ef6e0f78f 100644 --- a/docs/guide/api-plugin.md +++ b/docs/guide/api-plugin.md @@ -517,6 +517,10 @@ normalizePath('foo\\bar') // 'foo/bar' normalizePath('foo/bar') // 'foo/bar' ``` +## Filtering, include/exclude pattern + +Vite exposes [`@rollup/pluginutils`'s `createFilter`](https://github.com/rollup/plugins/tree/master/packages/pluginutils#createfilter) function to encourage Vite specific plugins and integrations to use the standard include/exclude filtering pattern, which is also used in Vite core itself. + ## Client-server Communication Since Vite 2.9, we provide some utilities for plugins to help handle the communication with clients. diff --git a/docs/guide/features.md b/docs/guide/features.md index 57d1e5a71e4a82..65d01f9b6c7239 100644 --- a/docs/guide/features.md +++ b/docs/guide/features.md @@ -511,6 +511,12 @@ By default, the worker script will be emitted as a separate chunk in the product import MyWorker from './worker?worker&inline' ``` +If you wish to retrieve the worker as a URL, add the `url` query: + +```js +import MyWorker from './worker?worker&url' +``` + See [Worker Options](/config/#worker-options) for details on configuring the bundling of all workers. ## Build Optimizations diff --git a/docs/guide/migration.md b/docs/guide/migration.md index 78b738afb82ddf..9885dec7117742 100644 --- a/docs/guide/migration.md +++ b/docs/guide/migration.md @@ -31,6 +31,8 @@ A small fraction of users will now require using [@vitejs/plugin-legacy](https:/ Vite's default dev server port is now 5173. You can use [`server.port`](../config/server-options.md#server-port) to set it to 3000. +Vite's default dev server host is now `localhost`. You can use [`server.host`](../config/server-options.md#server-host) to set it to `127.0.0.1`. + Vite optimizes dependencies with esbuild to both convert CJS-only deps to ESM and to reduce the number of modules the browser needs to request. In v3, the default strategy to discover and batch dependencies has changed. Vite no longer pre-scans user code with esbuild to get an initial list of dependencies on cold start. Instead, it delays the first dependency optimization run until every imported user module on load is processed. To get back the v2 strategy, you can use [`optimizeDeps.devScan`](../config/dep-optimization-options.md#optimizedepsdevscan). @@ -50,6 +52,10 @@ If using ESM for SSR isn't possible in your project, you can set `ssr.format: 'c ## General Changes - JS file extensions in SSR and lib mode now use a valid extension (`js`, `mjs`, or `cjs`) for output JS entries and chunks based on their format and the package type. +- Terser is now an optional dependency. If you are using `build.minify: 'terser'`, you need to install it. + ```shell + npm add -D terser + ``` ### `import.meta.glob` @@ -93,6 +99,8 @@ There are some changes which only affects plugin/tool creators. - `printHttpServerUrls` is removed - `server.app`, `server.transformWithEsbuild` are removed - `import.meta.hot.acceptDeps` is removed +- [[#6901] fix: sequential injection of tags in transformIndexHtml](https://github.com/vitejs/vite/pull/6901) + - `transformIndexHtml` now gets the correct content modified by earlier plugins, so the order of the injected tags now works as expected. - [[#7995] chore: do not fixStacktrace](https://github.com/vitejs/vite/pull/7995) - `ssrLoadModule`'s `fixStacktrace` option's default is now `false` - [[#8178] feat!: migrate to ESM](https://github.com/vitejs/vite/pull/8178) @@ -105,8 +113,12 @@ Also there are other breaking changes which only affect few users. - Transpile to ES5 is now necessary even if the user code only includes ES5. - [[#7877] fix: vite client types](https://github.com/vitejs/vite/pull/7877) - `/// ` is removed from `vite/client.d.ts`. `{ "lib": ["dom"] }` or `{ "lib": ["webworker"] }` is necessary in `tsconfig.json`. +- [[#8090] feat: preserve process env vars in lib build](https://github.com/vitejs/vite/pull/8090) + - `process.env.*` is now preserved in library mode - [[#8280] feat: non-blocking esbuild optimization at build time](https://github.com/vitejs/vite/pull/8280) - `server.force` option was removed in favor of `force` option. +- [[#8550] fix: dont handle sigterm in middleware mode](https://github.com/vitejs/vite/pull/8550) + - When running in middleware mode, Vite no longer kills process on `SIGTERM`. ## Migration from v1 diff --git a/package.json b/package.json index 5de01da5f99422..d9923fa7b80bf4 100644 --- a/package.json +++ b/package.json @@ -14,8 +14,8 @@ ], "scripts": { "preinstall": "npx only-allow pnpm", - "format": "prettier --write .", - "lint": "eslint packages/*/{src,types,__tests__}/** playground/**/__tests__/**/*.ts scripts/**", + "format": "prettier --write --cache .", + "lint": "eslint --cache packages/*/{src,types,__tests__}/** playground/**/__tests__/**/*.ts scripts/**", "typecheck": "tsc -p scripts --noEmit && tsc -p playground --noEmit", "test": "run-s test-unit test-serve test-build", "test-serve": "vitest run -c vitest.config.e2e.ts", @@ -50,7 +50,7 @@ "@types/micromatch": "^4.0.2", "@types/mime": "^2.0.3", "@types/minimist": "^1.2.2", - "@types/node": "^17.0.31", + "@types/node": "^17.0.42", "@types/prompts": "^2.4.0", "@types/resolve": "^1.20.2", "@types/sass": "~1.43.1", @@ -61,26 +61,26 @@ "@typescript-eslint/parser": "^5.27.1", "conventional-changelog-cli": "^2.2.2", "cross-env": "^7.0.3", - "esbuild": "^0.14.38", + "esbuild": "^0.14.43", "eslint": "^8.17.0", "eslint-define-config": "^1.5.1", "eslint-plugin-import": "^2.26.0", "eslint-plugin-node": "^11.1.0", "esno": "^0.16.3", - "execa": "^5.1.1", + "execa": "^6.1.0", "fs-extra": "^10.1.0", "kill-port": "^1.6.1", "lint-staged": "^13.0.1", "minimist": "^1.2.6", - "node-fetch": "^2.6.7", + "node-fetch": "^3.2.6", "npm-run-all": "^4.1.5", "picocolors": "^1.0.0", "playwright-chromium": "^1.22.2", "pnpm": "^7.2.1", - "prettier": "2.6.2", + "prettier": "2.7.0", "prompts": "^2.4.2", "rimraf": "^3.0.2", - "rollup": "^2.72.1", + "rollup": "^2.75.6", "semver": "^7.3.7", "simple-git-hooks": "^2.8.0", "sirv": "^2.0.2", @@ -88,7 +88,7 @@ "typescript": "^4.6.4", "unbuild": "^0.7.4", "vite": "workspace:*", - "vitepress": "1.0.0-draft.8", + "vitepress": "^1.0.0-alpha.2", "vitest": "^0.14.2", "vue": "^3.2.37" }, @@ -98,16 +98,16 @@ }, "lint-staged": { "*": [ - "prettier --write --ignore-unknown" + "prettier --write --cache --ignore-unknown" ], "packages/*/{src,types}/**/*.ts": [ - "eslint --fix" + "eslint --cache --fix" ], "packages/**/*.d.ts": [ - "eslint --fix" + "eslint --cache --fix" ], "playground/**/__tests__/**/*.ts": [ - "eslint --fix" + "eslint --cache --fix" ] }, "packageManager": "pnpm@7.2.1", diff --git a/packages/create-vite/__tests__/cli.spec.ts b/packages/create-vite/__tests__/cli.spec.ts index f9172aeece98d2..39ac7e1e547a36 100644 --- a/packages/create-vite/__tests__/cli.spec.ts +++ b/packages/create-vite/__tests__/cli.spec.ts @@ -1,6 +1,6 @@ import { join } from 'path' import type { ExecaSyncReturnValue, SyncOptions } from 'execa' -import { commandSync } from 'execa' +import { execaCommandSync } from 'execa' import { mkdirpSync, readdirSync, remove, writeFileSync } from 'fs-extra' import { afterEach, beforeAll, expect, test } from 'vitest' @@ -13,7 +13,7 @@ const run = ( args: string[], options: SyncOptions = {} ): ExecaSyncReturnValue => { - return commandSync(`node ${CLI_PATH} ${args.join(' ')}`, options) + return execaCommandSync(`node ${CLI_PATH} ${args.join(' ')}`, options) } // Helper to create a non-empty directory diff --git a/packages/create-vite/template-preact-ts/src/main.tsx b/packages/create-vite/template-preact-ts/src/main.tsx index 812f602e33bc95..e0ce3e9980eecd 100644 --- a/packages/create-vite/template-preact-ts/src/main.tsx +++ b/packages/create-vite/template-preact-ts/src/main.tsx @@ -2,4 +2,4 @@ import { render } from 'preact' import { App } from './app' import './index.css' -render(, document.getElementById('app')!) +render(, document.getElementById('app') as HTMLElement) diff --git a/packages/plugin-legacy/package.json b/packages/plugin-legacy/package.json index 0c77100be3b56f..9f854eb34d4228 100644 --- a/packages/plugin-legacy/package.json +++ b/packages/plugin-legacy/package.json @@ -35,7 +35,7 @@ }, "homepage": "https://github.com/vitejs/vite/tree/main/packages/plugin-legacy#readme", "dependencies": { - "@babel/standalone": "^7.18.4", + "@babel/standalone": "^7.18.5", "core-js": "^3.22.8", "magic-string": "^0.26.2", "regenerator-runtime": "^0.13.9", @@ -46,7 +46,7 @@ "vite": "^3.0.0-alpha" }, "devDependencies": { - "vite": "workspace:*", - "@babel/core": "^7.18.2" + "@babel/core": "^7.18.5", + "vite": "workspace:*" } } diff --git a/packages/plugin-legacy/src/index.ts b/packages/plugin-legacy/src/index.ts index 4aaab1aab2412d..0c9fb1c8c68d33 100644 --- a/packages/plugin-legacy/src/index.ts +++ b/packages/plugin-legacy/src/index.ts @@ -134,7 +134,7 @@ function viteLegacyPlugin(options: Options = {}): Plugin[] { config.build, 'es', opts, - options.externalSystemJS + true ) return } @@ -566,7 +566,7 @@ async function buildPolyfillChunk( buildOptions: BuildOptions, format: 'iife' | 'es', rollupOutputOptions: NormalizedOutputOptions, - externalSystemJS?: boolean + excludeSystemJS?: boolean ) { let { minify, assetsDir } = buildOptions minify = minify ? 'terser' : false @@ -575,7 +575,7 @@ async function buildPolyfillChunk( root: path.dirname(fileURLToPath(import.meta.url)), configFile: false, logLevel: 'error', - plugins: [polyfillsPlugin(imports, externalSystemJS)], + plugins: [polyfillsPlugin(imports, excludeSystemJS)], build: { write: false, target: false, @@ -614,7 +614,7 @@ const polyfillId = '\0vite/legacy-polyfills' function polyfillsPlugin( imports: Set, - externalSystemJS?: boolean + excludeSystemJS?: boolean ): Plugin { return { name: 'vite:legacy-polyfills', @@ -627,7 +627,7 @@ function polyfillsPlugin( if (id === polyfillId) { return ( [...imports].map((i) => `import "${i}";`).join('') + - (externalSystemJS ? '' : `import "systemjs/dist/s.min.js";`) + (excludeSystemJS ? '' : `import "systemjs/dist/s.min.js";`) ) } } diff --git a/packages/plugin-react/package.json b/packages/plugin-react/package.json index 2dbd0d2890afea..40cf3e60d72d6a 100644 --- a/packages/plugin-react/package.json +++ b/packages/plugin-react/package.json @@ -39,12 +39,11 @@ }, "homepage": "https://github.com/vitejs/vite/tree/main/packages/plugin-react#readme", "dependencies": { - "@babel/core": "^7.18.2", + "@babel/core": "^7.18.5", "@babel/plugin-transform-react-jsx": "^7.17.12", "@babel/plugin-transform-react-jsx-development": "^7.16.7", "@babel/plugin-transform-react-jsx-self": "^7.17.12", "@babel/plugin-transform-react-jsx-source": "^7.16.7", - "@rollup/pluginutils": "^4.2.1", "react-refresh": "^0.13.0" }, "peerDependencies": { diff --git a/packages/plugin-react/src/index.ts b/packages/plugin-react/src/index.ts index 4f3f745096fb36..77ce68a630f631 100644 --- a/packages/plugin-react/src/index.ts +++ b/packages/plugin-react/src/index.ts @@ -1,8 +1,7 @@ import path from 'path' import type { ParserOptions, TransformOptions, types as t } from '@babel/core' import * as babel from '@babel/core' -import { createFilter } from '@rollup/pluginutils' -import { normalizePath } from 'vite' +import { createFilter, normalizePath } from 'vite' import type { Plugin, PluginOption, ResolvedConfig } from 'vite' import { addRefreshWrapper, diff --git a/packages/plugin-vue-jsx/package.json b/packages/plugin-vue-jsx/package.json index 1e345081113256..e4e483dda373f6 100644 --- a/packages/plugin-vue-jsx/package.json +++ b/packages/plugin-vue-jsx/package.json @@ -35,10 +35,9 @@ }, "homepage": "https://github.com/vitejs/vite/tree/main/packages/plugin-vue-jsx#readme", "dependencies": { - "@babel/core": "^7.18.2", + "@babel/core": "^7.18.5", "@babel/plugin-syntax-import-meta": "^7.10.4", "@babel/plugin-transform-typescript": "^7.18.4", - "@rollup/pluginutils": "^4.2.1", "@vue/babel-plugin-jsx": "^1.1.1" }, "devDependencies": { diff --git a/packages/plugin-vue-jsx/src/index.ts b/packages/plugin-vue-jsx/src/index.ts index c3edb77c5b0e6e..48de3da46448ce 100644 --- a/packages/plugin-vue-jsx/src/index.ts +++ b/packages/plugin-vue-jsx/src/index.ts @@ -5,7 +5,7 @@ import * as babel from '@babel/core' import jsx from '@vue/babel-plugin-jsx' // @ts-expect-error missing type import importMeta from '@babel/plugin-syntax-import-meta' -import { createFilter, normalizePath } from '@rollup/pluginutils' +import { createFilter, normalizePath } from 'vite' import type { ComponentOptions } from 'vue' import type { Plugin } from 'vite' import type { Options } from './types' diff --git a/packages/plugin-vue-jsx/src/types.ts b/packages/plugin-vue-jsx/src/types.ts index aa30b7435329f4..a3be580859ea00 100644 --- a/packages/plugin-vue-jsx/src/types.ts +++ b/packages/plugin-vue-jsx/src/types.ts @@ -1,5 +1,5 @@ import type { VueJSXPluginOptions } from '@vue/babel-plugin-jsx' -import type { FilterPattern } from '@rollup/pluginutils' +import type { FilterPattern } from 'vite' export interface FilterOptions { include?: FilterPattern diff --git a/packages/plugin-vue/package.json b/packages/plugin-vue/package.json index d1ca5fd8f9ce46..7a5195fbf36703 100644 --- a/packages/plugin-vue/package.json +++ b/packages/plugin-vue/package.json @@ -42,13 +42,10 @@ "@jridgewell/gen-mapping": "^0.3.1", "@jridgewell/trace-mapping": "^0.3.13", "debug": "^4.3.4", - "rollup": "^2.72.1", + "rollup": "^2.75.6", "slash": "^4.0.0", "source-map": "^0.6.1", "vite": "workspace:*", "vue": "^3.2.37" - }, - "dependencies": { - "@rollup/pluginutils": "^4.2.1" } } diff --git a/packages/plugin-vue/src/index.ts b/packages/plugin-vue/src/index.ts index 6ccbe7bf06da80..3e1b2c3be1b7f5 100644 --- a/packages/plugin-vue/src/index.ts +++ b/packages/plugin-vue/src/index.ts @@ -1,6 +1,6 @@ import fs from 'fs' import type { Plugin, ViteDevServer } from 'vite' -import { createFilter } from '@rollup/pluginutils' +import { createFilter } from 'vite' /* eslint-disable import/no-duplicates */ import type { SFCBlock, diff --git a/packages/plugin-vue/src/main.ts b/packages/plugin-vue/src/main.ts index be11de7e33a23a..e92c48673a680e 100644 --- a/packages/plugin-vue/src/main.ts +++ b/packages/plugin-vue/src/main.ts @@ -1,13 +1,12 @@ import path from 'path' import type { SFCBlock, SFCDescriptor } from 'vue/compiler-sfc' -import type { PluginContext, SourceMap, TransformPluginContext } from 'rollup' -import { normalizePath } from '@rollup/pluginutils' +import type { PluginContext, TransformPluginContext } from 'rollup' import type { RawSourceMap } from 'source-map' import type { EncodedSourceMap as TraceEncodedSourceMap } from '@jridgewell/trace-mapping' import { TraceMap, eachMapping } from '@jridgewell/trace-mapping' import type { EncodedSourceMap as GenEncodedSourceMap } from '@jridgewell/gen-mapping' import { addMapping, fromMap, toEncodedMap } from '@jridgewell/gen-mapping' -import { transformWithEsbuild } from 'vite' +import { normalizePath, transformWithEsbuild } from 'vite' import { createDescriptor, getPrevDescriptor, @@ -47,7 +46,7 @@ export async function transformMain( const hasScoped = descriptor.styles.some((s) => s.scoped) // script - const { code: scriptCode, map } = await genScriptCode( + const { code: scriptCode, map: scriptMap } = await genScriptCode( descriptor, options, pluginContext, @@ -59,7 +58,7 @@ export async function transformMain( descriptor.template && !isUseInlineTemplate(descriptor, !devServer) let templateCode = '' - let templateMap: RawSourceMap | undefined + let templateMap: RawSourceMap | undefined = undefined if (hasTemplateImport) { ;({ code: templateCode, map: templateMap } = await genTemplateCode( descriptor, @@ -157,40 +156,46 @@ export async function transformMain( ) } - // if the template is inlined into the main module (indicated by the presence - // of templateMap, we need to concatenate the two source maps. - let resolvedMap = options.sourceMap ? map : undefined - if (resolvedMap && templateMap) { - const gen = fromMap( - // version property of result.map is declared as string - // but actually it is `3` - map as Omit as TraceEncodedSourceMap - ) - const tracer = new TraceMap( - // same above - templateMap as Omit as TraceEncodedSourceMap - ) - const offset = (scriptCode.match(/\r?\n/g)?.length ?? 0) + 1 - eachMapping(tracer, (m) => { - if (m.source == null) return - addMapping(gen, { - source: m.source, - original: { line: m.originalLine, column: m.originalColumn }, - generated: { - line: m.generatedLine + offset, - column: m.generatedColumn - } + let resolvedMap: RawSourceMap | undefined = undefined + if (options.sourceMap) { + if (scriptMap && templateMap) { + // if the template is inlined into the main module (indicated by the presence + // of templateMap, we need to concatenate the two source maps. + + const gen = fromMap( + // version property of result.map is declared as string + // but actually it is `3` + scriptMap as Omit as TraceEncodedSourceMap + ) + const tracer = new TraceMap( + // same above + templateMap as Omit as TraceEncodedSourceMap + ) + const offset = (scriptCode.match(/\r?\n/g)?.length ?? 0) + 1 + eachMapping(tracer, (m) => { + if (m.source == null) return + addMapping(gen, { + source: m.source, + original: { line: m.originalLine, column: m.originalColumn }, + generated: { + line: m.generatedLine + offset, + column: m.generatedColumn + } + }) }) - }) - // same above - resolvedMap = toEncodedMap(gen) as Omit< - GenEncodedSourceMap, - 'version' - > as RawSourceMap - // if this is a template only update, we will be reusing a cached version - // of the main module compile result, which has outdated sourcesContent. - resolvedMap.sourcesContent = templateMap.sourcesContent + // same above + resolvedMap = toEncodedMap(gen) as Omit< + GenEncodedSourceMap, + 'version' + > as RawSourceMap + // if this is a template only update, we will be reusing a cached version + // of the main module compile result, which has outdated sourcesContent. + resolvedMap.sourcesContent = templateMap.sourcesContent + } else { + // if one of `scriptMap` and `templateMap` is empty, use the other one + resolvedMap = scriptMap ?? templateMap + } } if (!attachedProps.length) { @@ -288,10 +293,10 @@ async function genScriptCode( ssr: boolean ): Promise<{ code: string - map: RawSourceMap + map: RawSourceMap | undefined }> { let scriptCode = `const _sfc_main = {}` - let map: RawSourceMap | SourceMap | undefined + let map: RawSourceMap | undefined const script = resolveScript(descriptor, options, ssr) if (script) { @@ -323,7 +328,7 @@ async function genScriptCode( } return { code: scriptCode, - map: map as any + map } } diff --git a/packages/vite/CHANGELOG.md b/packages/vite/CHANGELOG.md index 92a05ffb43ea64..03b7be90f66c65 100644 --- a/packages/vite/CHANGELOG.md +++ b/packages/vite/CHANGELOG.md @@ -1,3 +1,40 @@ +## 3.0.0-alpha.12 (2022-06-16) + +* chore: correct typo in console message (#8618) ([13d05bd](https://github.com/vitejs/vite/commit/13d05bd)), closes [#8618](https://github.com/vitejs/vite/issues/8618) +* chore: enable eslint and prettier cache (#8585) ([d7beaeb](https://github.com/vitejs/vite/commit/d7beaeb)), closes [#8585](https://github.com/vitejs/vite/issues/8585) +* chore: tweak server start output (#8582) ([3439132](https://github.com/vitejs/vite/commit/3439132)), closes [#8582](https://github.com/vitejs/vite/issues/8582) +* fix: allow cache overlap in parallel builds (#8592) ([2dd0b49](https://github.com/vitejs/vite/commit/2dd0b49)), closes [#8592](https://github.com/vitejs/vite/issues/8592) +* fix: avoid replacing defines and NODE_ENV in optimized deps (fix #8593) (#8606) ([739175b](https://github.com/vitejs/vite/commit/739175b)), closes [#8593](https://github.com/vitejs/vite/issues/8593) [#8606](https://github.com/vitejs/vite/issues/8606) +* fix: sequential injection of tags in transformIndexHtml (#5851) (#6901) ([649c7f6](https://github.com/vitejs/vite/commit/649c7f6)), closes [#5851](https://github.com/vitejs/vite/issues/5851) [#6901](https://github.com/vitejs/vite/issues/6901) +* fix(asset): respect assetFileNames if rollupOptions.output is an array (#8561) ([4e6c26f](https://github.com/vitejs/vite/commit/4e6c26f)), closes [#8561](https://github.com/vitejs/vite/issues/8561) +* fix(css): escape pattern chars from base path in postcss dir-dependency messages (#7081) ([5151e74](https://github.com/vitejs/vite/commit/5151e74)), closes [#7081](https://github.com/vitejs/vite/issues/7081) +* fix(optimizer): browser mapping for yarn pnp (#6493) ([c1c7af3](https://github.com/vitejs/vite/commit/c1c7af3)), closes [#6493](https://github.com/vitejs/vite/issues/6493) +* feat: 500 response if the node proxy request fails (#7398) ([73e1775](https://github.com/vitejs/vite/commit/73e1775)), closes [#7398](https://github.com/vitejs/vite/issues/7398) +* docs: worker related notes (#8554) ([c0c5e1a](https://github.com/vitejs/vite/commit/c0c5e1a)), closes [#8554](https://github.com/vitejs/vite/issues/8554) + + + +## 3.0.0-alpha.11 (2022-06-14) + +* fix: add missed JPEG file extensions to `KNOWN_ASSET_TYPES` (#8565) ([2dfc015](https://github.com/vitejs/vite/commit/2dfc015)), closes [#8565](https://github.com/vitejs/vite/issues/8565) +* fix: default export module transformation for vitest spy (#8567) ([d357e33](https://github.com/vitejs/vite/commit/d357e33)), closes [#8567](https://github.com/vitejs/vite/issues/8567) +* fix: default host to `localhost` instead of `127.0.0.1` (#8543) ([49c0896](https://github.com/vitejs/vite/commit/49c0896)), closes [#8543](https://github.com/vitejs/vite/issues/8543) +* fix: dont handle sigterm in middleware mode (#8550) ([c6f43dd](https://github.com/vitejs/vite/commit/c6f43dd)), closes [#8550](https://github.com/vitejs/vite/issues/8550) +* fix: mime missing extensions (#8568) ([acf3024](https://github.com/vitejs/vite/commit/acf3024)), closes [#8568](https://github.com/vitejs/vite/issues/8568) +* fix: objurl for type module, and concurrent tests (#8541) ([26ecd5a](https://github.com/vitejs/vite/commit/26ecd5a)), closes [#8541](https://github.com/vitejs/vite/issues/8541) +* fix: outdated optimized dep removed from module graph (#8533) ([3f4d22d](https://github.com/vitejs/vite/commit/3f4d22d)), closes [#8533](https://github.com/vitejs/vite/issues/8533) +* fix(config): only rewrite .js loader in `loadConfigFromBundledFile` (#8556) ([2548dd3](https://github.com/vitejs/vite/commit/2548dd3)), closes [#8556](https://github.com/vitejs/vite/issues/8556) +* fix(deps): update all non-major dependencies (#8558) ([9a1fd4c](https://github.com/vitejs/vite/commit/9a1fd4c)), closes [#8558](https://github.com/vitejs/vite/issues/8558) +* fix(ssr): dont replace rollup input (#7275) ([9a88afa](https://github.com/vitejs/vite/commit/9a88afa)), closes [#7275](https://github.com/vitejs/vite/issues/7275) +* chore: include 2.9.10-2.9.12 changelog in main (#8535) ([87f58ad](https://github.com/vitejs/vite/commit/87f58ad)), closes [#8535](https://github.com/vitejs/vite/issues/8535) +* chore: refactor interop named imports (#8544) ([63b523a](https://github.com/vitejs/vite/commit/63b523a)), closes [#8544](https://github.com/vitejs/vite/issues/8544) +* chore: remove rollup `namespaceToStringTag` (#8569) ([b85802a](https://github.com/vitejs/vite/commit/b85802a)), closes [#8569](https://github.com/vitejs/vite/issues/8569) +* chore: remove unused timestamp option (#8545) ([d641860](https://github.com/vitejs/vite/commit/d641860)), closes [#8545](https://github.com/vitejs/vite/issues/8545) +* chore: update major deps (#8572) ([0e20949](https://github.com/vitejs/vite/commit/0e20949)), closes [#8572](https://github.com/vitejs/vite/issues/8572) +* feat: expose createFilter util (#8562) ([c5c424a](https://github.com/vitejs/vite/commit/c5c424a)), closes [#8562](https://github.com/vitejs/vite/issues/8562) + + + ## 3.0.0-alpha.10 (2022-06-10) * fix: deps optimizer idle logic for workers (fix #8479) (#8511) ([1e05548](https://github.com/vitejs/vite/commit/1e05548)), closes [#8479](https://github.com/vitejs/vite/issues/8479) [#8511](https://github.com/vitejs/vite/issues/8511) diff --git a/packages/vite/LICENSE.md b/packages/vite/LICENSE.md index e65a09826f0f4a..995c28ad9fad20 100644 --- a/packages/vite/LICENSE.md +++ b/packages/vite/LICENSE.md @@ -535,6 +535,28 @@ License: MIT By: Rich Harris Repository: rollup/plugins +> The MIT License (MIT) +> +> Copyright (c) 2019 RollupJS Plugin Contributors (https://github.com/rollup/plugins/graphs/contributors) +> +> Permission is hereby granted, free of charge, to any person obtaining a copy +> of this software and associated documentation files (the "Software"), to deal +> in the Software without restriction, including without limitation the rights +> to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +> copies of the Software, and to permit persons to whom the Software is +> furnished to do so, subject to the following conditions: +> +> The above copyright notice and this permission notice shall be included in +> all copies or substantial portions of the Software. +> +> THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +> IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +> FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +> AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +> LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +> OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +> THE SOFTWARE. + --------------------------------------- ## @vue/compiler-core @@ -660,7 +682,7 @@ Repository: chalk/ansi-regex > MIT License > -> Copyright (c) Sindre Sorhus (sindresorhus.com) +> Copyright (c) Sindre Sorhus (https://sindresorhus.com) > > Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: > @@ -3455,7 +3477,7 @@ Repository: chalk/strip-ansi > MIT License > -> Copyright (c) Sindre Sorhus (sindresorhus.com) +> Copyright (c) Sindre Sorhus (https://sindresorhus.com) > > Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: > @@ -3832,7 +3854,7 @@ License: ISC By: Eemeli Aro Repository: github:eemeli/yaml -> Copyright 2018 Eemeli Aro +> Copyright Eemeli Aro > > Permission to use, copy, modify, and/or distribute this software for any purpose > with or without fee is hereby granted, provided that the above copyright notice diff --git a/packages/vite/client.d.ts b/packages/vite/client.d.ts index 948d66d1d7ad74..e99b4a526b7a58 100644 --- a/packages/vite/client.d.ts +++ b/packages/vite/client.d.ts @@ -74,6 +74,18 @@ declare module '*.jpeg' { const src: string export default src } +declare module '*.jfif' { + const src: string + export default src +} +declare module '*.pjpeg' { + const src: string + export default src +} +declare module '*.pjp' { + const src: string + export default src +} declare module '*.png' { const src: string export default src diff --git a/packages/vite/package.json b/packages/vite/package.json index 56087413ae7908..805d6485d53acd 100644 --- a/packages/vite/package.json +++ b/packages/vite/package.json @@ -1,6 +1,6 @@ { "name": "vite", - "version": "3.0.0-alpha.10", + "version": "3.0.0-alpha.12", "type": "module", "license": "MIT", "author": "Evan You", @@ -51,23 +51,23 @@ "patch-types": "esno scripts/patchTypes.ts", "roll-types": "api-extractor run && rimraf temp", "check-dist-types": "tsc --project tsconfig.check.json", - "lint": "eslint --ext .ts src/**", - "format": "prettier --write --parser typescript \"src/**/*.ts\"", + "lint": "eslint --cache --ext .ts src/**", + "format": "prettier --write --cache --parser typescript \"src/**/*.ts\"", "prepublishOnly": "npm run build" }, "//": "READ CONTRIBUTING.md to understand what to put under deps vs. devDeps!", "dependencies": { - "esbuild": "^0.14.38", + "esbuild": "^0.14.43", "postcss": "^8.4.14", "resolve": "^1.22.0", - "rollup": "^2.72.1" + "rollup": "^2.75.6" }, "optionalDependencies": { "fsevents": "~2.3.2" }, "devDependencies": { "@ampproject/remapping": "^2.2.0", - "@babel/parser": "^7.18.4", + "@babel/parser": "^7.18.5", "@babel/types": "^7.18.4", "@jridgewell/trace-mapping": "^0.3.13", "@rollup/plugin-alias": "^3.1.9", @@ -79,7 +79,7 @@ "@rollup/pluginutils": "^4.2.1", "@vue/compiler-dom": "^3.2.37", "acorn": "^8.7.1", - "cac": "6.7.9", + "cac": "^6.7.12", "chokidar": "^3.5.3", "connect": "^3.7.0", "connect-history-api-fallback": "^1.6.0", @@ -91,7 +91,7 @@ "dotenv-expand": "^5.1.0", "es-module-lexer": "^0.10.5", "esno": "^0.16.3", - "estree-walker": "^2.0.2", + "estree-walker": "^3.0.1", "etag": "^1.8.1", "fast-glob": "^3.2.11", "http-proxy": "^1.18.1", @@ -103,17 +103,17 @@ "node-forge": "^1.3.1", "okie": "^1.0.1", "open": "^8.4.0", - "periscopic": "^2.0.3", + "periscopic": "^3.0.4", "picocolors": "^1.0.0", "postcss-import": "^14.1.0", - "postcss-load-config": "^3.1.4", + "postcss-load-config": "^4.0.1", "postcss-modules": "^4.3.1", "resolve.exports": "^1.1.0", "rollup-plugin-license": "^2.8.1", "sirv": "^2.0.2", "source-map-js": "^1.0.2", "source-map-support": "^0.5.21", - "strip-ansi": "^6.0.1", + "strip-ansi": "^7.0.1", "strip-literal": "^0.3.0", "tsconfck": "^2.0.1", "tslib": "^2.4.0", diff --git a/packages/vite/rollup.config.ts b/packages/vite/rollup.config.ts index 62f14f7790fa1d..0e321956a0d287 100644 --- a/packages/vite/rollup.config.ts +++ b/packages/vite/rollup.config.ts @@ -123,11 +123,6 @@ function createNodePlugins( src: `require('fsevents')`, replacement: `__require('fsevents')` }, - // cac re-assigns module.exports even in its mjs dist - 'cac/dist/index.mjs': { - src: `if (typeof module !== "undefined") {`, - replacement: `if (false) {` - }, // postcss-import -> sugarss 'process-content.js': { src: 'require("sugarss")', @@ -208,7 +203,7 @@ function createCjsConfig(isProduction: boolean) { ...Object.keys(pkg.dependencies), ...(isProduction ? [] : Object.keys(pkg.devDependencies)) ], - plugins: [...createNodePlugins(false, false, false), bundleSizeLimit(55)] + plugins: [...createNodePlugins(false, false, false), bundleSizeLimit(120)] }) } diff --git a/packages/vite/src/node/__tests__/utils.spec.ts b/packages/vite/src/node/__tests__/utils.spec.ts index c8a824bbb3d28f..5a6ea4d55de30c 100644 --- a/packages/vite/src/node/__tests__/utils.spec.ts +++ b/packages/vite/src/node/__tests__/utils.spec.ts @@ -49,9 +49,9 @@ describe('injectQuery', () => { }) describe('resolveHostname', () => { - test('defaults to 127.0.0.1', () => { + test('defaults to localhost', () => { expect(resolveHostname(undefined)).toEqual({ - host: '127.0.0.1', + host: 'localhost', name: 'localhost' }) }) @@ -62,6 +62,27 @@ describe('resolveHostname', () => { name: 'localhost' }) }) + + test('accepts 0.0.0.0', () => { + expect(resolveHostname('0.0.0.0')).toEqual({ + host: '0.0.0.0', + name: 'localhost' + }) + }) + + test('accepts ::', () => { + expect(resolveHostname('::')).toEqual({ + host: '::', + name: 'localhost' + }) + }) + + test('accepts 0000:0000:0000:0000:0000:0000:0000:0000', () => { + expect(resolveHostname('0000:0000:0000:0000:0000:0000:0000:0000')).toEqual({ + host: '0000:0000:0000:0000:0000:0000:0000:0000', + name: 'localhost' + }) + }) }) test('ts import of file with .js extension', () => { diff --git a/packages/vite/src/node/build.ts b/packages/vite/src/node/build.ts index 03aad1a6d1a7bf..ccd644f3f8ee5e 100644 --- a/packages/vite/src/node/build.ts +++ b/packages/vite/src/node/build.ts @@ -407,7 +407,6 @@ async function doBuild( } const rollupOptions: RollupOptions = { - input, context: 'globalThis', preserveEntrySignatures: ssr ? 'allow-extension' @@ -415,6 +414,7 @@ async function doBuild( ? 'strict' : false, ...options.rollupOptions, + input, plugins, external, onwarn(warning, warn) { @@ -460,6 +460,9 @@ async function doBuild( exports: cjsSsrBuild ? 'named' : 'auto', sourcemap: options.sourcemap, name: libOptions ? libOptions.name : undefined, + // es2015 enables `generatedCode.symbols` + // - #764 add `Symbol.toStringTag` when build es module into cjs chunk + // - #1048 add `Symbol.toStringTag` for module default export generatedCode: 'es2015', entryFileNames: ssr ? `[name].${jsExt}` @@ -472,9 +475,6 @@ async function doBuild( assetFileNames: libOptions ? `[name].[ext]` : path.posix.join(options.assetsDir, `[name].[hash].[ext]`), - // #764 add `Symbol.toStringTag` when build es module into cjs chunk - // #1048 add `Symbol.toStringTag` for module default export - namespaceToStringTag: true, inlineDynamicImports: output.format === 'umd' || output.format === 'iife' || diff --git a/packages/vite/src/node/config.ts b/packages/vite/src/node/config.ts index 99ec143fa85f0f..24b0e67fc9d390 100644 --- a/packages/vite/src/node/config.ts +++ b/packages/vite/src/node/config.ts @@ -5,7 +5,6 @@ import { performance } from 'perf_hooks' import { createRequire } from 'module' import colors from 'picocolors' import type { Alias, AliasOptions } from 'types/alias' -import { createFilter } from '@rollup/pluginutils' import aliasPlugin from '@rollup/plugin-alias' import { build } from 'esbuild' import type { RollupOptions } from 'rollup' @@ -19,6 +18,7 @@ import { resolvePreviewOptions } from './preview' import type { CSSOptions } from './plugins/css' import { createDebugger, + createFilter, dynamicImport, isExternalUrl, isObject, @@ -253,6 +253,7 @@ export type ResolvedConfig = Readonly< command: 'build' | 'serve' mode: string isWorker: boolean + // in nested worker bundle to find the main config /** @internal */ mainConfig: ResolvedConfig | null isProduction: boolean @@ -521,7 +522,9 @@ export async function resolveConfig( spa: config.spa ?? true } - // flat config.worker.plugin + // Some plugins that aren't intended to work in the bundling of workers (doing post-processing at build time for example). + // And Plugins may also have cached that could be corrupted by being used in these extra rollup calls. + // So we need to separate the worker plugin from the plugin that vite needs to run. const [workerPrePlugins, workerNormalPlugins, workerPostPlugins] = sortUserPlugins(config.worker?.plugins as Plugin[]) const workerResolved: ResolvedConfig = { @@ -566,6 +569,29 @@ export async function resolveConfig( ) } + // Check if all assetFileNames have the same reference. + // If not, display a warn for user. + const outputOption = config.build?.rollupOptions?.output ?? [] + // Use isArray to narrow its type to array + if (Array.isArray(outputOption)) { + const assetFileNamesList = outputOption.map( + (output) => output.assetFileNames + ) + if (assetFileNamesList.length > 1) { + const firstAssetFileNames = assetFileNamesList[0] + const hasDifferentReference = assetFileNamesList.some( + (assetFileNames) => assetFileNames !== firstAssetFileNames + ) + if (hasDifferentReference) { + resolved.logger.warn( + colors.yellow(` +assetFileNames isn't equal for every build.rollupOptions.output. A single pattern across all outputs is supported by Vite. +`) + ) + } + } + } + return resolved } diff --git a/packages/vite/src/node/constants.ts b/packages/vite/src/node/constants.ts index 4e5d3da96f50da..3b4d496e30bd3c 100644 --- a/packages/vite/src/node/constants.ts +++ b/packages/vite/src/node/constants.ts @@ -62,11 +62,17 @@ export const CLIENT_DIR = path.dirname(CLIENT_ENTRY) // ** READ THIS ** before editing `KNOWN_ASSET_TYPES`. // If you add an asset to `KNOWN_ASSET_TYPES`, make sure to also add it -// to the TypeScript declaration file `packages/vite/client.d.ts`. +// to the TypeScript declaration file `packages/vite/client.d.ts` and +// add a mime type to the `registerCustomMime` in +// `packages/vite/src/node/plugin/assets.ts` if mime type cannot be +// looked up by mrmime. export const KNOWN_ASSET_TYPES = [ // images 'png', 'jpe?g', + 'jfif', + 'pjpeg', + 'pjp', 'gif', 'svg', 'ico', @@ -99,3 +105,15 @@ export const DEFAULT_ASSETS_RE = new RegExp( ) export const DEP_VERSION_RE = /[\?&](v=[\w\.-]+)\b/ + +export const loopbackHosts = new Set([ + 'localhost', + '127.0.0.1', + '::1', + '0000:0000:0000:0000:0000:0000:0000:0001' +]) +export const wildcardHosts = new Set([ + '0.0.0.0', + '::', + '0000:0000:0000:0000:0000:0000:0000:0000' +]) diff --git a/packages/vite/src/node/http.ts b/packages/vite/src/node/http.ts index 2a1c7c920b8ea9..c2757f36477600 100644 --- a/packages/vite/src/node/http.ts +++ b/packages/vite/src/node/http.ts @@ -5,6 +5,7 @@ import type { OutgoingHttpHeaders as HttpServerHeaders } from 'http' import type { ServerOptions as HttpsServerOptions } from 'https' +import { promises as dns } from 'dns' import type { Connect } from 'types/connect' import { isObject } from './utils' import type { ProxyOptions } from './server/middlewares/proxy' @@ -184,9 +185,16 @@ export async function httpServerStart( logger: Logger } ): Promise { - return new Promise((resolve, reject) => { - let { port, strictPort, host, logger } = serverOptions + let { port, strictPort, host, logger } = serverOptions + + // This could be removed when Vite only supports Node 17+ because verbatim=true is default + // https://github.com/nodejs/node/pull/39987 + if (host === 'localhost') { + const addr = await dns.lookup('localhost', { verbatim: true }) + host = addr.address + } + return new Promise((resolve, reject) => { const onError = (e: Error & { code?: string }) => { if (e.code === 'EADDRINUSE') { if (strictPort) { diff --git a/packages/vite/src/node/index.ts b/packages/vite/src/node/index.ts index 2eb69befb4ee2a..38952e8a466b62 100644 --- a/packages/vite/src/node/index.ts +++ b/packages/vite/src/node/index.ts @@ -10,6 +10,7 @@ export { resolvePackageData } from './packages' export * from './publicUtils' // additional types +export type { FilterPattern } from './utils' export type { CorsOptions, CorsOrigin, CommonServerOptions } from './http' export type { ViteDevServer, diff --git a/packages/vite/src/node/logger.ts b/packages/vite/src/node/logger.ts index 1176a265a4fcbe..0518d86b271ac7 100644 --- a/packages/vite/src/node/logger.ts +++ b/packages/vite/src/node/logger.ts @@ -8,6 +8,7 @@ import type { RollupError } from 'rollup' import type { CommonServerOptions } from './http' import type { Hostname } from './utils' import { resolveHostname } from './utils' +import { loopbackHosts, wildcardHosts } from './constants' import type { ResolvedConfig } from '.' export type LogType = 'error' | 'warn' | 'info' @@ -172,19 +173,30 @@ function printServerUrls( info: Logger['info'] ): void { const urls: Array<{ label: string; url: string }> = [] + const notes: Array<{ label: string; message: string }> = [] + + if (hostname.host && loopbackHosts.has(hostname.host)) { + let hostnameName = hostname.name + if ( + hostnameName === '::1' || + hostnameName === '0000:0000:0000:0000:0000:0000:0000:0001' + ) { + hostnameName = `[${hostnameName}]` + } - if (hostname.host === '127.0.0.1') { urls.push({ label: 'Local', url: colors.cyan( - `${protocol}://${hostname.name}:${colors.bold(port)}${base}` + `${protocol}://${hostnameName}:${colors.bold(port)}${base}` ) }) - if (hostname.name !== '127.0.0.1') { - urls.push({ - label: 'Network', - url: colors.dim(`use ${colors.white(colors.bold('--host'))} to expose`) + if (hostname.name === 'localhost') { + notes.push({ + label: 'Hint', + message: colors.dim( + `Use ${colors.white(colors.bold('--host'))} to expose to network.` + ) }) } } else { @@ -208,15 +220,34 @@ function printServerUrls( }) } - const length = urls.reduce( - (length, { label }) => Math.max(length, label.length), - 0 + if (!hostname.host || wildcardHosts.has(hostname.host)) { + notes.push({ + label: 'Note', + message: colors.dim( + 'You are using a wildcard host. Ports might be overridden.' + ) + }) + } + + const length = Math.max( + ...[...urls, ...notes].map(({ label }) => label.length) ) - urls.forEach(({ label, url: text }) => { + const print = ( + iconWithColor: string, + label: string, + messageWithColor: string + ) => { info( - ` ${colors.green('➜')} ${colors.bold(label)}: ${' '.repeat( + ` ${iconWithColor} ${colors.bold(label)}: ${' '.repeat( length - label.length - )}${text}` + )}${messageWithColor}` ) + } + + urls.forEach(({ label, url: text }) => { + print(colors.green('➜'), label, text) + }) + notes.forEach(({ label, message: text }) => { + print(colors.white('❖'), label, text) }) } diff --git a/packages/vite/src/node/optimizer/esbuildDepPlugin.ts b/packages/vite/src/node/optimizer/esbuildDepPlugin.ts index fbfb658e9f7505..aacabbdf9b1a3c 100644 --- a/packages/vite/src/node/optimizer/esbuildDepPlugin.ts +++ b/packages/vite/src/node/optimizer/esbuildDepPlugin.ts @@ -80,6 +80,24 @@ export function esbuildDepPlugin( return resolver(id, _importer, undefined) } + const resolveResult = (id: string, resolved: string) => { + if (resolved.startsWith(browserExternalId)) { + return { + path: id, + namespace: 'browser-external' + } + } + if (isExternalUrl(resolved)) { + return { + path: resolved, + external: true + } + } + return { + path: path.resolve(resolved) + } + } + return { name: 'vite:dep-pre-bundle', setup(build) { @@ -156,21 +174,7 @@ export function esbuildDepPlugin( // use vite's own resolver const resolved = await resolve(id, importer, kind) if (resolved) { - if (resolved.startsWith(browserExternalId)) { - return { - path: id, - namespace: 'browser-external' - } - } - if (isExternalUrl(resolved)) { - return { - path: resolved, - external: true - } - } - return { - path: path.resolve(resolved) - } + return resolveResult(id, resolved) } } ) @@ -220,24 +224,29 @@ export function esbuildDepPlugin( build.onLoad( { filter: /.*/, namespace: 'browser-external' }, ({ path }) => { - return { - // Return in CJS to intercept named imports. Use `Object.create` to - // create the Proxy in the prototype to workaround esbuild issue. Why? - // - // In short, esbuild cjs->esm flow: - // 1. Create empty object using `Object.create(Object.getPrototypeOf(module.exports))`. - // 2. Assign props of `module.exports` to the object. - // 3. Return object for ESM use. - // - // If we do `module.exports = new Proxy({}, {})`, step 1 returns empty object, - // step 2 does nothing as there's no props for `module.exports`. The final object - // is just an empty object. - // - // Creating the Proxy in the prototype satisfies step 1 immediately, which means - // the returned object is a Proxy that we can intercept. - // - // Note: Skip keys that are accessed by esbuild and browser devtools. - contents: `\ + if (config.isProduction) { + return { + contents: 'module.exports = {}' + } + } else { + return { + // Return in CJS to intercept named imports. Use `Object.create` to + // create the Proxy in the prototype to workaround esbuild issue. Why? + // + // In short, esbuild cjs->esm flow: + // 1. Create empty object using `Object.create(Object.getPrototypeOf(module.exports))`. + // 2. Assign props of `module.exports` to the object. + // 3. Return object for ESM use. + // + // If we do `module.exports = new Proxy({}, {})`, step 1 returns empty object, + // step 2 does nothing as there's no props for `module.exports`. The final object + // is just an empty object. + // + // Creating the Proxy in the prototype satisfies step 1 immediately, which means + // the returned object is a Proxy that we can intercept. + // + // Note: Skip keys that are accessed by esbuild and browser devtools. + contents: `\ module.exports = Object.create(new Proxy({}, { get(_, key) { if ( @@ -250,6 +259,7 @@ module.exports = Object.create(new Proxy({}, { } } }))` + } } } ) @@ -258,11 +268,20 @@ module.exports = Object.create(new Proxy({}, { if (isRunningWithYarnPnp) { build.onResolve( { filter: /.*/ }, - async ({ path, importer, kind, resolveDir }) => ({ - // pass along resolveDir for entries - path: await resolve(path, importer, kind, resolveDir) - }) + async ({ path: id, importer, kind, resolveDir, namespace }) => { + const resolved = await resolve( + id, + importer, + kind, + // pass along resolveDir for entries + namespace === 'dep' ? resolveDir : undefined + ) + if (resolved) { + return resolveResult(id, resolved) + } + } ) + build.onLoad({ filter: /.*/ }, async (args) => ({ contents: await fs.readFile(args.path), loader: 'default' diff --git a/packages/vite/src/node/optimizer/index.ts b/packages/vite/src/node/optimizer/index.ts index 61961b477dda3e..636c2a9a7763ec 100644 --- a/packages/vite/src/node/optimizer/index.ts +++ b/packages/vite/src/node/optimizer/index.ts @@ -448,6 +448,7 @@ export async function runOptimizeDeps( depsInfo: Record, ssr: boolean = !!resolvedConfig.build.ssr ): Promise { + const isBuild = resolvedConfig.command === 'build' const config: ResolvedConfig = { ...resolvedConfig, command: 'build' @@ -534,20 +535,15 @@ export async function runOptimizeDeps( flatIdToExports[flatId] = exportsData } - const define: Record = { - 'process.env.NODE_ENV': JSON.stringify(process.env.NODE_ENV || config.mode) - } - for (const key in config.define) { - const value = config.define[key] - define[key] = typeof value === 'string' ? value : JSON.stringify(value) - } - const start = performance.now() const result = await build({ absWorkingDir: process.cwd(), entryPoints: Object.keys(flatIdDeps), bundle: true, + // Ensure resolution is handled by esbuildDepPlugin and + // avoid replacing `process.env.NODE_ENV` for 'browser' + platform: 'neutral', format: 'esm', target: config.build.target || undefined, external: config.optimizeDeps?.exclude, @@ -555,9 +551,8 @@ export async function runOptimizeDeps( splitting: true, sourcemap: true, outdir: processingCacheDir, - ignoreAnnotations: resolvedConfig.command !== 'build', + ignoreAnnotations: !isBuild, metafile: true, - define, plugins: [ ...plugins, esbuildDepPlugin(flatIdDeps, flatIdToExports, config) @@ -686,7 +681,11 @@ export function getOptimizedDepPath( function getDepsCacheSuffix(config: ResolvedConfig, ssr: boolean): string { let suffix = '' if (config.command === 'build') { - suffix += '_build' + // Differentiate build caches depending on outDir to allow parallel builds + const { outDir } = config.build + const buildId = + outDir.length > 8 || outDir.includes('/') ? getHash(outDir) : outDir + suffix += `_build-${buildId}` } if (ssr) { suffix += '_ssr' @@ -958,7 +957,6 @@ export function getDepHash(config: ResolvedConfig): string { { mode: process.env.NODE_ENV || config.mode, root: config.root, - define: config.define, resolve: config.resolve, buildTarget: config.build.target, assetsInclude: config.assetsInclude, diff --git a/packages/vite/src/node/packages.ts b/packages/vite/src/node/packages.ts index 1fb2e7b4a21c06..e1c0c18eb5a119 100644 --- a/packages/vite/src/node/packages.ts +++ b/packages/vite/src/node/packages.ts @@ -1,7 +1,6 @@ import fs from 'fs' import path from 'path' -import { createFilter } from '@rollup/pluginutils' -import { createDebugger, resolveFrom } from './utils' +import { createDebugger, createFilter, resolveFrom } from './utils' import type { ResolvedConfig } from './config' import type { Plugin } from './plugin' diff --git a/packages/vite/src/node/plugins/asset.ts b/packages/vite/src/node/plugins/asset.ts index 4c1bcfea0fd50b..2a119808a4eb72 100644 --- a/packages/vite/src/node/plugins/asset.ts +++ b/packages/vite/src/node/plugins/asset.ts @@ -23,6 +23,18 @@ const assetHashToFilenameMap = new WeakMap< // save hashes of the files that has been emitted in build watch const emittedHashMap = new WeakMap>() +// add own dictionary entry by directly assigning mrmime +export function registerCustomMime(): void { + // https://github.com/lukeed/mrmime/issues/3 + mrmime.mimes['ico'] = 'image/x-icon' + // https://developer.mozilla.org/en-US/docs/Web/Media/Formats/Containers#flac + mrmime.mimes['flac'] = 'audio/flac' + // mrmime and mime-db is not released yet: https://github.com/jshttp/mime-db/commit/c9242a9b7d4bb25d7a0c9244adec74aeef08d8a1 + mrmime.mimes['aac'] = 'audio/aac' + // https://developer.mozilla.org/en-US/docs/Web/HTTP/Basics_of_HTTP/MIME_types/Common_types + mrmime.mimes['eot'] = 'application/vnd.ms-fontobject' +} + /** * Also supports loading plain strings with import text from './foo.txt?raw' */ @@ -31,9 +43,8 @@ export function assetPlugin(config: ResolvedConfig): Plugin { assetHashToFilenameMap.set(config, new Map()) const relativeBase = isRelativeBase(config.base) - // add own dictionary entry by directly assigning mrmine - // https://github.com/lukeed/mrmime/issues/3 - mrmime.mimes['ico'] = 'image/x-icon' + registerCustomMime() + return { name: 'vite:asset', @@ -338,8 +349,9 @@ async function fileToBuiltUrl( (!file.endsWith('.svg') && content.length < Number(config.build.assetsInlineLimit)) ) { + const mimeType = mrmime.lookup(file) ?? 'application/octet-stream' // base64 inlined as a string - url = `data:${mrmime.lookup(file)};base64,${content.toString('base64')}` + url = `data:${mimeType};base64,${content.toString('base64')}` } else { // emit as asset // rollup supports `import.meta.ROLLUP_FILE_URL_*`, but it generates code @@ -353,11 +365,22 @@ async function fileToBuiltUrl( const { search, hash } = parseUrl(id) const postfix = (search || '') + (hash || '') const output = config.build?.rollupOptions?.output - const assetFileNames = + + const defaultAssetFileNames = path.posix.join( + config.build.assetsDir, + '[name].[hash][extname]' + ) + // Steps to determine which assetFileNames will be actually used. + // First, if output is an object or string, use assetFileNames in it. + // And a default assetFileNames as fallback. + let assetFileNames: Exclude = (output && !Array.isArray(output) ? output.assetFileNames : undefined) ?? - // defaults to '/[name].[hash][extname]' - // slightly different from rollup's one ('assets/[name]-[hash][extname]') - path.posix.join(config.build.assetsDir, '[name].[hash][extname]') + defaultAssetFileNames + if (output && Array.isArray(output)) { + // Second, if output is an array, adopt assetFileNames in the first object. + assetFileNames = output[0].assetFileNames ?? assetFileNames + } + const fileName = assetFileNamesToFileName( assetFileNames, file, diff --git a/packages/vite/src/node/plugins/css.ts b/packages/vite/src/node/plugins/css.ts index ce068ac6104f81..45af9233bf4560 100644 --- a/packages/vite/src/node/plugins/css.ts +++ b/packages/vite/src/node/plugins/css.ts @@ -861,7 +861,9 @@ async function compileCSS( // https://github.com/postcss/postcss/blob/main/docs/guidelines/plugin.md#3-dependencies const { dir, glob: globPattern = '**' } = message const pattern = - normalizePath(path.resolve(path.dirname(id), dir)) + `/` + globPattern + glob.escapePath(normalizePath(path.resolve(path.dirname(id), dir))) + + `/` + + globPattern const files = glob.sync(pattern, { ignore: ['**/node_modules/**'] }) diff --git a/packages/vite/src/node/plugins/dynamicImportVars.ts b/packages/vite/src/node/plugins/dynamicImportVars.ts index b6047aa59e138d..ff20c5725dc341 100644 --- a/packages/vite/src/node/plugins/dynamicImportVars.ts +++ b/packages/vite/src/node/plugins/dynamicImportVars.ts @@ -3,11 +3,11 @@ import MagicString from 'magic-string' import { init, parse as parseImports } from 'es-module-lexer' import type { ImportSpecifier } from 'es-module-lexer' import { parse as parseJS } from 'acorn' -import { createFilter } from '@rollup/pluginutils' import { dynamicImportToGlob } from '@rollup/plugin-dynamic-import-vars' import type { Plugin } from '../plugin' import type { ResolvedConfig } from '../config' import { + createFilter, normalizePath, parseRequest, requestQuerySplitRE, diff --git a/packages/vite/src/node/plugins/esbuild.ts b/packages/vite/src/node/plugins/esbuild.ts index 11f5ea4dd6cec0..c423edca92f9c1 100644 --- a/packages/vite/src/node/plugins/esbuild.ts +++ b/packages/vite/src/node/plugins/esbuild.ts @@ -9,13 +9,13 @@ import type { import { transform } from 'esbuild' import type { RawSourceMap } from '@ampproject/remapping' import type { SourceMap } from 'rollup' -import { createFilter } from '@rollup/pluginutils' import type { TSConfckParseOptions, TSConfckParseResult } from 'tsconfck' import { TSConfckParseError, findAll, parse } from 'tsconfck' import { cleanUrl, combineSourcemaps, createDebugger, + createFilter, ensureWatchedFile, generateCodeFrame, toUpperCaseDriveLetter diff --git a/packages/vite/src/node/plugins/html.ts b/packages/vite/src/node/plugins/html.ts index 9d06d6e168f2e1..a66a6288f8e8c2 100644 --- a/packages/vite/src/node/plugins/html.ts +++ b/packages/vite/src/node/plugins/html.ts @@ -767,11 +767,6 @@ export async function applyHtmlTransforms( hooks: IndexHtmlTransformHook[], ctx: IndexHtmlTransformContext ): Promise { - const headTags: HtmlTagDescriptor[] = [] - const headPrependTags: HtmlTagDescriptor[] = [] - const bodyTags: HtmlTagDescriptor[] = [] - const bodyPrependTags: HtmlTagDescriptor[] = [] - for (const hook of hooks) { const res = await hook(html, ctx) if (!res) { @@ -787,6 +782,12 @@ export async function applyHtmlTransforms( html = res.html || html tags = res.tags } + + const headTags: HtmlTagDescriptor[] = [] + const headPrependTags: HtmlTagDescriptor[] = [] + const bodyTags: HtmlTagDescriptor[] = [] + const bodyPrependTags: HtmlTagDescriptor[] = [] + for (const tag of tags) { if (tag.injectTo === 'body') { bodyTags.push(tag) @@ -798,21 +799,12 @@ export async function applyHtmlTransforms( headPrependTags.push(tag) } } - } - } - // inject tags - if (headPrependTags.length) { - html = injectToHead(html, headPrependTags, true) - } - if (headTags.length) { - html = injectToHead(html, headTags) - } - if (bodyPrependTags.length) { - html = injectToBody(html, bodyPrependTags, true) - } - if (bodyTags.length) { - html = injectToBody(html, bodyTags) + html = injectToHead(html, headPrependTags, true) + html = injectToHead(html, headTags) + html = injectToBody(html, bodyPrependTags, true) + html = injectToBody(html, bodyTags) + } } return html @@ -859,6 +851,8 @@ function injectToHead( tags: HtmlTagDescriptor[], prepend = false ) { + if (tags.length === 0) return html + if (prepend) { // inject as the first element of head if (headPrependInjectRE.test(html)) { @@ -893,6 +887,8 @@ function injectToBody( tags: HtmlTagDescriptor[], prepend = false ) { + if (tags.length === 0) return html + if (prepend) { // inject after body open if (bodyPrependInjectRE.test(html)) { diff --git a/packages/vite/src/node/plugins/importAnalysis.ts b/packages/vite/src/node/plugins/importAnalysis.ts index 5c64370e67843c..4e368e1bd8e7c4 100644 --- a/packages/vite/src/node/plugins/importAnalysis.ts +++ b/packages/vite/src/node/plugins/importAnalysis.ts @@ -36,6 +36,7 @@ import { normalizePath, prettifyUrl, removeImportQuery, + stripBomTag, timeFrom, transformResult, unwrapId @@ -142,10 +143,7 @@ export function importAnalysisPlugin(config: ResolvedConfig): Plugin { const start = performance.now() await init let imports: readonly ImportSpecifier[] = [] - // strip UTF-8 BOM - if (source.charCodeAt(0) === 0xfeff) { - source = source.slice(1) - } + source = stripBomTag(source) try { imports = parseImports(source)[0] } catch (e: any) { diff --git a/packages/vite/src/node/plugins/json.ts b/packages/vite/src/node/plugins/json.ts index 9c142501ff651e..216679a22148cd 100644 --- a/packages/vite/src/node/plugins/json.ts +++ b/packages/vite/src/node/plugins/json.ts @@ -9,6 +9,7 @@ import { dataToEsm } from '@rollup/pluginutils' import { SPECIAL_QUERY_RE } from '../constants' import type { Plugin } from '../plugin' +import { stripBomTag } from '../utils' export interface JsonOptions { /** @@ -43,6 +44,8 @@ export function jsonPlugin( if (!jsonExtRE.test(id)) return null if (SPECIAL_QUERY_RE.test(id)) return null + json = stripBomTag(json) + try { if (options.stringify) { if (isBuild) { diff --git a/packages/vite/src/node/plugins/wasm.ts b/packages/vite/src/node/plugins/wasm.ts index 56aa56402df8e2..19bb9a0e541892 100644 --- a/packages/vite/src/node/plugins/wasm.ts +++ b/packages/vite/src/node/plugins/wasm.ts @@ -7,11 +7,21 @@ const wasmHelperId = '/__vite-wasm-helper' const wasmHelper = async (opts = {}, url: string) => { let result if (url.startsWith('data:')) { - // @ts-ignore - const binaryString = atob(url.replace(/^data:.*?base64,/, '')) - const bytes = new Uint8Array(binaryString.length) - for (let i = 0; i < binaryString.length; i++) { - bytes[i] = binaryString.charCodeAt(i) + const urlContent = url.replace(/^data:.*?base64,/, '') + let bytes + if (typeof Buffer === 'function' && typeof Buffer.from === 'function') { + bytes = Buffer.from(urlContent, 'base64') + } else if (typeof atob === 'function') { + // @ts-ignore + const binaryString = atob(urlContent) + bytes = new Uint8Array(binaryString.length) + for (let i = 0; i < binaryString.length; i++) { + bytes[i] = binaryString.charCodeAt(i) + } + } else { + throw new Error( + 'Failed to decode base64-encoded data URL, Buffer and atob are not supported' + ) } // @ts-ignore result = await WebAssembly.instantiate(bytes, opts) diff --git a/packages/vite/src/node/plugins/worker.ts b/packages/vite/src/node/plugins/worker.ts index 2f50acfcdfa0fe..848d118ba0eb64 100644 --- a/packages/vite/src/node/plugins/worker.ts +++ b/packages/vite/src/node/plugins/worker.ts @@ -281,7 +281,7 @@ export function webWorkerPlugin(config: ResolvedConfig): Plugin { export default function WorkerWrapper() { const objURL = blob && (window.URL || window.webkitURL).createObjectURL(blob); try { - return objURL ? new ${workerConstructor}(objURL${workerOptions}) : new ${workerConstructor}("data:application/javascript;base64," + encodedJs${workerOptions}); + return objURL ? new ${workerConstructor}(objURL) : new ${workerConstructor}("data:application/javascript;base64," + encodedJs${workerOptions}); } finally { objURL && (window.URL || window.webkitURL).revokeObjectURL(objURL); } diff --git a/packages/vite/src/node/publicUtils.ts b/packages/vite/src/node/publicUtils.ts index 31c7135965a3d8..664b9375275403 100644 --- a/packages/vite/src/node/publicUtils.ts +++ b/packages/vite/src/node/publicUtils.ts @@ -8,7 +8,7 @@ export { splitVendorChunkPlugin, splitVendorChunk } from './plugins/splitVendorChunk' -export { normalizePath, mergeConfig, mergeAlias } from './utils' +export { normalizePath, mergeConfig, mergeAlias, createFilter } from './utils' export { send } from './server/send' export { createLogger } from './logger' export { searchForWorkspaceRoot } from './server/searchRoot' diff --git a/packages/vite/src/node/server/middlewares/proxy.ts b/packages/vite/src/node/server/middlewares/proxy.ts index 5c447435911ecd..9cd529a0dbba7b 100644 --- a/packages/vite/src/node/server/middlewares/proxy.ts +++ b/packages/vite/src/node/server/middlewares/proxy.ts @@ -43,11 +43,16 @@ export function proxyMiddleware( } const proxy = httpProxy.createProxyServer(opts) as HttpProxy.Server - proxy.on('error', (err) => { + proxy.on('error', (err, req, res) => { config.logger.error(`${colors.red(`http proxy error:`)}\n${err.stack}`, { timestamp: true, error: err }) + res + .writeHead(500, { + 'Content-Type': 'text/plain' + }) + .end() }) if (opts.configure) { diff --git a/packages/vite/src/node/ssr/__tests__/ssrTransform.spec.ts b/packages/vite/src/node/ssr/__tests__/ssrTransform.spec.ts index f759a51e60ff58..fbf7bb7bbd22ad 100644 --- a/packages/vite/src/node/ssr/__tests__/ssrTransform.spec.ts +++ b/packages/vite/src/node/ssr/__tests__/ssrTransform.spec.ts @@ -306,7 +306,7 @@ test('should declare variable for imported super class', async () => { class A extends Foo {} class B extends Foo {} Object.defineProperty(__vite_ssr_exports__, \\"B\\", { enumerable: true, configurable: true, get(){ return B }}); - Object.defineProperty(__vite_ssr_exports__, \\"default\\", { enumerable: true, value: A });" + Object.defineProperty(__vite_ssr_exports__, \\"default\\", { enumerable: true, configurable: true, value: A });" `) }) @@ -338,7 +338,7 @@ test('should handle default export variants', async () => { ).toMatchInlineSnapshot(` "function foo() {} foo.prototype = Object.prototype; - Object.defineProperty(__vite_ssr_exports__, \\"default\\", { enumerable: true, value: foo });" + Object.defineProperty(__vite_ssr_exports__, \\"default\\", { enumerable: true, configurable: true, value: foo });" `) // default named classes expect( @@ -353,7 +353,7 @@ test('should handle default export variants', async () => { "class A {} class B extends A {} Object.defineProperty(__vite_ssr_exports__, \\"B\\", { enumerable: true, configurable: true, get(){ return B }}); - Object.defineProperty(__vite_ssr_exports__, \\"default\\", { enumerable: true, value: A });" + Object.defineProperty(__vite_ssr_exports__, \\"default\\", { enumerable: true, configurable: true, value: A });" `) }) diff --git a/packages/vite/src/node/ssr/ssrExternal.ts b/packages/vite/src/node/ssr/ssrExternal.ts index fbba0e735f7df1..de0dd5bab031e4 100644 --- a/packages/vite/src/node/ssr/ssrExternal.ts +++ b/packages/vite/src/node/ssr/ssrExternal.ts @@ -1,12 +1,12 @@ import fs from 'fs' import path from 'path' import { createRequire } from 'module' -import { createFilter } from '@rollup/pluginutils' import type { InternalResolveOptions } from '../plugins/resolve' import { tryNodeResolve } from '../plugins/resolve' import { bareImportRE, createDebugger, + createFilter, isBuiltin, isDefined, lookupFile, diff --git a/packages/vite/src/node/ssr/ssrTransform.ts b/packages/vite/src/node/ssr/ssrTransform.ts index d7dc610cb82f47..e6f0d8b3a4562c 100644 --- a/packages/vite/src/node/ssr/ssrTransform.ts +++ b/packages/vite/src/node/ssr/ssrTransform.ts @@ -8,6 +8,8 @@ import type { Node as _Node } from 'estree' import { extract_names as extractNames } from 'periscopic' +// `eslint-plugin-node` doesn't support package without main +// eslint-disable-next-line node/no-missing-import import { walk as eswalk } from 'estree-walker' import type { RawSourceMap } from '@ampproject/remapping' import type { TransformResult } from '../server/transformRequest' @@ -188,7 +190,7 @@ async function ssrTransformScript( s.remove(node.start, node.start + 15 /* 'export default '.length */) s.append( `\nObject.defineProperty(${ssrModuleExportsKey}, "default", ` + - `{ enumerable: true, value: ${name} });` + `{ enumerable: true, configurable: true, value: ${name} });` ) } else { // anonymous default exports diff --git a/packages/vite/src/node/utils.ts b/packages/vite/src/node/utils.ts index 4840cc413eb378..c3c58696921dc9 100644 --- a/packages/vite/src/node/utils.ts +++ b/packages/vite/src/node/utils.ts @@ -16,16 +16,32 @@ import type { Alias, AliasOptions } from 'types/alias' import type MagicString from 'magic-string' import type { TransformResult } from 'rollup' +import { createFilter as _createFilter } from '@rollup/pluginutils' import { CLIENT_ENTRY, CLIENT_PUBLIC_PATH, DEFAULT_EXTENSIONS, ENV_PUBLIC_PATH, FS_PREFIX, - VALID_ID_PREFIX + VALID_ID_PREFIX, + wildcardHosts } from './constants' import type { ResolvedConfig } from '.' +/** + * Inlined to keep `@rollup/pluginutils` in devDependencies + */ +export type FilterPattern = + | ReadonlyArray + | string + | RegExp + | null +export const createFilter = _createFilter as ( + include?: FilterPattern, + exclude?: FilterPattern, + options?: { resolve?: string | false | null } +) => (id: string | unknown) => boolean + export function slash(p: string): string { return p.replace(/\\/g, '/') } @@ -732,7 +748,7 @@ export function resolveHostname( let host: string | undefined if (optionsHost === undefined || optionsHost === false) { // Use a secure default - host = '127.0.0.1' + host = 'localhost' } else if (optionsHost === true) { // If passed --host in the CLI without arguments host = undefined // undefined typically means 0.0.0.0 or :: (listen on all IPs) @@ -740,14 +756,9 @@ export function resolveHostname( host = optionsHost } - // Set host name to localhost when possible, unless the user explicitly asked for '127.0.0.1' + // Set host name to localhost when possible const name = - (optionsHost !== '127.0.0.1' && host === '127.0.0.1') || - host === '0.0.0.0' || - host === '::' || - host === undefined - ? 'localhost' - : host + host === undefined || wildcardHosts.has(host) ? 'localhost' : host return { host, name } } @@ -1003,3 +1014,12 @@ export function transformResult( map: needSourceMap ? s.generateMap({ hires: true, source: id }) : null } } + +// strip UTF-8 BOM +export function stripBomTag(content: string): string { + if (content.charCodeAt(0) === 0xfeff) { + return content.slice(1) + } + + return content +} diff --git a/playground/assets/__tests__/assets.spec.ts b/playground/assets/__tests__/assets.spec.ts index 20040c173c7f87..fda5e4e4c83ef1 100644 --- a/playground/assets/__tests__/assets.spec.ts +++ b/playground/assets/__tests__/assets.spec.ts @@ -218,6 +218,12 @@ describe('svg fragments', () => { }) }) +test('Unknown extension assets import', async () => { + expect(await page.textContent('.unknown-ext')).toMatch( + isBuild ? 'data:application/octet-stream;' : '/nested/foo.unknown' + ) +}) + test('?raw import', async () => { expect(await page.textContent('.raw')).toMatch('SVG') }) diff --git a/playground/assets/__tests__/relative-base/relative-base-assets.spec.ts b/playground/assets/__tests__/relative-base/relative-base-assets.spec.ts index a09116c88a7ea6..828ece5ea27c5f 100644 --- a/playground/assets/__tests__/relative-base/relative-base-assets.spec.ts +++ b/playground/assets/__tests__/relative-base/relative-base-assets.spec.ts @@ -5,7 +5,8 @@ import { getBg, getColor, isBuild, - page + page, + viteConfig } from '~utils' const absoluteAssetMatch = isBuild @@ -137,7 +138,8 @@ describe('css url() references', () => { describe.runIf(isBuild)('index.css URLs', () => { let css: string beforeAll(() => { - css = findAssetFile(/index.*\.css$/, '', 'other-assets') + const base = viteConfig ? viteConfig?.testConfig?.baseRoute : '' + css = findAssetFile(/index.*\.css$/, base, 'other-assets') }) test('relative asset URL', () => { diff --git a/playground/assets/index.html b/playground/assets/index.html index 418cc06f05bcd1..a741379e82013e 100644 --- a/playground/assets/index.html +++ b/playground/assets/index.html @@ -166,6 +166,9 @@

SVG Fragments via JS Import

+

Unknown extension assets import

+ +

?raw import

@@ -318,6 +321,9 @@

style in svg

text('.svg-frag-import-path', svgFrag) document.querySelector('.svg-frag-import').src = svgFrag + '#icon-heart-view' + import unknownExtUrl from './nested/foo.unknown' + text('.unknown-ext', unknownExtUrl) + import rawSvg from './nested/fragment.svg?raw' text('.raw', rawSvg) diff --git a/playground/assets/nested/foo.unknown b/playground/assets/nested/foo.unknown new file mode 100644 index 00000000000000..e24f83b664c55c --- /dev/null +++ b/playground/assets/nested/foo.unknown @@ -0,0 +1 @@ +custom file diff --git a/playground/assets/vite.config-relative-base.js b/playground/assets/vite.config-relative-base.js index ba624964d3e0e3..ae09766c0768ac 100644 --- a/playground/assets/vite.config-relative-base.js +++ b/playground/assets/vite.config-relative-base.js @@ -8,7 +8,7 @@ module.exports = { base: './', // relative base to make dist portable build: { ...baseConfig.build, - outDir: 'dist', + outDir: 'dist/relative-base', watch: false, minify: false, assetsInlineLimit: 0, @@ -19,5 +19,8 @@ module.exports = { assetFileNames: 'other-assets/[name].[hash][extname]' } } + }, + testConfig: { + baseRoute: '/relative-base/' } } diff --git a/playground/assets/vite.config.js b/playground/assets/vite.config.js index e44ddda7995185..c9d821ae3d73ee 100644 --- a/playground/assets/vite.config.js +++ b/playground/assets/vite.config.js @@ -11,6 +11,7 @@ module.exports = { '@': path.resolve(__dirname, 'nested') } }, + assetsInclude: ['**/*.unknown'], build: { outDir: 'dist/foo', assetsInlineLimit: 8192, // 8kb diff --git a/playground/backend-integration/package.json b/playground/backend-integration/package.json index c1419548cb3bd3..a9c2b4f2bca63d 100644 --- a/playground/backend-integration/package.json +++ b/playground/backend-integration/package.json @@ -9,7 +9,7 @@ "preview": "vite preview" }, "dependencies": { - "tailwindcss": "^2.2.19" + "tailwindcss": "^3.1.2" }, "devDependencies": { "fast-glob": "^3.2.11" diff --git a/playground/backend-integration/tailwind.config.js b/playground/backend-integration/tailwind.config.js index 6e72a7e5d9d87b..0c3ae11de7565e 100644 --- a/playground/backend-integration/tailwind.config.js +++ b/playground/backend-integration/tailwind.config.js @@ -1,7 +1,5 @@ module.exports = { - mode: 'jit', - purge: [__dirname + '/frontend/**/*.{css,html,ts,js}'], - darkMode: false, // or 'media' or 'class' + content: [__dirname + '/frontend/**/*.{css,html,ts,js}'], theme: { extend: {} }, diff --git a/playground/cli-module/__tests__/serve.ts b/playground/cli-module/__tests__/serve.ts index 5d40149300f6a5..698cbd048851d1 100644 --- a/playground/cli-module/__tests__/serve.ts +++ b/playground/cli-module/__tests__/serve.ts @@ -1,7 +1,7 @@ // this is automatically detected by playground/vitestSetup.ts and will replace // the default e2e test serve behavior -import execa from 'execa' +import { execaCommand } from 'execa' import kill from 'kill-port' import { isBuild, @@ -45,7 +45,7 @@ export async function serve() { if (isBuild) { const buildCommand = `${viteBinPath} build` try { - const buildProcess = execa.command(buildCommand, { + const buildProcess = execaCommand(buildCommand, { cwd: rootDir, stdio: 'pipe' }) @@ -67,7 +67,7 @@ export async function serve() { viteServerArgs.unshift('preview') } const serverCommand = `${viteBinPath} ${viteServerArgs.join(' ')}` - const serverProcess = execa.command(serverCommand, { + const serverProcess = execaCommand(serverCommand, { cwd: rootDir, stdio: 'pipe' }) diff --git a/playground/cli/__tests__/serve.ts b/playground/cli/__tests__/serve.ts index 6dbc05708d9cec..66f00677169f2a 100644 --- a/playground/cli/__tests__/serve.ts +++ b/playground/cli/__tests__/serve.ts @@ -1,7 +1,7 @@ // this is automatically detected by playground/vitestSetup.ts and will replace // the default e2e test serve behavior -import execa from 'execa' +import { execaCommand } from 'execa' import kill from 'kill-port' import { isBuild, @@ -45,7 +45,7 @@ export async function serve() { if (isBuild) { const buildCommand = `${viteBinPath} build` try { - const buildProcess = execa.command(buildCommand, { + const buildProcess = execaCommand(buildCommand, { cwd: rootDir, stdio: 'pipe' }) @@ -67,7 +67,7 @@ export async function serve() { viteServerArgs.unshift('preview') } const serverCommand = `${viteBinPath} ${viteServerArgs.join(' ')}` - const serverProcess = execa.command(serverCommand, { + const serverProcess = execaCommand(serverCommand, { cwd: rootDir, stdio: 'pipe' }) diff --git a/playground/css/__tests__/css.spec.ts b/playground/css/__tests__/css.spec.ts index e9d1fccac61d6e..564f0665bf0cea 100644 --- a/playground/css/__tests__/css.spec.ts +++ b/playground/css/__tests__/css.spec.ts @@ -317,9 +317,11 @@ test('treeshaken async chunk', async () => { test('PostCSS dir-dependency', async () => { const el1 = await page.$('.dir-dep') const el2 = await page.$('.dir-dep-2') + const el3 = await page.$('.dir-dep-3') expect(await getColor(el1)).toBe('grey') expect(await getColor(el2)).toBe('grey') + expect(await getColor(el3)).toBe('grey') if (!isBuild) { editFile('glob-dep/foo.css', (code) => @@ -334,6 +336,13 @@ test('PostCSS dir-dependency', async () => { await untilUpdated(() => getColor(el2), 'red') expect(await getColor(el1)).toBe('blue') + editFile('glob-dep/nested (dir)/baz.css', (code) => + code.replace('color: grey', 'color: green') + ) + await untilUpdated(() => getColor(el3), 'green') + expect(await getColor(el1)).toBe('blue') + expect(await getColor(el2)).toBe('red') + // test add/remove removeFile('glob-dep/bar.css') await untilUpdated(() => getColor(el2), 'black') diff --git a/playground/css/glob-dep/nested (dir)/baz.css b/playground/css/glob-dep/nested (dir)/baz.css new file mode 100644 index 00000000000000..9a8b0f0ba47dc5 --- /dev/null +++ b/playground/css/glob-dep/nested (dir)/baz.css @@ -0,0 +1,3 @@ +.dir-dep-3 { + color: grey; +} diff --git a/playground/css/index.html b/playground/css/index.html index 15e81192cec7f1..4310967b6ca65b 100644 --- a/playground/css/index.html +++ b/playground/css/index.html @@ -113,6 +113,9 @@

CSS

PostCSS dir-dependency (file 2): this should be grey too

+

+ PostCSS dir-dependency (file 3): this should be grey too +

URL separation preservation: should have valid background-image diff --git a/playground/css/postcss-caching/css.spec.ts b/playground/css/postcss-caching/css.spec.ts index bbffdb618280e4..45e8aadaa5489c 100644 --- a/playground/css/postcss-caching/css.spec.ts +++ b/playground/css/postcss-caching/css.spec.ts @@ -36,7 +36,7 @@ test('postcss config', async () => { blueApp = null greenApp = await startServer(greenAppDir) - await page.goto(`http://localhost:${port}`) + await page.reload() // hmr reloads it automatically but reload here for consistency const greenA = await page.$('.postcss-a') expect(await getColor(greenA)).toBe('black') const greenB = await page.$('.postcss-b') diff --git a/playground/css/postcss.config.js b/playground/css/postcss.config.js index 33058023541515..b30209bff42097 100644 --- a/playground/css/postcss.config.js +++ b/playground/css/postcss.config.js @@ -16,7 +16,7 @@ function testDirDep() { AtRule(atRule, { result, Comment }) { if (atRule.name === 'test') { const pattern = normalizePath( - path.resolve(path.dirname(result.opts.from), './glob-dep/*.css') + path.resolve(path.dirname(result.opts.from), './glob-dep/**/*.css') ) const files = glob.sync(pattern) const text = files.map((f) => fs.readFileSync(f, 'utf-8')).join('\n') @@ -30,6 +30,14 @@ function testDirDep() { glob: '*.css', parent: result.opts.from }) + + result.messages.push({ + type: 'dir-dependency', + plugin: 'dir-dep', + dir: './glob-dep/nested (dir)', // includes special characters in glob + glob: '*.css', + parent: result.opts.from + }) } } } diff --git a/playground/define/__tests__/define.spec.ts b/playground/define/__tests__/define.spec.ts index 695d210a822ed6..76b1dfef5feb39 100644 --- a/playground/define/__tests__/define.spec.ts +++ b/playground/define/__tests__/define.spec.ts @@ -40,4 +40,7 @@ test('string', async () => { // html would't need to define replacement expect(await page.textContent('.exp-define')).toBe('__EXP__') expect(await page.textContent('.import-json')).toBe('__EXP__') + expect(await page.textContent('.define-in-dep')).toBe( + defines.__STRINGIFIED_OBJ__ + ) }) diff --git a/playground/define/commonjs-dep/index.js b/playground/define/commonjs-dep/index.js new file mode 100644 index 00000000000000..23e0bf1b32e32f --- /dev/null +++ b/playground/define/commonjs-dep/index.js @@ -0,0 +1 @@ +module.exports = { defined: __STRINGIFIED_OBJ__ } diff --git a/playground/define/commonjs-dep/package.json b/playground/define/commonjs-dep/package.json new file mode 100644 index 00000000000000..3047ae68c9f75a --- /dev/null +++ b/playground/define/commonjs-dep/package.json @@ -0,0 +1,6 @@ +{ + "name": "commonjs-dep", + "private": true, + "version": "1.0.0", + "type": "commonjs" +} diff --git a/playground/define/index.html b/playground/define/index.html index 1260b119149d28..c4f4c598aba563 100644 --- a/playground/define/index.html +++ b/playground/define/index.html @@ -16,6 +16,7 @@

Define

no identifier substring:

define variable in html: __EXP__

import json:

+

define in dep: