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