diff --git a/.circleci/config.yml b/.circleci/config.yml index 9335ce3054f0d8..74cb374c1c1640 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -7,8 +7,8 @@ defaults: &defaults step_restore_cache: &restore_cache restore_cache: keys: - - v1-dependencies-{{ checksum "yarn.lock" }}-1 - - v1-dependencies- + - v1-dependencies-{{ checksum "yarn.lock" }}-1 + - v1-dependencies- step_install_deps: &install_deps run: diff --git a/.eslintrc.js b/.eslintrc.js index edf6be5da94729..f19c57faa15bbc 100644 --- a/.eslintrc.js +++ b/.eslintrc.js @@ -1,4 +1,7 @@ -module.exports = { +// @ts-check +const { defineConfig } = require('eslint-define-config') + +module.exports = defineConfig({ root: true, extends: ['plugin:node/recommended'], parser: '@typescript-eslint/parser', @@ -51,7 +54,13 @@ module.exports = { 'node/no-unpublished-import': 'off', 'node/no-unpublished-require': 'off', 'node/no-unsupported-features/es-syntax': 'off', - 'no-process-exit': 'off' + 'no-process-exit': 'off', + 'prefer-const': [ + 'warn', + { + destructuring: 'all' + } + ] }, overrides: [ { @@ -74,4 +83,4 @@ module.exports = { } } ] -} +}) diff --git a/.github.amrom.workers.devmit-convention.md b/.github.amrom.workers.devmit-convention.md index c66c7b9676ab3a..baa447479e9c39 100644 --- a/.github.amrom.workers.devmit-convention.md +++ b/.github.amrom.workers.devmit-convention.md @@ -6,7 +6,8 @@ Messages must be matched by the following regex: -``` js + +```js /^(revert: )?(feat|fix|docs|dx|refactor|perf|test|workflow|build|ci|chore|types|wip|release|deps)(\(.+\))?: .{1,50}/ ``` @@ -44,7 +45,7 @@ This reverts commit 667ecc1654a317a13331b17617d973392f415f02. ### Full Message Format -A commit message consists of a **header**, **body** and **footer**. The header has a **type**, **scope** and **subject**: +A commit message consists of a **header**, **body** and **footer**. The header has a **type**, **scope** and **subject**: ``` (): @@ -74,9 +75,9 @@ The scope could be anything specifying the place of the commit change. For examp The subject contains a succinct description of the change: -* use the imperative, present tense: "change" not "changed" nor "changes" -* don't capitalize the first letter -* no dot (.) at the end +- use the imperative, present tense: "change" not "changed" nor "changes" +- don't capitalize the first letter +- no dot (.) at the end ### Body diff --git a/.github/contributing.md b/.github/contributing.md index 60c348cd49c36c..49f528295e4e1d 100644 --- a/.github/contributing.md +++ b/.github/contributing.md @@ -12,8 +12,8 @@ To development and test the core `vite` package: 2. Run `yarn link` in `packages/vite`. This links `vite` globally so that you can: - - Run `yarn link vite` in another Vite project to use the locally built Vite; - - Use the `vite` binary anywhere. + - Run `yarn link vite` in another Vite project to use the locally built Vite; + - Use the `vite` binary anywhere. ## Running Tests @@ -29,7 +29,7 @@ Each test can be run under either dev server mode or build mode. - You can also use `yarn test-serve [match]` or `yarn test-build [match]` to run tests in a specific playground package, e.g. `yarn test-serve css` will run tests for both `playground/css` and `playground/css-codesplit` under serve mode. - Note package matching is not aviable for the `yarn test` script, which always runs all tests. + Note package matching is not available for the `yarn test` script, which always runs all tests. ### Test Env and Helpers @@ -43,6 +43,38 @@ test('should work', async () => { Some common test helpers, e.g. `testDir`, `isBuild` or `editFile` are available in `packages/playground/testUtils.ts`. +### Extending the Test Suite + +To add new tests, you should find a related playground to the fix or feature (or create a new one). As an example, static assets loading are tested in the [assets playground](https://github.com/vitejs/vite/tree/main/packages/playground/assets). In this Vite App, there is a test for `?raw` imports, with [a section is defined in the `index.html` for it](https://github.com/vitejs/vite/blob/71215533ac60e8ff566dc3467feabfc2c71a01e2/packages/playground/assets/index.html#L121): + +```html +

?raw import

+ +``` + +This will be modified [with the result of a file import](https://github.com/vitejs/vite/blob/71215533ac60e8ff566dc3467feabfc2c71a01e2/packages/playground/assets/index.html#L151): + +```js +import rawSvg from './nested/fragment.svg?raw' +text('.raw', rawSvg) +``` + +Where the `text` util is defined as: + +```js +function text(el, text) { + document.querySelector(el).textContent = text +} +``` + +In the [spec tests](https://github.com/vitejs/vite/blob/71215533ac60e8ff566dc3467feabfc2c71a01e2/packages/playground/assets/__tests__/assets.spec.ts#L180), the modifications to the DOM listed above are used to test this feature: + +```js +test('?raw import', async () => { + expect(await page.textContent('.raw')).toMatch('SVG') +}) +``` + ## Pull Request Guidelines - Checkout a topic branch from a base branch, e.g. `main`, and merge back against that branch. diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml new file mode 100644 index 00000000000000..d4d50aa18c34c9 --- /dev/null +++ b/.github/workflows/ci.yml @@ -0,0 +1,93 @@ +name: CI + +on: + push: + branches: + - main + pull_request: + +jobs: + build: + runs-on: ${{ matrix.os }} + strategy: + matrix: + os: [ubuntu-latest] + node_version: [12, 14, 15] + include: + - os: macos-latest + node_version: 14 + - os: windows-latest + node_version: 14 + fail-fast: false + + name: 'Build&Test: node-${{ matrix.node_version }}, ${{ matrix.os }}' + steps: + - name: Checkout + uses: actions/checkout@v2 + + - name: Set node version to ${{ matrix.node_version }} + uses: actions/setup-node@v2 + with: + node-version: ${{ matrix.node_version }} + + - name: Get yarn cache directory + id: yarn-cache + run: echo "::set-output name=dir::$(yarn cache dir)" + + - name: Set dependencies cache + uses: actions/cache@v2 + with: + path: ${{ steps.yarn-cache.outputs.dir }} + key: ${{ runner.os }}-${{ matrix.node_version }}-${{ hashFiles('yarn.lock') }} + restore-keys: | + ${{ runner.os }}-${{ matrix.node_version }}-${{ hashFiles('yarn.lock') }} + ${{ runner.os }}-${{ matrix.node_version }}- + + - name: Versions + run: yarn versions + + - name: Install dependencies + run: yarn install --frozen-lockfile + + - name: Build vite + run: yarn build-vite + + - name: Build plugin-vue + run: yarn build-plugin-vue + + - name: Test serve + run: yarn test-serve --runInBand + + - name: Test build + run: yarn test-build --runInBand + + lint: + runs-on: ubuntu-latest + name: 'Lint: node-14, ubuntu-latest' + steps: + - uses: actions/checkout@v2 + with: + fetch-depth: 0 + + - name: Set node version to 14 + uses: actions/setup-node@v2 + with: + node-version: 14 + + - name: Set dependencies cache + uses: actions/cache@v2 + with: + path: ~/.cache/yarn + key: lint-dependencies-${{ hashFiles('yarn.lock') }} + restore-keys: | + lint-dependencies-${{ hashFiles('yarn.lock') }} + lint-dependencies- + + - name: Prepare + run: | + yarn install --frozen-lockfile + yarn build-vite + yarn build-plugin-vue + + - name: Lint + run: yarn lint diff --git a/.github/workflows/issue-close-require.yml b/.github/workflows/issue-close-require.yml index 058d89bbc26b98..244ae4e6297f01 100644 --- a/.github/workflows/issue-close-require.yml +++ b/.github/workflows/issue-close-require.yml @@ -2,7 +2,7 @@ name: Issue Close Require on: schedule: - - cron: "0 0 * * *" + - cron: '0 0 * * *' jobs: close-issues: diff --git a/.prettierignore b/.prettierignore new file mode 100644 index 00000000000000..ba0c80d22e344b --- /dev/null +++ b/.prettierignore @@ -0,0 +1,6 @@ +docs/.vitepress/dist/ +packages/vite/dist/ +packages/plugin-vue/dist/ +packages/*/CHANGELOG.md +LICENSE.md +.prettierrc \ No newline at end of file diff --git a/CODE_OF_CONDUCT.md b/CODE_OF_CONDUCT.md index 3c78f4082d7a64..8c0ecf5ab85f8c 100644 --- a/CODE_OF_CONDUCT.md +++ b/CODE_OF_CONDUCT.md @@ -34,7 +34,7 @@ This Code of Conduct applies both within project spaces and in public spaces whe ## Enforcement -Instances of abusive, harassing, or otherwise unacceptable behavior may be reported by contacting the project team by DM at [Vite Land](chat.vitejs.dev). All complaints will be reviewed and investigated and will result in a response that is deemed necessary and appropriate to the circumstances. The project team is obligated to maintain confidentiality with regard to the reporter of an incident. Further details of specific enforcement policies may be posted separately. +Instances of abusive, harassing, or otherwise unacceptable behavior may be reported by contacting the project team by DM at [Vite Land](https://chat.vitejs.dev). All complaints will be reviewed and investigated and will result in a response that is deemed necessary and appropriate to the circumstances. The project team is obligated to maintain confidentiality with regard to the reporter of an incident. Further details of specific enforcement policies may be posted separately. Project maintainers who do not follow or enforce the Code of Conduct in good faith may face temporary or permanent repercussions as determined by other members of the project's leadership. @@ -42,4 +42,4 @@ Project maintainers who do not follow or enforce the Code of Conduct in good fai This Code of Conduct is adapted from the [Contributor Covenant][homepage], version 1.4, available at https://www.contributor-covenant.org/version/1/4/code-of-conduct.html -[homepage]: https://www.contributor-covenant.org \ No newline at end of file +[homepage]: https://www.contributor-covenant.org diff --git a/docs/.vitepress/theme/sponsors.css b/docs/.vitepress/theme/sponsors.css index 2f30c1a888c540..bc0e9e1ca406ac 100644 --- a/docs/.vitepress/theme/sponsors.css +++ b/docs/.vitepress/theme/sponsors.css @@ -25,4 +25,4 @@ color: #999; font-size: 1.2rem; border: none; -} \ No newline at end of file +} diff --git a/docs/config/index.md b/docs/config/index.md index e6526efb34af53..631c461b9c9c50 100644 --- a/docs/config/index.md +++ b/docs/config/index.md @@ -68,6 +68,19 @@ export default ({ command, mode }) => { } ``` +### Async Config + +If the config needs to call async function, it can export a async function instead: + +```js +export default async ({ command, mode }) => { + const data = await asyncFunction(); + return { + // build specific config + } +} +``` + ## Shared Options ### root @@ -105,12 +118,16 @@ export default ({ command, mode }) => { - **Type:** `Record` - Define global variable replacements. Entries will be defined as globals during dev and statically replaced during build. + Define global constant replacements. Entries will be defined as globals during dev and statically replaced during build. - Starting from `2.0.0-beta.70`, string values will be used as raw expressions, so if defining a string constant, it needs to be explicitly quoted (e.g. with `JSON.stringify`). - Replacements are performed only when the match is surrounded by word boundaries (`\b`). + Because it's implemented as straightforward text replacements without any syntax analyzation, 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. + ### plugins - **Type:** ` (Plugin | Plugin[])[]` @@ -364,7 +381,7 @@ export default ({ command, mode }) => { target: 'http://jsonplaceholder.typicode.com', changeOrigin: true, rewrite: (path) => path.replace(/^\/api/, '') - } + }, // with RegEx '^/fallback/.*': { target: 'http://jsonplaceholder.typicode.com', @@ -485,10 +502,10 @@ export default ({ command, mode }) => { ### build.lib -- **Type:** `{ entry: string, name?: string, formats?: ('es' | 'cjs' | 'umd' | 'iife')[] }` +- **Type:** `{ entry: string, name?: string, formats?: ('es' | 'cjs' | 'umd' | 'iife')[], fileName?: string }` - **Related:** [Library Mode](/guide/build#library-mode) - Build as a library. `entry` is required since the library cannot use HTML as entry. `name` is the exposed global variable and is required when `formats` includes `'umd'` or `'iife'`. Default `formats` are `['es', 'umd']`. + Build as a library. `entry` is required since the library cannot use HTML as entry. `name` is the exposed global variable and is required when `formats` includes `'umd'` or `'iife'`. Default `formats` are `['es', 'umd']`. `fileName` is the name of the package file output, default `fileName` is the name option of package.json ### build.manifest @@ -544,6 +561,13 @@ export default ({ command, mode }) => { - **Default:** `500` Limit for chunk size warnings (in kbs). + +### build.watch + +- **Type:** [`WatcherOptions`](https://rollupjs.org/guide/en/#watch-options)`| null` +- **Default:** `null` + + Set to `{}` to enable rollup watcher. This is mostly used in cases that involve build-only plugins or integrations processes. ## Dep Optimization Options diff --git a/docs/guide/api-hmr.md b/docs/guide/api-hmr.md index cc58ebce612999..829c53a7f1ccc3 100644 --- a/docs/guide/api-hmr.md +++ b/docs/guide/api-hmr.md @@ -112,4 +112,4 @@ For now, calling `import.meta.hot.invalidate()` simply reloads the page. ## `hot.on(event, cb)` -Listen to a custom HMR event. Custom HMR events can be sent from plugins. See [handleHotUpdate](./api-plugin#handlehotupdate) for more details. \ No newline at end of file +Listen to a custom HMR event. Custom HMR events can be sent from plugins. See [handleHotUpdate](./api-plugin#handlehotupdate) for more details. diff --git a/docs/guide/api-javascript.md b/docs/guide/api-javascript.md index 5673da95c7859f..f67f0592255865 100644 --- a/docs/guide/api-javascript.md +++ b/docs/guide/api-javascript.md @@ -33,40 +33,41 @@ const { createServer } = require('vite') The `InlineConfig` interface extends `UserConfig` with additional properties: - `configFile`: specify config file to use. If not set, Vite will try to automatically resolve one from project root. Set to `false` to disable auto resolving. +- `envFile`: Set to `false` to disable `.env` files. ## `ViteDevServer` ```ts interface ViteDevServer { /** - * The resolved vite config object + * The resolved vite config object. */ config: ResolvedConfig /** * A connect app instance * - Can be used to attach custom middlewares to the dev server. * - Can also be used as the handler function of a custom http server - * or as a middleware in any connect-style Node.js frameworks + * or as a middleware in any connect-style Node.js frameworks. * * https://github.com/senchalabs/connect#use-middleware */ middlewares: Connect.Server /** - * native Node http server instance - * will be null in middleware mode + * Native Node http server instance. + * Will be null in middleware mode. */ httpServer: http.Server | null /** - * chokidar watcher instance + * Chokidar watcher instance. * https://github.com/paulmillr/chokidar#api */ watcher: FSWatcher /** - * web socket server with `send(payload)` method + * Web socket server with `send(payload)` method. */ ws: WebSocketServer /** - * Rollup plugin container that can run plugin hooks on a given file + * Rollup plugin container that can run plugin hooks on a given file. */ pluginContainer: PluginContainer /** @@ -104,7 +105,7 @@ interface ViteDevServer { options?: { isolated?: boolean } ): Promise> /** - * Fix ssr error stacktrace + * Fix ssr error stacktrace. */ ssrFixStacktrace(e: Error): void /** diff --git a/docs/guide/api-plugin.md b/docs/guide/api-plugin.md index 241031ef0a224e..dd96508d5307b8 100644 --- a/docs/guide/api-plugin.md +++ b/docs/guide/api-plugin.md @@ -189,8 +189,8 @@ Vite plugins can also provide hooks that serve Vite-specific purposes. These hoo ### `configResolved` -- **Type:** `(config: ResolvedConfig) => void` -- **Kind:** `sync`, `sequential` +- **Type:** `(config: ResolvedConfig) => void | Promise` +- **Kind:** `async`, `parallel` Called after the Vite config is resolved. Use this hook to read and store the final resolved config. It is also useful when the plugin needs to do something different based the command is being run. diff --git a/docs/guide/assets.md b/docs/guide/assets.md index e76b642d08657f..a932c08dd6bc48 100644 --- a/docs/guide/assets.md +++ b/docs/guide/assets.md @@ -45,7 +45,7 @@ import shaderString from './shader.glsl?raw' ### Importing Script as a Worker -Scripts can be imported as web workers with the `?worker` suffix. +Scripts can be imported as web workers with the `?worker` suffix. ```js // Separate chunk in the production build diff --git a/docs/guide/backend-integration.md b/docs/guide/backend-integration.md index 27d17ab1b05bb0..4669bd6c818dec 100644 --- a/docs/guide/backend-integration.md +++ b/docs/guide/backend-integration.md @@ -41,8 +41,8 @@ Or you can follow these steps to configure it manually: ```html ``` - diff --git a/docs/guide/build.md b/docs/guide/build.md index 0f8ea3f681f25e..5c6f45c46eb1b1 100644 --- a/docs/guide/build.md +++ b/docs/guide/build.md @@ -46,6 +46,21 @@ module.exports = { For example, you can specify multiple Rollup outputs with plugins that are only applied during build. +## 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`: + +```js +// vite.config.js +module.exports = { + build: { + watch: { + // https://rollupjs.org/guide/en/#watch-options + } + } +} +``` + ## Multi-Page App Suppose you have the following source code structure: @@ -80,6 +95,8 @@ module.exports = { } ``` +If you specify a different root, remember that `__dirname` will still be the folder of your vite.config.js file when resolving the input paths. Therfore, you will need to add your `root` entry to the arguments for `resolve`. + ## Library Mode When you are developing a browser-oriented library, you are likely spending most of the time on a test/demo page that imports your actual library. With Vite, you can use your `index.html` for that purpose to get the smooth development experience. diff --git a/docs/guide/features.md b/docs/guide/features.md index 70489d0e0115de..b9b343bad1ad1b 100644 --- a/docs/guide/features.md +++ b/docs/guide/features.md @@ -251,7 +251,7 @@ const modules = { Note that: - This is a Vite-only feature and is not a web or ES standard. -- The glob patterns are treated like import specifiers: they must be either relative (start with `./`) or absolute (start with `/`, resolved relative to project root). Globbing from dependencies is not supported. +- The glob patterns are treated like import specifiers: they must be either relative (start with `./`) or absolute (start with `/`, resolved relative to project root). - The glob matching is done via `fast-glob` - check out its documentation for [supported glob patterns](https://github.com/mrmlnc/fast-glob#pattern-syntax). ## Web Assembly diff --git a/docs/guide/static-deploy.md b/docs/guide/static-deploy.md index 48dc91fe1a8b53..c5c53efda936a5 100644 --- a/docs/guide/static-deploy.md +++ b/docs/guide/static-deploy.md @@ -64,33 +64,33 @@ Now the `preview` method will launch the server at http://localhost:8080. 2. Inside your project, create `deploy.sh` with the following content (with highlighted lines uncommented appropriately), and run it to deploy: -```bash{13,20,23} -#!/usr/bin/env sh + ```bash{13,20,23} + #!/usr/bin/env sh -# abort on errors -set -e + # abort on errors + set -e -# build -npm run build + # build + npm run build -# navigate into the build output directory -cd dist + # navigate into the build output directory + cd dist -# if you are deploying to a custom domain -# echo 'www.example.com' > CNAME + # if you are deploying to a custom domain + # echo 'www.example.com' > CNAME -git init -git add -A -git commit -m 'deploy' + git init + git add -A + git commit -m 'deploy' -# if you are deploying to https://.github.io -# git push -f git@github.com:/.github.io.git master + # if you are deploying to https://.github.io + # git push -f git@github.com:/.github.io.git master -# if you are deploying to https://.github.io/ -# git push -f git@github.com:/.git master:gh-pages + # if you are deploying to https://.github.io/ + # git push -f git@github.com:/.git master:gh-pages -cd - -``` + cd - + ``` ::: tip You can also run the above script in your CI setup to enable automatic deployment on each push. @@ -110,60 +110,60 @@ You can also run the above script in your CI setup to enable automatic deploymen 4. Use the GitHub Pages deploy provider template, and follow the [Travis CI documentation](https://docs.travis-ci.com/user/deployment/pages/). -```yaml -language: node_js -node_js: - - lts/* -install: - - npm ci -script: - - npm run build -deploy: - provider: pages - skip_cleanup: true - local_dir: dist - # A token generated on GitHub allowing Travis to push code on you repository. - # Set in the Travis settings page of your repository, as a secure variable. - github_token: $GITHUB_TOKEN - keep_history: true - on: - branch: master -``` + ```yaml + language: node_js + node_js: + - lts/* + install: + - npm ci + script: + - npm run build + deploy: + provider: pages + skip_cleanup: true + local_dir: dist + # A token generated on GitHub allowing Travis to push code on you repository. + # Set in the Travis settings page of your repository, as a secure variable. + github_token: $GITHUB_TOKEN + keep_history: true + on: + branch: master + ``` ## GitLab Pages and GitLab CI 1. Set the correct `base` in `vite.config.js`. - If you are deploying to `https://.gitlab.io/`, you can omit `base` as it defaults to `'/'`. + If you are deploying to `https://.gitlab.io/`, you can omit `base` as it defaults to `'/'`. - If you are deploying to `https://.gitlab.io//`, for example your repository is at `https://gitlab.com//`, then set `base` to `'//'`. + If you are deploying to `https://.gitlab.io//`, for example your repository is at `https://gitlab.com//`, then set `base` to `'//'`. 2. Set `build.outDir` in `vite.config.js` to `public`. 3. Create a file called `.gitlab-ci.yml` in the root of your project with the content below. This will build and deploy your site whenever you make changes to your content: -```yaml -image: node:10.22.0 -pages: - cache: - paths: - - node_modules/ - script: - - npm install - - npm run build - artifacts: - paths: - - public - only: - - master -``` + ```yaml + image: node:10.22.0 + pages: + cache: + paths: + - node_modules/ + script: + - npm install + - npm run build + artifacts: + paths: + - public + only: + - master + ``` ## Netlify 1. On [Netlify](https://netlify.com), setup up a new project from GitHub with the following settings: -- **Build Command:** `vite build` or `npm run build` -- **Publish directory:** `dist` + - **Build Command:** `vite build` or `npm run build` + - **Publish directory:** `dist` 2. Hit the deploy button. @@ -173,26 +173,26 @@ pages: 2. Create `firebase.json` and `.firebaserc` at the root of your project with the following content: - `firebase.json`: + `firebase.json`: - ```json - { - "hosting": { - "public": "dist", - "ignore": [] - } - } - ``` + ```json + { + "hosting": { + "public": "dist", + "ignore": [] + } + } + ``` - `.firebaserc`: + `.firebaserc`: - ```js - { - "projects": { - "default": "" + ```js + { + "projects": { + "default": "" + } } - } - ``` + ``` 3. After running `npm run build`, deploy using the command `firebase deploy`. @@ -214,46 +214,46 @@ You can also deploy to a [custom domain](http://surge.sh/help/adding-a-custom-do 3. Run `heroku login` and fill in your Heroku credentials: -```bash -$ heroku login -``` + ```bash + $ heroku login + ``` 4. Create a file called `static.json` in the root of your project with the below content: -`static.json`: + `static.json`: -```json -{ - "root": "./dist" -} -``` + ```json + { + "root": "./dist" + } + ``` -This is the configuration of your site; read more at [heroku-buildpack-static](https://github.com/heroku/heroku-buildpack-static). + This is the configuration of your site; read more at [heroku-buildpack-static](https://github.com/heroku/heroku-buildpack-static). 5. Set up your Heroku git remote: -```bash -# version change -$ git init -$ git add . -$ git commit -m "My site ready for deployment." + ```bash + # version change + $ git init + $ git add . + $ git commit -m "My site ready for deployment." -# creates a new app with a specified name -$ heroku apps:create example + # creates a new app with a specified name + $ heroku apps:create example -# set buildpack for static sites -$ heroku buildpacks:set https://github.com/heroku/heroku-buildpack-static.git -``` + # set buildpack for static sites + $ heroku buildpacks:set https://github.com/heroku/heroku-buildpack-static.git + ``` 6. Deploy your site: -```bash -# publish site -$ git push heroku master + ```bash + # publish site + $ git push heroku master -# opens a browser to view the Dashboard version of Heroku CI -$ heroku open -``` + # opens a browser to view the Dashboard version of Heroku CI + $ heroku open + ``` ## Vercel diff --git a/docs/plugins/index.md b/docs/plugins/index.md index 4a7c6e46df1819..647c9706e313de 100644 --- a/docs/plugins/index.md +++ b/docs/plugins/index.md @@ -28,4 +28,4 @@ Check out [awesome-vite](https://github.com/vitejs/awesome-vite#plugins) - you c ## Rollup Plugins -[Vite plugins](../guide/api-plugin) are an extension of Rollup's plugin interface. Check out the [Rollup Plugin Compatibility section](../guide/api-plugin#rollup-plugin-compatibility) for more information. \ No newline at end of file +[Vite plugins](../guide/api-plugin) are an extension of Rollup's plugin interface. Check out the [Rollup Plugin Compatibility section](../guide/api-plugin#rollup-plugin-compatibility) for more information. diff --git a/package.json b/package.json index aabfb68ab88c71..9dd439477eb0a4 100644 --- a/package.json +++ b/package.json @@ -31,6 +31,7 @@ "cross-env": "^7.0.3", "enquirer": "^2.3.6", "eslint": "^7.15.0", + "eslint-define-config": "^1.0.3", "eslint-plugin-node": "^11.1.0", "execa": "^5.0.0", "fs-extra": "^9.0.1", diff --git a/packages/create-app/CHANGELOG.md b/packages/create-app/CHANGELOG.md index 89c02e044146d7..be51b5d1c42d49 100644 --- a/packages/create-app/CHANGELOG.md +++ b/packages/create-app/CHANGELOG.md @@ -1,3 +1,19 @@ +## [2.2.4](https://github.com/vitejs/vite/compare/create-app@2.2.3...create-app@2.2.4) (2021-04-15) + + +### Bug Fixes + +* **create-app:** change index.html templates' favicon.svg href to absolute URL ([#2620](https://github.com/vitejs/vite/issues/2620)) ([3816f6e](https://github.com/vitejs/vite/commit/3816f6edb67e6bdf94db98e82e8acff4029bfe48)) +* **create-app:** the node in the svelte template is incorrectly mounted ([#2947](https://github.com/vitejs/vite/issues/2947)) ([0825f7e](https://github.com/vitejs/vite/commit/0825f7ee3574ae3f28f566da27835fbf3b210fac)) + + +### Features + +* **create-app:** add template vanilla-ts ([#2023](https://github.com/vitejs/vite/issues/2023)) ([89898d3](https://github.com/vitejs/vite/commit/89898d36cbe03bce9b6a7ab80a1c45de9989e56e)) +* **create-app:** two-level prompt for framework and variants ([#2941](https://github.com/vitejs/vite/issues/2941)) ([176e55d](https://github.com/vitejs/vite/commit/176e55dd1bf0232f483697d35b95f6e29a47fd74)) + + + ## [2.2.3](https://github.com/vitejs/vite/compare/create-app@2.2.2...create-app@2.2.3) (2021-03-31) diff --git a/packages/create-app/README.md b/packages/create-app/README.md index f3b4b76b598121..47e94c4c7c0d3e 100644 --- a/packages/create-app/README.md +++ b/packages/create-app/README.md @@ -35,6 +35,7 @@ yarn create @vitejs/app my-vue-app --template vue Currently supported template presets include: - `vanilla` +- `vanilla-ts` - `vue` - `vue-ts` - `react` diff --git a/packages/create-app/index.js b/packages/create-app/index.js index 4d5b7df838248a..e0548bec6b9949 100755 --- a/packages/create-app/index.js +++ b/packages/create-app/index.js @@ -4,33 +4,123 @@ const fs = require('fs') const path = require('path') const argv = require('minimist')(process.argv.slice(2)) +// eslint-disable-next-line node/no-restricted-require const { prompt } = require('enquirer') const { yellow, green, cyan, + blue, magenta, lightRed, - red, - stripColors + red } = require('kolorist') const cwd = process.cwd() -const TEMPLATES = [ - yellow('vanilla'), - green('vue'), - green('vue-ts'), - cyan('react'), - cyan('react-ts'), - magenta('preact'), - magenta('preact-ts'), - lightRed('lit-element'), - lightRed('lit-element-ts'), - red('svelte'), - red('svelte-ts') +const FRAMEWORKS = [ + { + name: 'vanilla', + color: yellow, + variants: [ + { + name: 'vanilla', + display: 'JavaScript', + color: yellow + }, + { + name: 'vanilla-ts', + display: 'TypeScript', + color: blue + } + ] + }, + { + name: 'vue', + color: green, + variants: [ + { + name: 'vue', + display: 'JavaScript', + color: yellow + }, + { + name: 'vue-ts', + display: 'TypeScript', + color: blue + } + ] + }, + { + name: 'react', + color: cyan, + variants: [ + { + name: 'react', + display: 'JavaScript', + color: yellow + }, + { + name: 'react-ts', + display: 'TypeScript', + color: blue + } + ] + }, + { + name: 'preact', + color: magenta, + variants: [ + { + name: 'preact', + display: 'JavaScript', + color: yellow + }, + { + name: 'preact-ts', + display: 'TypeScript', + color: blue + } + ] + }, + { + name: 'lit-element', + color: lightRed, + variants: [ + { + name: 'lit-element', + display: 'JavaScript', + color: yellow + }, + { + name: 'lit-element-ts', + display: 'TypeScript', + color: blue + } + ] + }, + { + name: 'svelte', + color: red, + variants: [ + { + name: 'svelte', + display: 'JavaScript', + color: yellow + }, + { + name: 'svelte-ts', + display: 'TypeScript', + color: blue + } + ] + } ] +const TEMPLATES = FRAMEWORKS.map( + (f) => (f.variants && f.variants.map((v) => v.name)) || [f.name] +).reduce((a, b) => a.concat(b), []) + const renameFiles = { _gitignore: '.gitignore' } @@ -51,7 +141,6 @@ async function init() { } const packageName = await getValidPackageName(targetDir) const root = path.join(cwd, targetDir) - console.log(`\nScaffolding project in ${root}...`) if (!fs.existsSync(root)) { fs.mkdirSync(root, { recursive: true }) @@ -79,29 +168,63 @@ async function init() { // determine template let template = argv.t || argv.template - let message = 'Select a template:' + let message = 'Select a framework:' let isValidTemplate = false // --template expects a value if (typeof template === 'string') { - const availableTemplates = TEMPLATES.map(stripColors) - isValidTemplate = availableTemplates.includes(template) + isValidTemplate = TEMPLATES.includes(template) message = `${template} isn't a valid template. Please choose from below:` } if (!template || !isValidTemplate) { /** - * @type {{ t: string }} + * @type {{ framework: string }} */ - const { t } = await prompt({ + const { framework } = await prompt({ type: 'select', - name: 't', + name: 'framework', message, - choices: TEMPLATES + format(name) { + const framework = FRAMEWORKS.find((v) => v.name === name) + return framework + ? framework.color(framework.display || framework.name) + : name + }, + choices: FRAMEWORKS.map((f) => ({ + name: f.name, + value: f.name, + message: f.color(f.display || f.name) + })) }) - template = stripColors(t) + const frameworkInfo = FRAMEWORKS.find((f) => f.name === framework) + + if (frameworkInfo.variants) { + /** + * @type {{ name: string }} + */ + const { name } = await prompt({ + type: 'select', + name: 'name', + format(name) { + const variant = frameworkInfo.variants.find((v) => v.name === name) + return variant ? variant.color(variant.display || variant.name) : name + }, + message: 'Select a variant:', + choices: frameworkInfo.variants.map((v) => ({ + name: v.name, + value: v.name, + message: v.color(v.display || v.name) + })) + }) + template = name + } else { + template = frameworkInfo.name + } } + console.log(`\nScaffolding project in ${root}...`) + const templateDir = path.join(__dirname, `template-${template}`) const write = (file, content) => { diff --git a/packages/create-app/package.json b/packages/create-app/package.json index 0436bf6c239b12..b1a84223ad8b1e 100644 --- a/packages/create-app/package.json +++ b/packages/create-app/package.json @@ -1,6 +1,6 @@ { "name": "@vitejs/create-app", - "version": "2.2.3", + "version": "2.2.4", "license": "MIT", "author": "Evan You", "bin": { diff --git a/packages/create-app/template-lit-element-ts/index.html b/packages/create-app/template-lit-element-ts/index.html index 887b8ee9ad4c27..30bf7f9c81694e 100644 --- a/packages/create-app/template-lit-element-ts/index.html +++ b/packages/create-app/template-lit-element-ts/index.html @@ -2,7 +2,7 @@ - + Vite + Lit-Element App diff --git a/packages/create-app/template-lit-element/index.html b/packages/create-app/template-lit-element/index.html index 96eb1f95c57393..ad2c3e9ac596f8 100644 --- a/packages/create-app/template-lit-element/index.html +++ b/packages/create-app/template-lit-element/index.html @@ -2,7 +2,7 @@ - + Vite + Lit-Element App diff --git a/packages/create-app/template-preact-ts/index.html b/packages/create-app/template-preact-ts/index.html index a917605727d172..5dd45a1e2ab404 100644 --- a/packages/create-app/template-preact-ts/index.html +++ b/packages/create-app/template-preact-ts/index.html @@ -2,7 +2,7 @@ - + Vite App diff --git a/packages/create-app/template-preact-ts/src/index.css b/packages/create-app/template-preact-ts/src/index.css index b8c94dfb55e8d0..3d36f1648343ec 100644 --- a/packages/create-app/template-preact-ts/src/index.css +++ b/packages/create-app/template-preact-ts/src/index.css @@ -1,18 +1,19 @@ -html, body { - height: 100%; - width: 100%; - padding: 0; - margin: 0; - background: #FAFAFA; - font-family: 'Helvetica Neue', arial, sans-serif; - font-weight: 400; - color: #444; - -webkit-font-smoothing: antialiased; - -moz-osx-font-smoothing: grayscale; +html, +body { + height: 100%; + width: 100%; + padding: 0; + margin: 0; + background: #fafafa; + font-family: 'Helvetica Neue', arial, sans-serif; + font-weight: 400; + color: #444; + -webkit-font-smoothing: antialiased; + -moz-osx-font-smoothing: grayscale; } * { - box-sizing: border-box; + box-sizing: border-box; } #app { diff --git a/packages/create-app/template-preact/index.html b/packages/create-app/template-preact/index.html index 87d9f988dde878..c06e9fce67a28f 100644 --- a/packages/create-app/template-preact/index.html +++ b/packages/create-app/template-preact/index.html @@ -2,7 +2,7 @@ - + Vite App diff --git a/packages/create-app/template-preact/src/app.jsx b/packages/create-app/template-preact/src/app.jsx index ae31e704597d4b..9d649444f686d9 100644 --- a/packages/create-app/template-preact/src/app.jsx +++ b/packages/create-app/template-preact/src/app.jsx @@ -17,4 +17,4 @@ export function App(props) {

) -} \ No newline at end of file +} diff --git a/packages/create-app/template-preact/src/index.css b/packages/create-app/template-preact/src/index.css index b8c94dfb55e8d0..3d36f1648343ec 100644 --- a/packages/create-app/template-preact/src/index.css +++ b/packages/create-app/template-preact/src/index.css @@ -1,18 +1,19 @@ -html, body { - height: 100%; - width: 100%; - padding: 0; - margin: 0; - background: #FAFAFA; - font-family: 'Helvetica Neue', arial, sans-serif; - font-weight: 400; - color: #444; - -webkit-font-smoothing: antialiased; - -moz-osx-font-smoothing: grayscale; +html, +body { + height: 100%; + width: 100%; + padding: 0; + margin: 0; + background: #fafafa; + font-family: 'Helvetica Neue', arial, sans-serif; + font-weight: 400; + color: #444; + -webkit-font-smoothing: antialiased; + -moz-osx-font-smoothing: grayscale; } * { - box-sizing: border-box; + box-sizing: border-box; } #app { diff --git a/packages/create-app/template-react-ts/index.html b/packages/create-app/template-react-ts/index.html index b755334c0946a7..38f386110323c3 100644 --- a/packages/create-app/template-react-ts/index.html +++ b/packages/create-app/template-react-ts/index.html @@ -2,7 +2,7 @@ - + Vite App diff --git a/packages/create-app/template-react-ts/src/index.css b/packages/create-app/template-react-ts/src/index.css index 4a1df4db71cdb3..ec2585e8c0bb81 100644 --- a/packages/create-app/template-react-ts/src/index.css +++ b/packages/create-app/template-react-ts/src/index.css @@ -1,13 +1,13 @@ body { margin: 0; - font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", "Roboto", "Oxygen", - "Ubuntu", "Cantarell", "Fira Sans", "Droid Sans", "Helvetica Neue", + font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', 'Roboto', 'Oxygen', + 'Ubuntu', 'Cantarell', 'Fira Sans', 'Droid Sans', 'Helvetica Neue', sans-serif; -webkit-font-smoothing: antialiased; -moz-osx-font-smoothing: grayscale; } code { - font-family: source-code-pro, Menlo, Monaco, Consolas, "Courier New", + font-family: source-code-pro, Menlo, Monaco, Consolas, 'Courier New', monospace; } diff --git a/packages/create-app/template-react/index.html b/packages/create-app/template-react/index.html index 642973e54da6f9..b46ab83364e374 100644 --- a/packages/create-app/template-react/index.html +++ b/packages/create-app/template-react/index.html @@ -2,7 +2,7 @@ - + Vite App diff --git a/packages/create-app/template-react/src/index.css b/packages/create-app/template-react/src/index.css index 4a1df4db71cdb3..ec2585e8c0bb81 100644 --- a/packages/create-app/template-react/src/index.css +++ b/packages/create-app/template-react/src/index.css @@ -1,13 +1,13 @@ body { margin: 0; - font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", "Roboto", "Oxygen", - "Ubuntu", "Cantarell", "Fira Sans", "Droid Sans", "Helvetica Neue", + font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', 'Roboto', 'Oxygen', + 'Ubuntu', 'Cantarell', 'Fira Sans', 'Droid Sans', 'Helvetica Neue', sans-serif; -webkit-font-smoothing: antialiased; -moz-osx-font-smoothing: grayscale; } code { - font-family: source-code-pro, Menlo, Monaco, Consolas, "Courier New", + font-family: source-code-pro, Menlo, Monaco, Consolas, 'Courier New', monospace; } diff --git a/packages/create-app/template-svelte-ts/src/main.ts b/packages/create-app/template-svelte-ts/src/main.ts index 0dfb1eefa5c33b..d8200ac4fe3078 100644 --- a/packages/create-app/template-svelte-ts/src/main.ts +++ b/packages/create-app/template-svelte-ts/src/main.ts @@ -1,7 +1,7 @@ import App from './App.svelte' const app = new App({ - target: document.body + target: document.getElementById('app') }) export default app diff --git a/packages/create-app/template-svelte/src/main.js b/packages/create-app/template-svelte/src/main.js index 0dfb1eefa5c33b..d8200ac4fe3078 100644 --- a/packages/create-app/template-svelte/src/main.js +++ b/packages/create-app/template-svelte/src/main.js @@ -1,7 +1,7 @@ import App from './App.svelte' const app = new App({ - target: document.body + target: document.getElementById('app') }) export default app diff --git a/packages/create-app/template-vanilla-ts/_gitignore b/packages/create-app/template-vanilla-ts/_gitignore new file mode 100644 index 00000000000000..53f7466aca7003 --- /dev/null +++ b/packages/create-app/template-vanilla-ts/_gitignore @@ -0,0 +1,5 @@ +node_modules +.DS_Store +dist +dist-ssr +*.local \ No newline at end of file diff --git a/packages/create-app/template-vanilla-ts/favicon.svg b/packages/create-app/template-vanilla-ts/favicon.svg new file mode 100644 index 00000000000000..de4aeddc12bdfe --- /dev/null +++ b/packages/create-app/template-vanilla-ts/favicon.svg @@ -0,0 +1,15 @@ + + + + + + + + + + + + + + + diff --git a/packages/create-app/template-vanilla-ts/index.html b/packages/create-app/template-vanilla-ts/index.html new file mode 100644 index 00000000000000..867581c5124fa1 --- /dev/null +++ b/packages/create-app/template-vanilla-ts/index.html @@ -0,0 +1,13 @@ + + + + + + + Vite App + + +
+ + + diff --git a/packages/create-app/template-vanilla-ts/package.json b/packages/create-app/template-vanilla-ts/package.json new file mode 100644 index 00000000000000..0c72ddab9a6e87 --- /dev/null +++ b/packages/create-app/template-vanilla-ts/package.json @@ -0,0 +1,13 @@ +{ + "name": "vite-typescript-starter", + "version": "0.0.0", + "scripts": { + "dev": "vite", + "build": "tsc && vite build", + "serve": "vite preview" + }, + "devDependencies": { + "typescript": "^4.2.3", + "vite": "^2.1.5" + } +} \ No newline at end of file diff --git a/packages/create-app/template-vanilla-ts/src/main.ts b/packages/create-app/template-vanilla-ts/src/main.ts new file mode 100644 index 00000000000000..f77db7a8fcab13 --- /dev/null +++ b/packages/create-app/template-vanilla-ts/src/main.ts @@ -0,0 +1,8 @@ +import './style.css' + +const app = document.querySelector('#app')! + +app.innerHTML = ` +

Hello Vite!

+ Documentation +` diff --git a/packages/create-app/template-vanilla-ts/src/style.css b/packages/create-app/template-vanilla-ts/src/style.css new file mode 100644 index 00000000000000..852de7aa2ae573 --- /dev/null +++ b/packages/create-app/template-vanilla-ts/src/style.css @@ -0,0 +1,8 @@ +#app { + font-family: Avenir, Helvetica, Arial, sans-serif; + -webkit-font-smoothing: antialiased; + -moz-osx-font-smoothing: grayscale; + text-align: center; + color: #2c3e50; + margin-top: 60px; +} diff --git a/packages/create-app/template-vanilla-ts/tsconfig.json b/packages/create-app/template-vanilla-ts/tsconfig.json new file mode 100644 index 00000000000000..43d4bc703dd9ff --- /dev/null +++ b/packages/create-app/template-vanilla-ts/tsconfig.json @@ -0,0 +1,18 @@ +{ + "compilerOptions": { + "target": "ESNext", + "module": "ESNext", + "lib": ["ESNext", "DOM"], + "moduleResolution": "Node", + "strict": true, + "sourceMap": true, + "resolveJsonModule": true, + "esModuleInterop": true, + "types": ["vite/client"], + "noEmit": true, + "noUnusedLocals": true, + "noUnusedParameters": true, + "noImplicitReturns": true + }, + "include": ["./src"] +} diff --git a/packages/create-app/template-vanilla/style.css b/packages/create-app/template-vanilla/style.css index 89778b81515f05..852de7aa2ae573 100644 --- a/packages/create-app/template-vanilla/style.css +++ b/packages/create-app/template-vanilla/style.css @@ -5,4 +5,4 @@ text-align: center; color: #2c3e50; margin-top: 60px; -} \ No newline at end of file +} diff --git a/packages/create-app/template-vue-ts/package.json b/packages/create-app/template-vue-ts/package.json index 4129cac07c9ebb..6433a593fe4079 100644 --- a/packages/create-app/template-vue-ts/package.json +++ b/packages/create-app/template-vue-ts/package.json @@ -14,6 +14,6 @@ "@vue/compiler-sfc": "^3.0.5", "typescript": "^4.1.3", "vite": "^2.1.5", - "vue-tsc": "^0.0.15" + "vue-tsc": "^0.0.24" } } \ No newline at end of file diff --git a/packages/create-app/template-vue-ts/src/App.vue b/packages/create-app/template-vue-ts/src/App.vue index 26d5731c4055d1..72cc3b82039dd2 100644 --- a/packages/create-app/template-vue-ts/src/App.vue +++ b/packages/create-app/template-vue-ts/src/App.vue @@ -24,4 +24,4 @@ export default defineComponent({ color: #2c3e50; margin-top: 60px; } - \ No newline at end of file + diff --git a/packages/create-app/template-vue-ts/src/components/HelloWorld.vue b/packages/create-app/template-vue-ts/src/components/HelloWorld.vue index f51da8271be163..f35d1b04facc6b 100644 --- a/packages/create-app/template-vue-ts/src/components/HelloWorld.vue +++ b/packages/create-app/template-vue-ts/src/components/HelloWorld.vue @@ -8,7 +8,9 @@ Vetur + > + Vetur + or Volar (if using @@ -18,7 +20,10 @@

See README.md for more information.

- Vite Docs | + + Vite Docs + + | Vue 3 Docs

diff --git a/packages/create-app/template-vue/src/App.vue b/packages/create-app/template-vue/src/App.vue index e9748276d24a0b..b67169df640bb4 100644 --- a/packages/create-app/template-vue/src/App.vue +++ b/packages/create-app/template-vue/src/App.vue @@ -19,4 +19,4 @@ import HelloWorld from './components/HelloWorld.vue' color: #2c3e50; margin-top: 60px; } - \ No newline at end of file + diff --git a/packages/create-app/template-vue/src/components/HelloWorld.vue b/packages/create-app/template-vue/src/components/HelloWorld.vue index e4f6590eeccdf0..d834212fe51377 100644 --- a/packages/create-app/template-vue/src/components/HelloWorld.vue +++ b/packages/create-app/template-vue/src/components/HelloWorld.vue @@ -2,7 +2,10 @@

{{ msg }}

- Vite Documentation | + + Vite Documentation + + | Vue 3 Documentation

@@ -27,4 +30,4 @@ const state = reactive({ count: 0 }) a { color: #42b983; } - \ No newline at end of file + diff --git a/packages/playground/alias/dir/test.css b/packages/playground/alias/dir/test.css index f3d1ca7aeaddc8..52ed24d8063f15 100644 --- a/packages/playground/alias/dir/test.css +++ b/packages/playground/alias/dir/test.css @@ -1,3 +1,3 @@ body { color: grey; -} \ No newline at end of file +} diff --git a/packages/playground/assets/css/fonts.css b/packages/playground/assets/css/fonts.css index 974d80fe726d9f..f9e06755951534 100644 --- a/packages/playground/assets/css/fonts.css +++ b/packages/playground/assets/css/fonts.css @@ -9,4 +9,4 @@ body { font-family: 'Inter'; -} \ No newline at end of file +} diff --git a/packages/playground/assets/package.json b/packages/playground/assets/package.json index ed26f61e22c261..3f6c5d0783cfe3 100644 --- a/packages/playground/assets/package.json +++ b/packages/playground/assets/package.json @@ -8,4 +8,4 @@ "debug": "node --inspect-brk ../../vite/bin/vite", "serve": "vite preview" } -} \ No newline at end of file +} diff --git a/packages/playground/assets/static/raw.css b/packages/playground/assets/static/raw.css index b60c26ff703106..ee4f36a2c29e64 100644 --- a/packages/playground/assets/static/raw.css +++ b/packages/playground/assets/static/raw.css @@ -1,3 +1,3 @@ .raw-css { color: red; -} \ No newline at end of file +} diff --git a/packages/playground/css-codesplit-cjs/__tests__/css-codesplit.spec.ts b/packages/playground/css-codesplit-cjs/__tests__/css-codesplit.spec.ts new file mode 100644 index 00000000000000..95fe97a1b953ba --- /dev/null +++ b/packages/playground/css-codesplit-cjs/__tests__/css-codesplit.spec.ts @@ -0,0 +1,20 @@ +import { findAssetFile, getColor, isBuild, readManifest } from '../../testUtils' + +test('should load both stylesheets', async () => { + expect(await getColor('h1')).toBe('red') + expect(await getColor('h2')).toBe('blue') +}) + +if (isBuild) { + test('should remove empty chunk', async () => { + expect(findAssetFile(/style.*\.js$/)).toBe('') + expect(findAssetFile('main.*.js$')).toMatch(`/* empty css`) + expect(findAssetFile('other.*.js$')).toMatch(`/* empty css`) + }) + + test('should generate correct manifest', async () => { + const manifest = readManifest() + expect(manifest['index.html'].css.length).toBe(2) + expect(manifest['other.js'].css.length).toBe(1) + }) +} diff --git a/packages/playground/css-codesplit-cjs/index.html b/packages/playground/css-codesplit-cjs/index.html new file mode 100644 index 00000000000000..6b7b3bb2b4dc2d --- /dev/null +++ b/packages/playground/css-codesplit-cjs/index.html @@ -0,0 +1,2 @@ + +
diff --git a/packages/playground/css-codesplit-cjs/main.css b/packages/playground/css-codesplit-cjs/main.css new file mode 100644 index 00000000000000..24aec3a5671d8f --- /dev/null +++ b/packages/playground/css-codesplit-cjs/main.css @@ -0,0 +1,3 @@ +h1 { + color: red; +} \ No newline at end of file diff --git a/packages/playground/css-codesplit-cjs/main.js b/packages/playground/css-codesplit-cjs/main.js new file mode 100644 index 00000000000000..8c80df2c181511 --- /dev/null +++ b/packages/playground/css-codesplit-cjs/main.js @@ -0,0 +1,6 @@ +import './style.css' +import './main.css' + +document.getElementById( + 'app' +).innerHTML = `

This should be red

This should be blue

` diff --git a/packages/playground/css-codesplit-cjs/other.js b/packages/playground/css-codesplit-cjs/other.js new file mode 100644 index 00000000000000..cab743adef7757 --- /dev/null +++ b/packages/playground/css-codesplit-cjs/other.js @@ -0,0 +1 @@ +import './style.css' diff --git a/packages/playground/css-codesplit-cjs/package.json b/packages/playground/css-codesplit-cjs/package.json new file mode 100644 index 00000000000000..f0ffc32390e161 --- /dev/null +++ b/packages/playground/css-codesplit-cjs/package.json @@ -0,0 +1,11 @@ +{ + "name": "test-css-codesplit-cjs", + "private": true, + "version": "0.0.0", + "scripts": { + "dev": "vite", + "build": "vite build", + "debug": "node --inspect-brk ../../vite/bin/vite", + "serve": "vite preview" + } +} diff --git a/packages/playground/css-codesplit-cjs/style.css b/packages/playground/css-codesplit-cjs/style.css new file mode 100644 index 00000000000000..2b4bb3671e654b --- /dev/null +++ b/packages/playground/css-codesplit-cjs/style.css @@ -0,0 +1,3 @@ +h2 { + color: blue; +} diff --git a/packages/playground/css-codesplit-cjs/vite.config.js b/packages/playground/css-codesplit-cjs/vite.config.js new file mode 100644 index 00000000000000..7cd8967121f021 --- /dev/null +++ b/packages/playground/css-codesplit-cjs/vite.config.js @@ -0,0 +1,20 @@ +const { resolve } = require('path') + +module.exports = { + build: { + outDir: './dist', + manifest: true, + rollupOptions: { + input: { + main: resolve(__dirname, './index.html'), + other: resolve(__dirname, './other.js') + }, + treeshake: false, + output: { + format: 'cjs', + freeze: false, + externalLiveBindings: false + } + } + } +} diff --git a/packages/playground/css-codesplit/main.css b/packages/playground/css-codesplit/main.css index 24aec3a5671d8f..adc68fa6a4dfa0 100644 --- a/packages/playground/css-codesplit/main.css +++ b/packages/playground/css-codesplit/main.css @@ -1,3 +1,3 @@ -h1 { +h1 { color: red; -} \ No newline at end of file +} diff --git a/packages/playground/css/__tests__/css.spec.ts b/packages/playground/css/__tests__/css.spec.ts index 010fde92347e7e..212778a375a5dd 100644 --- a/packages/playground/css/__tests__/css.spec.ts +++ b/packages/playground/css/__tests__/css.spec.ts @@ -56,11 +56,16 @@ test('postcss config', async () => { test('sass', async () => { const imported = await page.$('.sass') const atImport = await page.$('.sass-at-import') + const atImportAlias = await page.$('.sass-at-import-alias') const partialImport = await page.$('.sass-partial') expect(await getColor(imported)).toBe('orange') expect(await getColor(atImport)).toBe('olive') expect(await getBg(atImport)).toMatch(isBuild ? /base64/ : '/nested/icon.png') + expect(await getColor(atImportAlias)).toBe('olive') + expect(await getBg(atImportAlias)).toMatch( + isBuild ? /base64/ : '/nested/icon.png' + ) expect(await getColor(partialImport)).toBe('orchid') editFile('sass.scss', (code) => @@ -82,10 +87,15 @@ test('sass', async () => { test('less', async () => { const imported = await page.$('.less') const atImport = await page.$('.less-at-import') + const atImportAlias = await page.$('.less-at-import-alias') expect(await getColor(imported)).toBe('blue') expect(await getColor(atImport)).toBe('darkslateblue') expect(await getBg(atImport)).toMatch(isBuild ? /base64/ : '/nested/icon.png') + expect(await getColor(atImportAlias)).toBe('darkslateblue') + expect(await getBg(atImportAlias)).toMatch( + isBuild ? /base64/ : '/nested/icon.png' + ) editFile('less.less', (code) => code.replace('@color: blue', '@color: red')) await untilUpdated(() => getColor(imported), 'red') @@ -96,6 +106,35 @@ test('less', async () => { await untilUpdated(() => getColor(atImport), 'blue') }) +test('stylus', async () => { + const imported = await page.$('.stylus') + const additionalData = await page.$('.stylus-additional-data') + const relativeImport = await page.$('.stylus-import') + const relativeImportAlias = await page.$('.stylus-import-alias') + const optionsRelativeImport = await page.$('.stylus-options-relative-import') + const optionsAbsoluteImport = await page.$('.stylus-options-absolute-import') + + expect(await getColor(imported)).toBe('blue') + expect(await getColor(additionalData)).toBe('orange') + expect(await getColor(relativeImport)).toBe('darkslateblue') + expect(await getColor(relativeImportAlias)).toBe('darkslateblue') + expect(await getBg(relativeImportAlias)).toMatch( + isBuild ? /base64/ : '/nested/icon.png' + ) + expect(await getColor(optionsRelativeImport)).toBe('green') + expect(await getColor(optionsAbsoluteImport)).toBe('red') + + editFile('stylus.styl', (code) => + code.replace('$color ?= blue', '$color ?= red') + ) + await untilUpdated(() => getColor(imported), 'red') + + editFile('nested/nested.styl', (code) => + code.replace('color: darkslateblue', 'color: blue') + ) + await untilUpdated(() => getColor(relativeImport), 'blue') +}) + test('css modules', async () => { const imported = await page.$('.modules') expect(await getColor(imported)).toBe('turquoise') @@ -133,6 +172,10 @@ test('@import dependency w/ sass entry', async () => { expect(await getColor('.css-dep-sass')).toBe('orange') }) +test('@import dependency w/ stylus entry', async () => { + expect(await getColor('.css-dep-stylus')).toBe('red') +}) + test('async chunk', async () => { const el = await page.$('.async') expect(await getColor(el)).toBe('teal') diff --git a/packages/playground/css/async-treeshaken.css b/packages/playground/css/async-treeshaken.css index f0d7d653a8f8a5..9b609f308a63cf 100644 --- a/packages/playground/css/async-treeshaken.css +++ b/packages/playground/css/async-treeshaken.css @@ -1,3 +1,3 @@ .async-treeshaken { color: plum; -} \ No newline at end of file +} diff --git a/packages/playground/css/async.css b/packages/playground/css/async.css index a00539db8f3b1a..aee3dcafd84895 100644 --- a/packages/playground/css/async.css +++ b/packages/playground/css/async.css @@ -1,3 +1,3 @@ .async { color: teal; -} \ No newline at end of file +} diff --git a/packages/playground/css/css-dep/index.css b/packages/playground/css/css-dep/index.css index fb86e5496c1aee..0fdcd8118ad54c 100644 --- a/packages/playground/css/css-dep/index.css +++ b/packages/playground/css/css-dep/index.css @@ -1,3 +1,3 @@ .css-dep { color: purple; -} \ No newline at end of file +} diff --git a/packages/playground/css/css-dep/index.styl b/packages/playground/css/css-dep/index.styl new file mode 100644 index 00000000000000..68170594a118a9 --- /dev/null +++ b/packages/playground/css/css-dep/index.styl @@ -0,0 +1,2 @@ +.css-dep-stylus + color red diff --git a/packages/playground/css/dep.css b/packages/playground/css/dep.css index cb828d3383582a..8d5b62847cd70e 100644 --- a/packages/playground/css/dep.css +++ b/packages/playground/css/dep.css @@ -1 +1 @@ -@import 'css-dep' \ No newline at end of file +@import 'css-dep'; diff --git a/packages/playground/css/imported-at-import.css b/packages/playground/css/imported-at-import.css index 6395e26d6cb670..b71ef00e65b27a 100644 --- a/packages/playground/css/imported-at-import.css +++ b/packages/playground/css/imported-at-import.css @@ -1,3 +1,3 @@ .imported-at-import { color: purple; -} \ No newline at end of file +} diff --git a/packages/playground/css/imported.css b/packages/playground/css/imported.css index edd5c35b22ac3d..4bedd02a53f0f5 100644 --- a/packages/playground/css/imported.css +++ b/packages/playground/css/imported.css @@ -16,4 +16,4 @@ pre { .nesting { color: pink; } -} \ No newline at end of file +} diff --git a/packages/playground/css/index.html b/packages/playground/css/index.html index c1128b68039047..ac2d0e90fb5526 100644 --- a/packages/playground/css/index.html +++ b/packages/playground/css/index.html @@ -40,6 +40,26 @@

CSS

Imported Less string:


 
+  

Stylus: This should be blue

+

+ Stylus additionalData: This should be orange +

+

@import from Stylus: This should be darkslateblue

+

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

+

+ Stylus import (relative path) via vite config preprocessor options: This + should be green +

+

+ Stylus import (absolute path) via vite config preprocessor options: This + should be red +

+

Imported Stylus string:

+

+
   

CSS modules: this should be turquoise

Imported CSS module:


@@ -54,6 +74,9 @@ 

CSS

@import dependency w/ sass enrtrypoints: this should be orange

+

+ @import dependency w/ styl enrtrypoints: this should be red +

diff --git a/packages/playground/css/main.js b/packages/playground/css/main.js index 0026e6305a2eb9..31b44b5552a8e5 100644 --- a/packages/playground/css/main.js +++ b/packages/playground/css/main.js @@ -7,6 +7,9 @@ text('.imported-sass', sass) import less from './less.less' text('.imported-less', less) +import stylus from './stylus.styl' +text('.imported-stylus', stylus) + import mod from './mod.module.css' document.querySelector('.modules').classList.add(mod['apply-color']) text('.modules-code', JSON.stringify(mod, null, 2)) diff --git a/packages/playground/css/nested/nested.styl b/packages/playground/css/nested/nested.styl new file mode 100644 index 00000000000000..72e6f7a5074685 --- /dev/null +++ b/packages/playground/css/nested/nested.styl @@ -0,0 +1,6 @@ +.stylus-import + color darkslateblue + +.stylus-import-alias + color darkslateblue + background url('@/nested/icon.png') 10px no-repeat diff --git a/packages/playground/css/options/absolute-import.styl b/packages/playground/css/options/absolute-import.styl new file mode 100644 index 00000000000000..a057258d56c0c6 --- /dev/null +++ b/packages/playground/css/options/absolute-import.styl @@ -0,0 +1,3 @@ +.stylus-options-absolute-import + /* imported via vite.config.js */ + color red diff --git a/packages/playground/css/options/relative-import.styl b/packages/playground/css/options/relative-import.styl new file mode 100644 index 00000000000000..157e0ea8d500eb --- /dev/null +++ b/packages/playground/css/options/relative-import.styl @@ -0,0 +1,3 @@ +.stylus-options-relative-import + /* imported via vite.config.js */ + color green diff --git a/packages/playground/css/package.json b/packages/playground/css/package.json index dedf35b5834fd0..e5cc96c39fe40d 100644 --- a/packages/playground/css/package.json +++ b/packages/playground/css/package.json @@ -9,9 +9,10 @@ "serve": "vite preview" }, "devDependencies": { + "css-dep": "link:./css-dep", "less": "^4.1.0", "postcss-nested": "^5.0.3", "sass": "^1.32.5", - "css-dep": "link:./css-dep" + "stylus": "^0.54.8" } } diff --git a/packages/playground/css/stylus.styl b/packages/playground/css/stylus.styl new file mode 100644 index 00000000000000..7b77ca36531be8 --- /dev/null +++ b/packages/playground/css/stylus.styl @@ -0,0 +1,11 @@ +@import './nested/nested' +@import 'css-dep'; // package w/ styl entry points + +$color ?= blue + +.stylus + color $color + +.stylus-additional-data + /* injected via vite.config.js */ + color $injectedColor diff --git a/packages/playground/css/vite.config.js b/packages/playground/css/vite.config.js index 08b9eca9768a3f..5484342e3966a5 100644 --- a/packages/playground/css/vite.config.js +++ b/packages/playground/css/vite.config.js @@ -1,3 +1,4 @@ +const path = require('path') /** * @type {import('vite').UserConfig} */ @@ -32,6 +33,13 @@ module.exports = { preprocessorOptions: { scss: { additionalData: `$injectedColor: orange;` + }, + styl: { + additionalData: `$injectedColor ?= orange`, + imports: [ + './options/relative-import.styl', + path.join(__dirname, 'options/absolute-import.styl') + ] } } } diff --git a/packages/playground/data-uri/package.json b/packages/playground/data-uri/package.json index 54912c035d44e7..7f5cabc688332e 100644 --- a/packages/playground/data-uri/package.json +++ b/packages/playground/data-uri/package.json @@ -8,4 +8,4 @@ "debug": "node --inspect-brk ../../vite/bin/vite", "serve": "vite preview" } -} \ No newline at end of file +} diff --git a/packages/playground/dynamic-import/__tests__/dynamic-import.spec.ts b/packages/playground/dynamic-import/__tests__/dynamic-import.spec.ts index 32333059ad7381..292cdb2e861b4b 100644 --- a/packages/playground/dynamic-import/__tests__/dynamic-import.spec.ts +++ b/packages/playground/dynamic-import/__tests__/dynamic-import.spec.ts @@ -20,6 +20,22 @@ test('should load data URL of `data:`', async () => { await untilUpdated(() => page.textContent('.view'), 'data', true) }) +test('should have same reference on static and dynamic js import', async () => { + await page.click('.mxd') + await untilUpdated(() => page.textContent('.view'), 'true', true) +}) + +// in this case, it is not possible to detect the correct module +test('should have same reference on static and dynamic js import', async () => { + await page.click('.mxd2') + await untilUpdated(() => page.textContent('.view'), 'false', true) +}) + +test('should have same reference on static and dynamic js import', async () => { + await page.click('.mxdjson') + await untilUpdated(() => page.textContent('.view'), 'true', true) +}) + // since this test has a timeout, it should be put last so that it // does not bleed on the last test('should load dynamic import with vars', async () => { diff --git a/packages/playground/dynamic-import/index.html b/packages/playground/dynamic-import/index.html index 87bb6ebdaa37e8..3039d9eadf3c00 100644 --- a/packages/playground/dynamic-import/index.html +++ b/packages/playground/dynamic-import/index.html @@ -2,6 +2,9 @@ + + + diff --git a/packages/playground/dynamic-import/mxd.js b/packages/playground/dynamic-import/mxd.js new file mode 100644 index 00000000000000..ea9b101e1c2259 --- /dev/null +++ b/packages/playground/dynamic-import/mxd.js @@ -0,0 +1 @@ +export default function () {} diff --git a/packages/playground/dynamic-import/mxd.json b/packages/playground/dynamic-import/mxd.json new file mode 100644 index 00000000000000..9e26dfeeb6e641 --- /dev/null +++ b/packages/playground/dynamic-import/mxd.json @@ -0,0 +1 @@ +{} \ No newline at end of file diff --git a/packages/playground/dynamic-import/nested/index.js b/packages/playground/dynamic-import/nested/index.js index cc6522016fc1d8..48a7cb7f924c38 100644 --- a/packages/playground/dynamic-import/nested/index.js +++ b/packages/playground/dynamic-import/nested/index.js @@ -1,3 +1,6 @@ +import mxdStatic from '../mxd' +import mxdStaticJSON from '../mxd.json' + async function setView(view) { const { msg } = await import(`../views/${view}.js`) text('.view', msg) @@ -21,6 +24,27 @@ document.querySelector('.qux').addEventListener('click', async () => { text('.view', msg) }) +// mixed static and dynamic +document.querySelector('.mxd').addEventListener('click', async () => { + const view = 'mxd' + const { default: mxdDynamic } = await import(`../${view}.js`) + text('.view', mxdStatic === mxdDynamic) +}) + +document.querySelector('.mxd2').addEventListener('click', async () => { + const test = { jss: '../mxd.js' } + const ttest = test + const view = 'mxd' + const { default: mxdDynamic } = await import(test.jss) + text('.view', mxdStatic === mxdDynamic) +}) + +document.querySelector('.mxdjson').addEventListener('click', async () => { + const view = 'mxd' + const { default: mxdDynamicJSON } = await import(`../${view}.json`) + text('.view', mxdStaticJSON === mxdDynamicJSON) +}) + // data URLs (`blob:`) const code1 = 'export const msg = "blob"' const blob = new Blob([code1], { type: 'text/javascript;charset=UTF-8' }) diff --git a/packages/playground/dynamic-import/vite.config.js b/packages/playground/dynamic-import/vite.config.js index e0fb1f662fd3d0..010e47d6308d30 100644 --- a/packages/playground/dynamic-import/vite.config.js +++ b/packages/playground/dynamic-import/vite.config.js @@ -10,6 +10,14 @@ module.exports = { path.resolve(__dirname, 'qux.js'), path.resolve(__dirname, 'dist/qux.js') ) + fs.copyFileSync( + path.resolve(__dirname, 'mxd.js'), + path.resolve(__dirname, 'dist/mxd.js') + ) + fs.copyFileSync( + path.resolve(__dirname, 'mxd.json'), + path.resolve(__dirname, 'dist/mxd.json') + ) } } ] diff --git a/packages/playground/glob-import/dir/baz.json b/packages/playground/glob-import/dir/baz.json index ef46606326191e..e8677dc6e2dae3 100644 --- a/packages/playground/glob-import/dir/baz.json +++ b/packages/playground/glob-import/dir/baz.json @@ -1,3 +1,3 @@ { "msg": "baz" -} \ No newline at end of file +} diff --git a/packages/playground/optimize-deps/dep-linked-include/Test.vue b/packages/playground/optimize-deps/dep-linked-include/Test.vue index ca11049b3e883b..0f5052cb6a8853 100644 --- a/packages/playground/optimize-deps/dep-linked-include/Test.vue +++ b/packages/playground/optimize-deps/dep-linked-include/Test.vue @@ -1 +1 @@ - \ No newline at end of file + diff --git a/packages/playground/optimize-deps/dep-linked-include/test.css b/packages/playground/optimize-deps/dep-linked-include/test.css index b719c608467fdd..60f1eab97137f7 100644 --- a/packages/playground/optimize-deps/dep-linked-include/test.css +++ b/packages/playground/optimize-deps/dep-linked-include/test.css @@ -1,3 +1,3 @@ body { color: red; -} \ No newline at end of file +} diff --git a/packages/playground/react/App.jsx b/packages/playground/react/App.jsx index ef0ee76f43fcc5..1de7461b163776 100644 --- a/packages/playground/react/App.jsx +++ b/packages/playground/react/App.jsx @@ -7,7 +7,9 @@ function App() {

Hello Vite + React

- +

Edit App.jsx and save to test HMR updates. diff --git a/packages/playground/resolve-linked/package.json b/packages/playground/resolve-linked/package.json index 68263fb725c4c8..8042ce78119b8f 100644 --- a/packages/playground/resolve-linked/package.json +++ b/packages/playground/resolve-linked/package.json @@ -3,4 +3,4 @@ "version": "0.0.0", "private": true, "main": "src/index.js" -} \ No newline at end of file +} diff --git a/packages/playground/resolve/custom-ext.es b/packages/playground/resolve/custom-ext.es index 000f3d188d0dea..b7f7c98793dc2b 100644 --- a/packages/playground/resolve/custom-ext.es +++ b/packages/playground/resolve/custom-ext.es @@ -1 +1 @@ -export const msg = `[success] custom ext` \ No newline at end of file +export const msg = `[success] custom ext` diff --git a/packages/playground/resolve/exports-env/browser.mjs b/packages/playground/resolve/exports-env/browser.mjs index 6d8ec78613bd18..e4cb61295b1086 100644 --- a/packages/playground/resolve/exports-env/browser.mjs +++ b/packages/playground/resolve/exports-env/browser.mjs @@ -1 +1 @@ -export const msg = '[success] exports env (browser.mjs)' \ No newline at end of file +export const msg = '[success] exports env (browser.mjs)' diff --git a/packages/playground/resolve/exports-env/browser.prod.mjs b/packages/playground/resolve/exports-env/browser.prod.mjs index 8265343ed6220f..bace0b9cd0dfa7 100644 --- a/packages/playground/resolve/exports-env/browser.prod.mjs +++ b/packages/playground/resolve/exports-env/browser.prod.mjs @@ -1 +1 @@ -export const msg = '[success] exports env (browser.prod.mjs)' \ No newline at end of file +export const msg = '[success] exports env (browser.prod.mjs)' diff --git a/packages/playground/resolve/inline-package/package.json b/packages/playground/resolve/inline-package/package.json index d84c0c9332104d..335e35a9236d5b 100644 --- a/packages/playground/resolve/inline-package/package.json +++ b/packages/playground/resolve/inline-package/package.json @@ -3,4 +3,4 @@ "private": true, "sideEffects": false, "main": "./inline" -} \ No newline at end of file +} diff --git a/packages/playground/ssr-react/__tests__/ssr-react.spec.ts b/packages/playground/ssr-react/__tests__/ssr-react.spec.ts index 12660c81e4be14..bf161e03e5143c 100644 --- a/packages/playground/ssr-react/__tests__/ssr-react.spec.ts +++ b/packages/playground/ssr-react/__tests__/ssr-react.spec.ts @@ -38,9 +38,9 @@ test('hmr', async () => { }) test('client navigation', async () => { + await untilUpdated(() => page.textContent('a[href="/about"]'), 'About') await page.click('a[href="/about"]') - await page.waitForTimeout(10) - expect(await page.textContent('h1')).toMatch('About') + await untilUpdated(() => page.textContent('h1'), 'About') editFile('src/pages/About.jsx', (code) => code.replace('

About', '

changed') ) diff --git a/packages/playground/ssr-react/server.js b/packages/playground/ssr-react/server.js index 4a6a653a3a781b..7c0c1a81152445 100644 --- a/packages/playground/ssr-react/server.js +++ b/packages/playground/ssr-react/server.js @@ -26,7 +26,13 @@ async function createServer( root, logLevel: isTest ? 'error' : 'info', server: { - middlewareMode: true + middlewareMode: true, + watch: { + // During tests we edit the files too fast and sometimes chokidar + // misses change events, so enforce polling for consistency + usePolling: true, + interval: 100 + } } }) // use vite's connect instance as middleware diff --git a/packages/playground/ssr-vue/__tests__/ssr-vue.spec.ts b/packages/playground/ssr-vue/__tests__/ssr-vue.spec.ts index 63a6034cc7b36d..a86916b1ad7594 100644 --- a/packages/playground/ssr-vue/__tests__/ssr-vue.spec.ts +++ b/packages/playground/ssr-vue/__tests__/ssr-vue.spec.ts @@ -109,9 +109,9 @@ test('hmr', async () => { }) test('client navigation', async () => { + await untilUpdated(() => page.textContent('a[href="/about"]'), 'About') await page.click('a[href="/about"]') - await page.waitForTimeout(10) - expect(await page.textContent('h1')).toMatch('About') + await untilUpdated(() => page.textContent('h1'), 'About') editFile('src/pages/About.vue', (code) => code.replace('About', 'changed')) await untilUpdated(() => page.textContent('h1'), 'changed') }) diff --git a/packages/playground/ssr-vue/dep-import-type/deep/index.d.ts b/packages/playground/ssr-vue/dep-import-type/deep/index.d.ts new file mode 100644 index 00000000000000..39df3b83f7e9b8 --- /dev/null +++ b/packages/playground/ssr-vue/dep-import-type/deep/index.d.ts @@ -0,0 +1 @@ +export interface Foo {} diff --git a/packages/playground/ssr-vue/dep-import-type/package.json b/packages/playground/ssr-vue/dep-import-type/package.json new file mode 100644 index 00000000000000..b58c873340ebe2 --- /dev/null +++ b/packages/playground/ssr-vue/dep-import-type/package.json @@ -0,0 +1,5 @@ +{ + "name": "dep-import-type", + "version": "0.0.0", + "main": "index.js" +} diff --git a/packages/playground/ssr-vue/package.json b/packages/playground/ssr-vue/package.json index 6bf759471d3a93..77f02fedc6260e 100644 --- a/packages/playground/ssr-vue/package.json +++ b/packages/playground/ssr-vue/package.json @@ -20,6 +20,7 @@ "@vitejs/plugin-vue-jsx": "^1.1.2", "@vue/compiler-sfc": "^3.0.8", "@vue/server-renderer": "^3.0.6", + "dep-import-type": "link:./dep-import-type", "compression": "^1.7.4", "cross-env": "^7.0.3", "express": "^4.17.1", diff --git a/packages/playground/ssr-vue/server.js b/packages/playground/ssr-vue/server.js index 969ab6dacee01a..4ccbc21a4816ab 100644 --- a/packages/playground/ssr-vue/server.js +++ b/packages/playground/ssr-vue/server.js @@ -31,7 +31,13 @@ async function createServer( root, logLevel: isTest ? 'error' : 'info', server: { - middlewareMode: true + middlewareMode: true, + watch: { + // During tests we edit the files too fast and sometimes chokidar + // misses change events, so enforce polling for consistency + usePolling: true, + interval: 100 + } } }) // use vite's connect instance as middleware diff --git a/packages/playground/ssr-vue/src/App.vue b/packages/playground/ssr-vue/src/App.vue index 06f31569726915..8edc0dbf369b32 100644 --- a/packages/playground/ssr-vue/src/App.vue +++ b/packages/playground/ssr-vue/src/App.vue @@ -19,4 +19,4 @@ color: #2c3e50; margin-top: 60px; } - \ No newline at end of file + diff --git a/packages/playground/ssr-vue/src/components/Foo.jsx b/packages/playground/ssr-vue/src/components/Foo.jsx index c1670c6a13758f..427815b2d252d2 100644 --- a/packages/playground/ssr-vue/src/components/Foo.jsx +++ b/packages/playground/ssr-vue/src/components/Foo.jsx @@ -7,4 +7,4 @@ export const Foo = defineComponent({ setup() { return () =>
from JSX
} -}) \ No newline at end of file +}) diff --git a/packages/playground/ssr-vue/src/components/ImportType.vue b/packages/playground/ssr-vue/src/components/ImportType.vue new file mode 100644 index 00000000000000..7fa77170ff7d61 --- /dev/null +++ b/packages/playground/ssr-vue/src/components/ImportType.vue @@ -0,0 +1,9 @@ + + + diff --git a/packages/playground/ssr-vue/src/components/foo.css b/packages/playground/ssr-vue/src/components/foo.css index bfaacd33a2f805..f8baa0d15b90d3 100644 --- a/packages/playground/ssr-vue/src/components/foo.css +++ b/packages/playground/ssr-vue/src/components/foo.css @@ -1,3 +1,3 @@ .jsx { color: blue; -} \ No newline at end of file +} diff --git a/packages/playground/ssr-vue/src/pages/About.vue b/packages/playground/ssr-vue/src/pages/About.vue index 6ce83448eea6ec..5fe0930c7abc4f 100644 --- a/packages/playground/ssr-vue/src/pages/About.vue +++ b/packages/playground/ssr-vue/src/pages/About.vue @@ -16,4 +16,4 @@ export default { h1 { color: red; } - \ No newline at end of file + diff --git a/packages/playground/ssr-vue/src/pages/Home.vue b/packages/playground/ssr-vue/src/pages/Home.vue index 35260df7dae30f..f96fb6b4d5a66a 100644 --- a/packages/playground/ssr-vue/src/pages/Home.vue +++ b/packages/playground/ssr-vue/src/pages/Home.vue @@ -6,12 +6,17 @@

msg from virtual module: {{ foo.msg }}

+ + @@ -21,4 +26,4 @@ h1, a { color: green; } - \ No newline at end of file + diff --git a/packages/playground/vue/Assets.vue b/packages/playground/vue/Assets.vue index 9663d239faf8c4..875ac1b243b393 100644 --- a/packages/playground/vue/Assets.vue +++ b/packages/playground/vue/Assets.vue @@ -20,7 +20,7 @@ SVG Fragment reference @@ -39,4 +39,4 @@ img { background-image: url('./assets/asset.png'); background-size: 30px; } - \ No newline at end of file + diff --git a/packages/playground/vue/CustomBlock.vue b/packages/playground/vue/CustomBlock.vue index 2fae9f05441b47..cc49cd07242fdd 100644 --- a/packages/playground/vue/CustomBlock.vue +++ b/packages/playground/vue/CustomBlock.vue @@ -25,7 +25,7 @@ export default { en: - hello: "hello,vite!" + hello: 'hello,vite!' ja: - hello: "こんにちは、vite!" + hello: 'こんにちは、vite!' diff --git a/packages/playground/vue/Hmr.vue b/packages/playground/vue/Hmr.vue index 0be84398fddde8..89dade2c4f5879 100644 --- a/packages/playground/vue/Hmr.vue +++ b/packages/playground/vue/Hmr.vue @@ -16,4 +16,4 @@ const count = ref(foo) .hmr-inc { color: red; } - \ No newline at end of file + diff --git a/packages/playground/vue/Main.vue b/packages/playground/vue/Main.vue index 8ed3ba4de5f301..62cd2016730ee8 100644 --- a/packages/playground/vue/Main.vue +++ b/packages/playground/vue/Main.vue @@ -34,7 +34,7 @@ const time = ref('loading...') window.addEventListener('load', () => { setTimeout(() => { - const [entry] = performance.getEntriesByType("navigation") + const [entry] = performance.getEntriesByType('navigation') time.value = `loaded in ${entry.duration.toFixed(2)}ms.` }, 0) }) diff --git a/packages/playground/vue/Slotted.vue b/packages/playground/vue/Slotted.vue index 5d38a63762e05b..fb25a9c5100215 100644 --- a/packages/playground/vue/Slotted.vue +++ b/packages/playground/vue/Slotted.vue @@ -3,10 +3,10 @@

:slotted

-> + \ No newline at end of file + diff --git a/packages/plugin-legacy/index.js b/packages/plugin-legacy/index.js index 56ba4c10122161..0ecf2bd67b2ad5 100644 --- a/packages/plugin-legacy/index.js +++ b/packages/plugin-legacy/index.js @@ -226,6 +226,7 @@ function viteLegacyPlugin(options = {}) { // transform the legacy chunk with @babel/preset-env const sourceMaps = !!config.build.sourcemap const { code, map } = loadBabel().transform(raw, { + babelrc: false, configFile: false, compact: true, sourceMaps, @@ -408,6 +409,7 @@ function viteLegacyPlugin(options = {}) { function detectPolyfills(code, targets, list) { const { ast } = loadBabel().transform(code, { ast: true, + babelrc: false, configFile: false, presets: [ [ diff --git a/packages/plugin-react-refresh/README.md b/packages/plugin-react-refresh/README.md index cb73bfbf86d217..9f2abf5a5c54f6 100644 --- a/packages/plugin-react-refresh/README.md +++ b/packages/plugin-react-refresh/README.md @@ -45,14 +45,11 @@ To mitigate this issue, you can explicitly transform your `index.html` like this ```ts app.get('/', async (req, res, next) => { try { - let html = fs.readFileSync( - path.resolve(root, 'index.html'), - 'utf-8' - ); - html = await viteServer.transformIndexHtml(req.url, html); - res.send(html); + let html = fs.readFileSync(path.resolve(root, 'index.html'), 'utf-8') + html = await viteServer.transformIndexHtml(req.url, html) + res.send(html) } catch (e) { - return next(e); + return next(e) } -}); +}) ``` diff --git a/packages/plugin-react-refresh/index.js b/packages/plugin-react-refresh/index.js index a68875e5df2b83..fc4f47da6a0d30 100644 --- a/packages/plugin-react-refresh/index.js +++ b/packages/plugin-react-refresh/index.js @@ -110,6 +110,9 @@ function reactRefreshPlugin(opts) { allowAwaitOutsideFunction: true, plugins: parserPlugins }, + generatorOpts: { + decoratorsBeforeExport: true + }, plugins: [ require('@babel/plugin-transform-react-jsx-self'), require('@babel/plugin-transform-react-jsx-source'), diff --git a/packages/plugin-vue-jsx/README.md b/packages/plugin-vue-jsx/README.md index 098c7559a811b7..d1825db5fc0c36 100644 --- a/packages/plugin-vue-jsx/README.md +++ b/packages/plugin-vue-jsx/README.md @@ -18,6 +18,7 @@ export default { ## Options See [@vue/babel-plugin-jsx](https://github.com/vuejs/jsx-next). + ## HMR Detection This plugin supports HMR of Vue JSX components. The detection requirements are: @@ -53,4 +54,4 @@ export const Bar = { ... } // not exported const Foo = defineComponent(...) -``` \ No newline at end of file +``` diff --git a/packages/plugin-vue/src/handleHotUpdate.ts b/packages/plugin-vue/src/handleHotUpdate.ts index 3b3ec974d1638c..ae07de16e19c93 100644 --- a/packages/plugin-vue/src/handleHotUpdate.ts +++ b/packages/plugin-vue/src/handleHotUpdate.ts @@ -127,7 +127,7 @@ export async function handleHotUpdate({ } } - let updateType = [] + const updateType = [] if (needRerender) { updateType.push(`template`) // template is inlined into main, add main module instead @@ -144,7 +144,7 @@ export async function handleHotUpdate({ return [...affectedModules].filter(Boolean) as ModuleNode[] } -export function isEqualBlock(a: SFCBlock | null, b: SFCBlock | null) { +export function isEqualBlock(a: SFCBlock | null, b: SFCBlock | null): boolean { if (!a && !b) return true if (!a || !b) return false // src imports will trigger their own updates @@ -161,7 +161,7 @@ export function isEqualBlock(a: SFCBlock | null, b: SFCBlock | null) { export function isOnlyTemplateChanged( prev: SFCDescriptor, next: SFCDescriptor -) { +): boolean { return ( isEqualBlock(prev.script, next.script) && isEqualBlock(prev.scriptSetup, next.scriptSetup) && diff --git a/packages/plugin-vue/src/main.ts b/packages/plugin-vue/src/main.ts index 1d2f896e79cb2a..655d0db83e871b 100644 --- a/packages/plugin-vue/src/main.ts +++ b/packages/plugin-vue/src/main.ts @@ -249,7 +249,7 @@ async function genScriptCode( const classMatch = script.content.match(exportDefaultClassRE) if (classMatch) { scriptCode = - script.content.replace(exportDefaultClassRE, `class $1`) + + script.content.replace(exportDefaultClassRE, `\nclass $1`) + `\nconst _sfc_main = ${classMatch[1]}` if (/export\s+default/.test(scriptCode)) { // fallback if there are still export default diff --git a/packages/plugin-vue/src/script.ts b/packages/plugin-vue/src/script.ts index a80341c7255f7e..4857ad8c10e1ce 100644 --- a/packages/plugin-vue/src/script.ts +++ b/packages/plugin-vue/src/script.ts @@ -17,7 +17,7 @@ export function setResolvedScript( descriptor: SFCDescriptor, script: SFCScriptBlock, ssr: boolean -) { +): void { ;(ssr ? ssrCache : clientCache).set(descriptor, script) } @@ -25,7 +25,7 @@ export function resolveScript( descriptor: SFCDescriptor, options: ResolvedOptions, ssr: boolean -) { +): SFCScriptBlock | null { if (!descriptor.script && !descriptor.scriptSetup) { return null } diff --git a/packages/plugin-vue/src/utils/descriptorCache.ts b/packages/plugin-vue/src/utils/descriptorCache.ts index dc1cc7fc69d3f7..d03643dc8bc026 100644 --- a/packages/plugin-vue/src/utils/descriptorCache.ts +++ b/packages/plugin-vue/src/utils/descriptorCache.ts @@ -1,7 +1,13 @@ import path from 'path' import slash from 'slash' import hash from 'hash-sum' -import { parse, SFCDescriptor } from '@vue/compiler-sfc' +import { CompilerError, parse, SFCDescriptor } from '@vue/compiler-sfc' + +// node_modules/@vue/compiler-sfc/dist/compiler-sfc.d.ts SFCParseResult should be exported so it can be re-used +export interface SFCParseResult { + descriptor: SFCDescriptor + errors: Array +} const cache = new Map() const prevCache = new Map() @@ -11,7 +17,7 @@ export function createDescriptor( source: string, root: string, isProduction: boolean | undefined -) { +): SFCParseResult { const { descriptor, errors } = parse(source, { filename, sourceMap: true @@ -26,15 +32,21 @@ export function createDescriptor( return { descriptor, errors } } -export function getPrevDescriptor(filename: string) { +export function getPrevDescriptor(filename: string): SFCDescriptor | undefined { return prevCache.get(filename) } -export function setPrevDescriptor(filename: string, entry: SFCDescriptor) { +export function setPrevDescriptor( + filename: string, + entry: SFCDescriptor +): void { prevCache.set(filename, entry) } -export function getDescriptor(filename: string, errorOnMissing = true) { +export function getDescriptor( + filename: string, + errorOnMissing = true +): SFCDescriptor | undefined { if (cache.has(filename)) { return cache.get(filename)! } @@ -46,6 +58,6 @@ export function getDescriptor(filename: string, errorOnMissing = true) { } } -export function setDescriptor(filename: string, entry: SFCDescriptor) { +export function setDescriptor(filename: string, entry: SFCDescriptor): void { cache.set(filename, entry) } diff --git a/packages/plugin-vue/src/utils/query.ts b/packages/plugin-vue/src/utils/query.ts index c0bafedcbd6a0d..bd1c6394940be9 100644 --- a/packages/plugin-vue/src/utils/query.ts +++ b/packages/plugin-vue/src/utils/query.ts @@ -9,7 +9,9 @@ export interface VueQuery { raw?: boolean } -export function parseVueRequest(id: string) { +export function parseVueRequest( + id: string +): { filename: string; query: VueQuery } { const [filename, rawQuery] = id.split(`?`, 2) const query = qs.parse(rawQuery) as VueQuery if (query.vue != null) { diff --git a/packages/vite/README.md b/packages/vite/README.md index b04e97cb308db4..89490d7b480a8e 100644 --- a/packages/vite/README.md +++ b/packages/vite/README.md @@ -17,4 +17,4 @@ Vite (French word for "fast", pronounced `/vit/`) is a new breed of frontend bui In addition, Vite is highly extensible via its [Plugin API](https://vitejs.dev/guide/api-plugin.html) and [JavaScript API](https://vitejs.dev/guide/api-javascript.html) with full typing support. -[Read the Docs to Learn More](https://vitejs.dev). \ No newline at end of file +[Read the Docs to Learn More](https://vitejs.dev). diff --git a/packages/vite/package.json b/packages/vite/package.json index 3ff6c45a6f3fe1..d005451f6bdd62 100644 --- a/packages/vite/package.json +++ b/packages/vite/package.json @@ -60,7 +60,7 @@ "@rollup/plugin-dynamic-import-vars": "^1.1.1", "@rollup/plugin-json": "^4.1.0", "@rollup/plugin-node-resolve": "^11.0.1", - "@rollup/plugin-typescript": "^8.0.0", + "@rollup/plugin-typescript": "^8.2.1", "@rollup/pluginutils": "^4.1.0", "@types/clean-css": "^4.2.3", "@types/convert-source-map": "^1.5.1", diff --git a/packages/vite/rollup.config.js b/packages/vite/rollup.config.js index 378242bfb6657e..6814db9f9e437b 100644 --- a/packages/vite/rollup.config.js +++ b/packages/vite/rollup.config.js @@ -26,7 +26,8 @@ const envConfig = { }) ], output: { - dir: path.resolve(__dirname, 'dist/client') + dir: path.resolve(__dirname, 'dist/client'), + sourcemap: true } } @@ -47,7 +48,8 @@ const clientConfig = { }) ], output: { - dir: path.resolve(__dirname, 'dist/client') + dir: path.resolve(__dirname, 'dist/client'), + sourcemap: true } } @@ -67,7 +69,8 @@ const sharedNodeOptions = { exports: 'named', format: 'cjs', externalLiveBindings: false, - freeze: false + freeze: false, + sourcemap: true }, onwarn(warning, warn) { // node-resolve complains a lot about this but seems to still work? diff --git a/packages/vite/src/client/client.ts b/packages/vite/src/client/client.ts index 366896293b0490..6a089e10f45d5a 100644 --- a/packages/vite/src/client/client.ts +++ b/packages/vite/src/client/client.ts @@ -201,7 +201,7 @@ const supportsConstructedSheet = (() => { const sheetsMap = new Map() -export function updateStyle(id: string, content: string) { +export function updateStyle(id: string, content: string): void { let style = sheetsMap.get(id) if (supportsConstructedSheet && !content.includes('@import')) { if (style && !(style instanceof CSSStyleSheet)) { @@ -235,8 +235,8 @@ export function updateStyle(id: string, content: string) { sheetsMap.set(id, style) } -export function removeStyle(id: string) { - let style = sheetsMap.get(id) +export function removeStyle(id: string): void { + const style = sheetsMap.get(id) if (style) { if (style instanceof CSSStyleSheet) { // @ts-ignore @@ -334,6 +334,8 @@ const ctxToListenersMap = new Map< Map void)[]> >() +// Just infer the return type for now +// _This would have to be activated when used `plugin:@typescript-eslint/recommended`_ eslint-disable-next-line @typescript-eslint/explicit-module-boundary-types export const createHotContext = (ownerPath: string) => { if (!dataMap.has(ownerPath)) { dataMap.set(ownerPath, {}) @@ -436,7 +438,7 @@ export const createHotContext = (ownerPath: string) => { /** * urls here are dynamic import() urls that couldn't be statically analyzed */ -export function injectQuery(url: string, queryToInject: string) { +export function injectQuery(url: string, queryToInject: string): string { // skip urls that won't be handled by vite if (!url.startsWith('.') && !url.startsWith('/')) { return url diff --git a/packages/vite/src/client/overlay.ts b/packages/vite/src/client/overlay.ts index 41c44505aa65c4..47a7dacad85a1b 100644 --- a/packages/vite/src/client/overlay.ts +++ b/packages/vite/src/client/overlay.ts @@ -151,7 +151,7 @@ export class ErrorOverlay extends HTMLElement { }) } - text(selector: string, text: string, linkFiles = false) { + text(selector: string, text: string, linkFiles = false): void { const el = this.root.querySelector(selector)! if (!linkFiles) { el.textContent = text @@ -176,7 +176,7 @@ export class ErrorOverlay extends HTMLElement { } } - close() { + close(): void { this.parentNode?.removeChild(this) } } diff --git a/packages/vite/src/node/__tests__/config.spec.ts b/packages/vite/src/node/__tests__/config.spec.ts new file mode 100644 index 00000000000000..1e2dc6fce8124e --- /dev/null +++ b/packages/vite/src/node/__tests__/config.spec.ts @@ -0,0 +1,94 @@ +import { mergeConfig, UserConfigExport } from '../config' + +describe('mergeConfig', () => { + test('handles configs with different alias schemas', () => { + const baseConfig: UserConfigExport = { + resolve: { + alias: [ + { + find: 'foo', + replacement: 'foo-value' + } + ] + } + } + + const newConfig: UserConfigExport = { + resolve: { + alias: { + bar: 'bar-value', + baz: 'baz-value' + } + } + } + + const mergedConfig: UserConfigExport = { + resolve: { + alias: [ + { + find: 'foo', + replacement: 'foo-value' + }, + { + find: 'bar', + replacement: 'bar-value' + }, + { + find: 'baz', + replacement: 'baz-value' + } + ] + } + } + + expect(mergeConfig(baseConfig, newConfig)).toEqual(mergedConfig) + }) + + test('handles assetsInclude', () => { + const baseConfig: UserConfigExport = { + assetsInclude: 'some-string' + } + + const newConfig: UserConfigExport = { + assetsInclude: ['some-other-string', /regexp?/] + } + + const mergedConfig: UserConfigExport = { + assetsInclude: ['some-string', 'some-other-string', /regexp?/] + } + + expect(mergeConfig(baseConfig, newConfig)).toEqual(mergedConfig) + }) + + test('not handles alias not under `resolve`', () => { + const baseConfig = { + custom: { + alias: { + bar: 'bar-value', + baz: 'baz-value' + } + } + } + + const newConfig = { + custom: { + alias: { + bar: 'bar-value-2', + foo: 'foo-value' + } + } + } + + const mergedConfig = { + custom: { + alias: { + bar: 'bar-value-2', + baz: 'baz-value', + foo: 'foo-value' + } + } + } + + expect(mergeConfig(baseConfig, newConfig)).toEqual(mergedConfig) + }) +}) diff --git a/packages/vite/src/node/build.ts b/packages/vite/src/node/build.ts index 958f32dd83066a..0ee1ccf9d28428 100644 --- a/packages/vite/src/node/build.ts +++ b/packages/vite/src/node/build.ts @@ -12,7 +12,10 @@ import Rollup, { RollupOutput, ExternalOption, GetManualChunk, - GetModuleInfo + GetModuleInfo, + WatcherOptions, + RollupWatcher, + RollupError } from 'rollup' import { buildReporterPlugin } from './plugins/reporter' import { buildHtmlPlugin } from './plugins/html' @@ -176,12 +179,18 @@ export interface BuildOptions { * @default 500 */ chunkSizeWarningLimit?: number + /** + * Rollup watch options + * https://rollupjs.org/guide/en/#watchoptions + */ + watch?: WatcherOptions | null } export interface LibraryOptions { entry: string name?: string formats?: LibraryFormats[] + fileName?: string } export type LibraryFormats = 'es' | 'cjs' | 'umd' | 'iife' @@ -214,6 +223,7 @@ export function resolveBuildOptions(raw?: BuildOptions): ResolvedBuildOptions { ssrManifest: false, brotliSize: true, chunkSizeWarningLimit: 500, + watch: null, ...raw } @@ -279,7 +289,7 @@ const parallelBuilds: RollupBuild[] = [] */ export async function build( inlineConfig: InlineConfig = {} -): Promise { +): Promise { parallelCallCounts++ try { return await doBuild(inlineConfig) @@ -294,7 +304,7 @@ export async function build( async function doBuild( inlineConfig: InlineConfig = {} -): Promise { +): Promise { const config = await resolveConfig(inlineConfig, 'build', 'production') const options = config.build const ssr = !!options.ssr @@ -355,29 +365,39 @@ async function doBuild( } const rollup = require('rollup') as typeof Rollup + const rollupOptions: RollupOptions = { + input, + preserveEntrySignatures: ssr + ? 'allow-extension' + : libOptions + ? 'strict' + : false, + ...options.rollupOptions, + plugins, + external, + onwarn(warning, warn) { + onRollupWarning(warning, warn, config) + } + } - try { - const bundle = await rollup.rollup({ - input, - preserveEntrySignatures: ssr - ? 'allow-extension' - : libOptions - ? 'strict' - : false, - ...options.rollupOptions, - plugins, - external, - onwarn(warning, warn) { - onRollupWarning(warning, warn, config) - } - }) - - parallelBuilds.push(bundle) + const outputBuildError = (e: RollupError) => { + config.logger.error( + chalk.red(`${e.plugin ? `[${e.plugin}] ` : ''}${e.message}`) + ) + if (e.id) { + const loc = e.loc ? `:${e.loc.line}:${e.loc.column}` : '' + config.logger.error(`file: ${chalk.cyan(`${e.id}${loc}`)}`) + } + if (e.frame) { + config.logger.error(chalk.yellow(e.frame)) + } + } + try { const pkgName = libOptions && getPkgName(config.root) - const generate = (output: OutputOptions = {}) => { - return bundle[options.write ? 'write' : 'generate']({ + const buildOuputOptions = (output: OutputOptions = {}): OutputOptions => { + return { dir: outDir, format: ssr ? 'cjs' : 'es', exports: ssr ? 'named' : 'auto', @@ -386,7 +406,7 @@ async function doBuild( entryFileNames: ssr ? `[name].js` : libOptions - ? `${pkgName}.${output.format || `es`}.js` + ? `${libOptions.fileName || pkgName}.${output.format || `es`}.js` : path.posix.join(options.assetsDir, `[name].[hash].js`), chunkFileNames: libOptions ? `[name].js` @@ -406,7 +426,81 @@ async function doBuild( ? createMoveToVendorChunkFn(config) : undefined, ...output + } + } + + // resolve lib mode outputs + const outputs = resolveBuildOutputs( + options.rollupOptions?.output, + libOptions, + config.logger + ) + + // watch file changes with rollup + if (config.build.watch) { + config.logger.info(chalk.cyanBright(`\nwatching for file changes...`)) + + const output: OutputOptions[] = [] + if (Array.isArray(outputs)) { + for (const resolvedOutput of outputs) { + output.push(buildOuputOptions(resolvedOutput)) + } + } else { + output.push(buildOuputOptions(outputs)) + } + + const watcherOptions = config.build.watch + const watcher = rollup.watch({ + ...rollupOptions, + output, + watch: { + ...watcherOptions, + chokidar: { + ignored: [ + '**/node_modules/**', + '**/.git/**', + ...(watcherOptions?.chokidar?.ignored || []) + ], + ignoreInitial: true, + ignorePermissionErrors: true, + ...watcherOptions.chokidar + } + } + }) + + watcher.on('event', (event) => { + if (event.code === 'BUNDLE_START') { + config.logger.info(chalk.cyanBright(`\nbuild started...`)) + + // clean previous files + if (options.write) { + emptyDir(outDir) + if (fs.existsSync(config.publicDir)) { + copyDir(config.publicDir, outDir) + } + } + } else if (event.code === 'BUNDLE_END') { + event.result.close() + config.logger.info(chalk.cyanBright(`built in ${event.duration}ms.`)) + } else if (event.code === 'ERROR') { + outputBuildError(event.error) + } }) + + // stop watching + watcher.close() + + return watcher + } + + // write or generate files with rollup + const bundle = await rollup.rollup(rollupOptions) + parallelBuilds.push(bundle) + + const generate = (output: OutputOptions = {}) => { + return bundle[options.write ? 'write' : 'generate']( + buildOuputOptions(output) + ) } if (options.write) { @@ -434,12 +528,6 @@ async function doBuild( } } - // resolve lib mode outputs - const outputs = resolveBuildOutputs( - options.rollupOptions?.output, - libOptions, - config.logger - ) if (Array.isArray(outputs)) { const res = [] for (const output of outputs) { @@ -450,16 +538,7 @@ async function doBuild( return await generate(outputs) } } catch (e) { - config.logger.error( - chalk.red(`${e.plugin ? `[${e.plugin}] ` : ``}${e.message}`) - ) - if (e.id) { - const loc = e.loc ? `:${e.loc.line}:${e.loc.column}` : `` - config.logger.error(`file: ${chalk.cyan(`${e.id}${loc}`)}`) - } - if (e.frame) { - config.logger.error(chalk.yellow(e.frame)) - } + outputBuildError(e) throw e } } @@ -558,7 +637,7 @@ export function onRollupWarning( warning: RollupWarning, warn: WarningHandler, config: ResolvedConfig -) { +): void { if (warning.code === 'UNRESOLVED_IMPORT') { const id = warning.source const importer = warning.importer diff --git a/packages/vite/src/node/cli.ts b/packages/vite/src/node/cli.ts index 26384fd6b2511e..41ed363388d7a7 100644 --- a/packages/vite/src/node/cli.ts +++ b/packages/vite/src/node/cli.ts @@ -130,6 +130,7 @@ cli `[boolean] force empty outDir when it's outside of root` ) .option('-m, --mode ', `[string] set env mode`) + .option('-w, --watch', `[boolean] rebuilds when modules have changed on disk`) .action(async (root: string, options: BuildOptions & GlobalCLIOptions) => { const { build } = await import('./build') const buildOptions = cleanOptions(options) as BuildOptions diff --git a/packages/vite/src/node/config.ts b/packages/vite/src/node/config.ts index 68787fa3369a66..a0cf1e8ef971cd 100644 --- a/packages/vite/src/node/config.ts +++ b/packages/vite/src/node/config.ts @@ -45,8 +45,8 @@ export interface ConfigEnv { mode: string } -export type UserConfigFn = (env: ConfigEnv) => UserConfig -export type UserConfigExport = UserConfig | UserConfigFn +export type UserConfigFn = (env: ConfigEnv) => UserConfig | Promise +export type UserConfigExport = UserConfig | Promise | UserConfigFn /** * Type helper to make it easier to use vite.config.ts @@ -160,12 +160,13 @@ export interface SSROptions { export interface InlineConfig extends UserConfig { configFile?: string | false + envFile?: false } export type ResolvedConfig = Readonly< Omit & { configFile: string | undefined - inlineConfig: UserConfig + inlineConfig: InlineConfig root: string base: string publicDir: string @@ -242,14 +243,14 @@ export async function resolveConfig( // run config hooks const userPlugins = [...prePlugins, ...normalPlugins, ...postPlugins] - userPlugins.forEach((p) => { + for (const p of userPlugins) { if (p.config) { - const res = p.config(config, configEnv) + const res = await p.config(config, configEnv) if (res) { config = mergeConfig(config, res) } } - }) + } // resolve root const resolvedRoot = normalizePath( @@ -273,7 +274,7 @@ export async function resolveConfig( } // load .env files - const userEnv = loadEnv(mode, resolvedRoot) + const userEnv = inlineConfig.envFile !== false && loadEnv(mode, resolvedRoot) // Note it is possible for user to have a custom mode, e.g. `staging` where // production-like behavior is expected. This is indicated by NODE_ENV=production @@ -376,11 +377,7 @@ export async function resolveConfig( ) // call configResolved hooks - userPlugins.forEach((p) => { - if (p.configResolved) { - p.configResolved(resolved) - } - }) + await Promise.all(userPlugins.map((p) => p.configResolved?.(resolved))) if (process.env.DEBUG) { debug(`using resolved config: %O`, { @@ -505,11 +502,11 @@ function resolveBaseUrl( return base } -export function mergeConfig( +function mergeConfigRecursively( a: Record, b: Record, - isRoot = true -): Record { + rootPath: string +) { const merged: Record = { ...a } for (const key in b) { const value = b[key] @@ -523,16 +520,20 @@ export function mergeConfig( continue } if (isObject(existing) && isObject(value)) { - merged[key] = mergeConfig(existing, value, false) + merged[key] = mergeConfigRecursively( + existing, + value, + rootPath ? `${rootPath}.${key}` : key + ) continue } - // root fields that require special handling - if (existing != null && isRoot) { - if (key === 'alias') { + // fields that require special handling + if (existing != null) { + if (key === 'alias' && (rootPath === 'resolve' || rootPath === '')) { merged[key] = mergeAlias(existing, value) continue - } else if (key === 'assetsInclude') { + } else if (key === 'assetsInclude' && rootPath === '') { merged[key] = [].concat(existing, value) continue } @@ -543,6 +544,14 @@ export function mergeConfig( return merged } +export function mergeConfig( + a: Record, + b: Record, + isRoot = true +): Record { + return mergeConfigRecursively(a, b, isRoot ? '' : '.') +} + function mergeAlias(a: AliasOptions = [], b: AliasOptions = []): Alias[] { return [...normalizeAlias(a), ...normalizeAlias(b)] } @@ -707,8 +716,9 @@ export async function loadConfigFromFile( debug(`bundled config file loaded in ${Date.now() - start}ms`) } - const config = - typeof userConfig === 'function' ? userConfig(configEnv) : userConfig + const config = await (typeof userConfig === 'function' + ? userConfig(configEnv) + : userConfig) if (!isObject(config)) { throw new Error(`config must export or return an object.`) } @@ -801,7 +811,11 @@ async function loadConfigFromBundledFile( return config } -export function loadEnv(mode: string, root: string, prefix = 'VITE_') { +export function loadEnv( + mode: string, + root: string, + prefix = 'VITE_' +): Record { if (mode === 'local') { throw new Error( `"local" cannot be used as a mode name because it conflicts with ` + diff --git a/packages/vite/src/node/importGlob.ts b/packages/vite/src/node/importGlob.ts index f14d7bd8bfddda..75a57953894e36 100644 --- a/packages/vite/src/node/importGlob.ts +++ b/packages/vite/src/node/importGlob.ts @@ -42,7 +42,7 @@ export async function transformImportGlob( } let base let parentDepth = 0 - let isAbsolute = pattern.startsWith('/') + const isAbsolute = pattern.startsWith('/') if (isAbsolute) { base = path.resolve(root) pattern = pattern.slice(1) diff --git a/packages/vite/src/node/index.ts b/packages/vite/src/node/index.ts index e75611ad66f475..e020f10ad7f0a2 100644 --- a/packages/vite/src/node/index.ts +++ b/packages/vite/src/node/index.ts @@ -43,6 +43,7 @@ export type { export type { CSSOptions, CSSModulesOptions } from './plugins/css' export type { JsonOptions } from './plugins/json' export type { ESBuildOptions, ESBuildTransformResult } from './plugins/esbuild' +export type { Manifest, ManifestChunk } from './plugins/manifest' export type { PackageData, ResolveOptions, diff --git a/packages/vite/src/node/optimizer/registerMissing.ts b/packages/vite/src/node/optimizer/registerMissing.ts index d65481bc5283d1..32fdce1526d1ba 100644 --- a/packages/vite/src/node/optimizer/registerMissing.ts +++ b/packages/vite/src/node/optimizer/registerMissing.ts @@ -9,7 +9,9 @@ import { resolveSSRExternal } from '../ssr/ssrExternal' */ const debounceMs = 100 -export function createMissingImporterRegisterFn(server: ViteDevServer) { +export function createMissingImporterRegisterFn( + server: ViteDevServer +): (id: string, resolved: string) => void { const { logger } = server.config let knownOptimized = server._optimizeDepsMetadata!.optimized let currentMissing: Record = {} diff --git a/packages/vite/src/node/optimizer/scan.ts b/packages/vite/src/node/optimizer/scan.ts index 030164345e1469..e40a1b1dea7acc 100644 --- a/packages/vite/src/node/optimizer/scan.ts +++ b/packages/vite/src/node/optimizer/scan.ts @@ -38,7 +38,7 @@ const htmlTypesRE = /\.(html|vue|svelte)$/ // use Acorn because it's slow. Luckily this doesn't have to be bullet proof // since even missed imports can be caught at runtime, and false positives will // simply be ignored. -const importsRE = /\bimport(?:[\w*{}\n\r\t, ]+from\s*)?\s*("[^"]+"|'[^']+')/gm +const importsRE = /\bimport(?!\s+type)(?:[\w*{}\n\r\t, ]+from\s*)?\s*("[^"]+"|'[^']+')/gm export async function scanImports( config: ResolvedConfig @@ -417,7 +417,10 @@ async function transformGlob( return s.toString() } -export function shouldExternalizeDep(resolvedId: string, rawId: string) { +export function shouldExternalizeDep( + resolvedId: string, + rawId: string +): boolean { // not a valid file path if (!path.isAbsolute(resolvedId)) { return true @@ -430,4 +433,5 @@ export function shouldExternalizeDep(resolvedId: string, rawId: string) { if (!JS_TYPES_RE.test(resolvedId) && !htmlTypesRE.test(resolvedId)) { return true } + return false } diff --git a/packages/vite/src/node/plugin.ts b/packages/vite/src/node/plugin.ts index 7bef2402de4729..012c32e0fa6dfb 100644 --- a/packages/vite/src/node/plugin.ts +++ b/packages/vite/src/node/plugin.ts @@ -61,11 +61,14 @@ export interface Plugin extends RollupPlugin { * Note: User plugins are resolved before running this hook so injecting other * plugins inside the `config` hook will have no effect. */ - config?: (config: UserConfig, env: ConfigEnv) => UserConfig | null | void + config?: ( + config: UserConfig, + env: ConfigEnv + ) => UserConfig | null | void | Promise /** * Use this hook to read and store the final resolved vite config. */ - configResolved?: (config: ResolvedConfig) => void + configResolved?: (config: ResolvedConfig) => void | Promise /** * Configure the vite server. The hook receives the {@link ViteDevServer} * instance. This can also be used to store a reference to the server diff --git a/packages/vite/src/node/plugins/asset.ts b/packages/vite/src/node/plugins/asset.ts index 0e426fd6e5d934..950f7e86c59696 100644 --- a/packages/vite/src/node/plugins/asset.ts +++ b/packages/vite/src/node/plugins/asset.ts @@ -102,7 +102,7 @@ export function assetPlugin(config: ResolvedConfig): Plugin { } } -export function registerAssetToChunk(chunk: RenderedChunk, file: string) { +export function registerAssetToChunk(chunk: RenderedChunk, file: string): void { let emitted = chunkToEmittedAssetsMap.get(chunk) if (!emitted) { emitted = new Set() @@ -132,7 +132,7 @@ export function fileToUrl( id: string, config: ResolvedConfig, ctx: PluginContext -) { +): string | Promise { if (config.command === 'serve') { return fileToDevUrl(id, config) } else { @@ -163,7 +163,10 @@ const assetHashToFilenameMap = new WeakMap< Map >() -export function getAssetFilename(hash: string, config: ResolvedConfig) { +export function getAssetFilename( + hash: string, + config: ResolvedConfig +): string | undefined { return assetHashToFilenameMap.get(config)?.get(hash) } diff --git a/packages/vite/src/node/plugins/css.ts b/packages/vite/src/node/plugins/css.ts index 8dfcb2a15faf14..9d17b245767fc1 100644 --- a/packages/vite/src/node/plugins/css.ts +++ b/packages/vite/src/node/plugins/css.ts @@ -35,7 +35,9 @@ import { import MagicString from 'magic-string' import * as Postcss from 'postcss' import type Sass from 'sass' -import type Stylus from 'stylus' +// We need to disable check of extraneous import which is buggy for stylus, +// and causes the CI tests fail, see: https://github.com/vitejs/vite/pull/2860 +import type Stylus from 'stylus' // eslint-disable-line node/no-extraneous-import import type Less from 'less' import { Alias } from 'types/alias' @@ -95,10 +97,10 @@ const enum PureCssLang { } type CssLang = keyof typeof PureCssLang | keyof typeof PreprocessLang -export const isCSSRequest = (request: string) => +export const isCSSRequest = (request: string): boolean => cssLangRE.test(request) && !directRequestRE.test(request) -export const isDirectCSSRequest = (request: string) => +export const isDirectCSSRequest = (request: string): boolean => cssLangRE.test(request) && directRequestRE.test(request) const cssModulesCache = new WeakMap< @@ -338,7 +340,7 @@ export function cssPostPlugin(config: ResolvedConfig): Plugin { // this is a shared CSS-only chunk that is empty. pureCssChunks.add(chunk.fileName) } - if (opts.format === 'es') { + if (opts.format === 'es' || opts.format === 'cjs') { chunkCSS = await processChunkCSS(chunkCSS, { inlined: false, minify: true @@ -397,7 +399,9 @@ export function cssPostPlugin(config: ResolvedConfig): Plugin { .join('|') .replace(/\./g, '\\.') const emptyChunkRE = new RegExp( - `\\bimport\\s*"[^"]*(?:${emptyChunkFiles})";\n?`, + opts.format === 'es' + ? `\\bimport\\s*"[^"]*(?:${emptyChunkFiles})";\n?` + : `\\brequire\\(\\s*"[^"]*(?:${emptyChunkFiles})"\\);\n?`, 'g' ) for (const file in bundle) { @@ -764,7 +768,7 @@ function rewriteCssUrls( replacer: CssUrlReplacer ): Promise { return asyncReplace(css, cssUrlRE, async (match) => { - let [matched, rawUrl] = match + const [matched, rawUrl] = match return await doUrlReplace(rawUrl, matched, replacer) }) } @@ -774,7 +778,7 @@ function rewriteCssImageSet( replacer: CssUrlReplacer ): Promise { return asyncReplace(css, cssImageSetRE, async (match) => { - let [matched, rawUrl] = match + const [matched, rawUrl] = match const url = await processSrcSet(rawUrl, ({ url }) => doUrlReplace(url, matched, replacer) ) @@ -1102,18 +1106,31 @@ function createViteLessPlugin( } // .styl -const styl: StylePreprocessor = (source, root, options) => { +const styl: StylePreprocessor = async (source, root, options) => { const nodeStylus = loadPreprocessor(PreprocessLang.stylus, root) + // Get source with preprocessor options.additionalData. Make sure a new line separator + // is added to avoid any render error, as added stylus content may not have semi-colon separators + source = await getSource( + source, + options.filename, + options.additionalData, + '\n' + ) + // Get preprocessor options.imports dependencies as stylus + // does not return them with its builtin `.deps()` method + const importsDeps = (options.imports ?? []).map((dep: string) => + path.resolve(dep) + ) try { - const ref = nodeStylus(source) + const ref = nodeStylus(source, options) - Object.keys(options).forEach((key) => ref.set(key, options[key])) // if (map) ref.set('sourcemap', { inline: false, comment: false }) const result = ref.render() // @ts-expect-error: https://github.com/DefinitelyTyped/DefinitelyTyped/pull/51919 - const deps = ref.deps() + // Concat imports deps with computed deps + const deps = [...ref.deps(), ...importsDeps] return { code: result, errors: [], deps } } catch (e) { @@ -1124,13 +1141,14 @@ const styl: StylePreprocessor = (source, root, options) => { function getSource( source: string, filename: string, - additionalData?: PreprocessorAdditionalData + additionalData?: PreprocessorAdditionalData, + sep: string = '' ): string | Promise { if (!additionalData) return source if (typeof additionalData === 'function') { return additionalData(source, filename) } - return additionalData + source + return additionalData + sep + source } const preProcessors = Object.freeze({ diff --git a/packages/vite/src/node/plugins/define.ts b/packages/vite/src/node/plugins/define.ts index 069e41417902aa..573c0d362c46c7 100644 --- a/packages/vite/src/node/plugins/define.ts +++ b/packages/vite/src/node/plugins/define.ts @@ -31,7 +31,7 @@ export function definePlugin(config: ResolvedConfig): Plugin { } const replacements: Record = { - 'process.env.NODE_ENV': JSON.stringify(config.mode), + 'process.env.NODE_ENV': JSON.stringify(process.env.NODE_ENV || config.mode), ...userDefine, ...importMetaKeys, 'process.env.': `({}).` diff --git a/packages/vite/src/node/plugins/html.ts b/packages/vite/src/node/plugins/html.ts index cd147519332bce..66991fe93e6215 100644 --- a/packages/vite/src/node/plugins/html.ts +++ b/packages/vite/src/node/plugins/html.ts @@ -29,7 +29,7 @@ import { } from '@vue/compiler-dom' const htmlProxyRE = /\?html-proxy&index=(\d+)\.js$/ -export const isHTMLProxy = (id: string) => htmlProxyRE.test(id) +export const isHTMLProxy = (id: string): boolean => htmlProxyRE.test(id) const htmlCommentRE = //g const scriptModuleRE = /(]*type\s*=\s*(?:"module"|'module')[^>]*>)(.*?)<\/script>/gims @@ -79,7 +79,7 @@ export async function traverseHtml( html: string, filePath: string, visitor: NodeTransform -) { +): Promise { // lazy load compiler const { parse, transform } = await import('@vue/compiler-dom') // @vue/compiler-core doesn't like lowercase doctypes @@ -101,7 +101,12 @@ export async function traverseHtml( } } -export function getScriptInfo(node: ElementNode) { +export function getScriptInfo( + node: ElementNode +): { + src: AttributeNode | undefined + isModule: boolean +} { let src: AttributeNode | undefined let isModule = false for (let i = 0; i < node.props.length; i++) { @@ -267,6 +272,7 @@ export function buildHtmlPlugin(config: ResolvedConfig): Plugin { }, async generateBundle(_, bundle) { + const analyzedChunk: Map = new Map() const getPreloadLinksForChunk = ( chunk: OutputChunk, seen: Set = new Set() @@ -274,7 +280,7 @@ export function buildHtmlPlugin(config: ResolvedConfig): Plugin { const tags: HtmlTagDescriptor[] = [] chunk.imports.forEach((file) => { const importee = bundle[file] - if (importee && importee.type === 'chunk' && !seen.has(file)) { + if (importee?.type === 'chunk' && !seen.has(file)) { seen.add(file) tags.push({ tag: 'link', @@ -294,12 +300,16 @@ export function buildHtmlPlugin(config: ResolvedConfig): Plugin { seen: Set = new Set() ): HtmlTagDescriptor[] => { const tags: HtmlTagDescriptor[] = [] - chunk.imports.forEach((file) => { - const importee = bundle[file] - if (importee && importee.type === 'chunk') { - tags.push(...getCssTagsForChunk(importee, seen)) - } - }) + if (!analyzedChunk.has(chunk)) { + analyzedChunk.set(chunk, 1) + chunk.imports.forEach((file) => { + const importee = bundle[file] + if (importee?.type === 'chunk') { + tags.push(...getCssTagsForChunk(importee, seen)) + } + }) + } + const cssFiles = chunkToEmittedCssFileMap.get(chunk) if (cssFiles) { cssFiles.forEach((file) => { @@ -435,7 +445,9 @@ export type IndexHtmlTransform = transform: IndexHtmlTransformHook } -export function resolveHtmlTransforms(plugins: readonly Plugin[]) { +export function resolveHtmlTransforms( + plugins: readonly Plugin[] +): [IndexHtmlTransformHook[], IndexHtmlTransformHook[]] { const preHooks: IndexHtmlTransformHook[] = [] const postHooks: IndexHtmlTransformHook[] = [] diff --git a/packages/vite/src/node/plugins/importAnalysis.ts b/packages/vite/src/node/plugins/importAnalysis.ts index e37b04393bf824..040558b9f79645 100644 --- a/packages/vite/src/node/plugins/importAnalysis.ts +++ b/packages/vite/src/node/plugins/importAnalysis.ts @@ -48,8 +48,12 @@ const clientDir = normalizePath(CLIENT_DIR) const skipRE = /\.(map|json)$/ const canSkip = (id: string) => skipRE.test(id) || isDirectCSSRequest(id) +function isExplicitImportRequired(url: string) { + return !isJSRequest(cleanUrl(url)) && !isCSSRequest(url) +} + function markExplicitImport(url: string) { - if (!isJSRequest(cleanUrl(url)) && !isCSSRequest(url)) { + if (isExplicitImportRequired(url)) { return injectQuery(url, 'import') } return url @@ -407,8 +411,13 @@ export function importAnalysisPlugin(config: ResolvedConfig): Plugin { `/* @vite-ignore */ comment inside the import() call to suppress this warning.\n` ) } - needQueryInjectHelper = true - str().overwrite(start, end, `__vite__injectQuery(${url}, 'import')`) + if ( + !/^('.*'|".*"|`.*`)$/.test(url) || + isExplicitImportRequired(url.slice(1, -1)) + ) { + needQueryInjectHelper = true + str().overwrite(start, end, `__vite__injectQuery(${url}, 'import')`) + } } } diff --git a/packages/vite/src/node/plugins/importAnalysisBuild.ts b/packages/vite/src/node/plugins/importAnalysisBuild.ts index 0baba266958434..0826031779549f 100644 --- a/packages/vite/src/node/plugins/importAnalysisBuild.ts +++ b/packages/vite/src/node/plugins/importAnalysisBuild.ts @@ -96,7 +96,10 @@ export function buildImportAnalysisPlugin(config: ResolvedConfig): Plugin { }, async transform(source, importer) { - if (importer.includes('node_modules')) { + if ( + importer.includes('node_modules') && + !source.includes('import.meta.glob') + ) { return } @@ -218,6 +221,7 @@ export function buildImportAnalysisPlugin(config: ResolvedConfig): Plugin { if (imports.length) { const s = new MagicString(code) + const analyzed: Set = new Set() for (let index = 0; index < imports.length; index++) { const { s: start, e: end, d: dynamicIndex } = imports[index] // if dynamic import polyfill is used, rewrite the import to @@ -234,6 +238,8 @@ export function buildImportAnalysisPlugin(config: ResolvedConfig): Plugin { // literal import - trace direct imports and add to deps const addDeps = (filename: string) => { if (filename === ownerFilename) return + if (analyzed.has(filename)) return + analyzed.add(filename) const chunk = bundle[filename] as OutputChunk | undefined if (chunk) { deps.add(config.base + chunk.fileName) diff --git a/packages/vite/src/node/plugins/manifest.ts b/packages/vite/src/node/plugins/manifest.ts index b17020c013265f..bc1c3434eb464b 100644 --- a/packages/vite/src/node/plugins/manifest.ts +++ b/packages/vite/src/node/plugins/manifest.ts @@ -6,9 +6,9 @@ import { chunkToEmittedCssFileMap } from './css' import { chunkToEmittedAssetsMap } from './asset' import { normalizePath } from '../utils' -type Manifest = Record +export type Manifest = Record -interface ManifestChunk { +export interface ManifestChunk { src?: string file: string css?: string[] diff --git a/packages/vite/src/node/plugins/resolve.ts b/packages/vite/src/node/plugins/resolve.ts index e6eca61f08ef8d..891767e1ba1323 100644 --- a/packages/vite/src/node/plugins/resolve.ts +++ b/packages/vite/src/node/plugins/resolve.ts @@ -121,10 +121,13 @@ export function resolvePlugin(baseOptions: InternalResolveOptions): Plugin { // relative if (id.startsWith('.') || (preferRelative && /^\w/.test(id))) { const basedir = importer ? path.dirname(importer) : process.cwd() - let fsPath = path.resolve(basedir, id) + const fsPath = path.resolve(basedir, id) // handle browser field mapping for relative imports - if ((res = tryResolveBrowserMapping(fsPath, importer, options, true))) { + if ( + !ssr && + (res = tryResolveBrowserMapping(fsPath, importer, options, true)) + ) { return res } diff --git a/packages/vite/src/node/plugins/worker.ts b/packages/vite/src/node/plugins/worker.ts index 2eb6e32ac83d79..b30f39fb2cc6d7 100644 --- a/packages/vite/src/node/plugins/worker.ts +++ b/packages/vite/src/node/plugins/worker.ts @@ -41,10 +41,7 @@ export function webWorkerPlugin(config: ResolvedConfig): Plugin { } let url: string - if (config.command === 'serve') { - url = await fileToUrl(cleanUrl(id), config, this) - url = injectQuery(url, WorkerFileId) - } else { + if (isBuild) { if (query.inline != null) { // bundle the file as entry to support imports and inline as base64 // data url @@ -71,6 +68,9 @@ export function webWorkerPlugin(config: ResolvedConfig): Plugin { id: cleanUrl(id) })}__` } + } else { + url = await fileToUrl(cleanUrl(id), config, this) + url = injectQuery(url, WorkerFileId) } return `export default function WorkerWrapper() { diff --git a/packages/vite/src/node/preview.ts b/packages/vite/src/node/preview.ts index c76a68337cdfdf..2be9a40b73dec7 100644 --- a/packages/vite/src/node/preview.ts +++ b/packages/vite/src/node/preview.ts @@ -11,7 +11,10 @@ import { openBrowser } from './server/openBrowser' import corsMiddleware from 'cors' import { proxyMiddleware } from './server/middlewares/proxy' -export async function preview(config: ResolvedConfig, port = 5000) { +export async function preview( + config: ResolvedConfig, + port = 5000 +): Promise { const app = connect() as Connect.Server const httpServer = await resolveHttpServer(config.server, app) diff --git a/packages/vite/src/node/server/hmr.ts b/packages/vite/src/node/server/hmr.ts index c0df3effb731a9..2692ce557d8b32 100644 --- a/packages/vite/src/node/server/hmr.ts +++ b/packages/vite/src/node/server/hmr.ts @@ -44,11 +44,15 @@ export async function handleHMRUpdate( const { ws, config, moduleGraph } = server const shortFile = getShortName(file, config.root) - if (file === config.configFile || file.endsWith('.env')) { + const isConfig = file === config.configFile + const isEnv = config.inlineConfig.envFile !== false && file.endsWith('.env') + if (isConfig || isEnv) { // auto restart server debugHmr(`[config change] ${chalk.dim(shortFile)}`) config.logger.info( - chalk.green('config or .env file changed, restarting server...'), + chalk.green( + `${isConfig ? 'config' : '.env'} file changed, restarting server...` + ), { clear: true, timestamp: true } ) await restartServer(server) @@ -164,7 +168,7 @@ export async function handleFileAddUnlink( file: string, server: ViteDevServer, isUnlink = false -) { +): Promise { const modules = [...(server.moduleGraph.getModulesByFile(file) ?? [])] if (isUnlink && file in server._globImporters) { delete server._globImporters[file] @@ -247,7 +251,7 @@ function invalidate(mod: ModuleNode, timestamp: number, seen: Set) { export function handlePrunedModules( mods: Set, { ws }: ViteDevServer -) { +): void { // update the disposed modules' hmr timestamp // since if it's re-imported, it should re-apply side effects // and without the timestamp the browser will not re-import it! diff --git a/packages/vite/src/node/server/http.ts b/packages/vite/src/node/server/http.ts index d701525807b064..b31fc3be991ede 100644 --- a/packages/vite/src/node/server/http.ts +++ b/packages/vite/src/node/server/http.ts @@ -30,7 +30,9 @@ export async function resolveHttpServer( } } -export async function resolveHttpsConfig(httpsOption: HttpsServerOptions) { +export async function resolveHttpsConfig( + httpsOption: HttpsServerOptions +): Promise { const { ca, cert, key, pfx } = httpsOption Object.assign(httpsOption, { ca: readFileIfExists(ca), diff --git a/packages/vite/src/node/server/index.ts b/packages/vite/src/node/server/index.ts index baf84145f7143a..045e1d8dcda389 100644 --- a/packages/vite/src/node/server/index.ts +++ b/packages/vite/src/node/server/index.ts @@ -285,6 +285,7 @@ export async function createServer( const moduleGraph = new ModuleGraph(container) const closeHttpServer = createServerCloseFn(httpServer) + // eslint-disable-next-line prefer-const let exitProcess: () => void const server: ViteDevServer = { diff --git a/packages/vite/src/node/server/middlewares/error.ts b/packages/vite/src/node/server/middlewares/error.ts index 71fbaa665a151a..42809b1d4424eb 100644 --- a/packages/vite/src/node/server/middlewares/error.ts +++ b/packages/vite/src/node/server/middlewares/error.ts @@ -24,7 +24,7 @@ export function buildErrorMessage( err: RollupError, args: string[] = [], includeStack = true -) { +): string { if (err.plugin) args.push(` Plugin: ${chalk.magenta(err.plugin)}`) if (err.id) args.push(` File: ${chalk.cyan(err.id)}`) if (err.frame) args.push(chalk.yellow(pad(err.frame))) diff --git a/packages/vite/src/node/server/middlewares/indexHtml.ts b/packages/vite/src/node/server/middlewares/indexHtml.ts index 89f448d615f435..36a29769aeb37a 100644 --- a/packages/vite/src/node/server/middlewares/indexHtml.ts +++ b/packages/vite/src/node/server/middlewares/indexHtml.ts @@ -16,7 +16,9 @@ import { CLIENT_PUBLIC_PATH, FS_PREFIX } from '../../constants' import { cleanUrl, fsPathFromId } from '../../utils' import { assetAttrsConfig } from '../../plugins/html' -export function createDevHtmlTransformFn(server: ViteDevServer) { +export function createDevHtmlTransformFn( + server: ViteDevServer +): (url: string, html: string) => Promise { const [preHooks, postHooks] = resolveHtmlTransforms(server.config.plugins) return (url: string, html: string): Promise => { diff --git a/packages/vite/src/node/server/middlewares/static.ts b/packages/vite/src/node/server/middlewares/static.ts index 2ccf85e0bced8c..c4c39c68028361 100644 --- a/packages/vite/src/node/server/middlewares/static.ts +++ b/packages/vite/src/node/server/middlewares/static.ts @@ -1,12 +1,26 @@ import os from 'os' import path from 'path' -import sirv from 'sirv' +import sirv, { Options } from 'sirv' import { Connect } from 'types/connect' import { ResolvedConfig } from '../..' import { FS_PREFIX } from '../../constants' import { cleanUrl, isImportRequest } from '../../utils' -const sirvOptions = { dev: true, etag: true, extensions: [] } +const sirvOptions: Options = { + dev: true, + etag: true, + extensions: [], + setHeaders(res, pathname) { + // Matches js, jsx, ts, tsx. + // The reason this is done, is that the .ts file extension is reserved + // for the MIME type video/mp2t. In almost all cases, we can expect + // these files to be TypeScript files, and for Vite to serve them with + // this Content-Type. + if (/\.[tj]sx?$/.test(pathname)) { + res.setHeader('Content-Type', 'application/javascript') + } + } +} export function servePublicMiddleware(dir: string): Connect.NextHandleFunction { const serve = sirv(dir, sirvOptions) @@ -27,7 +41,7 @@ export function serveStaticMiddleware( const serve = sirv(dir, sirvOptions) return (req, res, next) => { - let url = req.url! + const url = req.url! // only serve the file if it's not an html request // so that html requests can fallthrough to our html middleware for diff --git a/packages/vite/src/node/server/moduleGraph.ts b/packages/vite/src/node/server/moduleGraph.ts index eb1331645759f9..c5c94b749c9add 100644 --- a/packages/vite/src/node/server/moduleGraph.ts +++ b/packages/vite/src/node/server/moduleGraph.ts @@ -56,20 +56,20 @@ export class ModuleGraph { this.container = container } - async getModuleByUrl(rawUrl: string) { + async getModuleByUrl(rawUrl: string): Promise { const [url] = await this.resolveUrl(rawUrl) return this.urlToModuleMap.get(url) } - getModuleById(id: string) { + getModuleById(id: string): ModuleNode | undefined { return this.idToModuleMap.get(removeTimestampQuery(id)) } - getModulesByFile(file: string) { + getModulesByFile(file: string): Set | undefined { return this.fileToModulesMap.get(file) } - onFileChange(file: string) { + onFileChange(file: string): void { const mods = this.getModulesByFile(file) if (mods) { const seen = new Set() @@ -79,13 +79,13 @@ export class ModuleGraph { } } - invalidateModule(mod: ModuleNode, seen: Set = new Set()) { + invalidateModule(mod: ModuleNode, seen: Set = new Set()): void { mod.transformResult = null mod.ssrTransformResult = null invalidateSSRModule(mod, seen) } - invalidateAll() { + invalidateAll(): void { const seen = new Set() this.idToModuleMap.forEach((mod) => { this.invalidateModule(mod, seen) @@ -138,7 +138,7 @@ export class ModuleGraph { return noLongerImported } - async ensureEntryFromUrl(rawUrl: string) { + async ensureEntryFromUrl(rawUrl: string): Promise { const [url, resolvedId] = await this.resolveUrl(rawUrl) let mod = this.urlToModuleMap.get(url) if (!mod) { @@ -161,7 +161,7 @@ export class ModuleGraph { // url because they are inlined into the main css import. But they still // need to be represented in the module graph so that they can trigger // hmr in the importing css file. - createFileOnlyEntry(file: string) { + createFileOnlyEntry(file: string): ModuleNode { file = normalizePath(file) const url = `${FS_PREFIX}${file}` let fileMappedModules = this.fileToModulesMap.get(file) diff --git a/packages/vite/src/node/server/openBrowser.ts b/packages/vite/src/node/server/openBrowser.ts index 68b65a400b65e9..817eed3ae8e2b6 100644 --- a/packages/vite/src/node/server/openBrowser.ts +++ b/packages/vite/src/node/server/openBrowser.ts @@ -91,7 +91,7 @@ function startBrowserProcess(browser: string | undefined, url: string) { // Fallback to open // (It will always open new tab) try { - var options = { app: browser, url: true } + const options = { app: browser, url: true } open(url, options).catch(() => {}) // Prevent `unhandledRejection` error. return true } catch (err) { diff --git a/packages/vite/src/node/server/send.ts b/packages/vite/src/node/server/send.ts index 4d50af7cd3c624..1c54ec46f88af8 100644 --- a/packages/vite/src/node/server/send.ts +++ b/packages/vite/src/node/server/send.ts @@ -19,7 +19,7 @@ export function send( etag = getEtag(content, { weak: true }), cacheControl = 'no-cache', map?: SourceMap | null -) { +): void { if (req.headers['if-none-match'] === etag) { res.statusCode = 304 return res.end() diff --git a/packages/vite/src/node/server/sourcemap.ts b/packages/vite/src/node/server/sourcemap.ts index 864082bc7b766b..79d303391f7e43 100644 --- a/packages/vite/src/node/server/sourcemap.ts +++ b/packages/vite/src/node/server/sourcemap.ts @@ -4,7 +4,7 @@ import path from 'path' export async function injectSourcesContent( map: { sources: string[]; sourcesContent?: string[]; sourceRoot?: string }, file: string -) { +): Promise { const sourceRoot = await fs.realpath( path.resolve(path.dirname(file), map.sourceRoot || '') ) diff --git a/packages/vite/src/node/ssr/__tests__/ssrTransform.spec.ts b/packages/vite/src/node/ssr/__tests__/ssrTransform.spec.ts index cb29121356cb1f..163097c3c9ff18 100644 --- a/packages/vite/src/node/ssr/__tests__/ssrTransform.spec.ts +++ b/packages/vite/src/node/ssr/__tests__/ssrTransform.spec.ts @@ -156,6 +156,21 @@ test('do not rewrite method definition', async () => { `) }) +test('do not rewrite catch clause', async () => { + expect( + ( + await ssrTransform( + `import {error} from './dependency';try {} catch(error) {}`, + null, + null + ) + ).code + ).toMatchInlineSnapshot(` + "const __vite_ssr_import_0__ = __vite_ssr_import__(\\"./dependency\\") + try {} catch(error) {}" + `) +}) + // #2221 test('should declare variable for imported super class', async () => { expect( diff --git a/packages/vite/src/node/ssr/ssrStacktrace.ts b/packages/vite/src/node/ssr/ssrStacktrace.ts index d15c4d94ae0de3..615a03d0b35964 100644 --- a/packages/vite/src/node/ssr/ssrStacktrace.ts +++ b/packages/vite/src/node/ssr/ssrStacktrace.ts @@ -12,7 +12,10 @@ try { offset = match ? +match[1] - 1 : 0 } -export function ssrRewriteStacktrace(stack: string, moduleGraph: ModuleGraph) { +export function ssrRewriteStacktrace( + stack: string, + moduleGraph: ModuleGraph +): string { return stack .split('\n') .map((line) => { diff --git a/packages/vite/src/node/ssr/ssrTransform.ts b/packages/vite/src/node/ssr/ssrTransform.ts index b291a74a6c00a2..0e838f1932f8c6 100644 --- a/packages/vite/src/node/ssr/ssrTransform.ts +++ b/packages/vite/src/node/ssr/ssrTransform.ts @@ -322,9 +322,10 @@ function walk( function isRefIdentifier(id: Identifier, parent: _Node, parentStack: _Node[]) { // declaration id if ( - (parent.type === 'VariableDeclarator' || + parent.type === 'CatchClause' || + ((parent.type === 'VariableDeclarator' || parent.type === 'ClassDeclaration') && - parent.id === id + parent.id === id) ) { return false } diff --git a/packages/vite/src/node/utils.ts b/packages/vite/src/node/utils.ts index 57a857d73b55c3..2a49685df92571 100644 --- a/packages/vite/src/node/utils.ts +++ b/packages/vite/src/node/utils.ts @@ -24,7 +24,7 @@ export function unwrapId(id: string): string { return id.startsWith(VALID_ID_PREFIX) ? id.slice(VALID_ID_PREFIX.length) : id } -export const flattenId = (id: string) => id.replace(/[\/\.]/g, '_') +export const flattenId = (id: string): string => id.replace(/[\/\.]/g, '_') export function isBuiltin(id: string): boolean { return builtins.includes(id) @@ -40,7 +40,7 @@ try { const ssrExtensions = ['.js', '.json', '.node'] -export function resolveFrom(id: string, basedir: string, ssr = false) { +export function resolveFrom(id: string, basedir: string, ssr = false): string { return resolve.sync(id, { basedir, extensions: ssr ? ssrExtensions : DEFAULT_EXTENSIONS, @@ -58,7 +58,10 @@ interface DebuggerOptions { onlyWhenFocused?: boolean | string } -export function createDebugger(ns: string, options: DebuggerOptions = {}) { +export function createDebugger( + ns: string, + options: DebuggerOptions = {} +): debug.Debugger['log'] { const log = debug(ns) const { onlyWhenFocused } = options const focus = typeof onlyWhenFocused === 'string' ? onlyWhenFocused : ns @@ -94,17 +97,17 @@ export function ensureVolumeInPath(file: string): string { export const queryRE = /\?.*$/ export const hashRE = /#.*$/ -export const cleanUrl = (url: string) => +export const cleanUrl = (url: string): string => url.replace(hashRE, '').replace(queryRE, '') export const externalRE = /^(https?:)?\/\// -export const isExternalUrl = (url: string) => externalRE.test(url) +export const isExternalUrl = (url: string): boolean => externalRE.test(url) export const dataUrlRE = /^\s*data:/i -export const isDataUrl = (url: string) => dataUrlRE.test(url) +export const isDataUrl = (url: string): boolean => dataUrlRE.test(url) const knownJsSrcRE = /\.((j|t)sx?|mjs|vue)($|\?)/ -export const isJSRequest = (url: string) => { +export const isJSRequest = (url: string): boolean => { if (knownJsSrcRE.test(url)) { return true } @@ -117,13 +120,13 @@ export const isJSRequest = (url: string) => { const importQueryRE = /(\?|&)import(?:&|$)/ const trailingSeparatorRE = /[\?&]$/ -export const isImportRequest = (url: string) => importQueryRE.test(url) +export const isImportRequest = (url: string): boolean => importQueryRE.test(url) -export function removeImportQuery(url: string) { +export function removeImportQuery(url: string): string { return url.replace(importQueryRE, '$1').replace(trailingSeparatorRE, '') } -export function injectQuery(url: string, queryToInject: string) { +export function injectQuery(url: string, queryToInject: string): string { // encode percents for consistent behavior with pathToFileURL // see #2614 for details let resolvedUrl = new URL(url.replace(/%/g, '%25'), 'relative:///') @@ -141,7 +144,7 @@ export function injectQuery(url: string, queryToInject: string) { } const timestampRE = /\bt=\d{13}&?\b/ -export function removeTimestampQuery(url: string) { +export function removeTimestampQuery(url: string): string { return url.replace(timestampRE, '').replace(trailingSeparatorRE, '') } @@ -149,7 +152,7 @@ export async function asyncReplace( input: string, re: RegExp, replacer: (match: RegExpExecArray) => string | Promise -) { +): Promise { let match: RegExpExecArray | null let remaining = input let rewritten = '' @@ -162,7 +165,7 @@ export async function asyncReplace( return rewritten } -export function timeFrom(start: number, subtract = 0) { +export function timeFrom(start: number, subtract = 0): string { const time: number | string = Date.now() - start - subtract const timeString = (time + `ms`).padEnd(5, ' ') if (time < 10) { @@ -177,7 +180,7 @@ export function timeFrom(start: number, subtract = 0) { /** * pretty url for logging. */ -export function prettifyUrl(url: string, root: string) { +export function prettifyUrl(url: string, root: string): string { url = removeTimestampQuery(url) const isAbsoluteFile = url.startsWith(root) if (isAbsoluteFile || url.startsWith(FS_PREFIX)) { @@ -223,7 +226,7 @@ const splitRE = /\r?\n/ const range: number = 2 -export function pad(source: string, n = 2) { +export function pad(source: string, n = 2): string { const lines = source.split(splitRE) return lines.map((l) => ` `.repeat(n) + l).join(`\n`) } @@ -245,7 +248,7 @@ export function posToNumber( export function numberToPos( source: string, offset: number | { line: number; column: number } -) { +): { line: number; column: number } { if (typeof offset !== 'number') return offset if (offset > source.length) { throw new Error('offset is longer than source length!') @@ -309,7 +312,10 @@ export function generateCodeFrame( return res.join('\n') } -export function writeFile(filename: string, content: string | Uint8Array) { +export function writeFile( + filename: string, + content: string | Uint8Array +): void { const dir = path.dirname(filename) if (!fs.existsSync(dir)) { fs.mkdirSync(dir, { recursive: true }) @@ -317,7 +323,7 @@ export function writeFile(filename: string, content: string | Uint8Array) { fs.writeFileSync(filename, content) } -export function emptyDir(dir: string) { +export function emptyDir(dir: string): void { if (!fs.existsSync(dir)) { return } @@ -333,7 +339,7 @@ export function emptyDir(dir: string) { } } -export function copyDir(srcDir: string, destDir: string) { +export function copyDir(srcDir: string, destDir: string): void { fs.mkdirSync(destDir, { recursive: true }) for (const file of fs.readdirSync(srcDir)) { const srcFile = path.resolve(srcDir, file) @@ -351,7 +357,7 @@ export function ensureWatchedFile( watcher: FSWatcher, file: string | null, root: string -) { +): void { if ( file && // only need to watch if out of root @@ -373,7 +379,7 @@ const escapedSpaceCharacters = /( |\\t|\\n|\\f|\\r)+/g export async function processSrcSet( srcs: string, replacer: (arg: ImageCandidate) => Promise -) { +): Promise { const imageCandidates: ImageCandidate[] = srcs.split(',').map((s) => { const [url, descriptor] = s .replace(escapedSpaceCharacters, ' ') diff --git a/packages/vite/types/clean-css.d.ts b/packages/vite/types/clean-css.d.ts index 6344df8fa509e8..9c6368e7c52f7f 100644 --- a/packages/vite/types/clean-css.d.ts +++ b/packages/vite/types/clean-css.d.ts @@ -453,7 +453,7 @@ export namespace CleanCSS { /** * denotes a number of /*! ... * / comments preserved; defaults to `all` */ - specialComments?: string + specialComments?: 'all' | '1' | 1 | '0' | 0 /** * Controls at-rules (e.g. `@charset`, `@import`) optimizing; defaults to `true` diff --git a/yarn.lock b/yarn.lock index a4827d67f217b6..468fcaa1671024 100644 --- a/yarn.lock +++ b/yarn.lock @@ -831,7 +831,7 @@ is-module "^1.0.0" resolve "^1.19.0" -"@rollup/plugin-typescript@^8.0.0": +"@rollup/plugin-typescript@^8.2.1": version "8.2.1" resolved "https://registry.yarnpkg.com/@rollup/plugin-typescript/-/plugin-typescript-8.2.1.tgz#f1a32d4030cc83432ce36a80a922280f0f0b5d44" integrity sha512-Qd2E1pleDR4bwyFxqbjt4eJf+wB0UKVMLc7/BAFDGVdAXQMCsD4DUv5/7/ww47BZCYxWtJqe1Lo0KVNswBJlRw== @@ -2483,6 +2483,23 @@ css-color-names@^1.0.1: version "0.0.0" uid "" +css-parse@~2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/css-parse/-/css-parse-2.0.0.tgz#a468ee667c16d81ccf05c58c38d2a97c780dbfd4" + integrity sha1-pGjuZnwW2BzPBcWMONKpfHgNv9Q= + dependencies: + css "^2.0.0" + +css@^2.0.0: + version "2.2.4" + resolved "https://registry.yarnpkg.com/css/-/css-2.2.4.tgz#c646755c73971f2bba6a601e2cf2fd71b1298929" + integrity sha512-oUnjmWpy0niI3x/mPL8dVEI1l7MnG3+HHyRPHf+YFSbK+svOhXpmSOcDURUh2aOCgl2grzrOPt1nHLuCVFULLw== + dependencies: + inherits "^2.0.3" + source-map "^0.6.1" + source-map-resolve "^0.5.2" + urix "^0.1.0" + cssesc@^3.0.0: version "3.0.0" resolved "https://registry.yarnpkg.com/cssesc/-/cssesc-3.0.0.tgz#37741919903b868565e1c09ea747445cd18983ee" @@ -2564,6 +2581,13 @@ debug@^3.2.6: dependencies: ms "^2.1.1" +debug@~3.1.0: + version "3.1.0" + resolved "https://registry.yarnpkg.com/debug/-/debug-3.1.0.tgz#5bb5a0672628b64149566ba16819e61518c67261" + integrity sha512-OX8XqP7/1a9cqkxYw2yXss15f26NKWBpDXQd0/uK/KPqdQhxbPa994hnzjcE2VqQpDslf55723cKPUOGSmMY3g== + dependencies: + ms "2.0.0" + decamelize-keys@^1.1.0: version "1.1.0" resolved "https://registry.yarnpkg.com/decamelize-keys/-/decamelize-keys-1.1.0.tgz#d171a87933252807eb3cb61dc1c1445d078df2d9" @@ -2652,6 +2676,10 @@ delegate@^3.1.2: version "0.0.0" uid "" +"dep-import-type@link:./packages/playground/ssr-vue/dep-import-type": + version "0.0.0" + uid "" + "dep-linked-include@link:./packages/playground/optimize-deps/dep-linked-include": version "0.0.0" uid "" @@ -2882,6 +2910,11 @@ escodegen@^2.0.0: optionalDependencies: source-map "~0.6.1" +eslint-define-config@^1.0.3: + version "1.0.3" + resolved "https://registry.yarnpkg.com/eslint-define-config/-/eslint-define-config-1.0.3.tgz#26efbf9124d3855c8ff49233e6d3e3e3be939f60" + integrity sha512-GlfTqk2lysLHRx4YH3xuliY06t/p4WMZc3GABLqZ84RcxJxwRVMp0qF9cQSL42gf0pO7IY+dyUsxMrv2b6rZTQ== + eslint-plugin-es@^3.0.0: version "3.0.1" resolved "https://registry.yarnpkg.com/eslint-plugin-es/-/eslint-plugin-es-3.0.1.tgz#75a7cdfdccddc0589934aeeb384175f221c57893" @@ -5408,7 +5441,7 @@ mixin-deep@^1.2.0: for-in "^1.0.2" is-extendable "^1.0.1" -mkdirp@1.0.4, mkdirp@1.x: +mkdirp@1.0.4, mkdirp@1.x, mkdirp@~1.0.4: version "1.0.4" resolved "https://registry.yarnpkg.com/mkdirp/-/mkdirp-1.0.4.tgz#3eb5ed62622756d79a5f0e2a221dfebad75c2f7e" integrity sha512-vVqVZQyf3WLx2Shd0qJ9xuvqgAyKPLAiqITEtqW0oIUjzo3PePDd6fW9iFz30ef7Ysp/oiWqbhszeGWW2T6Gzw== @@ -6853,7 +6886,7 @@ safe-regex@^1.1.0: dependencies: ret "~0.1.10" -"safer-buffer@>= 2.1.2 < 3", safer-buffer@^2.0.2, safer-buffer@^2.1.0, safer-buffer@~2.1.0: +"safer-buffer@>= 2.1.2 < 3", safer-buffer@^2.0.2, safer-buffer@^2.1.0, safer-buffer@^2.1.2, safer-buffer@~2.1.0: version "2.1.2" resolved "https://registry.yarnpkg.com/safer-buffer/-/safer-buffer-2.1.2.tgz#44fa161b0187b9549dd84bb91802f9bd8385cd6a" integrity sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg== @@ -6880,7 +6913,7 @@ sass@^1.30.0, sass@^1.32.5: dependencies: chokidar ">=2.0.0 <4.0.0" -sax@^1.2.4: +sax@^1.2.4, sax@~1.2.4: version "1.2.4" resolved "https://registry.yarnpkg.com/sax/-/sax-1.2.4.tgz#2816234e2378bddc4e5354fab5caa895df7100d9" integrity sha512-NqVDv9TpANUjFm0N8uM5GxL36UgKi9/atZw+x7YFnQ8ckwFGKrl4xX4yWtrey3UJm5nP1kUbnYgLopqWNSRhWw== @@ -7106,7 +7139,7 @@ snapdragon@^0.8.1: source-map-resolve "^0.5.0" use "^3.1.0" -source-map-resolve@^0.5.0: +source-map-resolve@^0.5.0, source-map-resolve@^0.5.2: version "0.5.3" resolved "https://registry.yarnpkg.com/source-map-resolve/-/source-map-resolve-0.5.3.tgz#190866bece7553e1f8f267a2ee82c606b5509a1a" integrity sha512-Htz+RnsXWk5+P2slx5Jh3Q66vhQj1Cllm0zvnaY98+NFx+Dv2CF/f5O/t8x+KaNdrdIAsruNzoh/KpialbqAnw== @@ -7417,6 +7450,20 @@ strip-json-comments@^3.1.0, strip-json-comments@^3.1.1, strip-json-comments@~3.1 resolved "https://registry.yarnpkg.com/strip-json-comments/-/strip-json-comments-3.1.1.tgz#31f1281b3832630434831c310c01cccda8cbe006" integrity sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig== +stylus@^0.54.8: + version "0.54.8" + resolved "https://registry.yarnpkg.com/stylus/-/stylus-0.54.8.tgz#3da3e65966bc567a7b044bfe0eece653e099d147" + integrity sha512-vr54Or4BZ7pJafo2mpf0ZcwA74rpuYCZbxrHBsH8kbcXOwSfvBFwsRfpGO5OD5fhG5HDCFW737PKaawI7OqEAg== + dependencies: + css-parse "~2.0.0" + debug "~3.1.0" + glob "^7.1.6" + mkdirp "~1.0.4" + safer-buffer "^2.1.2" + sax "~1.2.4" + semver "^6.3.0" + source-map "^0.7.3" + supports-color@^2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/supports-color/-/supports-color-2.0.0.tgz#535d045ce6b6363fa40117084629995e9df324c7"