Repository: vbenjs/vite-plugin-html Branch: main Commit: 4a32df2b416b Files: 60 Total size: 56.1 KB Directory structure: gitextract_p7e8ovn3/ ├── .commitlintrc.json ├── .editorconfig ├── .eslintignore ├── .eslintrc.json ├── .github/ │ └── workflows/ │ ├── publish.yml │ ├── release.yml │ └── test.yml ├── .gitignore ├── .husky/ │ ├── commit-msg │ ├── common.sh │ └── pre-commit ├── .npmrc ├── .prettierrc.json ├── .vscode/ │ ├── extensions.json │ └── settings.json ├── CHANGELOG.md ├── LICENSE ├── README.md ├── README.zh_CN.md ├── package.json ├── packages/ │ ├── core/ │ │ ├── build.config.ts │ │ ├── package.json │ │ └── src/ │ │ ├── __tests__/ │ │ │ ├── html.spec.ts │ │ │ ├── minify.spec.ts │ │ │ └── utils.spec.ts │ │ ├── htmlPlugin.ts │ │ ├── index.ts │ │ ├── minifyHtml.ts │ │ ├── typing.ts │ │ └── utils/ │ │ ├── createHtmlFilter.ts │ │ └── index.ts │ └── playground/ │ ├── basic/ │ │ ├── basic.html │ │ ├── index.html │ │ ├── package.json │ │ ├── public/ │ │ │ └── inject.js │ │ ├── server.js │ │ ├── src/ │ │ │ ├── App.vue │ │ │ ├── main.ts │ │ │ └── test.vue │ │ └── vite.config.ts │ ├── custom-entry/ │ │ ├── package.json │ │ ├── public/ │ │ │ └── inject.js │ │ ├── src/ │ │ │ ├── App.vue │ │ │ ├── main.ts │ │ │ └── test.vue │ │ ├── static/ │ │ │ └── index.html │ │ └── vite.config.ts │ └── mpa/ │ ├── index.html │ ├── other.html │ ├── package.json │ ├── public/ │ │ └── inject.js │ ├── src/ │ │ ├── App.vue │ │ ├── main.ts │ │ ├── other-app.vue │ │ ├── other-main.ts │ │ └── test.vue │ └── vite.config.ts ├── pnpm-workspace.yaml ├── tsconfig.json └── vitest.config.ts ================================================ FILE CONTENTS ================================================ ================================================ FILE: .commitlintrc.json ================================================ { "extends": ["@commitlint/config-conventional"] } ================================================ FILE: .editorconfig ================================================ root = true [*] charset = utf-8 end_of_line = lf trim_trailing_whitespace = true insert_final_newline = true indent_size = 2 indent_style = space max_line_length = 100 quote_type = single [*.md] indent_size = 2 insert_final_newline = false trim_trailing_whitespace = false [*.py] indent_size = 4 [Makefile] indent_style = tab ================================================ FILE: .eslintignore ================================================ dist node_modules ================================================ FILE: .eslintrc.json ================================================ { "root": true, "env": { "browser": true, "es2021": true, "es6": true, "node": true }, "extends": [ "eslint:recommended", "plugin:@typescript-eslint/recommended", "prettier" ], "parser": "@typescript-eslint/parser", "parserOptions": { "ecmaVersion": 12, "sourceType": "module" }, "rules": { "no-console": 1, "@typescript-eslint/no-non-null-assertion": "off", "@typescript-eslint/ban-ts-comment": "off", "@typescript-eslint/no-explicit-any": "off" } } ================================================ FILE: .github/workflows/publish.yml ================================================ name: Npm Publish on: push: branches: - main jobs: publish-npm: if: "contains(github.event.head_commit.message, 'release')" runs-on: ${{matrix.os}} strategy: matrix: os: [ubuntu-latest] node-version: [16.x] fail-fast: false steps: - name: Checkout uses: actions/checkout@v2 - name: Install pnpm uses: pnpm/action-setup@v2.0.1 with: version: 6.31.0 - name: Use Node.js ${{ matrix.node-version }} uses: actions/setup-node@v2 with: node-version: ${{ matrix.node-version }} registry-url: https://registry.npmjs.org/ cache: 'pnpm' - name: Install Dependencies run: pnpm install - name: Publish to NPM run: pnpm -r publish --access public --no-git-checks env: NODE_AUTH_TOKEN: ${{secrets.npm_token}} ================================================ FILE: .github/workflows/release.yml ================================================ name: Create Release on: push: tags: - v* jobs: build: name: Create Release runs-on: ${{matrix.os}} strategy: matrix: os: [ubuntu-latest] fail-fast: false steps: - name: Checkout code uses: actions/checkout@master - name: Create Release for Tag id: release_tag uses: yyx990803/release-tag@master env: GITHUB_TOKEN: ${{ secrets.OPER_TOKEN }} with: tag_name: ${{ github.ref }} body: | Please refer to [CHANGELOG.md](https://github.com/anncwb/vite-plugin-html/blob/main/CHANGELOG.md) for details. ================================================ FILE: .github/workflows/test.yml ================================================ name: Test on: push: branches: - main pull_request: branches: - main jobs: build: runs-on: ${{ matrix.os }} strategy: matrix: node-version: [16.x] os: [ubuntu-latest] fail-fast: false steps: - uses: actions/checkout@v2 - name: Install pnpm uses: pnpm/action-setup@v2.0.1 with: version: 6.28.0 - name: Use Node.js ${{ matrix.node-version }} uses: actions/setup-node@v2 with: node-version: ${{ matrix.node-version }} registry-url: https://registry.npmjs.org/ cache: 'pnpm' - run: pnpm install - name: Test run: pnpm run test ================================================ FILE: .gitignore ================================================ node_modules dist ================================================ FILE: .husky/commit-msg ================================================ #!/bin/sh # shellcheck source=./_/husky.sh . "$(dirname "$0")/_/husky.sh" npx --no-install commitlint --edit "$1" ================================================ FILE: .husky/common.sh ================================================ #!/bin/sh command_exists () { command -v "$1" >/dev/null 2>&1 } # Workaround for Windows 10, Git Bash and Yarn if command_exists winpty && test -t 1; then exec < /dev/tty fi ================================================ FILE: .husky/pre-commit ================================================ #!/bin/sh . "$(dirname "$0")/_/husky.sh" . "$(dirname "$0")/common.sh" [ -n "$CI" ] && exit 0 # Format and submit code according to lintstagedrc.js configuration pnpm exec lint-staged --concurrent false ================================================ FILE: .npmrc ================================================ ignore-workspace-root-check=true public-hoist-pattern[]=* ================================================ FILE: .prettierrc.json ================================================ { "semi": false, "tabWidth": 2, "singleQuote": true, "printWidth": 80, "trailingComma": "all" } ================================================ FILE: .vscode/extensions.json ================================================ { "recommendations": [ "octref.vetur", "dbaeumer.vscode-eslint", "stylelint.vscode-stylelint", "esbenp.prettier-vscode", "mrmlnc.vscode-less", "lokalise.i18n-ally", "antfu.iconify", "mikestead.dotenv", "heybourn.headwind" ] } ================================================ FILE: .vscode/settings.json ================================================ { "typescript.tsdk": "./node_modules/typescript/lib", "typescript.enablePromptUseWorkspaceTsdk": true, //=========================================== //============= Editor ====================== //=========================================== "explorer.openEditors.visible": 0, "editor.tabSize": 2, "editor.defaultFormatter": "esbenp.prettier-vscode", "diffEditor.ignoreTrimWhitespace": false, //=========================================== //============= Other ======================= //=========================================== "breadcrumbs.enabled": true, "open-in-browser.default": "chrome", //=========================================== //============= files ======================= //=========================================== "files.eol": "\n", "search.exclude": { "**/node_modules": true, "**/*.log": true, "**/*.log*": true, "**/bower_components": true, "**/dist": true, "**/elehukouben": true, "**/.git": true, "**/.gitignore": true, "**/.svn": true, "**/.DS_Store": true, "**/.idea": true, "**/.vscode": false, "**/yarn.lock": true, "**/tmp": true, "out": true, "dist": true, "node_modules": true, "CHANGELOG.md": true, "examples": true, "res": true, "screenshots": true, "yarn-error.log": true, "**/pnpm-lock.yaml": true, "**/.yarn": true }, "files.exclude": { "**/.cache": true, "**/.editorconfig": true, "**/.eslintcache": true, "**/bower_components": true, "**/.idea": true, "**/tmp": true, "**/.git": true, "**/.svn": true, "**/.hg": true, "**/CVS": true, "**/.DS_Store": true }, "files.watcherExclude": { "**/.git/objects/**": true, "**/.git/subtree-cache/**": true, "**/.vscode/**": true, "**/node_modules/**": true, "**/tmp/**": true, "**/bower_components/**": true, "**/dist/**": true, "**/yarn.lock": true }, "stylelint.enable": true, "stylelint.packageManager": "yarn", "liveServer.settings.donotShowInfoMsg": true, "workbench.settings.enableNaturalLanguageSearch": false, "prettier.requireConfig": true, "workbench.sideBar.location": "left", "cSpell.words": [ "vben", "windi", "browserslist", "tailwindcss", "esnext", "antv", "tinymce", "qrcode", "sider", "pinia", "sider", "nprogress", "INTLIFY", "stylelint", "esno", "vitejs", "sortablejs", "mockjs", "codemirror", "iconify", "commitlint", "vditor", "echarts", "cropperjs", "logicflow", "vueuse", "zxcvbn", "lintstagedrc", "brotli", "tailwindcss", "sider" ] } ================================================ FILE: CHANGELOG.md ================================================ # [3.2.1](https://github.com/vbenjs/vite-plugin-html/compare/v3.0.6...v3.2.1) (2023-12-26) ### Features - support vite5.0 # [3.2.0](https://github.com/vbenjs/vite-plugin-html/compare/v3.0.6...v3.2.0) (2022-03-15) ### Bug Fixes - improve middleware logic ([36e143a](https://github.com/vbenjs/vite-plugin-html/commit/36e143a55b62710c7435ed0ca5ed4b035930c3af)) ### Features - support tags ([d07c9db](https://github.com/vbenjs/vite-plugin-html/commit/d07c9db4541432b94576e1fd4dce1b17098a60d0)) ## [3.0.6](https://github.com/vbenjs/vite-plugin-html/compare/v3.0.0...v3.0.6) (2022-02-10) ### Bug Fixes - fix base configuration causing local development errors ([6deeead](https://github.com/vbenjs/vite-plugin-html/commit/6deeead53f02007effd42b013d0eb03390f0a9a2)) - fs-extra no longer exports existsSync ([#25](https://github.com/vbenjs/vite-plugin-html/issues/25)) ([d6614da](https://github.com/vbenjs/vite-plugin-html/commit/d6614dae2ab5d2f53d54ec480e1212613819186b)) - history mode support ([20a7c69](https://github.com/vbenjs/vite-plugin-html/commit/20a7c69ed7f8f355bda923dd9f84717727276c67)) - make sure template defaults are correct ([697626c](https://github.com/vbenjs/vite-plugin-html/commit/697626cb62db42c1853788ac4019a834822b19e5)) ## [3.0.5](https://github.com/vbenjs/vite-plugin-html/compare/v3.0.0...v3.0.5) (2022-02-09) ### Bug Fixes - fix base configuration causing local development errors ([6deeead](https://github.com/vbenjs/vite-plugin-html/commit/6deeead53f02007effd42b013d0eb03390f0a9a2)) - fs-extra no longer exports existsSync ([#25](https://github.com/vbenjs/vite-plugin-html/issues/25)) ([d6614da](https://github.com/vbenjs/vite-plugin-html/commit/d6614dae2ab5d2f53d54ec480e1212613819186b)) - history mode support ([20a7c69](https://github.com/vbenjs/vite-plugin-html/commit/20a7c69ed7f8f355bda923dd9f84717727276c67)) - make sure template defaults are correct ([697626c](https://github.com/vbenjs/vite-plugin-html/commit/697626cb62db42c1853788ac4019a834822b19e5)) ## [3.0.4](https://github.com/vbenjs/vite-plugin-html/compare/v3.0.0...v3.0.4) (2022-02-07) ### Bug Fixes - fix base configuration causing local development errors ([6deeead](https://github.com/vbenjs/vite-plugin-html/commit/6deeead53f02007effd42b013d0eb03390f0a9a2)) - fs-extra no longer exports existsSync ([#25](https://github.com/vbenjs/vite-plugin-html/issues/25)) ([d6614da](https://github.com/vbenjs/vite-plugin-html/commit/d6614dae2ab5d2f53d54ec480e1212613819186b)) - history mode support ([4b0a54f](https://github.com/vbenjs/vite-plugin-html/commit/4b0a54fd08dd3e065b239ef0587dc683263db343)) - make sure template defaults are correct ([697626c](https://github.com/vbenjs/vite-plugin-html/commit/697626cb62db42c1853788ac4019a834822b19e5)) ## [3.0.2](https://github.com/vbenjs/vite-plugin-html/compare/v3.0.0...v3.0.2) (2022-01-28) ### Bug Fixes - fix base configuration causing local development errors ([6deeead](https://github.com/vbenjs/vite-plugin-html/commit/6deeead53f02007effd42b013d0eb03390f0a9a2)) - fs-extra no longer exports existsSync ([#25](https://github.com/vbenjs/vite-plugin-html/issues/25)) ([d6614da](https://github.com/vbenjs/vite-plugin-html/commit/d6614dae2ab5d2f53d54ec480e1212613819186b)) - make sure template defaults are correct ([697626c](https://github.com/vbenjs/vite-plugin-html/commit/697626cb62db42c1853788ac4019a834822b19e5)) ## [3.0.2](https://github.com/vbenjs/vite-plugin-html/compare/v3.0.0...v3.0.2) (2022-01-27) ### Bug Fixes - fs-extra no longer exports existsSync ([#25](https://github.com/vbenjs/vite-plugin-html/issues/25)) ([d6614da](https://github.com/vbenjs/vite-plugin-html/commit/d6614dae2ab5d2f53d54ec480e1212613819186b)) - make sure template defaults are correct ([697626c](https://github.com/vbenjs/vite-plugin-html/commit/697626cb62db42c1853788ac4019a834822b19e5)) ## [3.0.1](https://github.com/vbenjs/vite-plugin-html/compare/v3.0.0...v3.0.1) (2022-01-27) ### Bug Fixes - make sure template defaults are correct ([697626c](https://github.com/vbenjs/vite-plugin-html/commit/697626cb62db42c1853788ac4019a834822b19e5)) # [3.0.0-beta.1](https://github.com/vbenjs/vite-plugin-html/compare/v3.0.0...v3.0.0-beta.1) (2022-01-27) ### Bug Fixes - make sure template defaults are correct ([697626c](https://github.com/vbenjs/vite-plugin-html/commit/697626cb62db42c1853788ac4019a834822b19e5)) ## [2.1.2](https://github.com/vbenjs/vite-plugin-html/compare/v2.0.6...v2.1.2) (2021-12-27) ### Bug Fixes - ssr error,close [#18](https://github.com/vbenjs/vite-plugin-html/issues/18) ([f799d98](https://github.com/vbenjs/vite-plugin-html/commit/f799d9821ec9b22bbbfd8b92ddcb4d25cc18219e)) ### Features - expose minifyFn ([c6409dc](https://github.com/vbenjs/vite-plugin-html/commit/c6409dc25e118b47adff250ab4dd0a239803258b)) ## [2.1.1](https://github.com/vbenjs/vite-plugin-html/compare/v2.0.6...v2.1.1) (2021-09-27) ### Features - expose minifyFn ([c6409dc](https://github.com/vbenjs/vite-plugin-html/commit/c6409dc25e118b47adff250ab4dd0a239803258b)) # [2.1.0](https://github.com/vbenjs/vite-plugin-html/compare/v2.0.6...v2.1.0) (2021-08-20) ### Features - expose minifyFn ([c6409dc](https://github.com/vbenjs/vite-plugin-html/commit/c6409dc25e118b47adff250ab4dd0a239803258b)) - **inject:** inject the contents of the .env file into index.html ([5b52d7e](https://github.com/vbenjs/vite-plugin-html/commit/5b52d7e654c1056f6a368f4c7df0de8a63b61874)) ## [2.0.7](https://github.com/vbenjs/vite-plugin-html/compare/v2.0.6...v2.0.7) (2021-04-16) ### Features - expose minifyFn ([c6409dc](https://github.com/vbenjs/vite-plugin-html/commit/c6409dc25e118b47adff250ab4dd0a239803258b)) ## [2.0.4](https://github.com/vbenjs/vite-plugin-html/compare/v2.0.2...v2.0.4) (2021-04-05) ## [2.0.3](https://github.com/vbenjs/vite-plugin-html/compare/v2.0.2...v2.0.3) (2021-03-02) ## [2.0.2](https://github.com/vbenjs/vite-plugin-html/compare/v2.0.1...v2.0.2) (2021-02-23) ### Features - add gihub action ([3569c1c](https://github.com/vbenjs/vite-plugin-html/commit/3569c1c097be457fe91b5bb39c2bd56e61753fc9)) # [2.0.0-rc.1](https://github.com/vbenjs/vite-plugin-html/compare/v2.0.0-beta.2...v2.0.0-rc.1) (2021-01-29) ### Bug Fixes - css build error ([12cd218](https://github.com/vbenjs/vite-plugin-html/commit/12cd218c3f02267022eed06eea18c8e67d4119ff)) - fix css compression failure [#1](https://github.com/vbenjs/vite-plugin-html/issues/1) ([b62e99c](https://github.com/vbenjs/vite-plugin-html/commit/b62e99cd809a0a581cbd1e1dae9260d0b35e9abb)) ### Features - inject title to viteHtmlPluginOptions ([3b34151](https://github.com/vbenjs/vite-plugin-html/commit/3b341516cc78c83619d672ab1c5316a4339a92ac)) # 2.0.0-beta.2 (2021-01-03) ================================================ FILE: LICENSE ================================================ MIT License Copyright (c) 2020-present, Vben Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. ================================================ FILE: README.md ================================================ # vite-plugin-html **English** | [中文](./README.zh_CN.md) ## Features - HTML compression capability - EJS template capability - Multi-page application support - Support custom `entry` - Support custom `template` ## Install (yarn or npm) **node version:** >=12.0.0 **vite version:** >=2.0.0 ```bash yarn add vite-plugin-html -D ``` 或 ```bash npm i vite-plugin-html -D ``` ## Usage - Add EJS tags to `index.html`, e.g. ```html <%- title %> <%- injectScript %> ``` - Configure in `vite.config.ts`, this method can introduce the required functions as needed ```ts import { defineConfig, Plugin } from 'vite' import vue from '@vitejs/plugin-vue' import { createHtmlPlugin } from 'vite-plugin-html' export default defineConfig({ plugins: [ vue(), createHtmlPlugin({ minify: true, /** * After writing entry here, you will not need to add script tags in `index.html`, the original tags need to be deleted * @default src/main.ts */ entry: 'src/main.ts', /** * If you want to store `index.html` in the specified folder, you can modify it, otherwise no configuration is required * @default index.html */ template: 'public/index.html', /** * Data that needs to be injected into the index.html ejs template */ inject: { data: { title: 'index', injectScript: ``, }, tags: [ { injectTo: 'body-prepend', tag: 'div', attrs: { id: 'tag', }, }, ], }, }), ], }) ``` Multi-page application configuration ```ts import { defineConfig } from 'vite' import { createHtmlPlugin } from 'vite-plugin-html' export default defineConfig({ plugins: [ createHtmlPlugin({ minify: true, pages: [ { entry: 'src/main.ts', filename: 'index.html', template: 'public/index.html', injectOptions: { data: { title: 'index', injectScript: ``, }, tags: [ { injectTo: 'body-prepend', tag: 'div', attrs: { id: 'tag1', }, }, ], }, }, { entry: 'src/other-main.ts', filename: 'other.html', template: 'public/other.html', injectOptions: { data: { title: 'other page', injectScript: ``, }, tags: [ { injectTo: 'body-prepend', tag: 'div', attrs: { id: 'tag2', }, }, ], }, }, ], }), ], }) ``` ## Parameter Description `createHtmlPlugin(options: UserOptions)` ### UserOptions | Parameter | Types | Default | Description | | --------- | ------------------------ | ------------- | ------------------------------------------------- | | entry | `string` | `src/main.ts` | entry file path | | template | `string` | `index.html` | relative path to the template | | inject | `InjectOptions` | - | Data injected into HTML | | minify | `boolean|MinifyOptions` | - | whether to compress html | | pages | `PageOption` | - | Multi-page configuration | ### InjectOptions | Parameter | Types | Default | Description | | ---------- | --------------------- | ------- | ------------------------------------------------------------------------- | | data | `Record` | - | injected data | | ejsOptions | `EJSOptions` | - | ejs configuration Options[EJSOptions](https://github.com/mde/ejs#options) | | tags | `HtmlTagDescriptor` | - | List of tags to inject | `data` can be accessed in `html` using the `ejs` template syntax #### Env inject By default, the contents of the `.env` file will be injected into index.html, similar to vite's `loadEnv` function ### PageOption | Parameter | Types | Default | Description | | ------------- | --------------- | ------------- | ----------------------------- | | filename | `string` | - | html file name | | template | `string` | `index.html` | relative path to the template | | entry | `string` | `src/main.ts` | entry file path | | injectOptions | `InjectOptions` | - | Data injected into HTML | ### MinifyOptions Default compression configuration ```ts collapseWhitespace: true, keepClosingSlash: true, removeComments: true, removeRedundantAttributes: true, removeScriptTypeAttributes: true, removeStyleLinkTypeAttributes: true, useShortDoctype: true, minifyCSS: true, ``` ### Run the playground ```bash pnpm install # spa cd ./packages/playground/basic pnpm run dev # map cd ./packages/playground/mpa pnpm run dev ``` ## Example project [Vben Admin](https://github.com/anncwb/vue-vben-admin) ## License MIT [npm-img]: https://img.shields.io/npm/v/vite-plugin-html.svg [npm-url]: https://npmjs.com/package/vite-plugin-html [node-img]: https://img.shields.io/node/v/vite-plugin-html.svg [node-url]: https://nodejs.org/en/about/releases/ ================================================ FILE: README.zh_CN.md ================================================ # vite-plugin-html **中文** | [English](./README.md) [![npm][npm-img]][npm-url] [![node][node-img]][node-url] ## 功能 - HTML 压缩能力 - EJS 模版能力 - 多页应用支持 - 支持自定义`entry` - 支持自定义`template` ## 安装 (yarn or npm) **node version:** >=12.0.0 **vite version:** >=2.0.0 ```bash yarn add vite-plugin-html -D ``` 或 ```bash npm i vite-plugin-html -D ``` ## 使用 - 在 `index.html` 中增加 EJS 标签,例如 ```html <%- title %> <%- injectScript %> ``` - 在 `vite.config.ts` 中配置,该方式可以按需引入需要的功能即可 ```ts import { defineConfig, Plugin } from 'vite' import vue from '@vitejs/plugin-vue' import { createHtmlPlugin } from 'vite-plugin-html' export default defineConfig({ plugins: [ vue(), createHtmlPlugin({ minify: true, /** * 在这里写entry后,你将不需要在`index.html`内添加 script 标签,原有标签需要删除 * @default src/main.ts */ entry: 'src/main.ts', /** * 如果你想将 `index.html`存放在指定文件夹,可以修改它,否则不需要配置 * @default index.html */ template: 'public/index.html', /** * 需要注入 index.html ejs 模版的数据 */ inject: { data: { title: 'index', injectScript: ``, }, tags: [ { injectTo: 'body-prepend', tag: 'div', attrs: { id: 'tag', }, }, ], }, }), ], }) ``` 多页应用配置 ```ts import { defineConfig } from 'vite' import { createHtmlPlugin } from 'vite-plugin-html' export default defineConfig({ plugins: [ createHtmlPlugin({ minify: true, pages: [ { entry: 'src/main.ts', filename: 'index.html', template: 'public/index.html', injectOptions: { data: { title: 'index', injectScript: ``, }, tags: [ { injectTo: 'body-prepend', tag: 'div', attrs: { id: 'tag1', }, }, ], }, }, { entry: 'src/other-main.ts', filename: 'other.html', template: 'public/other.html', injectOptions: { data: { title: 'other page', injectScript: ``, }, tags: [ { injectTo: 'body-prepend', tag: 'div', attrs: { id: 'tag2', }, }, ], }, }, ], }), ], }) ``` ## 参数说明 `createHtmlPlugin(options: UserOptions)` ### UserOptions | 参数 | 类型 | 默认值 | 说明 | | -------- | ------------------------ | ------------- | ------------------------------- | | entry | `string` | `src/main.ts` | 入口文件 | | template | `string` | `index.html` | 模板的相对路径 | | inject | `InjectOptions` | - | 注入 HTML 的数据 | | minify | `boolean|MinifyOptions` | - | 是否压缩 html | | pages | `PageOption` | - | 多页配置 | ### InjectOptions | 参数 | 类型 | 默认值 | 说明 | | ---------- | --------------------- | ------ | ---------------------------------------------------------- | | data | `Record` | - | 注入的数据 | | ejsOptions | `EJSOptions` | - | ejs 配置项[EJSOptions](https://github.com/mde/ejs#options) | | tags | `HtmlTagDescriptor` | - | 需要注入的标签列表 | `data` 可以在 `html` 中使用 `ejs` 模版语法获取 #### env 注入 默认会向 index.html 注入 `.env` 文件的内容,类似 vite 的 `loadEnv`函数 ### PageOption | 参数 | 类型 | 默认值 | 说明 | | ------------- | --------------- | ------------- | ---------------- | | filename | `string` | - | html 文件名 | | template | `string` | `index.html` | 模板的相对路径 | | entry | `string` | `src/main.ts` | 入口文件 | | injectOptions | `InjectOptions` | - | 注入 HTML 的数据 | ### MinifyOptions 默认压缩配置 ```ts collapseWhitespace: true, keepClosingSlash: true, removeComments: true, removeRedundantAttributes: true, removeScriptTypeAttributes: true, removeStyleLinkTypeAttributes: true, useShortDoctype: true, minifyCSS: true, ``` ### 运行示例 ```bash pnpm install # spa cd ./packages/playground/basic pnpm run dev # map cd ./packages/playground/mpa pnpm run dev ``` ## 示例项目 [Vben Admin](https://github.com/anncwb/vue-vben-admin) ## License MIT [npm-img]: https://img.shields.io/npm/v/vite-plugin-html.svg [npm-url]: https://npmjs.com/package/vite-plugin-html [node-img]: https://img.shields.io/node/v/vite-plugin-html.svg [node-url]: https://nodejs.org/en/about/releases/ ================================================ FILE: package.json ================================================ { "name": "vite-plugin-html-monorepo", "version": "3.2.1", "private": true, "scripts": { "stub": "pnpm run prepack --filter ./packages -- --stub", "postinstall": "pnpm run stub", "log": "conventional-changelog -p angular -i CHANGELOG.md -s", "lint:pretty": "pretty-quick --staged", "lint:eslint": "eslint \"packages/**/*.{ts,tsx}\" --fix", "prepare": "husky install", "preinstall": "npx only-allow pnpm", "test": "vitest" }, "author": "Vben", "license": "MIT", "repository": { "type": "git", "url": "https://github.com/vbenjs/vite-plugin-html" }, "bugs": { "url": "https://github.com/vbenjs/vite-plugin-html/issues" }, "homepage": "https://github.com/vbenjs/vite-plugin-html/tree/master/#readme", "devDependencies": { "@commitlint/cli": "^16.2.1", "@commitlint/config-conventional": "^16.2.1", "@types/html-minifier-terser": "^6.1.0", "@types/jsdom": "^16.2.14", "@types/node": "^17.0.21", "@typescript-eslint/eslint-plugin": "^5.14.0", "@typescript-eslint/parser": "^5.14.0", "commitizen": "^4.2.4", "conventional-changelog-cli": "^2.2.2", "cross-env": "^7.0.3", "eslint": "^8.11.0", "eslint-config-prettier": "^8.5.0", "eslint-plugin-html": "^6.2.0", "husky": "^7.0.4", "lint-staged": "^12.3.5", "prettier": "^2.5.1", "rimraf": "^3.0.2", "tsup": "^5.12.1", "typescript": "^4.6.2", "unbuild": "^0.7.0", "vite": "^2.8.6", "vitest": "^0.6.1" }, "lint-staged": { "*": [ "prettier --write --ignore-unknown" ], "packages/*/{src,types}/**/*.ts": [ "eslint --ext .ts" ], "packages/**/*.d.ts": [ "eslint --ext .ts" ] } } ================================================ FILE: packages/core/build.config.ts ================================================ import { defineBuildConfig } from 'unbuild' export default defineBuildConfig({ clean: true, entries: ['./src/index'], declaration: true, rollup: { emitCJS: true, }, }) ================================================ FILE: packages/core/package.json ================================================ { "name": "vite-plugin-html", "version": "3.2.2", "description": "A plugin for vite to Minimize index.html and use lodash.template template syntax in index.html", "main": "dist/index.cjs", "module": "dist/index.mjs", "types": "dist/index.d.ts", "exports": { ".": { "require": "./dist/index.cjs", "import": "./dist/index.mjs", "types": "./dist/index.d.ts" } }, "files": [ "dist", "CHANGELOG.md", "README.md", "README.zh_CN.md" ], "scripts": { "dev": "pnpm unbuild --stub", "build": "pnpm unbuild", "prepublishOnly": "npm run build", "prepack": "pnpm unbuild" }, "keywords": [ "vite", "html", "minify", "vite-plugin" ], "author": "Vben", "license": "MIT", "repository": { "type": "git", "url": "https://github.com/vbenjs/vite-plugin-html", "directory": "packages/core" }, "bugs": { "url": "https://github.com/vbenjs/vite-plugin-html/issues" }, "homepage": "https://github.com/vbenjs/vite-plugin-html/tree/master/#readme", "dependencies": { "@rollup/pluginutils": "^4.2.0", "colorette": "^2.0.16", "connect-history-api-fallback": "^1.6.0", "consola": "^2.15.3", "dotenv": "^16.0.0", "dotenv-expand": "^8.0.2", "ejs": "^3.1.6", "fast-glob": "^3.2.11", "fs-extra": "^10.0.1", "html-minifier-terser": "^6.1.0", "node-html-parser": "^5.3.3", "pathe": "^0.2.0" }, "peerDependencies": { "vite": ">=2.0.0" }, "devDependencies": { "@babel/types": "^7.17.0", "@types/ejs": "^3.1.0", "@types/fs-extra": "^9.0.13", "@types/html-minifier-terser": "^6.1.0", "@types/node": "^17.0.21", "typescript": "^4.6.2", "vite": "^2.8.6" } } ================================================ FILE: packages/core/src/__tests__/html.spec.ts ================================================ import { createPlugin, createSpaPage, renderHtml } from '../htmlPlugin' import { describe, test, expect } from 'vitest' const createVitePlugin = () => { const { name } = createPlugin() return { name } } describe('html plugin test.', () => { test('make sure name.', async () => { const { name } = await createVitePlugin() expect(name).toEqual('vite:html') }) }) describe('function test.', () => { test('createSpaPage function test.', async () => { const page = createSpaPage('main.ts', 'public/index.html', { data: { a: '1' }, ejsOptions: {}, }) expect(page).toEqual({ filename: 'index.html', template: 'public/index.html', entry: 'main.ts', injectOptions: { data: { a: '1' }, ejsOptions: {} }, }) }) test('renderHtml function test.', async () => { const content = await renderHtml( ` <%- title %> <%- ENV_TITLE %> `, { injectOptions: { data: { title: 'test-title' }, }, viteConfig: {} as any, env: { ENV_TITLE: 'env-title', }, }, ) expect(content).toEqual(` test-title env-title `) }) }) ================================================ FILE: packages/core/src/__tests__/minify.spec.ts ================================================ import { createMinifyHtmlPlugin, minifyHtml } from '../minifyHtml' import { describe, test, expect } from 'vitest' const createVitePlugin = () => { const { name, generateBundle } = createMinifyHtmlPlugin() return { name, generateBundle } } describe('minify html plugin test.', () => { test('make sure name.', async () => { const { name } = await createVitePlugin() expect(name).toEqual('vite:minify-html') }) test('make sure generateBundle.', async () => { const { generateBundle } = await createVitePlugin() const generate: any = generateBundle const testBundle = { test: { type: 'asset', fileName: 'index.html', source: ` `, }, } await generate(null, testBundle, false) expect(testBundle.test.source).toEqual( ``, ) }) test('minify is true.', async () => { const ret = await minifyHtml( ` `, true, ) expect(ret).toEqual(``) }) test('minify is false.', async () => { const ret = await minifyHtml( ` `, false, ) expect(ret).toEqual( ` `, ) }) test('minify css.', async () => { const ret = await minifyHtml( ` `, true, ) expect(ret).toEqual( ``, ) }) test('custom minify options.', async () => { const ret = await minifyHtml( ` `, { minifyCSS: true }, ) expect(ret).toEqual(` `) }) }) ================================================ FILE: packages/core/src/__tests__/utils.spec.ts ================================================ import { describe, test, expect } from 'vitest' import { htmlFilter } from '../utils/createHtmlFilter' describe('utils test.', () => { test('createHtmlFilter > htmlFilter.', async () => { expect(htmlFilter('index.html')).toBe(true) expect(htmlFilter('index.html.html')).toBe(true) expect(htmlFilter('/index.html')).toBe(true) expect(htmlFilter('/index.html/index.html')).toBe(true) expect(htmlFilter('users/index.html')).toBe(true) expect(htmlFilter('index.htm')).toBe(false) expect(htmlFilter('index.css')).toBe(false) expect(htmlFilter('index.js')).toBe(false) expect(htmlFilter('index.ts')).toBe(false) expect(htmlFilter('./index.html')).toBe(false) }) }) ================================================ FILE: packages/core/src/htmlPlugin.ts ================================================ import type { ResolvedConfig, PluginOption } from 'vite' import type { InjectOptions, PageOption, Pages, UserOptions } from './typing' import { render } from 'ejs' import { isDirEmpty, loadEnv } from './utils' import { normalizePath } from 'vite' import { parse } from 'node-html-parser' import fs from 'fs-extra' import path from 'pathe' import fg from 'fast-glob' import consola from 'consola' import { dim } from 'colorette' import history from 'connect-history-api-fallback' import * as vite from 'vite' const DEFAULT_TEMPLATE = 'index.html' const ignoreDirs = ['.', '', '/'] const bodyInjectRE = /<\/body>/ function getViteMajorVersion() { return vite?.version ? Number(vite.version.split('.')[0]) : 2 } export function createPlugin(userOptions: UserOptions = {}): PluginOption { const { entry, template = DEFAULT_TEMPLATE, pages = [], verbose = false, } = userOptions let viteConfig: ResolvedConfig let env: Record = {} const transformIndexHtmlHandler = async (html, ctx) => { const url = ctx.filename const base = viteConfig.base const excludeBaseUrl = url.replace(base, '/') const htmlName = path.relative(process.cwd(), excludeBaseUrl) const page = getPage(userOptions, htmlName, viteConfig) const { injectOptions = {} } = page const _html = await renderHtml(html, { injectOptions, viteConfig, env, entry: page.entry || entry, verbose, }) const { tags = [] } = injectOptions return { html: _html, tags: tags, } } return { name: 'vite:html', enforce: 'pre', configResolved(resolvedConfig) { viteConfig = resolvedConfig env = loadEnv(viteConfig.mode, viteConfig.root, '') }, config(conf) { const input = createInput(userOptions, conf as unknown as ResolvedConfig) if (input) { return { build: { rollupOptions: { input, }, }, } } }, configureServer(server) { let _pages: { filename: string; template: string }[] = [] const rewrites: { from: RegExp; to: any }[] = [] if (!isMpa(viteConfig)) { const template = userOptions.template || DEFAULT_TEMPLATE const filename = DEFAULT_TEMPLATE _pages.push({ filename, template, }) } else { _pages = pages.map((page) => { return { filename: page.filename || DEFAULT_TEMPLATE, template: page.template || DEFAULT_TEMPLATE, } }) } const proxy = viteConfig.server?.proxy ?? {} const baseUrl = viteConfig.base ?? '/' const keys = Object.keys(proxy) let indexPage: any = null for (const page of _pages) { if (page.filename !== 'index.html') { rewrites.push(createRewire(page.template, page, baseUrl, keys)) } else { indexPage = page } } // ensure order if (indexPage) { rewrites.push(createRewire('', indexPage, baseUrl, keys)) } server.middlewares.use( history({ disableDotRule: undefined, htmlAcceptHeaders: ['text/html', 'application/xhtml+xml'], rewrites: rewrites, }), ) }, transformIndexHtml: getViteMajorVersion() >= 5 ? { // @ts-ignore order: 'pre', handler: transformIndexHtmlHandler, } : { enforce: 'pre', transform: transformIndexHtmlHandler, }, async closeBundle() { const outputDirs: string[] = [] if (isMpa(viteConfig) || pages.length) { for (const page of pages) { const dir = path.dirname(page.template) if (!ignoreDirs.includes(dir)) { outputDirs.push(dir) } } } else { const dir = path.dirname(template) if (!ignoreDirs.includes(dir)) { outputDirs.push(dir) } } const cwd = path.resolve(viteConfig.root, viteConfig.build.outDir) const htmlFiles = await fg( outputDirs.map((dir) => `${dir}/*.html`), { cwd: path.resolve(cwd), absolute: true }, ) await Promise.all( htmlFiles.map((file) => fs.move(file, path.resolve(cwd, path.basename(file)), { overwrite: true, }), ), ) const htmlDirs = await fg( outputDirs.map((dir) => dir), { cwd: path.resolve(cwd), onlyDirectories: true, absolute: true }, ) await Promise.all( htmlDirs.map(async (item) => { const isEmpty = await isDirEmpty(item) if (isEmpty) { return fs.remove(item) } }), ) }, } } export function createInput( { pages = [], template = DEFAULT_TEMPLATE }: UserOptions, viteConfig: ResolvedConfig, ) { const input: Record = {} if (isMpa(viteConfig) || pages?.length) { const templates = pages.map((page) => page.template) templates.forEach((temp) => { let dirName = path.dirname(temp) const file = path.basename(temp) dirName = dirName.replace(/\s+/g, '').replace(/\//g, '-') const key = dirName === '.' || dirName === 'public' || !dirName ? file.replace(/\.html/, '') : dirName input[key] = path.resolve(viteConfig.root, temp) }) return input } else { const dir = path.dirname(template) if (ignoreDirs.includes(dir)) { return undefined } else { const file = path.basename(template) const key = file.replace(/\.html/, '') return { [key]: path.resolve(viteConfig.root, template), } } } } export async function renderHtml( html: string, config: { injectOptions: InjectOptions viteConfig: ResolvedConfig env: Record entry?: string verbose?: boolean }, ) { const { injectOptions, viteConfig, env, entry, verbose } = config const { data, ejsOptions } = injectOptions const ejsData: Record = { ...(viteConfig?.env ?? {}), ...(viteConfig?.define ?? {}), ...(env || {}), ...data, } let result = await render(html, ejsData, ejsOptions) if (entry) { result = removeEntryScript(result, verbose) result = result.replace( bodyInjectRE, `\n`, ) } return result } export function getPage( { pages = [], entry, template = DEFAULT_TEMPLATE, inject = {} }: UserOptions, name: string, viteConfig: ResolvedConfig, ) { let page: PageOption if (isMpa(viteConfig) || pages?.length) { page = getPageConfig(name, pages, DEFAULT_TEMPLATE) } else { page = createSpaPage(entry, template, inject) } return page } function isMpa(viteConfig: ResolvedConfig) { const input = viteConfig?.build?.rollupOptions?.input ?? undefined return typeof input !== 'string' && Object.keys(input || {}).length > 1 } export function removeEntryScript(html: string, verbose = false) { if (!html) { return html } const root = parse(html) const scriptNodes = root.querySelectorAll('script[type=module]') || [] const removedNode: string[] = [] scriptNodes.forEach((item) => { removedNode.push(item.toString()) item.parentNode.removeChild(item) }) verbose && removedNode.length && consola.warn(`vite-plugin-html: Since you have already configured entry, ${dim( removedNode.toString(), )} is deleted. You may also delete it from the index.html. `) return root.toString() } export function createSpaPage( entry: string | undefined, template: string, inject: InjectOptions = {}, ): PageOption { return { entry, filename: 'index.html', template: template, injectOptions: inject, } } export function getPageConfig( htmlName: string, pages: Pages, defaultPage: string, ): PageOption { const defaultPageOption: PageOption = { filename: defaultPage, template: `./${defaultPage}`, } const page = pages.filter((page) => { return path.resolve('/' + page.template) === path.resolve('/' + htmlName) })?.[0] return page ?? defaultPageOption ?? undefined } export function getHtmlInPages(page: PageOption, root: string) { const htmlPath = getHtmlPath(page, root) return readHtml(htmlPath) } export function getHtmlPath(page: PageOption, root: string) { const { template } = page const templatePath = template.startsWith('.') ? template : `./${template}` return path.resolve(root, templatePath) } export async function readHtml(path: string) { if (!fs.pathExistsSync(path)) { throw new Error(`html is not exist in ${path}`) } return await fs.readFile(path).then((buffer) => buffer.toString()) } function createRewire( reg: string, page: any, baseUrl: string, proxyUrlKeys: string[], ) { return { from: new RegExp(`^/${reg}*`), to({ parsedUrl }: any) { const pathname: string = parsedUrl.path const excludeBaseUrl = pathname.replace(baseUrl, '/') const template = path.resolve(baseUrl, page.template) if (excludeBaseUrl.startsWith("/static")) { return excludeBaseUrl; } if (excludeBaseUrl === '/') { return template } const isApiUrl = proxyUrlKeys.some((item) => pathname.startsWith(path.resolve(baseUrl, item)), ) return isApiUrl ? parsedUrl.path : template }, } } ================================================ FILE: packages/core/src/index.ts ================================================ import type { PluginOption } from 'vite' import type { UserOptions } from './typing' import { createPlugin } from './htmlPlugin' import { createMinifyHtmlPlugin } from './minifyHtml' import consola from 'consola' consola.wrapConsole() export function createHtmlPlugin( userOptions: UserOptions = {}, ): PluginOption[] { return [createPlugin(userOptions), createMinifyHtmlPlugin(userOptions)] } ================================================ FILE: packages/core/src/minifyHtml.ts ================================================ import type { PluginOption } from 'vite' import type { UserOptions } from './typing' import type { Options as MinifyOptions } from 'html-minifier-terser' import { minify as minifyFn } from 'html-minifier-terser' import { htmlFilter } from './utils/createHtmlFilter' function getOptions(minify: boolean): MinifyOptions { return { collapseWhitespace: minify, keepClosingSlash: minify, removeComments: minify, removeRedundantAttributes: minify, removeScriptTypeAttributes: minify, removeStyleLinkTypeAttributes: minify, useShortDoctype: minify, minifyCSS: minify, } } export async function minifyHtml( html: string, minify: boolean | MinifyOptions, ) { if (typeof minify === 'boolean' && !minify) { return html } let minifyOptions: boolean | MinifyOptions = minify if (typeof minify === 'boolean' && minify) { minifyOptions = getOptions(minify) } return await minifyFn(html, minifyOptions as MinifyOptions) } export function createMinifyHtmlPlugin({ minify = true, }: UserOptions = {}): PluginOption { return { name: 'vite:minify-html', // apply: 'build', enforce: 'post', async generateBundle(_, outBundle) { if (minify) { for (const bundle of Object.values(outBundle)) { if ( bundle.type === 'asset' && htmlFilter(bundle.fileName) && typeof bundle.source === 'string' ) { bundle.source = await minifyHtml(bundle.source, minify) } } } }, } } ================================================ FILE: packages/core/src/typing.ts ================================================ import type { Options as EJSOptions } from 'ejs' import type { Options as MinifyOptions } from 'html-minifier-terser' import type { HtmlTagDescriptor } from 'vite' export type Entry = string | Record export interface InjectOptions { /** * @description Data injected into the html template */ data?: Record tags?: HtmlTagDescriptor[] /** * @description ejs options configuration */ ejsOptions?: EJSOptions } export interface PageOption { filename: string template: string entry?: string injectOptions?: InjectOptions } export type Pages = PageOption[] export interface UserOptions { /** * @description Page options */ pages?: Pages /** * @description Minimize options */ minify?: MinifyOptions | boolean /** * page entry */ entry?: string /** * template path */ template?: string /** * @description inject options */ inject?: InjectOptions /** * output warning log * @default false */ verbose?: boolean } ================================================ FILE: packages/core/src/utils/createHtmlFilter.ts ================================================ import { createFilter } from '@rollup/pluginutils' export const htmlFilter = createFilter(['**/*.html']) ================================================ FILE: packages/core/src/utils/index.ts ================================================ import { expand } from 'dotenv-expand' import dotenv from 'dotenv' import { join, dirname } from 'pathe' import fse from 'fs-extra' export function loadEnv( mode: string, envDir: string, prefix = '', ): Record { if (mode === 'local') { throw new Error( `"local" cannot be used as a mode name because it conflicts with ` + `the .local postfix for .env files.`, ) } const env: Record = {} const envFiles = [ /** mode local file */ `.env.${mode}.local`, /** mode file */ `.env.${mode}`, /** local file */ `.env.local`, /** default file */ `.env`, ] for (const file of envFiles) { const path = lookupFile(envDir, [file], true) if (path) { const parsed = dotenv.parse(fse.readFileSync(path)) // let environment variables use each other expand({ parsed, // prevent process.env mutation ignoreProcessEnv: true, }) // only keys that start with prefix are exposed to client for (const [key, value] of Object.entries(parsed)) { if (key.startsWith(prefix) && env[key] === undefined) { env[key] = value } else if (key === 'NODE_ENV') { // NODE_ENV override in .env file process.env.VITE_USER_NODE_ENV = value } } } } return env } export function lookupFile( dir: string, formats: string[], pathOnly = false, ): string | undefined { for (const format of formats) { const fullPath = join(dir, format) if (fse.pathExistsSync(fullPath) && fse.statSync(fullPath).isFile()) { return pathOnly ? fullPath : fse.readFileSync(fullPath, 'utf-8') } } const parentDir = dirname(dir) if (parentDir !== dir) { return lookupFile(parentDir, formats, pathOnly) } } export async function isDirEmpty(dir: string) { return fse.readdir(dir).then((files) => { return files.length === 0 }) } ================================================ FILE: packages/playground/basic/basic.html ================================================ test-<%- title %>
<%- ENV %>
<%- NODE_ENV %>
basic-html <%- injectScript %> ================================================ FILE: packages/playground/basic/index.html ================================================ test-<%- title %>
<%- ENV %>
<%- NODE_ENV %>
<%- injectScript %> ================================================ FILE: packages/playground/basic/package.json ================================================ { "name": "playground-basic", "version": "0.0.1", "scripts": { "dev": "vite", "build": "vite build" }, "dependencies": { "axios": "^0.26.1", "vite-plugin-html": "workspace:*", "vue": "^3.2.31", "vue-router": "^4.0.14" }, "devDependencies": { "@vitejs/plugin-vue": "^2.2.4", "@vue/compiler-sfc": "^3.2.31", "vite": "^2.8.6" } } ================================================ FILE: packages/playground/basic/public/inject.js ================================================ console.log('inject successfully!') ================================================ FILE: packages/playground/basic/server.js ================================================ const http = require('http') const { URL } = require('url') const port = 8080 const server = http.createServer((req, res) => { const { pathname } = new URL(req.url) // api开头的是API请求 if (pathname.startsWith('/api')) { // 再判断路由 if (pathname === '/api/users') { // 获取HTTP动词 const method = req.method if (method === 'GET') { // 写一个假数据 const resData = [ { id: 1, name: '小明', age: 18, }, { id: 2, name: '小红', age: 19, }, ] res.setHeader('Content-Type', 'application/json') res.end(JSON.stringify(resData)) return } } } res.statusCode = 200 res.setHeader('Content-Type', 'text/plain') res.end('Hello World') }) server.listen(port, () => { console.log(`Server is running on http://127.0.0.1:${port}/`) }) ================================================ FILE: packages/playground/basic/src/App.vue ================================================ ================================================ FILE: packages/playground/basic/src/main.ts ================================================ import { createApp } from 'vue' import App from './App.vue' import { createRouter, createWebHashHistory } from 'vue-router' // import { createRouter, createWebHistory } from 'vue-router' import axios from 'axios' const router = createRouter({ history: createWebHashHistory(), routes: [ { path: '/test', component: () => import('./test.vue'), }, ], }) createApp(App).use(router).mount('#app') axios.get('/api/users').then((res) => { console.log(res) }) ================================================ FILE: packages/playground/basic/src/test.vue ================================================ ================================================ FILE: packages/playground/basic/vite.config.ts ================================================ import { defineConfig } from 'vite' import vue from '@vitejs/plugin-vue' import { createHtmlPlugin } from 'vite-plugin-html' export default defineConfig({ // base: '/aaa/', server: { proxy: { '/api': 'http://localhost:8080', }, }, plugins: [ vue(), createHtmlPlugin({ minify: true, inject: { data: { title: 'index', injectScript: ``, }, tags: [ { tag: 'div', attrs: { id: 'ddd' }, injectTo: 'body-prepend', }, ], }, }), ], }) ================================================ FILE: packages/playground/custom-entry/package.json ================================================ { "name": "playground-entry", "version": "0.0.1", "scripts": { "dev": "vite", "build": "vite build" }, "dependencies": { "vite-plugin-html": "workspace:*", "vue": "^3.2.31", "vue-router": "^4.0.14" }, "devDependencies": { "@vitejs/plugin-vue": "^2.2.4", "@vue/compiler-sfc": "^3.2.31", "vite": "^2.8.6" } } ================================================ FILE: packages/playground/custom-entry/public/inject.js ================================================ console.log('inject successfully!') ================================================ FILE: packages/playground/custom-entry/src/App.vue ================================================ ================================================ FILE: packages/playground/custom-entry/src/main.ts ================================================ import { createApp } from 'vue' import App from './App.vue' import { createRouter, createWebHashHistory } from 'vue-router' // import { createRouter, createWebHistory } from 'vue-router' const router = createRouter({ history: createWebHashHistory(), routes: [ { path: '/test', component: () => import('./test.vue'), }, ], }) createApp(App).use(router).mount('#app') ================================================ FILE: packages/playground/custom-entry/src/test.vue ================================================ ================================================ FILE: packages/playground/custom-entry/static/index.html ================================================ test-<%- title %>
<%- ENV %>
<%- NODE_ENV %>
<%- injectScript %> ================================================ FILE: packages/playground/custom-entry/vite.config.ts ================================================ import { defineConfig } from 'vite' import vue from '@vitejs/plugin-vue' import { createHtmlPlugin } from 'vite-plugin-html' export default defineConfig({ server: { proxy: { '/api': 'http://localhost:8080', }, }, plugins: [ vue(), createHtmlPlugin({ minify: true, entry: 'src/main.ts', /** * @default index.html */ template: 'static/index.html', inject: { data: { title: 'index', injectScript: ``, }, }, }), ], }) ================================================ FILE: packages/playground/mpa/index.html ================================================ test-<%- title %>
index app
<%- ENV %>
<%- NODE_ENV %>
================================================ FILE: packages/playground/mpa/other.html ================================================ <%- title %>
<%- ENV %>
<%- NODE_ENV %>
other page
================================================ FILE: packages/playground/mpa/package.json ================================================ { "name": "playground-map", "version": "0.0.1", "private": true, "scripts": { "dev": "vite", "build": "vite build" }, "dependencies": { "vite-plugin-html": "workspace:*", "vue": "^3.2.31", "vue-router": "^4.0.14" }, "devDependencies": { "@vitejs/plugin-vue": "^2.2.4", "@vue/compiler-sfc": "^3.2.31", "vite": "^2.8.6" } } ================================================ FILE: packages/playground/mpa/public/inject.js ================================================ console.log('inject successfully!') ================================================ FILE: packages/playground/mpa/src/App.vue ================================================ ================================================ FILE: packages/playground/mpa/src/main.ts ================================================ import { createApp } from 'vue' import App from './App.vue' import { createRouter, createWebHistory } from 'vue-router' // import { createRouter, createWebHashHistory } from 'vue-router' const router = createRouter({ history: createWebHistory(), routes: [ { path: '/test', component: () => import('./test.vue'), }, ], }) createApp(App).use(router).mount('#app') ================================================ FILE: packages/playground/mpa/src/other-app.vue ================================================ ================================================ FILE: packages/playground/mpa/src/other-main.ts ================================================ import { createApp } from 'vue' import App from './other-app.vue' import { createRouter, createWebHashHistory } from 'vue-router' const router = createRouter({ history: createWebHashHistory(), routes: [ { path: '/test', component: () => import('./test.vue'), }, ], }) createApp(App).use(router).mount('#app') ================================================ FILE: packages/playground/mpa/src/test.vue ================================================ ================================================ FILE: packages/playground/mpa/vite.config.ts ================================================ import { defineConfig } from 'vite' import vue from '@vitejs/plugin-vue' import { createHtmlPlugin } from 'vite-plugin-html' export default defineConfig({ server: { proxy: { '/api': 'http://localhost:8080', }, }, plugins: [ vue(), createHtmlPlugin({ minify: true, pages: [ { entry: 'src/main.ts', filename: 'index.html', template: 'index.html', injectOptions: { data: { title: 'index', injectScript: ``, }, tags: [ { injectTo: 'body-prepend', tag: 'div', attrs: { id: 'tag1', }, }, ], }, }, { entry: 'src/other-main.ts', filename: 'other.html', template: 'other.html', injectOptions: { data: { title: 'other page', injectScript: ``, }, tags: [ { injectTo: 'body-prepend', tag: 'div', attrs: { id: 'tag2', }, }, ], }, }, ], }), ], }) ================================================ FILE: pnpm-workspace.yaml ================================================ packages: - packages/* - packages/playground/* ================================================ FILE: tsconfig.json ================================================ { "compilerOptions": { "module": "ESNext", "target": "es2017", "moduleResolution": "node", "strict": true, "declaration": true, "noUnusedLocals": true, "esModuleInterop": true, "outDir": "dist", "lib": ["ESNext"], "sourceMap": false, "noEmitOnError": true, "noImplicitAny": false }, "include": ["./packages"], "exclude": ["**/dist", "**/node_modules", "**/test"] } ================================================ FILE: vitest.config.ts ================================================ /// import { defineConfig } from 'vite' export default defineConfig({})