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