├── .github └── workflows │ ├── codeql-analysis.yml │ ├── npm-publish.yml │ ├── publish-test.yml │ └── test.js.yml ├── .gitignore ├── .npmrc ├── LICENSE ├── README.md ├── README_ZH.md ├── demo ├── demo1 │ ├── demo.vue │ ├── demo1.html │ └── demo1.ts ├── demo2 │ ├── demo.ts │ ├── demo.vue │ ├── demo2.html │ └── route │ │ └── route1.vue ├── demoIndex1.html ├── demoIndex2.html ├── partials │ └── test.ejs └── template.html ├── demo3.html ├── demo4.html ├── package.json ├── pnpm-lock.yaml ├── src ├── history-api │ ├── historyApiFallbackPlugin.ts │ └── types.ts ├── html │ ├── Base.ts │ ├── Build.ts │ ├── Serve.ts │ ├── VirtualHtmlPlugin.ts │ └── types.ts └── index.ts ├── test ├── __snapshots__ │ ├── pages.test.ts.snap │ └── template.test.ts.snap ├── demo │ ├── demo1 │ │ ├── demo1.html │ │ └── demo1.ts │ └── template │ │ ├── demo1.html │ │ ├── demo2.html │ │ ├── demo3.html │ │ └── test.ejs ├── pages.test.ts └── template.test.ts ├── tsconfig.json ├── tsup.config.ts ├── vite.config.ts └── vitestSetup.ts /.github/workflows/codeql-analysis.yml: -------------------------------------------------------------------------------- 1 | # For most projects, this workflow file will not need changing; you simply need 2 | # to commit it to your repository. 3 | # 4 | # You may wish to alter this file to override the set of languages analyzed, 5 | # or to provide custom queries or build logic. 6 | # 7 | # ******** NOTE ******** 8 | # We have attempted to detect the languages in your repository. Please check 9 | # the `language` matrix defined below to confirm you have the correct set of 10 | # supported CodeQL languages. 11 | # 12 | name: "CodeQL" 13 | 14 | on: 15 | push: 16 | branches: [ main ] 17 | pull_request: 18 | # The branches below must be a subset of the branches above 19 | branches: [ main ] 20 | schedule: 21 | - cron: '22 3 * * 2' 22 | 23 | jobs: 24 | analyze: 25 | name: Analyze 26 | runs-on: ubuntu-latest 27 | permissions: 28 | actions: read 29 | contents: read 30 | security-events: write 31 | 32 | strategy: 33 | fail-fast: false 34 | matrix: 35 | language: [ 'javascript' ] 36 | # CodeQL supports [ 'cpp', 'csharp', 'go', 'java', 'javascript', 'python', 'ruby' ] 37 | # Learn more about CodeQL language support at https://git.io/codeql-language-support 38 | 39 | steps: 40 | - name: Checkout repository 41 | uses: actions/checkout@v2 42 | 43 | # Initializes the CodeQL tools for scanning. 44 | - name: Initialize CodeQL 45 | uses: github/codeql-action/init@v1 46 | with: 47 | languages: ${{ matrix.language }} 48 | # If you wish to specify custom queries, you can do so here or in a config file. 49 | # By default, queries listed here will override any specified in a config file. 50 | # Prefix the list here with "+" to use these queries and those in the config file. 51 | # queries: ./path/to/local/query, your-org/your-repo/queries@main 52 | 53 | # Autobuild attempts to build any compiled languages (C/C++, C#, or Java). 54 | # If this step fails, then you should remove it and run the build manually (see below) 55 | - name: Autobuild 56 | uses: github/codeql-action/autobuild@v1 57 | 58 | # ℹ️ Command-line programs to run using the OS shell. 59 | # 📚 https://git.io/JvXDl 60 | 61 | # ✏️ If the Autobuild fails above, remove it and uncomment the following three lines 62 | # and modify them (or add more) to build your code if your project 63 | # uses a compiled language 64 | 65 | #- run: | 66 | # make bootstrap 67 | # make release 68 | 69 | - name: Perform CodeQL Analysis 70 | uses: github/codeql-action/analyze@v1 71 | -------------------------------------------------------------------------------- /.github/workflows/npm-publish.yml: -------------------------------------------------------------------------------- 1 | # This workflow will run tests using node and then publish a package to GitHub Packages when a release is created 2 | # For more information see: https://docs.github.com/en/actions/publishing-packages/publishing-nodejs-packages 3 | 4 | name: Node.js Package 5 | 6 | on: 7 | release: 8 | types: [created] 9 | 10 | jobs: 11 | publish-npm: 12 | runs-on: ubuntu-latest 13 | steps: 14 | - uses: actions/checkout@v4 15 | - uses: pnpm/action-setup@v4.0.0 16 | with: 17 | version: 9.12.1 18 | - uses: actions/setup-node@v4.0.4 19 | with: 20 | node-version: 22.9.0 21 | registry-url: https://registry.npmjs.org/ 22 | cache: 'pnpm' 23 | - run: npm install corepack -g 24 | - run: corepack enable 25 | - run: corepack install -g pnpm@9.12.1 26 | - run: pnpm install 27 | - run: npx playwright install 28 | - run: npm publish 29 | env: 30 | NODE_AUTH_TOKEN: ${{secrets.NPM_TOKEN}} 31 | -------------------------------------------------------------------------------- /.github/workflows/publish-test.yml: -------------------------------------------------------------------------------- 1 | # This workflow will run tests using node and then publish a package to GitHub Packages when a release is created 2 | # For more information see: https://docs.github.com/en/actions/publishing-packages/publishing-nodejs-packages 3 | 4 | name: Publish npm package 5 | 6 | on: 7 | workflow_dispatch: 8 | 9 | jobs: 10 | publish-npm: 11 | runs-on: ubuntu-latest 12 | steps: 13 | - uses: actions/checkout@v4 14 | - uses: pnpm/action-setup@v4.0.0 15 | with: 16 | version: 9.12.1 17 | - uses: actions/setup-node@v4.0.4 18 | with: 19 | node-version: 22.9.0 20 | registry-url: https://registry.npmjs.org/ 21 | cache: 'pnpm' 22 | - run: npm install corepack -g 23 | - run: corepack enable 24 | - run: corepack install -g pnpm@9.12.1 25 | - run: pnpm install 26 | - run: npx playwright install 27 | - run: npm publish 28 | env: 29 | NODE_AUTH_TOKEN: ${{secrets.NPM_TOKEN}} 30 | -------------------------------------------------------------------------------- /.github/workflows/test.js.yml: -------------------------------------------------------------------------------- 1 | # This workflow will do a clean installation of node dependencies, cache/restore them, build the source code and run tests across different versions of node 2 | # For more information see: https://docs.github.com/en/actions/automating-builds-and-tests/building-and-testing-nodejs 3 | 4 | name: Test commit 5 | 6 | on: 7 | push: 8 | branches: [ "main" ] 9 | pull_request: 10 | branches: [ "main" ] 11 | 12 | jobs: 13 | build: 14 | 15 | runs-on: ubuntu-latest 16 | 17 | strategy: 18 | matrix: 19 | node-version: [18.18.0] 20 | # See supported Node.js release schedule at https://nodejs.org/en/about/releases/ 21 | 22 | steps: 23 | - uses: actions/checkout@v3 24 | - uses: pnpm/action-setup@646cdf48217256a3d0b80361c5a50727664284f2 25 | with: 26 | version: 7.16.0 27 | - name: Use Node.js ${{ matrix.node-version }} 28 | uses: actions/setup-node@v3 29 | with: 30 | node-version: ${{ matrix.node-version }} 31 | cache: 'pnpm' 32 | - run: pnpm i 33 | - run: npx playwright install 34 | - run: pnpm test 35 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | .DS_Store 3 | dist 4 | dist-ssr 5 | *.local 6 | .idea 7 | vite.config.js 8 | vite.config.js.map 9 | vite.config.d.ts 10 | *.d.ts 11 | *.js 12 | *.map 13 | -------------------------------------------------------------------------------- /.npmrc: -------------------------------------------------------------------------------- 1 | registry=https://registry.npmjs.org 2 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2021 windson1806 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 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # vite-plugin-virtual-html 2 | 3 | [中文文档](./README_ZH.md) 4 | 5 | ## Motivation 6 | 7 | vite's [MPA](https://vitejs.dev/guide/build.html#multi-page-app) unlike `@vue/cli`'s `pages` option have a configuration 8 | in dev mode. 9 | 10 | vite's html file need to place in project's root to have same behavior in dev and production mode, it makes your 11 | project's root dir looks chaotic. 12 | 13 | And if you follow vite's MPA, put other file in other directory, unlike `index.html`, you need useless middle directory( 14 | Ex. from vite's MPA doc `http://localhost:3000/nested/nested.html`) to located it. 15 | 16 | so, i write this plugin to make vite's MPA more configurable and in dev mode or production has same behavior. 17 | 18 | this plugin use vite's `configureServer` Hook to intercept html request and response the html content requested from 19 | browser. 20 | 21 | ## update 22 | 23 | 1. `1.1.9` add `urlTransformer`function to support developer edit url 24 | 1. `1.0.2` add `connect-history-api-fallback` support 25 | 1. `0.4.0` add a new option to `pages`,just need an js file,plugin will auto generate a html file which include the 26 | entry js 27 | 1. `0.3.0` change the behavior of `defaultRender`, if in project, it has `ejs` as dependency, it will return html code 28 | which rendered by `ejs`, otherwise it will return html code directly as old version do 29 | 1. `0.2.9` add a new option `injectCode` to add some code before/after tag in html file 30 | 2. `0.2.8` add a new option `extraGlobPattern` to customize `fast-glob`'s pattern. Default pattern 31 | is `['**/*.html', '!node_modules/**/*.html', '!.**/*.html']`, attention: if your config has problems, such as you 32 | didn't ignore `dist`, when build,it will occur 33 | error: `new Error('[vite]: Rollup failed to resolve import "${id}" from "${importer}".\n'` 34 | 3. `0.2.6` `pages` now correctly identify multi-level directories 35 | 4. `0.2.3` `pages` options now can set to true to allow all html in project. 36 | 5. `0.2.1` now works fine with `@vitejs/plugin-react`. 37 | 6. `0.2.0` has reworked, so config have a little change 38 | 1. plugin does not require your html exists, but you must provide a template file(as html) 39 | 2. `page`'s config renamed to `template` 40 | 3. each `page` can have a independent `render` function 41 | 4. add a global config `data`, its' config will be covered by `page`'s `data` 42 | 5. all you `pages`' will be treat as template file 43 | 44 | ## features 45 | 46 | + allow you put your html file anywhere in your project(like `@vue/cli`'s `pages`) 47 | + when you run in dev,it will intercept html requests,and response with the html content which you set in `pages`. 48 | + when you run build, it will copy files(reading config from `pages` options) under dist's sub-folder to dist 49 | folder, and then delete the rest html file. 50 | + auto config `build.rollupOptions.input` from pages 51 | + if your html do not have a module script import. plugin will try to add a js/ts script import using the html file's 52 | name. 53 | 54 | ## Usage 55 | 56 | `yarn add vite-plugin-virtual-html --dev # npm install vite-plugin-virtual-html -D` 57 | 58 | Add it to `vite.config.js` 59 | 60 | ``` js 61 | // vite.config.js 62 | const virtualHtml = require('vite-plugin-virtual-html') 63 | 64 | const pages = { 65 | index: '/src/index/index.html', 66 | login: '/src/login/login.html', 67 | } 68 | 69 | module.exports = { 70 | plugins: [virtualHtml({ 71 | pages, 72 | indexPage: 'login' 73 | })], 74 | } 75 | ``` 76 | 77 | ## Configuration 78 | 79 | ### pages 80 | 81 | config your project's all html/template file's path 82 | 83 | it will be used for: 84 | 85 | + dev mode, it will intercept your html request, and response with html file in this config 86 | + build mode, inject into `build.rollupOptions.input` 87 | + when you build, plugin will copy all your config pages to project ROOT, and when build finished, the copied HTML file 88 | will auto remove from project ROOT. 89 | + if you want to use template system,you can send a object which contains `template` and `data` to render it. By 90 | default, it will return the html content in your HTML/template file, when you define a render function, it(html 91 | template) will rendered by your custom render function. 92 | 93 | ``` 94 | // all config 95 | { 96 | // 1. directly input html/template path 97 | login1: '/src/index/index.html', 98 | // 2. a object with template 99 | login2: { 100 | template: '/src/login/login.html', // if there is no data prop, the login.html must only contain HTML content 101 | }, 102 | // 3. a object with template and data, maybe with render 103 | login3: { 104 | template: '/src/login1/login1.html', 105 | data: { 106 | users: ['a', 'b', 'c'] 107 | }, 108 | // each page can have independent render function 109 | // render(template, data){ 110 | // return template 111 | // } 112 | }, 113 | // 4. config a js file path to auto-generate a html file 114 | login4: { 115 | entry: '/src/login/login.js', // MUST 116 | title: 'login4',// optional, default: '' 117 | body: '
' // optional, default: '
' 118 | } 119 | } 120 | ``` 121 | 122 | **notice:** 123 | 124 | 1. if your html page contains any template content(such as `<$= users.join(" | "); $>`), you **must** contain `template` 125 | and `data`. 126 | 2. The `pages` options' `key` is the real HTML file after build 127 | 3. The `pages` options' `key` and `value`/ `template` file's name can different. 128 | 4. for example 1, you can access `login1.html` when `dev` mode, and it will generate a `login1.html` when build. 129 | 5. when `pages` set to `true`, the `template.html` will only generate **ONLY ONE** `html` file 130 | 6. when use `entry` config, plugin will auto generate a html file like this project's `demo4.html`, according 131 | your `entry`/`title`/`body` config, it will has a little difference 132 | 133 | ### indexPage 134 | 135 | config the index page 136 | 137 | Ex. when you open `http://localhost:3000`, your project's root dir has no `index.html` file, then browser will 138 | show `404`. 139 | 140 | now, if you set this, plugin will intercept `/` request, and response with page you set. 141 | 142 | Like this: 143 | when you set `indexPage` to `login`,then you access `http://localhost:3000` in browser, it will show the `/login.html` 144 | page. 145 | 146 | it equals to access `http://localhost:3000/login.html`. 147 | 148 | ### render 149 | 150 | from `0.1.0` , you can use `render` function to render html template. 151 | i have just test in `ejs`, but i think other template system will(maybe) work correctly. 152 | 153 | **notice:** 154 | 155 | 1. `0.3.0` or later,if your html code/template use `ejs` template, you **MUST** install `ejs` 156 | 157 | ### extraGlobPattern 158 | 159 | Customize `fast-glob`'s pattern 160 | When set this options, it will replace default `fast-glob` pattern, it's default value 161 | is `['**/*.html', '!node_modules/**/*.html', '!.**/*.html']` 162 | 163 | ### injectCode 164 | 165 | [options](./src/html/types.ts#28) 166 | 167 | In html file, put `replacement`before/after `find` 168 | 169 | ### urlTransformer 170 | 171 | Allow plugin's user to fully customize the `url`, this is a function which has two param:(`resolvedUrl`,`req`) 172 | 173 | First param: `resolvedUrl` is a `string`, it means the plugin handles from `req` param. 174 | 175 | Second param : `req`, is a `http.IncommingMessage` 176 | 177 | This function must return a `string` 178 | 179 | ## NOTICE 180 | 181 | 1. if you use same `template` file for multiple page, please make sure the page's key is different. 182 | 2. please DO NOT use this plugin when you build a library(you can use this in dev NOT in build) 183 | 184 | ## Thanks 185 | 186 | ![JetBrains Logo (Main) logo](https://resources.jetbrains.com/storage/products/company/brand/logos/jb_beam.svg) 187 | -------------------------------------------------------------------------------- /README_ZH.md: -------------------------------------------------------------------------------- 1 | # vite-plugin-virtual-html 2 | 3 | ## 简介 4 | 5 | `Vite`的[多页面应用](https://cn.vitejs.dev/guide/build.html#multi-page-app)需要将`html`文件放到项目根目录或通过从根目录起的路径访问。 6 | 7 | 这会导致在开发和构建过程的`html`文件访问不一致.例如在开发时,你需要访问`/nested/a.html`,但是在构建后,你需要访问的时`/a.html` 8 | 9 | 所以,为了解决这个问题,需要在开发时对`html`请求进行拦截并返回相应的`html`内容 10 | 11 | 这个插件使用了以下的钩子: 12 | 13 | - 开发过程 14 | - `config`: 将下面的3个钩子注入到插件中 15 | - `configureServer`: 拦截并响应`html`请求, 处理`connect-history-api-fallback`的请求 16 | - `load`: 加载并处理`html`代码 17 | - `transform`: 在`html`文件中注入一些可配置的代码等 18 | - 构建过程 19 | - `config`: 将下面的3个钩子注入到插件中,并将`html`文件复制到项目根目录 20 | - `load`: 加载并处理`html`代码 21 | - `transform`: 在`html`文件中注入一些可配置的代码等 22 | - `closeBundle`: 移除在`config`中复制的`html`文件 23 | 24 | ## 更新信息 25 | 26 | 27 | ## 功能 28 | + 允许你将`html`文件放置到项目的任意位置(与`@vue/cli`的`pages`配置相同) 29 | + 当`dev`开发时,拦截`html`请求,然后将配置的相应的`html`文件内容返回给浏览器 30 | + 当`build`项目时,将配置的`html`文件复制到`dist`目录下,同时删除其他`html`文件及其目录 31 | + 自动配置`build.rollupOptions.input` 32 | + 如果你的`html`文件没有配置入口文件,则将会在`html`文件附近寻找与`html`文件同名的`js/ts`文件,并将其添加到`html`的文件内容中 33 | 34 | ## 使用方法 35 | 36 | `pnpm install vite-plugin-virtual-html -D` 37 | 38 | 在`vite.config.ts`中配置插件 39 | 40 | ``` typescript 41 | // vite.config.ts 42 | const virtualHtml from 'vite-plugin-virtual-html' 43 | 44 | const pages = { 45 | index: '/src/index/index.html', 46 | login: '/src/login/login.html', 47 | } 48 | 49 | module.exports = { 50 | plugins: [ 51 | virtualHtml({ 52 | pages, 53 | }) 54 | ], 55 | } 56 | ``` 57 | 58 | **插件使用事项:** 59 | 60 | - 一定不要再编译库时使用这个插件!!! 61 | 62 | ## 配置 63 | 64 | ### pages 65 | 66 | 配置项目的html文件路径 67 | 68 | `pages`可配置为[Pages](./src/html/types.ts#33)对象或`true` 69 | 70 | 当配置为`true`时,会根据`extraGlobPattern`的配置自动读取项目中的`html`文件路径并生成`pages`对象 71 | 72 | **注意:** 73 | 74 | - `entry`与`template`目前不能同时使用 75 | 76 | 77 | ### extraGlobPattern 78 | 79 | 仅`pages`为`true`时可用,默认值为 80 | ``` 81 | '**/*.html', 82 | '!node_modules/**/*.html', 83 | '!.**/*.html' 84 | ``` 85 | 86 | ### indexPage 87 | 88 | 指定当访问`index`或`/`页面时应当返回`pages`中的哪一个`html`文件的内容 89 | 90 | ### render, data 91 | 92 | 自定义全局模版渲染函数及渲染时使用的数据 93 | 94 | **注意:** 95 | 96 | - 目前我只测试了`ejs`,其他模版系统应该也可以正常工作 97 | 98 | ### injectCode 99 | 100 | 将配置的`replacement`放到`find`前面或后面 101 | 102 | ### rewrites 103 | 104 | 处理`connect-history-api-fallback`的重写请求 105 | 106 | ### urlTransformer 107 | 108 | 完全由开发者自定义处理`dev-server`拦截到的url,传入的参数为(`resolvedUrl`,`req`) 109 | 110 | 其中,第一个参数是插件初步处理的`url`字符串, 第二个参数是一个`req`对象(`http.IncomingMessage`) 111 | 112 | 返回值为一个新的`url`字符串 113 | -------------------------------------------------------------------------------- /demo/demo1/demo.vue: -------------------------------------------------------------------------------- 1 | 4 | 5 | 13 | -------------------------------------------------------------------------------- /demo/demo1/demo1.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Demo 6 | 7 | 8 | 9 |
10 |
<%= users.join(" | "); %>
11 | <%= include('/demo/partials/test') %> 12 | 13 | 14 | -------------------------------------------------------------------------------- /demo/demo1/demo1.ts: -------------------------------------------------------------------------------- 1 | import {createApp} from 'vue' 2 | import demo from './demo.vue' 3 | 4 | createApp(demo).mount('#app') 5 | -------------------------------------------------------------------------------- /demo/demo2/demo.ts: -------------------------------------------------------------------------------- 1 | import {createApp} from 'vue' 2 | import {createRouter,createWebHashHistory} from 'vue-router' 3 | import demo from './demo.vue' 4 | 5 | const router = createRouter({ 6 | history: createWebHashHistory(), 7 | routes: [ 8 | { 9 | path: '/', 10 | component: demo 11 | }, 12 | { 13 | path: '/route1', 14 | component: ()=>import('./route/route1.vue') 15 | } 16 | ] 17 | }) 18 | const app = createApp({}) 19 | app.use(router) 20 | app.mount('#app') 21 | -------------------------------------------------------------------------------- /demo/demo2/demo.vue: -------------------------------------------------------------------------------- 1 | 4 | 5 | 13 | -------------------------------------------------------------------------------- /demo/demo2/demo2.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Demo 6 | 7 | 8 | 9 |
10 |

Hello App!

11 |

12 | 13 | 14 | 15 | Go to Home 16 | Go to About 17 |

18 | 19 | 20 | 21 |
22 | 23 | 24 | -------------------------------------------------------------------------------- /demo/demo2/route/route1.vue: -------------------------------------------------------------------------------- 1 | 4 | 5 | 15 | 16 | -------------------------------------------------------------------------------- /demo/demoIndex1.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | Demo 7 | 8 | 9 | 10 | 11 |

demoIndex1

12 |
13 | 14 | 15 | -------------------------------------------------------------------------------- /demo/demoIndex2.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | Demo 7 | 8 | 9 | 10 | 11 |

demoIndex2

12 |
13 | 14 | 15 | -------------------------------------------------------------------------------- /demo/partials/test.ejs: -------------------------------------------------------------------------------- 1 |

I am a test partial

-------------------------------------------------------------------------------- /demo/template.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Title 6 | <%- script; %> 7 | 8 | 9 |

fdf

10 | 11 | 12 | -------------------------------------------------------------------------------- /demo3.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Demo 6 | 7 | 8 | 9 |
10 |

Hello App!

11 |

12 | 13 | 14 | 15 | Go to Home 16 | Go to About 17 |

18 | 19 | 20 | 21 |
22 | 23 | 24 | -------------------------------------------------------------------------------- /demo4.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | login4 6 | 7 | 8 | 9 |
10 | 11 | 12 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "vite-plugin-virtual-html", 3 | "version": "1.2.6", 4 | "description": "Vite plugin to load html anywhere", 5 | "type": "module", 6 | "main": "./dist/index.cjs", 7 | "module": "./dist/index.js", 8 | "types": "./dist/index.d.ts", 9 | "exports": { 10 | ".": { 11 | "import": "./dist/index.js" 12 | } 13 | }, 14 | "scripts": { 15 | "build": "tsup && npm publish && git clean -f", 16 | "test:build": "vite build", 17 | "test:dev": "vite", 18 | "test": "vitest" 19 | }, 20 | "files": [ 21 | "dist" 22 | ], 23 | "keywords": [ 24 | "vite-plugin", 25 | "virtual-html" 26 | ], 27 | "author": "windson", 28 | "license": "MIT", 29 | "repository": { 30 | "type": "git", 31 | "url": "git+https://github.com/Windson1806/vite-plugin-virtual-html.git" 32 | }, 33 | "bugs": { 34 | "url": "https://github.com/Windson1806/vite-plugin-virtual-html/issues" 35 | }, 36 | "homepage": "https://github.com/Windson1806/vite-plugin-virtual-html/", 37 | "devDependencies": { 38 | "@types/connect-history-api-fallback": "^1.5.4", 39 | "@types/debug": "^4.1.12", 40 | "@types/ejs": "^3.1.5", 41 | "@types/node": "^22.12.0", 42 | "@vitejs/plugin-vue": "^5.2.1", 43 | "@vue/compiler-sfc": "^3.5.13", 44 | "playwright-chromium": "^1.50.0", 45 | "tsup": "^8.3.6", 46 | "typescript": "^5.7.3", 47 | "vite-plugin-inspect": "^10.1.0", 48 | "vitest": "^3.0.4", 49 | "vue": "^3.5.13", 50 | "vue-router": "^4.5.0" 51 | }, 52 | "dependencies": { 53 | "connect-history-api-fallback": "^2.0.0", 54 | "debug": "^4.3.7", 55 | "ejs": "^3.1.10", 56 | "fast-glob": "^3.3.3", 57 | "magic-string": "^0.30.11", 58 | "vite": "^6.0.11" 59 | } 60 | } 61 | -------------------------------------------------------------------------------- /pnpm-lock.yaml: -------------------------------------------------------------------------------- 1 | lockfileVersion: '9.0' 2 | 3 | settings: 4 | autoInstallPeers: true 5 | excludeLinksFromLockfile: false 6 | 7 | importers: 8 | 9 | .: 10 | dependencies: 11 | connect-history-api-fallback: 12 | specifier: ^2.0.0 13 | version: 2.0.0 14 | debug: 15 | specifier: ^4.3.7 16 | version: 4.4.0 17 | ejs: 18 | specifier: ^3.1.10 19 | version: 3.1.10 20 | fast-glob: 21 | specifier: ^3.3.3 22 | version: 3.3.3 23 | magic-string: 24 | specifier: ^0.30.11 25 | version: 0.30.17 26 | vite: 27 | specifier: ^6.0.11 28 | version: 6.0.11(@types/node@22.12.0) 29 | devDependencies: 30 | '@types/connect-history-api-fallback': 31 | specifier: ^1.5.4 32 | version: 1.5.4 33 | '@types/debug': 34 | specifier: ^4.1.12 35 | version: 4.1.12 36 | '@types/ejs': 37 | specifier: ^3.1.5 38 | version: 3.1.5 39 | '@types/node': 40 | specifier: ^22.12.0 41 | version: 22.12.0 42 | '@vitejs/plugin-vue': 43 | specifier: ^5.2.1 44 | version: 5.2.1(vite@6.0.11(@types/node@22.12.0))(vue@3.5.13(typescript@5.7.3)) 45 | '@vue/compiler-sfc': 46 | specifier: ^3.5.13 47 | version: 3.5.13 48 | playwright-chromium: 49 | specifier: ^1.50.0 50 | version: 1.50.0 51 | tsup: 52 | specifier: ^8.3.6 53 | version: 8.3.6(postcss@8.4.49)(typescript@5.7.3) 54 | typescript: 55 | specifier: ^5.7.3 56 | version: 5.7.3 57 | vite-plugin-inspect: 58 | specifier: ^10.1.0 59 | version: 10.1.0(vite@6.0.11(@types/node@22.12.0)) 60 | vitest: 61 | specifier: ^3.0.4 62 | version: 3.0.4(@types/debug@4.1.12)(@types/node@22.12.0) 63 | vue: 64 | specifier: ^3.5.13 65 | version: 3.5.13(typescript@5.7.3) 66 | vue-router: 67 | specifier: ^4.5.0 68 | version: 4.5.0(vue@3.5.13(typescript@5.7.3)) 69 | 70 | packages: 71 | 72 | '@babel/helper-string-parser@7.25.9': 73 | resolution: {integrity: sha512-4A/SCr/2KLd5jrtOMFzaKjVtAei3+2r/NChoBNoZ3EyP/+GlhoaEGoWOZUmFmoITP7zOJyHIMm+DYRd8o3PvHA==} 74 | engines: {node: '>=6.9.0'} 75 | 76 | '@babel/helper-validator-identifier@7.25.9': 77 | resolution: {integrity: sha512-Ed61U6XJc3CVRfkERJWDz4dJwKe7iLmmJsbOGu9wSloNSFttHV0I8g6UAgb7qnK5ly5bGLPd4oXZlxCdANBOWQ==} 78 | engines: {node: '>=6.9.0'} 79 | 80 | '@babel/parser@7.26.5': 81 | resolution: {integrity: sha512-SRJ4jYmXRqV1/Xc+TIVG84WjHBXKlxO9sHQnA2Pf12QQEAp1LOh6kDzNHXcUnbH1QI0FDoPPVOt+vyUDucxpaw==} 82 | engines: {node: '>=6.0.0'} 83 | hasBin: true 84 | 85 | '@babel/types@7.26.5': 86 | resolution: {integrity: sha512-L6mZmwFDK6Cjh1nRCLXpa6no13ZIioJDz7mdkzHv399pThrTa/k0nUlNaenOeh2kWu/iaOQYElEpKPUswUa9Vg==} 87 | engines: {node: '>=6.9.0'} 88 | 89 | '@esbuild/aix-ppc64@0.24.2': 90 | resolution: {integrity: sha512-thpVCb/rhxE/BnMLQ7GReQLLN8q9qbHmI55F4489/ByVg2aQaQ6kbcLb6FHkocZzQhxc4gx0sCk0tJkKBFzDhA==} 91 | engines: {node: '>=18'} 92 | cpu: [ppc64] 93 | os: [aix] 94 | 95 | '@esbuild/android-arm64@0.24.2': 96 | resolution: {integrity: sha512-cNLgeqCqV8WxfcTIOeL4OAtSmL8JjcN6m09XIgro1Wi7cF4t/THaWEa7eL5CMoMBdjoHOTh/vwTO/o2TRXIyzg==} 97 | engines: {node: '>=18'} 98 | cpu: [arm64] 99 | os: [android] 100 | 101 | '@esbuild/android-arm@0.24.2': 102 | resolution: {integrity: sha512-tmwl4hJkCfNHwFB3nBa8z1Uy3ypZpxqxfTQOcHX+xRByyYgunVbZ9MzUUfb0RxaHIMnbHagwAxuTL+tnNM+1/Q==} 103 | engines: {node: '>=18'} 104 | cpu: [arm] 105 | os: [android] 106 | 107 | '@esbuild/android-x64@0.24.2': 108 | resolution: {integrity: sha512-B6Q0YQDqMx9D7rvIcsXfmJfvUYLoP722bgfBlO5cGvNVb5V/+Y7nhBE3mHV9OpxBf4eAS2S68KZztiPaWq4XYw==} 109 | engines: {node: '>=18'} 110 | cpu: [x64] 111 | os: [android] 112 | 113 | '@esbuild/darwin-arm64@0.24.2': 114 | resolution: {integrity: sha512-kj3AnYWc+CekmZnS5IPu9D+HWtUI49hbnyqk0FLEJDbzCIQt7hg7ucF1SQAilhtYpIujfaHr6O0UHlzzSPdOeA==} 115 | engines: {node: '>=18'} 116 | cpu: [arm64] 117 | os: [darwin] 118 | 119 | '@esbuild/darwin-x64@0.24.2': 120 | resolution: {integrity: sha512-WeSrmwwHaPkNR5H3yYfowhZcbriGqooyu3zI/3GGpF8AyUdsrrP0X6KumITGA9WOyiJavnGZUwPGvxvwfWPHIA==} 121 | engines: {node: '>=18'} 122 | cpu: [x64] 123 | os: [darwin] 124 | 125 | '@esbuild/freebsd-arm64@0.24.2': 126 | resolution: {integrity: sha512-UN8HXjtJ0k/Mj6a9+5u6+2eZ2ERD7Edt1Q9IZiB5UZAIdPnVKDoG7mdTVGhHJIeEml60JteamR3qhsr1r8gXvg==} 127 | engines: {node: '>=18'} 128 | cpu: [arm64] 129 | os: [freebsd] 130 | 131 | '@esbuild/freebsd-x64@0.24.2': 132 | resolution: {integrity: sha512-TvW7wE/89PYW+IevEJXZ5sF6gJRDY/14hyIGFXdIucxCsbRmLUcjseQu1SyTko+2idmCw94TgyaEZi9HUSOe3Q==} 133 | engines: {node: '>=18'} 134 | cpu: [x64] 135 | os: [freebsd] 136 | 137 | '@esbuild/linux-arm64@0.24.2': 138 | resolution: {integrity: sha512-7HnAD6074BW43YvvUmE/35Id9/NB7BeX5EoNkK9obndmZBUk8xmJJeU7DwmUeN7tkysslb2eSl6CTrYz6oEMQg==} 139 | engines: {node: '>=18'} 140 | cpu: [arm64] 141 | os: [linux] 142 | 143 | '@esbuild/linux-arm@0.24.2': 144 | resolution: {integrity: sha512-n0WRM/gWIdU29J57hJyUdIsk0WarGd6To0s+Y+LwvlC55wt+GT/OgkwoXCXvIue1i1sSNWblHEig00GBWiJgfA==} 145 | engines: {node: '>=18'} 146 | cpu: [arm] 147 | os: [linux] 148 | 149 | '@esbuild/linux-ia32@0.24.2': 150 | resolution: {integrity: sha512-sfv0tGPQhcZOgTKO3oBE9xpHuUqguHvSo4jl+wjnKwFpapx+vUDcawbwPNuBIAYdRAvIDBfZVvXprIj3HA+Ugw==} 151 | engines: {node: '>=18'} 152 | cpu: [ia32] 153 | os: [linux] 154 | 155 | '@esbuild/linux-loong64@0.24.2': 156 | resolution: {integrity: sha512-CN9AZr8kEndGooS35ntToZLTQLHEjtVB5n7dl8ZcTZMonJ7CCfStrYhrzF97eAecqVbVJ7APOEe18RPI4KLhwQ==} 157 | engines: {node: '>=18'} 158 | cpu: [loong64] 159 | os: [linux] 160 | 161 | '@esbuild/linux-mips64el@0.24.2': 162 | resolution: {integrity: sha512-iMkk7qr/wl3exJATwkISxI7kTcmHKE+BlymIAbHO8xanq/TjHaaVThFF6ipWzPHryoFsesNQJPE/3wFJw4+huw==} 163 | engines: {node: '>=18'} 164 | cpu: [mips64el] 165 | os: [linux] 166 | 167 | '@esbuild/linux-ppc64@0.24.2': 168 | resolution: {integrity: sha512-shsVrgCZ57Vr2L8mm39kO5PPIb+843FStGt7sGGoqiiWYconSxwTiuswC1VJZLCjNiMLAMh34jg4VSEQb+iEbw==} 169 | engines: {node: '>=18'} 170 | cpu: [ppc64] 171 | os: [linux] 172 | 173 | '@esbuild/linux-riscv64@0.24.2': 174 | resolution: {integrity: sha512-4eSFWnU9Hhd68fW16GD0TINewo1L6dRrB+oLNNbYyMUAeOD2yCK5KXGK1GH4qD/kT+bTEXjsyTCiJGHPZ3eM9Q==} 175 | engines: {node: '>=18'} 176 | cpu: [riscv64] 177 | os: [linux] 178 | 179 | '@esbuild/linux-s390x@0.24.2': 180 | resolution: {integrity: sha512-S0Bh0A53b0YHL2XEXC20bHLuGMOhFDO6GN4b3YjRLK//Ep3ql3erpNcPlEFed93hsQAjAQDNsvcK+hV90FubSw==} 181 | engines: {node: '>=18'} 182 | cpu: [s390x] 183 | os: [linux] 184 | 185 | '@esbuild/linux-x64@0.24.2': 186 | resolution: {integrity: sha512-8Qi4nQcCTbLnK9WoMjdC9NiTG6/E38RNICU6sUNqK0QFxCYgoARqVqxdFmWkdonVsvGqWhmm7MO0jyTqLqwj0Q==} 187 | engines: {node: '>=18'} 188 | cpu: [x64] 189 | os: [linux] 190 | 191 | '@esbuild/netbsd-arm64@0.24.2': 192 | resolution: {integrity: sha512-wuLK/VztRRpMt9zyHSazyCVdCXlpHkKm34WUyinD2lzK07FAHTq0KQvZZlXikNWkDGoT6x3TD51jKQ7gMVpopw==} 193 | engines: {node: '>=18'} 194 | cpu: [arm64] 195 | os: [netbsd] 196 | 197 | '@esbuild/netbsd-x64@0.24.2': 198 | resolution: {integrity: sha512-VefFaQUc4FMmJuAxmIHgUmfNiLXY438XrL4GDNV1Y1H/RW3qow68xTwjZKfj/+Plp9NANmzbH5R40Meudu8mmw==} 199 | engines: {node: '>=18'} 200 | cpu: [x64] 201 | os: [netbsd] 202 | 203 | '@esbuild/openbsd-arm64@0.24.2': 204 | resolution: {integrity: sha512-YQbi46SBct6iKnszhSvdluqDmxCJA+Pu280Av9WICNwQmMxV7nLRHZfjQzwbPs3jeWnuAhE9Jy0NrnJ12Oz+0A==} 205 | engines: {node: '>=18'} 206 | cpu: [arm64] 207 | os: [openbsd] 208 | 209 | '@esbuild/openbsd-x64@0.24.2': 210 | resolution: {integrity: sha512-+iDS6zpNM6EnJyWv0bMGLWSWeXGN/HTaF/LXHXHwejGsVi+ooqDfMCCTerNFxEkM3wYVcExkeGXNqshc9iMaOA==} 211 | engines: {node: '>=18'} 212 | cpu: [x64] 213 | os: [openbsd] 214 | 215 | '@esbuild/sunos-x64@0.24.2': 216 | resolution: {integrity: sha512-hTdsW27jcktEvpwNHJU4ZwWFGkz2zRJUz8pvddmXPtXDzVKTTINmlmga3ZzwcuMpUvLw7JkLy9QLKyGpD2Yxig==} 217 | engines: {node: '>=18'} 218 | cpu: [x64] 219 | os: [sunos] 220 | 221 | '@esbuild/win32-arm64@0.24.2': 222 | resolution: {integrity: sha512-LihEQ2BBKVFLOC9ZItT9iFprsE9tqjDjnbulhHoFxYQtQfai7qfluVODIYxt1PgdoyQkz23+01rzwNwYfutxUQ==} 223 | engines: {node: '>=18'} 224 | cpu: [arm64] 225 | os: [win32] 226 | 227 | '@esbuild/win32-ia32@0.24.2': 228 | resolution: {integrity: sha512-q+iGUwfs8tncmFC9pcnD5IvRHAzmbwQ3GPS5/ceCyHdjXubwQWI12MKWSNSMYLJMq23/IUCvJMS76PDqXe1fxA==} 229 | engines: {node: '>=18'} 230 | cpu: [ia32] 231 | os: [win32] 232 | 233 | '@esbuild/win32-x64@0.24.2': 234 | resolution: {integrity: sha512-7VTgWzgMGvup6aSqDPLiW5zHaxYJGTO4OokMjIlrCtf+VpEL+cXKtCvg723iguPYI5oaUNdS+/V7OU2gvXVWEg==} 235 | engines: {node: '>=18'} 236 | cpu: [x64] 237 | os: [win32] 238 | 239 | '@isaacs/cliui@8.0.2': 240 | resolution: {integrity: sha512-O8jcjabXaleOG9DQ0+ARXWZBTfnP4WNAqzuiJK7ll44AmxGKv/J2M4TPjxjY3znBCfvBXFzucm1twdyFybFqEA==} 241 | engines: {node: '>=12'} 242 | 243 | '@jridgewell/gen-mapping@0.3.8': 244 | resolution: {integrity: sha512-imAbBGkb+ebQyxKgzv5Hu2nmROxoDOXHh80evxdoXNOrvAnVx7zimzc1Oo5h9RlfV4vPXaE2iM5pOFbvOCClWA==} 245 | engines: {node: '>=6.0.0'} 246 | 247 | '@jridgewell/resolve-uri@3.1.2': 248 | resolution: {integrity: sha512-bRISgCIjP20/tbWSPWMEi54QVPRZExkuD9lJL+UIxUKtwVJA8wW1Trb1jMs1RFXo1CBTNZ/5hpC9QvmKWdopKw==} 249 | engines: {node: '>=6.0.0'} 250 | 251 | '@jridgewell/set-array@1.2.1': 252 | resolution: {integrity: sha512-R8gLRTZeyp03ymzP/6Lil/28tGeGEzhx1q2k703KGWRAI1VdvPIXdG70VJc2pAMw3NA6JKL5hhFu1sJX0Mnn/A==} 253 | engines: {node: '>=6.0.0'} 254 | 255 | '@jridgewell/sourcemap-codec@1.5.0': 256 | resolution: {integrity: sha512-gv3ZRaISU3fjPAgNsriBRqGWQL6quFx04YMPW/zD8XMLsU32mhCCbfbO6KZFLjvYpCZ8zyDEgqsgf+PwPaM7GQ==} 257 | 258 | '@jridgewell/trace-mapping@0.3.25': 259 | resolution: {integrity: sha512-vNk6aEwybGtawWmy/PzwnGDOjCkLWSD2wqvjGGAgOAwCGWySYXfYoxt00IJkTF+8Lb57DwOb3Aa0o9CApepiYQ==} 260 | 261 | '@nodelib/fs.scandir@2.1.5': 262 | resolution: {integrity: sha512-vq24Bq3ym5HEQm2NKCr3yXDwjc7vTsEThRDnkp2DK9p1uqLR+DHurm/NOTo0KG7HYHU7eppKZj3MyqYuMBf62g==} 263 | engines: {node: '>= 8'} 264 | 265 | '@nodelib/fs.stat@2.0.5': 266 | resolution: {integrity: sha512-RkhPPp2zrqDAQA/2jNhnztcPAlv64XdhIp7a7454A5ovI7Bukxgt7MX7udwAu3zg1DcpPU0rz3VV1SeaqvY4+A==} 267 | engines: {node: '>= 8'} 268 | 269 | '@nodelib/fs.walk@1.2.8': 270 | resolution: {integrity: sha512-oGB+UxlgWcgQkgwo8GcEGwemoTFt3FIO9ababBmaGwXIoBKZ+GTy0pP185beGg7Llih/NSHSV2XAs1lnznocSg==} 271 | engines: {node: '>= 8'} 272 | 273 | '@pkgjs/parseargs@0.11.0': 274 | resolution: {integrity: sha512-+1VkjdD0QBLPodGrJUeqarH8VAIvQODIbwh9XpP5Syisf7YoQgsJKPNFoqqLQlu+VQ/tVSshMR6loPMn8U+dPg==} 275 | engines: {node: '>=14'} 276 | 277 | '@polka/url@1.0.0-next.24': 278 | resolution: {integrity: sha512-2LuNTFBIO0m7kKIQvvPHN6UE63VjpmL9rnEEaOOaiSPbZK+zUOYIzBAWcED+3XYzhYsd/0mD57VdxAEqqV52CQ==} 279 | 280 | '@rollup/rollup-android-arm-eabi@4.30.1': 281 | resolution: {integrity: sha512-pSWY+EVt3rJ9fQ3IqlrEUtXh3cGqGtPDH1FQlNZehO2yYxCHEX1SPsz1M//NXwYfbTlcKr9WObLnJX9FsS9K1Q==} 282 | cpu: [arm] 283 | os: [android] 284 | 285 | '@rollup/rollup-android-arm64@4.30.1': 286 | resolution: {integrity: sha512-/NA2qXxE3D/BRjOJM8wQblmArQq1YoBVJjrjoTSBS09jgUisq7bqxNHJ8kjCHeV21W/9WDGwJEWSN0KQ2mtD/w==} 287 | cpu: [arm64] 288 | os: [android] 289 | 290 | '@rollup/rollup-darwin-arm64@4.30.1': 291 | resolution: {integrity: sha512-r7FQIXD7gB0WJ5mokTUgUWPl0eYIH0wnxqeSAhuIwvnnpjdVB8cRRClyKLQr7lgzjctkbp5KmswWszlwYln03Q==} 292 | cpu: [arm64] 293 | os: [darwin] 294 | 295 | '@rollup/rollup-darwin-x64@4.30.1': 296 | resolution: {integrity: sha512-x78BavIwSH6sqfP2xeI1hd1GpHL8J4W2BXcVM/5KYKoAD3nNsfitQhvWSw+TFtQTLZ9OmlF+FEInEHyubut2OA==} 297 | cpu: [x64] 298 | os: [darwin] 299 | 300 | '@rollup/rollup-freebsd-arm64@4.30.1': 301 | resolution: {integrity: sha512-HYTlUAjbO1z8ywxsDFWADfTRfTIIy/oUlfIDmlHYmjUP2QRDTzBuWXc9O4CXM+bo9qfiCclmHk1x4ogBjOUpUQ==} 302 | cpu: [arm64] 303 | os: [freebsd] 304 | 305 | '@rollup/rollup-freebsd-x64@4.30.1': 306 | resolution: {integrity: sha512-1MEdGqogQLccphhX5myCJqeGNYTNcmTyaic9S7CG3JhwuIByJ7J05vGbZxsizQthP1xpVx7kd3o31eOogfEirw==} 307 | cpu: [x64] 308 | os: [freebsd] 309 | 310 | '@rollup/rollup-linux-arm-gnueabihf@4.30.1': 311 | resolution: {integrity: sha512-PaMRNBSqCx7K3Wc9QZkFx5+CX27WFpAMxJNiYGAXfmMIKC7jstlr32UhTgK6T07OtqR+wYlWm9IxzennjnvdJg==} 312 | cpu: [arm] 313 | os: [linux] 314 | 315 | '@rollup/rollup-linux-arm-musleabihf@4.30.1': 316 | resolution: {integrity: sha512-B8Rcyj9AV7ZlEFqvB5BubG5iO6ANDsRKlhIxySXcF1axXYUyqwBok+XZPgIYGBgs7LDXfWfifxhw0Ik57T0Yug==} 317 | cpu: [arm] 318 | os: [linux] 319 | 320 | '@rollup/rollup-linux-arm64-gnu@4.30.1': 321 | resolution: {integrity: sha512-hqVyueGxAj3cBKrAI4aFHLV+h0Lv5VgWZs9CUGqr1z0fZtlADVV1YPOij6AhcK5An33EXaxnDLmJdQikcn5NEw==} 322 | cpu: [arm64] 323 | os: [linux] 324 | 325 | '@rollup/rollup-linux-arm64-musl@4.30.1': 326 | resolution: {integrity: sha512-i4Ab2vnvS1AE1PyOIGp2kXni69gU2DAUVt6FSXeIqUCPIR3ZlheMW3oP2JkukDfu3PsexYRbOiJrY+yVNSk9oA==} 327 | cpu: [arm64] 328 | os: [linux] 329 | 330 | '@rollup/rollup-linux-loongarch64-gnu@4.30.1': 331 | resolution: {integrity: sha512-fARcF5g296snX0oLGkVxPmysetwUk2zmHcca+e9ObOovBR++9ZPOhqFUM61UUZ2EYpXVPN1redgqVoBB34nTpQ==} 332 | cpu: [loong64] 333 | os: [linux] 334 | 335 | '@rollup/rollup-linux-powerpc64le-gnu@4.30.1': 336 | resolution: {integrity: sha512-GLrZraoO3wVT4uFXh67ElpwQY0DIygxdv0BNW9Hkm3X34wu+BkqrDrkcsIapAY+N2ATEbvak0XQ9gxZtCIA5Rw==} 337 | cpu: [ppc64] 338 | os: [linux] 339 | 340 | '@rollup/rollup-linux-riscv64-gnu@4.30.1': 341 | resolution: {integrity: sha512-0WKLaAUUHKBtll0wvOmh6yh3S0wSU9+yas923JIChfxOaaBarmb/lBKPF0w/+jTVozFnOXJeRGZ8NvOxvk/jcw==} 342 | cpu: [riscv64] 343 | os: [linux] 344 | 345 | '@rollup/rollup-linux-s390x-gnu@4.30.1': 346 | resolution: {integrity: sha512-GWFs97Ruxo5Bt+cvVTQkOJ6TIx0xJDD/bMAOXWJg8TCSTEK8RnFeOeiFTxKniTc4vMIaWvCplMAFBt9miGxgkA==} 347 | cpu: [s390x] 348 | os: [linux] 349 | 350 | '@rollup/rollup-linux-x64-gnu@4.30.1': 351 | resolution: {integrity: sha512-UtgGb7QGgXDIO+tqqJ5oZRGHsDLO8SlpE4MhqpY9Llpzi5rJMvrK6ZGhsRCST2abZdBqIBeXW6WPD5fGK5SDwg==} 352 | cpu: [x64] 353 | os: [linux] 354 | 355 | '@rollup/rollup-linux-x64-musl@4.30.1': 356 | resolution: {integrity: sha512-V9U8Ey2UqmQsBT+xTOeMzPzwDzyXmnAoO4edZhL7INkwQcaW1Ckv3WJX3qrrp/VHaDkEWIBWhRwP47r8cdrOow==} 357 | cpu: [x64] 358 | os: [linux] 359 | 360 | '@rollup/rollup-win32-arm64-msvc@4.30.1': 361 | resolution: {integrity: sha512-WabtHWiPaFF47W3PkHnjbmWawnX/aE57K47ZDT1BXTS5GgrBUEpvOzq0FI0V/UYzQJgdb8XlhVNH8/fwV8xDjw==} 362 | cpu: [arm64] 363 | os: [win32] 364 | 365 | '@rollup/rollup-win32-ia32-msvc@4.30.1': 366 | resolution: {integrity: sha512-pxHAU+Zv39hLUTdQQHUVHf4P+0C47y/ZloorHpzs2SXMRqeAWmGghzAhfOlzFHHwjvgokdFAhC4V+6kC1lRRfw==} 367 | cpu: [ia32] 368 | os: [win32] 369 | 370 | '@rollup/rollup-win32-x64-msvc@4.30.1': 371 | resolution: {integrity: sha512-D6qjsXGcvhTjv0kI4fU8tUuBDF/Ueee4SVX79VfNDXZa64TfCW1Slkb6Z7O1p7vflqZjcmOVdZlqf8gvJxc6og==} 372 | cpu: [x64] 373 | os: [win32] 374 | 375 | '@types/connect-history-api-fallback@1.5.4': 376 | resolution: {integrity: sha512-n6Cr2xS1h4uAulPRdlw6Jl6s1oG8KrVilPN2yUITEs+K48EzMJJ3W1xy8K5eWuFvjp3R74AOIGSmp2UfBJ8HFw==} 377 | 378 | '@types/debug@4.1.12': 379 | resolution: {integrity: sha512-vIChWdVG3LG1SMxEvI/AK+FWJthlrqlTu7fbrlywTkkaONwk/UAGaULXRlf8vkzFBLVm0zkMdCquhL5aOjhXPQ==} 380 | 381 | '@types/ejs@3.1.5': 382 | resolution: {integrity: sha512-nv+GSx77ZtXiJzwKdsASqi+YQ5Z7vwHsTP0JY2SiQgjGckkBRKZnk8nIM+7oUZ1VCtuTz0+By4qVR7fqzp/Dfg==} 383 | 384 | '@types/estree@1.0.6': 385 | resolution: {integrity: sha512-AYnb1nQyY49te+VRAVgmzfcgjYS91mY5P0TKUDCLEM+gNnA+3T6rWITXRLYCpahpqSQbN5cE+gHpnPyXjHWxcw==} 386 | 387 | '@types/express-serve-static-core@4.17.33': 388 | resolution: {integrity: sha512-TPBqmR/HRYI3eC2E5hmiivIzv+bidAfXofM+sbonAGvyDhySGw9/PQZFt2BLOrjUUR++4eJVpx6KnLQK1Fk9tA==} 389 | 390 | '@types/ms@0.7.31': 391 | resolution: {integrity: sha512-iiUgKzV9AuaEkZqkOLDIvlQiL6ltuZd9tGcW3gwpnX8JbuiuhFlEGmmFXEXkN50Cvq7Os88IY2v0dkDqXYWVgA==} 392 | 393 | '@types/node@22.12.0': 394 | resolution: {integrity: sha512-Fll2FZ1riMjNmlmJOdAyY5pUbkftXslB5DgEzlIuNaiWhXd00FhWxVC/r4yV/4wBb9JfImTu+jiSvXTkJ7F/gA==} 395 | 396 | '@types/qs@6.9.7': 397 | resolution: {integrity: sha512-FGa1F62FT09qcrueBA6qYTrJPVDzah9a+493+o2PCXsesWHIn27G98TsSMs3WPNbZIEj4+VJf6saSFpvD+3Zsw==} 398 | 399 | '@types/range-parser@1.2.4': 400 | resolution: {integrity: sha512-EEhsLsD6UsDM1yFhAvy0Cjr6VwmpMWqFBCb9w07wVugF7w9nfajxLuVmngTIpgS6svCnm6Vaw+MZhoDCKnOfsw==} 401 | 402 | '@vitejs/plugin-vue@5.2.1': 403 | resolution: {integrity: sha512-cxh314tzaWwOLqVes2gnnCtvBDcM1UMdn+iFR+UjAn411dPT3tOmqrJjbMd7koZpMAmBM/GqeV4n9ge7JSiJJQ==} 404 | engines: {node: ^18.0.0 || >=20.0.0} 405 | peerDependencies: 406 | vite: ^5.0.0 || ^6.0.0 407 | vue: ^3.2.25 408 | 409 | '@vitest/expect@3.0.4': 410 | resolution: {integrity: sha512-Nm5kJmYw6P2BxhJPkO3eKKhGYKRsnqJqf+r0yOGRKpEP+bSCBDsjXgiu1/5QFrnPMEgzfC38ZEjvCFgaNBC0Eg==} 411 | 412 | '@vitest/mocker@3.0.4': 413 | resolution: {integrity: sha512-gEef35vKafJlfQbnyOXZ0Gcr9IBUsMTyTLXsEQwuyYAerpHqvXhzdBnDFuHLpFqth3F7b6BaFr4qV/Cs1ULx5A==} 414 | peerDependencies: 415 | msw: ^2.4.9 416 | vite: ^5.0.0 || ^6.0.0 417 | peerDependenciesMeta: 418 | msw: 419 | optional: true 420 | vite: 421 | optional: true 422 | 423 | '@vitest/pretty-format@3.0.4': 424 | resolution: {integrity: sha512-ts0fba+dEhK2aC9PFuZ9LTpULHpY/nd6jhAQ5IMU7Gaj7crPCTdCFfgvXxruRBLFS+MLraicCuFXxISEq8C93g==} 425 | 426 | '@vitest/runner@3.0.4': 427 | resolution: {integrity: sha512-dKHzTQ7n9sExAcWH/0sh1elVgwc7OJ2lMOBrAm73J7AH6Pf9T12Zh3lNE1TETZaqrWFXtLlx3NVrLRb5hCK+iw==} 428 | 429 | '@vitest/snapshot@3.0.4': 430 | resolution: {integrity: sha512-+p5knMLwIk7lTQkM3NonZ9zBewzVp9EVkVpvNta0/PlFWpiqLaRcF4+33L1it3uRUCh0BGLOaXPPGEjNKfWb4w==} 431 | 432 | '@vitest/spy@3.0.4': 433 | resolution: {integrity: sha512-sXIMF0oauYyUy2hN49VFTYodzEAu744MmGcPR3ZBsPM20G+1/cSW/n1U+3Yu/zHxX2bIDe1oJASOkml+osTU6Q==} 434 | 435 | '@vitest/utils@3.0.4': 436 | resolution: {integrity: sha512-8BqC1ksYsHtbWH+DfpOAKrFw3jl3Uf9J7yeFh85Pz52IWuh1hBBtyfEbRNNZNjl8H8A5yMLH9/t+k7HIKzQcZQ==} 437 | 438 | '@vue/compiler-core@3.5.13': 439 | resolution: {integrity: sha512-oOdAkwqUfW1WqpwSYJce06wvt6HljgY3fGeM9NcVA1HaYOij3mZG9Rkysn0OHuyUAGMbEbARIpsG+LPVlBJ5/Q==} 440 | 441 | '@vue/compiler-dom@3.5.13': 442 | resolution: {integrity: sha512-ZOJ46sMOKUjO3e94wPdCzQ6P1Lx/vhp2RSvfaab88Ajexs0AHeV0uasYhi99WPaogmBlRHNRuly8xV75cNTMDA==} 443 | 444 | '@vue/compiler-sfc@3.5.13': 445 | resolution: {integrity: sha512-6VdaljMpD82w6c2749Zhf5T9u5uLBWKnVue6XWxprDobftnletJ8+oel7sexFfM3qIxNmVE7LSFGTpv6obNyaQ==} 446 | 447 | '@vue/compiler-ssr@3.5.13': 448 | resolution: {integrity: sha512-wMH6vrYHxQl/IybKJagqbquvxpWCuVYpoUJfCqFZwa/JY1GdATAQ+TgVtgrwwMZ0D07QhA99rs/EAAWfvG6KpA==} 449 | 450 | '@vue/devtools-api@6.6.4': 451 | resolution: {integrity: sha512-sGhTPMuXqZ1rVOk32RylztWkfXTRhuS7vgAKv0zjqk8gbsHkJ7xfFf+jbySxt7tWObEJwyKaHMikV/WGDiQm8g==} 452 | 453 | '@vue/reactivity@3.5.13': 454 | resolution: {integrity: sha512-NaCwtw8o48B9I6L1zl2p41OHo/2Z4wqYGGIK1Khu5T7yxrn+ATOixn/Udn2m+6kZKB/J7cuT9DbWWhRxqixACg==} 455 | 456 | '@vue/runtime-core@3.5.13': 457 | resolution: {integrity: sha512-Fj4YRQ3Az0WTZw1sFe+QDb0aXCerigEpw418pw1HBUKFtnQHWzwojaukAs2X/c9DQz4MQ4bsXTGlcpGxU/RCIw==} 458 | 459 | '@vue/runtime-dom@3.5.13': 460 | resolution: {integrity: sha512-dLaj94s93NYLqjLiyFzVs9X6dWhTdAlEAciC3Moq7gzAc13VJUdCnjjRurNM6uTLFATRHexHCTu/Xp3eW6yoog==} 461 | 462 | '@vue/server-renderer@3.5.13': 463 | resolution: {integrity: sha512-wAi4IRJV/2SAW3htkTlB+dHeRmpTiVIK1OGLWV1yeStVSebSQQOwGwIq0D3ZIoBj2C2qpgz5+vX9iEBkTdk5YA==} 464 | peerDependencies: 465 | vue: 3.5.13 466 | 467 | '@vue/shared@3.5.13': 468 | resolution: {integrity: sha512-/hnE/qP5ZoGpol0a5mDi45bOd7t3tjYJBjsgCsivow7D48cJeV5l05RD82lPqi7gRiphZM37rnhW1l6ZoCNNnQ==} 469 | 470 | ansi-regex@5.0.1: 471 | resolution: {integrity: sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==} 472 | engines: {node: '>=8'} 473 | 474 | ansi-regex@6.1.0: 475 | resolution: {integrity: sha512-7HSX4QQb4CspciLpVFwyRe79O3xsIZDDLER21kERQ71oaPodF8jL725AgJMFAYbooIqolJoRLuM81SpeUkpkvA==} 476 | engines: {node: '>=12'} 477 | 478 | ansi-styles@4.3.0: 479 | resolution: {integrity: sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==} 480 | engines: {node: '>=8'} 481 | 482 | ansi-styles@6.2.1: 483 | resolution: {integrity: sha512-bN798gFfQX+viw3R7yrGWRqnrN2oRkEkUjjl4JNn4E8GxxbjtG3FbrEIIY3l8/hrwUwIeCZvi4QuOTP4MErVug==} 484 | engines: {node: '>=12'} 485 | 486 | any-promise@1.3.0: 487 | resolution: {integrity: sha512-7UvmKalWRt1wgjL1RrGxoSJW/0QZFIegpeGvZG9kjp8vrRu55XTHbwnqq2GpXm9uLbcuhxm3IqX9OB4MZR1b2A==} 488 | 489 | assertion-error@2.0.1: 490 | resolution: {integrity: sha512-Izi8RQcffqCeNVgFigKli1ssklIbpHnCYc6AknXGYoB6grJqyeby7jv12JUQgmTAnIDnbck1uxksT4dzN3PWBA==} 491 | engines: {node: '>=12'} 492 | 493 | async@3.2.4: 494 | resolution: {integrity: sha512-iAB+JbDEGXhyIUavoDl9WP/Jj106Kz9DEn1DPgYw5ruDn0e3Wgi3sKFm55sASdGBNOQB8F59d9qQ7deqrHA8wQ==} 495 | 496 | balanced-match@1.0.2: 497 | resolution: {integrity: sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==} 498 | 499 | brace-expansion@1.1.11: 500 | resolution: {integrity: sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==} 501 | 502 | brace-expansion@2.0.1: 503 | resolution: {integrity: sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA==} 504 | 505 | braces@3.0.3: 506 | resolution: {integrity: sha512-yQbXgO/OSZVD2IsiLlro+7Hf6Q18EJrKSEsdoMzKePKXct3gvD8oLcOQdIzGupr5Fj+EDe8gO/lxc1BzfMpxvA==} 507 | engines: {node: '>=8'} 508 | 509 | bundle-name@4.1.0: 510 | resolution: {integrity: sha512-tjwM5exMg6BGRI+kNmTntNsvdZS1X8BFYS6tnJ2hdH0kVxM6/eVZ2xy+FqStSWvYmtfFMDLIxurorHwDKfDz5Q==} 511 | engines: {node: '>=18'} 512 | 513 | bundle-require@5.1.0: 514 | resolution: {integrity: sha512-3WrrOuZiyaaZPWiEt4G3+IffISVC9HYlWueJEBWED4ZH4aIAC2PnkdnuRrR94M+w6yGWn4AglWtJtBI8YqvgoA==} 515 | engines: {node: ^12.20.0 || ^14.13.1 || >=16.0.0} 516 | peerDependencies: 517 | esbuild: '>=0.18' 518 | 519 | cac@6.7.14: 520 | resolution: {integrity: sha512-b6Ilus+c3RrdDk+JhLKUAQfzzgLEPy6wcXqS7f/xe1EETvsDP6GORG7SFuOs6cID5YkqchW/LXZbX5bc8j7ZcQ==} 521 | engines: {node: '>=8'} 522 | 523 | chai@5.1.2: 524 | resolution: {integrity: sha512-aGtmf24DW6MLHHG5gCx4zaI3uBq3KRtxeVs0DjFH6Z0rDNbsvTxFASFvdj79pxjxZ8/5u3PIiN3IwEIQkiiuPw==} 525 | engines: {node: '>=12'} 526 | 527 | chalk@4.1.2: 528 | resolution: {integrity: sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==} 529 | engines: {node: '>=10'} 530 | 531 | check-error@2.1.1: 532 | resolution: {integrity: sha512-OAlb+T7V4Op9OwdkjmguYRqncdlx5JiofwOAUkmTF+jNdHwzTaTs4sRAGpzLF3oOz5xAyDGrPgeIDFQmDOTiJw==} 533 | engines: {node: '>= 16'} 534 | 535 | chokidar@4.0.3: 536 | resolution: {integrity: sha512-Qgzu8kfBvo+cA4962jnP1KkS6Dop5NS6g7R5LFYJr4b8Ub94PPQXUksCw9PvXoeXPRRddRNC5C1JQUR2SMGtnA==} 537 | engines: {node: '>= 14.16.0'} 538 | 539 | color-convert@2.0.1: 540 | resolution: {integrity: sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==} 541 | engines: {node: '>=7.0.0'} 542 | 543 | color-name@1.1.4: 544 | resolution: {integrity: sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==} 545 | 546 | commander@4.1.1: 547 | resolution: {integrity: sha512-NOKm8xhkzAjzFx8B2v5OAHT+u5pRQc2UCa2Vq9jYL/31o2wi9mxBA7LIFs3sV5VSC49z6pEhfbMULvShKj26WA==} 548 | engines: {node: '>= 6'} 549 | 550 | concat-map@0.0.1: 551 | resolution: {integrity: sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==} 552 | 553 | connect-history-api-fallback@2.0.0: 554 | resolution: {integrity: sha512-U73+6lQFmfiNPrYbXqr6kZ1i1wiRqXnp2nhMsINseWXO8lDau0LGEffJ8kQi4EjLZympVgRdvqjAgiZ1tgzDDA==} 555 | engines: {node: '>=0.8'} 556 | 557 | consola@3.4.0: 558 | resolution: {integrity: sha512-EiPU8G6dQG0GFHNR8ljnZFki/8a+cQwEQ+7wpxdChl02Q8HXlwEZWD5lqAF8vC2sEC3Tehr8hy7vErz88LHyUA==} 559 | engines: {node: ^14.18.0 || >=16.10.0} 560 | 561 | cross-spawn@7.0.3: 562 | resolution: {integrity: sha512-iRDPJKUPVEND7dHPO8rkbOnPpyDygcDFtWjpeWNCgy8WP2rXcxXL8TskReQl6OrB2G7+UJrags1q15Fudc7G6w==} 563 | engines: {node: '>= 8'} 564 | 565 | csstype@3.1.3: 566 | resolution: {integrity: sha512-M1uQkMl8rQK/szD0LNhtqxIPLpimGm8sOBwU7lLnCpSbTyY3yeU1Vc7l4KT5zT4s/yOxHH5O7tIuuLOCnLADRw==} 567 | 568 | debug@4.4.0: 569 | resolution: {integrity: sha512-6WTZ/IxCY/T6BALoZHaE4ctp9xm+Z5kY/pzYaCHRFeyVhojxlrm+46y68HA6hr0TcwEssoxNiDEUJQjfPZ/RYA==} 570 | engines: {node: '>=6.0'} 571 | peerDependencies: 572 | supports-color: '*' 573 | peerDependenciesMeta: 574 | supports-color: 575 | optional: true 576 | 577 | deep-eql@5.0.2: 578 | resolution: {integrity: sha512-h5k/5U50IJJFpzfL6nO9jaaumfjO/f2NjK/oYB2Djzm4p9L+3T9qWpZqZ2hAbLPuuYq9wrU08WQyBTL5GbPk5Q==} 579 | engines: {node: '>=6'} 580 | 581 | default-browser-id@5.0.0: 582 | resolution: {integrity: sha512-A6p/pu/6fyBcA1TRz/GqWYPViplrftcW2gZC9q79ngNCKAeR/X3gcEdXQHl4KNXV+3wgIJ1CPkJQ3IHM6lcsyA==} 583 | engines: {node: '>=18'} 584 | 585 | default-browser@5.2.1: 586 | resolution: {integrity: sha512-WY/3TUME0x3KPYdRRxEJJvXRHV4PyPoUsxtZa78lwItwRQRHhd2U9xOscaT/YTf8uCXIAjeJOFBVEh/7FtD8Xg==} 587 | engines: {node: '>=18'} 588 | 589 | define-lazy-prop@3.0.0: 590 | resolution: {integrity: sha512-N+MeXYoqr3pOgn8xfyRPREN7gHakLYjhsHhWGT3fWAiL4IkAt0iDw14QiiEm2bE30c5XX5q0FtAA3CK5f9/BUg==} 591 | engines: {node: '>=12'} 592 | 593 | eastasianwidth@0.2.0: 594 | resolution: {integrity: sha512-I88TYZWc9XiYHRQ4/3c5rjjfgkjhLyW2luGIheGERbNQ6OY7yTybanSpDXZa8y7VUP9YmDcYa+eyq4ca7iLqWA==} 595 | 596 | ejs@3.1.10: 597 | resolution: {integrity: sha512-UeJmFfOrAQS8OJWPZ4qtgHyWExa088/MtK5UEyoJGFH67cDEXkZSviOiKRCZ4Xij0zxI3JECgYs3oKx+AizQBA==} 598 | engines: {node: '>=0.10.0'} 599 | hasBin: true 600 | 601 | emoji-regex@8.0.0: 602 | resolution: {integrity: sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==} 603 | 604 | emoji-regex@9.2.2: 605 | resolution: {integrity: sha512-L18DaJsXSUk2+42pv8mLs5jJT2hqFkFE4j21wOmgbUqsZ2hL72NsUU785g9RXgo3s0ZNgVl42TiHp3ZtOv/Vyg==} 606 | 607 | entities@4.5.0: 608 | resolution: {integrity: sha512-V0hjH4dGPh9Ao5p0MoRY6BVqtwCjhz6vI5LT8AJ55H+4g9/4vbHx1I54fS0XuclLhDHArPQCiMjDxjaL8fPxhw==} 609 | engines: {node: '>=0.12'} 610 | 611 | error-stack-parser-es@1.0.5: 612 | resolution: {integrity: sha512-5qucVt2XcuGMcEGgWI7i+yZpmpByQ8J1lHhcL7PwqCwu9FPP3VUXzT4ltHe5i2z9dePwEHcDVOAfSnHsOlCXRA==} 613 | 614 | es-module-lexer@1.6.0: 615 | resolution: {integrity: sha512-qqnD1yMU6tk/jnaMosogGySTZP8YtUgAffA9nMN+E/rjxcfRQ6IEk7IiozUjgxKoFHBGjTLnrHB/YC45r/59EQ==} 616 | 617 | esbuild@0.24.2: 618 | resolution: {integrity: sha512-+9egpBW8I3CD5XPe0n6BfT5fxLzxrlDzqydF3aviG+9ni1lDC/OvMHcxqEFV0+LANZG5R1bFMWfUrjVsdwxJvA==} 619 | engines: {node: '>=18'} 620 | hasBin: true 621 | 622 | estree-walker@2.0.2: 623 | resolution: {integrity: sha512-Rfkk/Mp/DL7JVje3u18FxFujQlTNR2q6QfMSMB7AvCBx91NGj/ba3kCfza0f6dVDbw7YlRf/nDrn7pQrCCyQ/w==} 624 | 625 | estree-walker@3.0.3: 626 | resolution: {integrity: sha512-7RUKfXgSMMkzt6ZuXmqapOurLGPPfgj6l9uRZ7lRGolvk0y2yocc35LdcxKC5PQZdn2DMqioAQ2NoWcrTKmm6g==} 627 | 628 | expect-type@1.1.0: 629 | resolution: {integrity: sha512-bFi65yM+xZgk+u/KRIpekdSYkTB5W1pEf0Lt8Q8Msh7b+eQ7LXVtIB1Bkm4fvclDEL1b2CZkMhv2mOeF8tMdkA==} 630 | engines: {node: '>=12.0.0'} 631 | 632 | fast-glob@3.3.3: 633 | resolution: {integrity: sha512-7MptL8U0cqcFdzIzwOTHoilX9x5BrNqye7Z/LuC7kCMRio1EMSyqRK3BEAUD7sXRq4iT4AzTVuZdhgQ2TCvYLg==} 634 | engines: {node: '>=8.6.0'} 635 | 636 | fastq@1.13.0: 637 | resolution: {integrity: sha512-YpkpUnK8od0o1hmeSc7UUs/eB/vIPWJYjKck2QKIzAf71Vm1AAQ3EbuZB3g2JIy+pg+ERD0vqI79KyZiB2e2Nw==} 638 | 639 | fdir@6.4.3: 640 | resolution: {integrity: sha512-PMXmW2y1hDDfTSRc9gaXIuCCRpuoz3Kaz8cUelp3smouvfT632ozg2vrT6lJsHKKOF59YLbOGfAWGUcKEfRMQw==} 641 | peerDependencies: 642 | picomatch: ^3 || ^4 643 | peerDependenciesMeta: 644 | picomatch: 645 | optional: true 646 | 647 | filelist@1.0.4: 648 | resolution: {integrity: sha512-w1cEuf3S+DrLCQL7ET6kz+gmlJdbq9J7yXCSjK/OZCPA+qEN1WyF4ZAf0YYJa4/shHJra2t/d/r8SV4Ji+x+8Q==} 649 | 650 | fill-range@7.1.1: 651 | resolution: {integrity: sha512-YsGpe3WHLK8ZYi4tWDg2Jy3ebRz2rXowDxnld4bkQB00cc/1Zw9AWnC0i9ztDJitivtQvaI9KaLyKrc+hBW0yg==} 652 | engines: {node: '>=8'} 653 | 654 | foreground-child@3.3.0: 655 | resolution: {integrity: sha512-Ld2g8rrAyMYFXBhEqMz8ZAHBi4J4uS1i/CxGMDnjyFWddMXLVcDp051DZfu+t7+ab7Wv6SMqpWmyFIj5UbfFvg==} 656 | engines: {node: '>=14'} 657 | 658 | fsevents@2.3.3: 659 | resolution: {integrity: sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==} 660 | engines: {node: ^8.16.0 || ^10.6.0 || >=11.0.0} 661 | os: [darwin] 662 | 663 | glob-parent@5.1.2: 664 | resolution: {integrity: sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==} 665 | engines: {node: '>= 6'} 666 | 667 | glob@10.4.5: 668 | resolution: {integrity: sha512-7Bv8RF0k6xjo7d4A/PxYLbUCfb6c+Vpd2/mB2yRDlew7Jb5hEXiCD9ibfO7wpk8i4sevK6DFny9h7EYbM3/sHg==} 669 | hasBin: true 670 | 671 | has-flag@4.0.0: 672 | resolution: {integrity: sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==} 673 | engines: {node: '>=8'} 674 | 675 | is-docker@3.0.0: 676 | resolution: {integrity: sha512-eljcgEDlEns/7AXFosB5K/2nCM4P7FQPkGc/DWLy5rmFEWvZayGrik1d9/QIY5nJ4f9YsVvBkA6kJpHn9rISdQ==} 677 | engines: {node: ^12.20.0 || ^14.13.1 || >=16.0.0} 678 | hasBin: true 679 | 680 | is-extglob@2.1.1: 681 | resolution: {integrity: sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==} 682 | engines: {node: '>=0.10.0'} 683 | 684 | is-fullwidth-code-point@3.0.0: 685 | resolution: {integrity: sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==} 686 | engines: {node: '>=8'} 687 | 688 | is-glob@4.0.3: 689 | resolution: {integrity: sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==} 690 | engines: {node: '>=0.10.0'} 691 | 692 | is-inside-container@1.0.0: 693 | resolution: {integrity: sha512-KIYLCCJghfHZxqjYBE7rEy0OBuTd5xCHS7tHVgvCLkx7StIoaxwNW3hCALgEUjFfeRk+MG/Qxmp/vtETEF3tRA==} 694 | engines: {node: '>=14.16'} 695 | hasBin: true 696 | 697 | is-number@7.0.0: 698 | resolution: {integrity: sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==} 699 | engines: {node: '>=0.12.0'} 700 | 701 | is-wsl@3.1.0: 702 | resolution: {integrity: sha512-UcVfVfaK4Sc4m7X3dUSoHoozQGBEFeDC+zVo06t98xe8CzHSZZBekNXH+tu0NalHolcJ/QAGqS46Hef7QXBIMw==} 703 | engines: {node: '>=16'} 704 | 705 | isexe@2.0.0: 706 | resolution: {integrity: sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==} 707 | 708 | jackspeak@3.4.3: 709 | resolution: {integrity: sha512-OGlZQpz2yfahA/Rd1Y8Cd9SIEsqvXkLVoSw/cgwhnhFMDbsQFeZYoJJ7bIZBS9BcamUW96asq/npPWugM+RQBw==} 710 | 711 | jake@10.8.5: 712 | resolution: {integrity: sha512-sVpxYeuAhWt0OTWITwT98oyV0GsXyMlXCF+3L1SuafBVUIr/uILGRB+NqwkzhgXKvoJpDIpQvqkUALgdmQsQxw==} 713 | engines: {node: '>=10'} 714 | hasBin: true 715 | 716 | joycon@3.1.1: 717 | resolution: {integrity: sha512-34wB/Y7MW7bzjKRjUKTa46I2Z7eV62Rkhva+KkopW7Qvv/OSWBqvkSY7vusOPrNuZcUG3tApvdVgNB8POj3SPw==} 718 | engines: {node: '>=10'} 719 | 720 | lilconfig@3.1.3: 721 | resolution: {integrity: sha512-/vlFKAoH5Cgt3Ie+JLhRbwOsCQePABiU3tJ1egGvyQ+33R/vcwM2Zl2QR/LzjsBeItPt3oSVXapn+m4nQDvpzw==} 722 | engines: {node: '>=14'} 723 | 724 | lines-and-columns@1.2.4: 725 | resolution: {integrity: sha512-7ylylesZQ/PV29jhEDl3Ufjo6ZX7gCqJr5F7PKrqc93v7fzSymt1BpwEU8nAUXs8qzzvqhbjhK5QZg6Mt/HkBg==} 726 | 727 | load-tsconfig@0.2.3: 728 | resolution: {integrity: sha512-iyT2MXws+dc2Wi6o3grCFtGXpeMvHmJqS27sMPGtV2eUu4PeFnG+33I8BlFK1t1NWMjOpcx9bridn5yxLDX2gQ==} 729 | engines: {node: ^12.20.0 || ^14.13.1 || >=16.0.0} 730 | 731 | lodash.sortby@4.7.0: 732 | resolution: {integrity: sha512-HDWXG8isMntAyRF5vZ7xKuEvOhT4AhlRt/3czTSjvGUxjYCBVRQY48ViDHyfYz9VIoBkW4TMGQNapx+l3RUwdA==} 733 | 734 | loupe@3.1.3: 735 | resolution: {integrity: sha512-kkIp7XSkP78ZxJEsSxW3712C6teJVoeHHwgo9zJ380de7IYyJ2ISlxojcH2pC5OFLewESmnRi/+XCDIEEVyoug==} 736 | 737 | lru-cache@10.4.3: 738 | resolution: {integrity: sha512-JNAzZcXrCt42VGLuYz0zfAzDfAvJWW6AfYlDBQyDV5DClI2m5sAmK+OIO7s59XfsRsWHp02jAJrRadPRGTt6SQ==} 739 | 740 | magic-string@0.30.17: 741 | resolution: {integrity: sha512-sNPKHvyjVf7gyjwS4xGTaW/mCnF8wnjtifKBEhxfZ7E/S8tQ0rssrwGNn6q8JH/ohItJfSQp9mBtQYuTlH5QnA==} 742 | 743 | merge2@1.4.1: 744 | resolution: {integrity: sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg==} 745 | engines: {node: '>= 8'} 746 | 747 | micromatch@4.0.8: 748 | resolution: {integrity: sha512-PXwfBhYu0hBCPw8Dn0E+WDYb7af3dSLVWKi3HGv84IdF4TyFoC0ysxFd0Goxw7nSv4T/PzEJQxsYsEiFCKo2BA==} 749 | engines: {node: '>=8.6'} 750 | 751 | minimatch@3.1.2: 752 | resolution: {integrity: sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==} 753 | 754 | minimatch@5.1.1: 755 | resolution: {integrity: sha512-362NP+zlprccbEt/SkxKfRMHnNY85V74mVnpUpNyr3F35covl09Kec7/sEFLt3RA4oXmewtoaanoIf67SE5Y5g==} 756 | engines: {node: '>=10'} 757 | 758 | minimatch@9.0.5: 759 | resolution: {integrity: sha512-G6T0ZX48xgozx7587koeX9Ys2NYy6Gmv//P89sEte9V9whIapMNF4idKxnW2QtCcLiTWlb/wfCabAtAFWhhBow==} 760 | engines: {node: '>=16 || 14 >=14.17'} 761 | 762 | minipass@7.1.2: 763 | resolution: {integrity: sha512-qOOzS1cBTWYF4BH8fVePDBOO9iptMnGUEZwNc/cMWnTV2nVLZ7VoNWEPHkYczZA0pdoA7dl6e7FL659nX9S2aw==} 764 | engines: {node: '>=16 || 14 >=14.17'} 765 | 766 | mrmime@2.0.0: 767 | resolution: {integrity: sha512-eu38+hdgojoyq63s+yTpN4XMBdt5l8HhMhc4VKLO9KM5caLIBvUm4thi7fFaxyTmCKeNnXZ5pAlBwCUnhA09uw==} 768 | engines: {node: '>=10'} 769 | 770 | ms@2.1.3: 771 | resolution: {integrity: sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==} 772 | 773 | mz@2.7.0: 774 | resolution: {integrity: sha512-z81GNO7nnYMEhrGh9LeymoE4+Yr0Wn5McHIZMK5cfQCl+NDX08sCZgUc9/6MHni9IWuFLm1Z3HTCXu2z9fN62Q==} 775 | 776 | nanoid@3.3.7: 777 | resolution: {integrity: sha512-eSRppjcPIatRIMC1U6UngP8XFcz8MQWGQdt1MTBQ7NaAmvXDfvNxbvWV3x2y6CdEUciCSsDHDQZbhYaB8QEo2g==} 778 | engines: {node: ^10 || ^12 || ^13.7 || ^14 || >=15.0.1} 779 | hasBin: true 780 | 781 | object-assign@4.1.1: 782 | resolution: {integrity: sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg==} 783 | engines: {node: '>=0.10.0'} 784 | 785 | open@10.1.0: 786 | resolution: {integrity: sha512-mnkeQ1qP5Ue2wd+aivTD3NHd/lZ96Lu0jgf0pwktLPtx6cTZiH7tyeGRRHs0zX0rbrahXPnXlUnbeXyaBBuIaw==} 787 | engines: {node: '>=18'} 788 | 789 | package-json-from-dist@1.0.1: 790 | resolution: {integrity: sha512-UEZIS3/by4OC8vL3P2dTXRETpebLI2NiI5vIrjaD/5UtrkFX/tNbwjTSRAGC/+7CAo2pIcBaRgWmcBBHcsaCIw==} 791 | 792 | path-key@3.1.1: 793 | resolution: {integrity: sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==} 794 | engines: {node: '>=8'} 795 | 796 | path-scurry@1.11.1: 797 | resolution: {integrity: sha512-Xa4Nw17FS9ApQFJ9umLiJS4orGjm7ZzwUrwamcGQuHSzDyth9boKDaycYdDcZDuqYATXw4HFXgaqWTctW/v1HA==} 798 | engines: {node: '>=16 || 14 >=14.18'} 799 | 800 | pathe@2.0.2: 801 | resolution: {integrity: sha512-15Ztpk+nov8DR524R4BF7uEuzESgzUEAV4Ah7CUMNGXdE5ELuvxElxGXndBl32vMSsWa1jpNf22Z+Er3sKwq+w==} 802 | 803 | pathval@2.0.0: 804 | resolution: {integrity: sha512-vE7JKRyES09KiunauX7nd2Q9/L7lhok4smP9RZTDeD4MVs72Dp2qNFVz39Nz5a0FVEW0BJR6C0DYrq6unoziZA==} 805 | engines: {node: '>= 14.16'} 806 | 807 | picocolors@1.1.1: 808 | resolution: {integrity: sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==} 809 | 810 | picomatch@2.3.1: 811 | resolution: {integrity: sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==} 812 | engines: {node: '>=8.6'} 813 | 814 | picomatch@4.0.2: 815 | resolution: {integrity: sha512-M7BAV6Rlcy5u+m6oPhAPFgJTzAioX/6B0DxyvDlo9l8+T3nLKbrczg2WLUyzd45L8RqfUMyGPzekbMvX2Ldkwg==} 816 | engines: {node: '>=12'} 817 | 818 | pirates@4.0.5: 819 | resolution: {integrity: sha512-8V9+HQPupnaXMA23c5hvl69zXvTwTzyAYasnkb0Tts4XvO4CliqONMOnvlq26rkhLC3nWDFBJf73LU1e1VZLaQ==} 820 | engines: {node: '>= 6'} 821 | 822 | playwright-chromium@1.50.0: 823 | resolution: {integrity: sha512-gyI1ATjSCn0kCHCV8lGS65h0tRSlJvdlwgvXwY5EyUHW+YLPnOtnMaCiNmHgrcVK8ofsXWq3alaUfnmirNzBlA==} 824 | engines: {node: '>=18'} 825 | hasBin: true 826 | 827 | playwright-core@1.50.0: 828 | resolution: {integrity: sha512-CXkSSlr4JaZs2tZHI40DsZUN/NIwgaUPsyLuOAaIZp2CyF2sN5MM5NJsyB188lFSSozFxQ5fPT4qM+f0tH/6wQ==} 829 | engines: {node: '>=18'} 830 | hasBin: true 831 | 832 | postcss-load-config@6.0.1: 833 | resolution: {integrity: sha512-oPtTM4oerL+UXmx+93ytZVN82RrlY/wPUV8IeDxFrzIjXOLF1pN+EmKPLbubvKHT2HC20xXsCAH2Z+CKV6Oz/g==} 834 | engines: {node: '>= 18'} 835 | peerDependencies: 836 | jiti: '>=1.21.0' 837 | postcss: '>=8.0.9' 838 | tsx: ^4.8.1 839 | yaml: ^2.4.2 840 | peerDependenciesMeta: 841 | jiti: 842 | optional: true 843 | postcss: 844 | optional: true 845 | tsx: 846 | optional: true 847 | yaml: 848 | optional: true 849 | 850 | postcss@8.4.49: 851 | resolution: {integrity: sha512-OCVPnIObs4N29kxTjzLfUryOkvZEq+pf8jTF0lg8E7uETuWHA+v7j3c/xJmiqpX450191LlmZfUKkXxkTry7nA==} 852 | engines: {node: ^10 || ^12 || >=14} 853 | 854 | punycode@2.1.1: 855 | resolution: {integrity: sha512-XRsRjdf+j5ml+y/6GKHPZbrF/8p2Yga0JPtdqTIY2Xe5ohJPD9saDJJLPvp9+NSBprVvevdXZybnj2cv8OEd0A==} 856 | engines: {node: '>=6'} 857 | 858 | queue-microtask@1.2.3: 859 | resolution: {integrity: sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A==} 860 | 861 | readdirp@4.1.1: 862 | resolution: {integrity: sha512-h80JrZu/MHUZCyHu5ciuoI0+WxsCxzxJTILn6Fs8rxSnFPh+UVHYfeIxK1nVGugMqkfC4vJcBOYbkfkwYK0+gw==} 863 | engines: {node: '>= 14.18.0'} 864 | 865 | resolve-from@5.0.0: 866 | resolution: {integrity: sha512-qYg9KP24dD5qka9J47d0aVky0N+b4fTU89LN9iDnjB5waksiC49rvMB0PrUJQGoTmH50XPiqOvAjDfaijGxYZw==} 867 | engines: {node: '>=8'} 868 | 869 | reusify@1.0.4: 870 | resolution: {integrity: sha512-U9nH88a3fc/ekCF1l0/UP1IosiuIjyTh7hBvXVMHYgVcfGvt897Xguj2UOLDeI5BG2m7/uwyaLVT6fbtCwTyzw==} 871 | engines: {iojs: '>=1.0.0', node: '>=0.10.0'} 872 | 873 | rollup@4.30.1: 874 | resolution: {integrity: sha512-mlJ4glW020fPuLi7DkM/lN97mYEZGWeqBnrljzN0gs7GLctqX3lNWxKQ7Gl712UAX+6fog/L3jh4gb7R6aVi3w==} 875 | engines: {node: '>=18.0.0', npm: '>=8.0.0'} 876 | hasBin: true 877 | 878 | run-applescript@7.0.0: 879 | resolution: {integrity: sha512-9by4Ij99JUr/MCFBUkDKLWK3G9HVXmabKz9U5MlIAIuvuzkiOicRYs8XJLxX+xahD+mLiiCYDqF9dKAgtzKP1A==} 880 | engines: {node: '>=18'} 881 | 882 | run-parallel@1.2.0: 883 | resolution: {integrity: sha512-5l4VyZR86LZ/lDxZTR6jqL8AFE2S0IFLMP26AbjsLVADxHdhB/c0GUsH+y39UfCi3dzz8OlQuPmnaJOMoDHQBA==} 884 | 885 | shebang-command@2.0.0: 886 | resolution: {integrity: sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==} 887 | engines: {node: '>=8'} 888 | 889 | shebang-regex@3.0.0: 890 | resolution: {integrity: sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==} 891 | engines: {node: '>=8'} 892 | 893 | siginfo@2.0.0: 894 | resolution: {integrity: sha512-ybx0WO1/8bSBLEWXZvEd7gMW3Sn3JFlW3TvX1nREbDLRNQNaeNN8WK0meBwPdAaOI7TtRRRJn/Es1zhrrCHu7g==} 895 | 896 | signal-exit@4.1.0: 897 | resolution: {integrity: sha512-bzyZ1e88w9O1iNJbKnOlvYTrWPDl46O1bG0D3XInv+9tkPrxrN8jUUTiFlDkkmKWgn1M6CfIA13SuGqOa9Korw==} 898 | engines: {node: '>=14'} 899 | 900 | sirv@3.0.0: 901 | resolution: {integrity: sha512-BPwJGUeDaDCHihkORDchNyyTvWFhcusy1XMmhEVTQTwGeybFbp8YEmB+njbPnth1FibULBSBVwCQni25XlCUDg==} 902 | engines: {node: '>=18'} 903 | 904 | source-map-js@1.2.1: 905 | resolution: {integrity: sha512-UXWMKhLOwVKb728IUtQPXxfYU+usdybtUrK/8uGE8CQMvrhOpwvzDBwj0QhSL7MQc7vIsISBG8VQ8+IDQxpfQA==} 906 | engines: {node: '>=0.10.0'} 907 | 908 | source-map@0.8.0-beta.0: 909 | resolution: {integrity: sha512-2ymg6oRBpebeZi9UUNsgQ89bhx01TcTkmNTGnNO88imTmbSgy4nfujrgVEFKWpMTEGA11EDkTt7mqObTPdigIA==} 910 | engines: {node: '>= 8'} 911 | 912 | stackback@0.0.2: 913 | resolution: {integrity: sha512-1XMJE5fQo1jGH6Y/7ebnwPOBEkIEnT4QF32d5R1+VXdXveM0IBMJt8zfaxX1P3QhVwrYe+576+jkANtSS2mBbw==} 914 | 915 | std-env@3.8.0: 916 | resolution: {integrity: sha512-Bc3YwwCB+OzldMxOXJIIvC6cPRWr/LxOp48CdQTOkPyk/t4JWWJbrilwBd7RJzKV8QW7tJkcgAmeuLLJugl5/w==} 917 | 918 | string-width@4.2.3: 919 | resolution: {integrity: sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==} 920 | engines: {node: '>=8'} 921 | 922 | string-width@5.1.2: 923 | resolution: {integrity: sha512-HnLOCR3vjcY8beoNLtcjZ5/nxn2afmME6lhrDrebokqMap+XbeW8n9TXpPDOqdGK5qcI3oT0GKTW6wC7EMiVqA==} 924 | engines: {node: '>=12'} 925 | 926 | strip-ansi@6.0.1: 927 | resolution: {integrity: sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==} 928 | engines: {node: '>=8'} 929 | 930 | strip-ansi@7.1.0: 931 | resolution: {integrity: sha512-iq6eVVI64nQQTRYq2KtEg2d2uU7LElhTJwsH4YzIHZshxlgZms/wIc4VoDQTlG/IvVIrBKG06CrZnp0qv7hkcQ==} 932 | engines: {node: '>=12'} 933 | 934 | sucrase@3.35.0: 935 | resolution: {integrity: sha512-8EbVDiu9iN/nESwxeSxDKe0dunta1GOlHufmSSXxMD2z2/tMZpDMpvXQGsc+ajGo8y2uYUmixaSRUc/QPoQ0GA==} 936 | engines: {node: '>=16 || 14 >=14.17'} 937 | hasBin: true 938 | 939 | supports-color@7.2.0: 940 | resolution: {integrity: sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==} 941 | engines: {node: '>=8'} 942 | 943 | thenify-all@1.6.0: 944 | resolution: {integrity: sha512-RNxQH/qI8/t3thXJDwcstUO4zeqo64+Uy/+sNVRBx4Xn2OX+OZ9oP+iJnNFqplFra2ZUVeKCSa2oVWi3T4uVmA==} 945 | engines: {node: '>=0.8'} 946 | 947 | thenify@3.3.1: 948 | resolution: {integrity: sha512-RVZSIV5IG10Hk3enotrhvz0T9em6cyHBLkH/YAZuKqd8hRkKhSfCGIcP2KUY0EPxndzANBmNllzWPwak+bheSw==} 949 | 950 | tinybench@2.9.0: 951 | resolution: {integrity: sha512-0+DUvqWMValLmha6lr4kD8iAMK1HzV0/aKnCtWb9v9641TnP/MFb7Pc2bxoxQjTXAErryXVgUOfv2YqNllqGeg==} 952 | 953 | tinyexec@0.3.2: 954 | resolution: {integrity: sha512-KQQR9yN7R5+OSwaK0XQoj22pwHoTlgYqmUscPYoknOoWCWfj/5/ABTMRi69FrKU5ffPVh5QcFikpWJI/P1ocHA==} 955 | 956 | tinyglobby@0.2.10: 957 | resolution: {integrity: sha512-Zc+8eJlFMvgatPZTl6A9L/yht8QqdmUNtURHaKZLmKBE12hNPSrqNkUp2cs3M/UKmNVVAMFQYSjYIVHDjW5zew==} 958 | engines: {node: '>=12.0.0'} 959 | 960 | tinypool@1.0.2: 961 | resolution: {integrity: sha512-al6n+QEANGFOMf/dmUMsuS5/r9B06uwlyNjZZql/zv8J7ybHCgoihBNORZCY2mzUuAnomQa2JdhyHKzZxPCrFA==} 962 | engines: {node: ^18.0.0 || >=20.0.0} 963 | 964 | tinyrainbow@2.0.0: 965 | resolution: {integrity: sha512-op4nsTR47R6p0vMUUoYl/a+ljLFVtlfaXkLQmqfLR1qHma1h/ysYk4hEXZ880bf2CYgTskvTa/e196Vd5dDQXw==} 966 | engines: {node: '>=14.0.0'} 967 | 968 | tinyspy@3.0.2: 969 | resolution: {integrity: sha512-n1cw8k1k0x4pgA2+9XrOkFydTerNcJ1zWCO5Nn9scWHTD+5tp8dghT2x1uduQePZTZgd3Tupf+x9BxJjeJi77Q==} 970 | engines: {node: '>=14.0.0'} 971 | 972 | to-regex-range@5.0.1: 973 | resolution: {integrity: sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==} 974 | engines: {node: '>=8.0'} 975 | 976 | totalist@3.0.0: 977 | resolution: {integrity: sha512-eM+pCBxXO/njtF7vdFsHuqb+ElbxqtI4r5EAvk6grfAFyJ6IvWlSkfZ5T9ozC6xWw3Fj1fGoSmrl0gUs46JVIw==} 978 | engines: {node: '>=6'} 979 | 980 | tr46@1.0.1: 981 | resolution: {integrity: sha512-dTpowEjclQ7Kgx5SdBkqRzVhERQXov8/l9Ft9dVM9fmg0W0KQSVaXX9T4i6twCPNtYiZM53lpSSUAwJbFPOHxA==} 982 | 983 | tree-kill@1.2.2: 984 | resolution: {integrity: sha512-L0Orpi8qGpRG//Nd+H90vFB+3iHnue1zSSGmNOOCh1GLJ7rUKVwV2HvijphGQS2UmhUZewS9VgvxYIdgr+fG1A==} 985 | hasBin: true 986 | 987 | ts-interface-checker@0.1.13: 988 | resolution: {integrity: sha512-Y/arvbn+rrz3JCKl9C4kVNfTfSm2/mEp5FSz5EsZSANGPSlQrpRI5M4PKF+mJnE52jOO90PnPSc3Ur3bTQw0gA==} 989 | 990 | tsup@8.3.6: 991 | resolution: {integrity: sha512-XkVtlDV/58S9Ye0JxUUTcrQk4S+EqlOHKzg6Roa62rdjL1nGWNUstG0xgI4vanHdfIpjP448J8vlN0oK6XOJ5g==} 992 | engines: {node: '>=18'} 993 | hasBin: true 994 | peerDependencies: 995 | '@microsoft/api-extractor': ^7.36.0 996 | '@swc/core': ^1 997 | postcss: ^8.4.12 998 | typescript: '>=4.5.0' 999 | peerDependenciesMeta: 1000 | '@microsoft/api-extractor': 1001 | optional: true 1002 | '@swc/core': 1003 | optional: true 1004 | postcss: 1005 | optional: true 1006 | typescript: 1007 | optional: true 1008 | 1009 | typescript@5.7.3: 1010 | resolution: {integrity: sha512-84MVSjMEHP+FQRPy3pX9sTVV/INIex71s9TL2Gm5FG/WG1SqXeKyZ0k7/blY/4FdOzI12CBy1vGc4og/eus0fw==} 1011 | engines: {node: '>=14.17'} 1012 | hasBin: true 1013 | 1014 | undici-types@6.20.0: 1015 | resolution: {integrity: sha512-Ny6QZ2Nju20vw1SRHe3d9jVu6gJ+4e3+MMpqu7pqE5HT6WsTSlce++GQmK5UXS8mzV8DSYHrQH+Xrf2jVcuKNg==} 1016 | 1017 | vite-node@3.0.4: 1018 | resolution: {integrity: sha512-7JZKEzcYV2Nx3u6rlvN8qdo3QV7Fxyt6hx+CCKz9fbWxdX5IvUOmTWEAxMrWxaiSf7CKGLJQ5rFu8prb/jBjOA==} 1019 | engines: {node: ^18.0.0 || ^20.0.0 || >=22.0.0} 1020 | hasBin: true 1021 | 1022 | vite-plugin-inspect@10.1.0: 1023 | resolution: {integrity: sha512-solJQhkZULyR7Qq2CRGbO/8ijNPTwmxxLDMx3FkMzGQAuVqKrgqmV2cw/u8SXBsKDHgSMykipW+78MBMZz3O0g==} 1024 | engines: {node: '>=14'} 1025 | peerDependencies: 1026 | '@nuxt/kit': '*' 1027 | vite: ^6.0.0 1028 | peerDependenciesMeta: 1029 | '@nuxt/kit': 1030 | optional: true 1031 | 1032 | vite@6.0.11: 1033 | resolution: {integrity: sha512-4VL9mQPKoHy4+FE0NnRE/kbY51TOfaknxAjt3fJbGJxhIpBZiqVzlZDEesWWsuREXHwNdAoOFZ9MkPEVXczHwg==} 1034 | engines: {node: ^18.0.0 || ^20.0.0 || >=22.0.0} 1035 | hasBin: true 1036 | peerDependencies: 1037 | '@types/node': ^18.0.0 || ^20.0.0 || >=22.0.0 1038 | jiti: '>=1.21.0' 1039 | less: '*' 1040 | lightningcss: ^1.21.0 1041 | sass: '*' 1042 | sass-embedded: '*' 1043 | stylus: '*' 1044 | sugarss: '*' 1045 | terser: ^5.16.0 1046 | tsx: ^4.8.1 1047 | yaml: ^2.4.2 1048 | peerDependenciesMeta: 1049 | '@types/node': 1050 | optional: true 1051 | jiti: 1052 | optional: true 1053 | less: 1054 | optional: true 1055 | lightningcss: 1056 | optional: true 1057 | sass: 1058 | optional: true 1059 | sass-embedded: 1060 | optional: true 1061 | stylus: 1062 | optional: true 1063 | sugarss: 1064 | optional: true 1065 | terser: 1066 | optional: true 1067 | tsx: 1068 | optional: true 1069 | yaml: 1070 | optional: true 1071 | 1072 | vitest@3.0.4: 1073 | resolution: {integrity: sha512-6XG8oTKy2gnJIFTHP6LD7ExFeNLxiTkK3CfMvT7IfR8IN+BYICCf0lXUQmX7i7JoxUP8QmeP4mTnWXgflu4yjw==} 1074 | engines: {node: ^18.0.0 || ^20.0.0 || >=22.0.0} 1075 | hasBin: true 1076 | peerDependencies: 1077 | '@edge-runtime/vm': '*' 1078 | '@types/debug': ^4.1.12 1079 | '@types/node': ^18.0.0 || ^20.0.0 || >=22.0.0 1080 | '@vitest/browser': 3.0.4 1081 | '@vitest/ui': 3.0.4 1082 | happy-dom: '*' 1083 | jsdom: '*' 1084 | peerDependenciesMeta: 1085 | '@edge-runtime/vm': 1086 | optional: true 1087 | '@types/debug': 1088 | optional: true 1089 | '@types/node': 1090 | optional: true 1091 | '@vitest/browser': 1092 | optional: true 1093 | '@vitest/ui': 1094 | optional: true 1095 | happy-dom: 1096 | optional: true 1097 | jsdom: 1098 | optional: true 1099 | 1100 | vue-router@4.5.0: 1101 | resolution: {integrity: sha512-HDuk+PuH5monfNuY+ct49mNmkCRK4xJAV9Ts4z9UFc4rzdDnxQLyCMGGc8pKhZhHTVzfanpNwB/lwqevcBwI4w==} 1102 | peerDependencies: 1103 | vue: ^3.2.0 1104 | 1105 | vue@3.5.13: 1106 | resolution: {integrity: sha512-wmeiSMxkZCSc+PM2w2VRsOYAZC8GdipNFRTsLSfodVqI9mbejKeXEGr8SckuLnrQPGe3oJN5c3K0vpoU9q/wCQ==} 1107 | peerDependencies: 1108 | typescript: '*' 1109 | peerDependenciesMeta: 1110 | typescript: 1111 | optional: true 1112 | 1113 | webidl-conversions@4.0.2: 1114 | resolution: {integrity: sha512-YQ+BmxuTgd6UXZW3+ICGfyqRyHXVlD5GtQr5+qjiNW7bF0cqrzX500HVXPBOvgXb5YnzDd+h0zqyv61KUD7+Sg==} 1115 | 1116 | whatwg-url@7.1.0: 1117 | resolution: {integrity: sha512-WUu7Rg1DroM7oQvGWfOiAK21n74Gg+T4elXEQYkOhtyLeWiJFoOGLXPKI/9gzIie9CtwVLm8wtw6YJdKyxSjeg==} 1118 | 1119 | which@2.0.2: 1120 | resolution: {integrity: sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==} 1121 | engines: {node: '>= 8'} 1122 | hasBin: true 1123 | 1124 | why-is-node-running@2.3.0: 1125 | resolution: {integrity: sha512-hUrmaWBdVDcxvYqnyh09zunKzROWjbZTiNy8dBEjkS7ehEDQibXJ7XvlmtbwuTclUiIyN+CyXQD4Vmko8fNm8w==} 1126 | engines: {node: '>=8'} 1127 | hasBin: true 1128 | 1129 | wrap-ansi@7.0.0: 1130 | resolution: {integrity: sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==} 1131 | engines: {node: '>=10'} 1132 | 1133 | wrap-ansi@8.1.0: 1134 | resolution: {integrity: sha512-si7QWI6zUMq56bESFvagtmzMdGOtoxfR+Sez11Mobfc7tm+VkUckk9bW2UeffTGVUbOksxmSw0AA2gs8g71NCQ==} 1135 | engines: {node: '>=12'} 1136 | 1137 | snapshots: 1138 | 1139 | '@babel/helper-string-parser@7.25.9': {} 1140 | 1141 | '@babel/helper-validator-identifier@7.25.9': {} 1142 | 1143 | '@babel/parser@7.26.5': 1144 | dependencies: 1145 | '@babel/types': 7.26.5 1146 | 1147 | '@babel/types@7.26.5': 1148 | dependencies: 1149 | '@babel/helper-string-parser': 7.25.9 1150 | '@babel/helper-validator-identifier': 7.25.9 1151 | 1152 | '@esbuild/aix-ppc64@0.24.2': 1153 | optional: true 1154 | 1155 | '@esbuild/android-arm64@0.24.2': 1156 | optional: true 1157 | 1158 | '@esbuild/android-arm@0.24.2': 1159 | optional: true 1160 | 1161 | '@esbuild/android-x64@0.24.2': 1162 | optional: true 1163 | 1164 | '@esbuild/darwin-arm64@0.24.2': 1165 | optional: true 1166 | 1167 | '@esbuild/darwin-x64@0.24.2': 1168 | optional: true 1169 | 1170 | '@esbuild/freebsd-arm64@0.24.2': 1171 | optional: true 1172 | 1173 | '@esbuild/freebsd-x64@0.24.2': 1174 | optional: true 1175 | 1176 | '@esbuild/linux-arm64@0.24.2': 1177 | optional: true 1178 | 1179 | '@esbuild/linux-arm@0.24.2': 1180 | optional: true 1181 | 1182 | '@esbuild/linux-ia32@0.24.2': 1183 | optional: true 1184 | 1185 | '@esbuild/linux-loong64@0.24.2': 1186 | optional: true 1187 | 1188 | '@esbuild/linux-mips64el@0.24.2': 1189 | optional: true 1190 | 1191 | '@esbuild/linux-ppc64@0.24.2': 1192 | optional: true 1193 | 1194 | '@esbuild/linux-riscv64@0.24.2': 1195 | optional: true 1196 | 1197 | '@esbuild/linux-s390x@0.24.2': 1198 | optional: true 1199 | 1200 | '@esbuild/linux-x64@0.24.2': 1201 | optional: true 1202 | 1203 | '@esbuild/netbsd-arm64@0.24.2': 1204 | optional: true 1205 | 1206 | '@esbuild/netbsd-x64@0.24.2': 1207 | optional: true 1208 | 1209 | '@esbuild/openbsd-arm64@0.24.2': 1210 | optional: true 1211 | 1212 | '@esbuild/openbsd-x64@0.24.2': 1213 | optional: true 1214 | 1215 | '@esbuild/sunos-x64@0.24.2': 1216 | optional: true 1217 | 1218 | '@esbuild/win32-arm64@0.24.2': 1219 | optional: true 1220 | 1221 | '@esbuild/win32-ia32@0.24.2': 1222 | optional: true 1223 | 1224 | '@esbuild/win32-x64@0.24.2': 1225 | optional: true 1226 | 1227 | '@isaacs/cliui@8.0.2': 1228 | dependencies: 1229 | string-width: 5.1.2 1230 | string-width-cjs: string-width@4.2.3 1231 | strip-ansi: 7.1.0 1232 | strip-ansi-cjs: strip-ansi@6.0.1 1233 | wrap-ansi: 8.1.0 1234 | wrap-ansi-cjs: wrap-ansi@7.0.0 1235 | 1236 | '@jridgewell/gen-mapping@0.3.8': 1237 | dependencies: 1238 | '@jridgewell/set-array': 1.2.1 1239 | '@jridgewell/sourcemap-codec': 1.5.0 1240 | '@jridgewell/trace-mapping': 0.3.25 1241 | 1242 | '@jridgewell/resolve-uri@3.1.2': {} 1243 | 1244 | '@jridgewell/set-array@1.2.1': {} 1245 | 1246 | '@jridgewell/sourcemap-codec@1.5.0': {} 1247 | 1248 | '@jridgewell/trace-mapping@0.3.25': 1249 | dependencies: 1250 | '@jridgewell/resolve-uri': 3.1.2 1251 | '@jridgewell/sourcemap-codec': 1.5.0 1252 | 1253 | '@nodelib/fs.scandir@2.1.5': 1254 | dependencies: 1255 | '@nodelib/fs.stat': 2.0.5 1256 | run-parallel: 1.2.0 1257 | 1258 | '@nodelib/fs.stat@2.0.5': {} 1259 | 1260 | '@nodelib/fs.walk@1.2.8': 1261 | dependencies: 1262 | '@nodelib/fs.scandir': 2.1.5 1263 | fastq: 1.13.0 1264 | 1265 | '@pkgjs/parseargs@0.11.0': 1266 | optional: true 1267 | 1268 | '@polka/url@1.0.0-next.24': {} 1269 | 1270 | '@rollup/rollup-android-arm-eabi@4.30.1': 1271 | optional: true 1272 | 1273 | '@rollup/rollup-android-arm64@4.30.1': 1274 | optional: true 1275 | 1276 | '@rollup/rollup-darwin-arm64@4.30.1': 1277 | optional: true 1278 | 1279 | '@rollup/rollup-darwin-x64@4.30.1': 1280 | optional: true 1281 | 1282 | '@rollup/rollup-freebsd-arm64@4.30.1': 1283 | optional: true 1284 | 1285 | '@rollup/rollup-freebsd-x64@4.30.1': 1286 | optional: true 1287 | 1288 | '@rollup/rollup-linux-arm-gnueabihf@4.30.1': 1289 | optional: true 1290 | 1291 | '@rollup/rollup-linux-arm-musleabihf@4.30.1': 1292 | optional: true 1293 | 1294 | '@rollup/rollup-linux-arm64-gnu@4.30.1': 1295 | optional: true 1296 | 1297 | '@rollup/rollup-linux-arm64-musl@4.30.1': 1298 | optional: true 1299 | 1300 | '@rollup/rollup-linux-loongarch64-gnu@4.30.1': 1301 | optional: true 1302 | 1303 | '@rollup/rollup-linux-powerpc64le-gnu@4.30.1': 1304 | optional: true 1305 | 1306 | '@rollup/rollup-linux-riscv64-gnu@4.30.1': 1307 | optional: true 1308 | 1309 | '@rollup/rollup-linux-s390x-gnu@4.30.1': 1310 | optional: true 1311 | 1312 | '@rollup/rollup-linux-x64-gnu@4.30.1': 1313 | optional: true 1314 | 1315 | '@rollup/rollup-linux-x64-musl@4.30.1': 1316 | optional: true 1317 | 1318 | '@rollup/rollup-win32-arm64-msvc@4.30.1': 1319 | optional: true 1320 | 1321 | '@rollup/rollup-win32-ia32-msvc@4.30.1': 1322 | optional: true 1323 | 1324 | '@rollup/rollup-win32-x64-msvc@4.30.1': 1325 | optional: true 1326 | 1327 | '@types/connect-history-api-fallback@1.5.4': 1328 | dependencies: 1329 | '@types/express-serve-static-core': 4.17.33 1330 | '@types/node': 22.12.0 1331 | 1332 | '@types/debug@4.1.12': 1333 | dependencies: 1334 | '@types/ms': 0.7.31 1335 | 1336 | '@types/ejs@3.1.5': {} 1337 | 1338 | '@types/estree@1.0.6': {} 1339 | 1340 | '@types/express-serve-static-core@4.17.33': 1341 | dependencies: 1342 | '@types/node': 22.12.0 1343 | '@types/qs': 6.9.7 1344 | '@types/range-parser': 1.2.4 1345 | 1346 | '@types/ms@0.7.31': {} 1347 | 1348 | '@types/node@22.12.0': 1349 | dependencies: 1350 | undici-types: 6.20.0 1351 | 1352 | '@types/qs@6.9.7': {} 1353 | 1354 | '@types/range-parser@1.2.4': {} 1355 | 1356 | '@vitejs/plugin-vue@5.2.1(vite@6.0.11(@types/node@22.12.0))(vue@3.5.13(typescript@5.7.3))': 1357 | dependencies: 1358 | vite: 6.0.11(@types/node@22.12.0) 1359 | vue: 3.5.13(typescript@5.7.3) 1360 | 1361 | '@vitest/expect@3.0.4': 1362 | dependencies: 1363 | '@vitest/spy': 3.0.4 1364 | '@vitest/utils': 3.0.4 1365 | chai: 5.1.2 1366 | tinyrainbow: 2.0.0 1367 | 1368 | '@vitest/mocker@3.0.4(vite@6.0.11(@types/node@22.12.0))': 1369 | dependencies: 1370 | '@vitest/spy': 3.0.4 1371 | estree-walker: 3.0.3 1372 | magic-string: 0.30.17 1373 | optionalDependencies: 1374 | vite: 6.0.11(@types/node@22.12.0) 1375 | 1376 | '@vitest/pretty-format@3.0.4': 1377 | dependencies: 1378 | tinyrainbow: 2.0.0 1379 | 1380 | '@vitest/runner@3.0.4': 1381 | dependencies: 1382 | '@vitest/utils': 3.0.4 1383 | pathe: 2.0.2 1384 | 1385 | '@vitest/snapshot@3.0.4': 1386 | dependencies: 1387 | '@vitest/pretty-format': 3.0.4 1388 | magic-string: 0.30.17 1389 | pathe: 2.0.2 1390 | 1391 | '@vitest/spy@3.0.4': 1392 | dependencies: 1393 | tinyspy: 3.0.2 1394 | 1395 | '@vitest/utils@3.0.4': 1396 | dependencies: 1397 | '@vitest/pretty-format': 3.0.4 1398 | loupe: 3.1.3 1399 | tinyrainbow: 2.0.0 1400 | 1401 | '@vue/compiler-core@3.5.13': 1402 | dependencies: 1403 | '@babel/parser': 7.26.5 1404 | '@vue/shared': 3.5.13 1405 | entities: 4.5.0 1406 | estree-walker: 2.0.2 1407 | source-map-js: 1.2.1 1408 | 1409 | '@vue/compiler-dom@3.5.13': 1410 | dependencies: 1411 | '@vue/compiler-core': 3.5.13 1412 | '@vue/shared': 3.5.13 1413 | 1414 | '@vue/compiler-sfc@3.5.13': 1415 | dependencies: 1416 | '@babel/parser': 7.26.5 1417 | '@vue/compiler-core': 3.5.13 1418 | '@vue/compiler-dom': 3.5.13 1419 | '@vue/compiler-ssr': 3.5.13 1420 | '@vue/shared': 3.5.13 1421 | estree-walker: 2.0.2 1422 | magic-string: 0.30.17 1423 | postcss: 8.4.49 1424 | source-map-js: 1.2.1 1425 | 1426 | '@vue/compiler-ssr@3.5.13': 1427 | dependencies: 1428 | '@vue/compiler-dom': 3.5.13 1429 | '@vue/shared': 3.5.13 1430 | 1431 | '@vue/devtools-api@6.6.4': {} 1432 | 1433 | '@vue/reactivity@3.5.13': 1434 | dependencies: 1435 | '@vue/shared': 3.5.13 1436 | 1437 | '@vue/runtime-core@3.5.13': 1438 | dependencies: 1439 | '@vue/reactivity': 3.5.13 1440 | '@vue/shared': 3.5.13 1441 | 1442 | '@vue/runtime-dom@3.5.13': 1443 | dependencies: 1444 | '@vue/reactivity': 3.5.13 1445 | '@vue/runtime-core': 3.5.13 1446 | '@vue/shared': 3.5.13 1447 | csstype: 3.1.3 1448 | 1449 | '@vue/server-renderer@3.5.13(vue@3.5.13(typescript@5.7.3))': 1450 | dependencies: 1451 | '@vue/compiler-ssr': 3.5.13 1452 | '@vue/shared': 3.5.13 1453 | vue: 3.5.13(typescript@5.7.3) 1454 | 1455 | '@vue/shared@3.5.13': {} 1456 | 1457 | ansi-regex@5.0.1: {} 1458 | 1459 | ansi-regex@6.1.0: {} 1460 | 1461 | ansi-styles@4.3.0: 1462 | dependencies: 1463 | color-convert: 2.0.1 1464 | 1465 | ansi-styles@6.2.1: {} 1466 | 1467 | any-promise@1.3.0: {} 1468 | 1469 | assertion-error@2.0.1: {} 1470 | 1471 | async@3.2.4: {} 1472 | 1473 | balanced-match@1.0.2: {} 1474 | 1475 | brace-expansion@1.1.11: 1476 | dependencies: 1477 | balanced-match: 1.0.2 1478 | concat-map: 0.0.1 1479 | 1480 | brace-expansion@2.0.1: 1481 | dependencies: 1482 | balanced-match: 1.0.2 1483 | 1484 | braces@3.0.3: 1485 | dependencies: 1486 | fill-range: 7.1.1 1487 | 1488 | bundle-name@4.1.0: 1489 | dependencies: 1490 | run-applescript: 7.0.0 1491 | 1492 | bundle-require@5.1.0(esbuild@0.24.2): 1493 | dependencies: 1494 | esbuild: 0.24.2 1495 | load-tsconfig: 0.2.3 1496 | 1497 | cac@6.7.14: {} 1498 | 1499 | chai@5.1.2: 1500 | dependencies: 1501 | assertion-error: 2.0.1 1502 | check-error: 2.1.1 1503 | deep-eql: 5.0.2 1504 | loupe: 3.1.3 1505 | pathval: 2.0.0 1506 | 1507 | chalk@4.1.2: 1508 | dependencies: 1509 | ansi-styles: 4.3.0 1510 | supports-color: 7.2.0 1511 | 1512 | check-error@2.1.1: {} 1513 | 1514 | chokidar@4.0.3: 1515 | dependencies: 1516 | readdirp: 4.1.1 1517 | 1518 | color-convert@2.0.1: 1519 | dependencies: 1520 | color-name: 1.1.4 1521 | 1522 | color-name@1.1.4: {} 1523 | 1524 | commander@4.1.1: {} 1525 | 1526 | concat-map@0.0.1: {} 1527 | 1528 | connect-history-api-fallback@2.0.0: {} 1529 | 1530 | consola@3.4.0: {} 1531 | 1532 | cross-spawn@7.0.3: 1533 | dependencies: 1534 | path-key: 3.1.1 1535 | shebang-command: 2.0.0 1536 | which: 2.0.2 1537 | 1538 | csstype@3.1.3: {} 1539 | 1540 | debug@4.4.0: 1541 | dependencies: 1542 | ms: 2.1.3 1543 | 1544 | deep-eql@5.0.2: {} 1545 | 1546 | default-browser-id@5.0.0: {} 1547 | 1548 | default-browser@5.2.1: 1549 | dependencies: 1550 | bundle-name: 4.1.0 1551 | default-browser-id: 5.0.0 1552 | 1553 | define-lazy-prop@3.0.0: {} 1554 | 1555 | eastasianwidth@0.2.0: {} 1556 | 1557 | ejs@3.1.10: 1558 | dependencies: 1559 | jake: 10.8.5 1560 | 1561 | emoji-regex@8.0.0: {} 1562 | 1563 | emoji-regex@9.2.2: {} 1564 | 1565 | entities@4.5.0: {} 1566 | 1567 | error-stack-parser-es@1.0.5: {} 1568 | 1569 | es-module-lexer@1.6.0: {} 1570 | 1571 | esbuild@0.24.2: 1572 | optionalDependencies: 1573 | '@esbuild/aix-ppc64': 0.24.2 1574 | '@esbuild/android-arm': 0.24.2 1575 | '@esbuild/android-arm64': 0.24.2 1576 | '@esbuild/android-x64': 0.24.2 1577 | '@esbuild/darwin-arm64': 0.24.2 1578 | '@esbuild/darwin-x64': 0.24.2 1579 | '@esbuild/freebsd-arm64': 0.24.2 1580 | '@esbuild/freebsd-x64': 0.24.2 1581 | '@esbuild/linux-arm': 0.24.2 1582 | '@esbuild/linux-arm64': 0.24.2 1583 | '@esbuild/linux-ia32': 0.24.2 1584 | '@esbuild/linux-loong64': 0.24.2 1585 | '@esbuild/linux-mips64el': 0.24.2 1586 | '@esbuild/linux-ppc64': 0.24.2 1587 | '@esbuild/linux-riscv64': 0.24.2 1588 | '@esbuild/linux-s390x': 0.24.2 1589 | '@esbuild/linux-x64': 0.24.2 1590 | '@esbuild/netbsd-arm64': 0.24.2 1591 | '@esbuild/netbsd-x64': 0.24.2 1592 | '@esbuild/openbsd-arm64': 0.24.2 1593 | '@esbuild/openbsd-x64': 0.24.2 1594 | '@esbuild/sunos-x64': 0.24.2 1595 | '@esbuild/win32-arm64': 0.24.2 1596 | '@esbuild/win32-ia32': 0.24.2 1597 | '@esbuild/win32-x64': 0.24.2 1598 | 1599 | estree-walker@2.0.2: {} 1600 | 1601 | estree-walker@3.0.3: 1602 | dependencies: 1603 | '@types/estree': 1.0.6 1604 | 1605 | expect-type@1.1.0: {} 1606 | 1607 | fast-glob@3.3.3: 1608 | dependencies: 1609 | '@nodelib/fs.stat': 2.0.5 1610 | '@nodelib/fs.walk': 1.2.8 1611 | glob-parent: 5.1.2 1612 | merge2: 1.4.1 1613 | micromatch: 4.0.8 1614 | 1615 | fastq@1.13.0: 1616 | dependencies: 1617 | reusify: 1.0.4 1618 | 1619 | fdir@6.4.3(picomatch@4.0.2): 1620 | optionalDependencies: 1621 | picomatch: 4.0.2 1622 | 1623 | filelist@1.0.4: 1624 | dependencies: 1625 | minimatch: 5.1.1 1626 | 1627 | fill-range@7.1.1: 1628 | dependencies: 1629 | to-regex-range: 5.0.1 1630 | 1631 | foreground-child@3.3.0: 1632 | dependencies: 1633 | cross-spawn: 7.0.3 1634 | signal-exit: 4.1.0 1635 | 1636 | fsevents@2.3.3: 1637 | optional: true 1638 | 1639 | glob-parent@5.1.2: 1640 | dependencies: 1641 | is-glob: 4.0.3 1642 | 1643 | glob@10.4.5: 1644 | dependencies: 1645 | foreground-child: 3.3.0 1646 | jackspeak: 3.4.3 1647 | minimatch: 9.0.5 1648 | minipass: 7.1.2 1649 | package-json-from-dist: 1.0.1 1650 | path-scurry: 1.11.1 1651 | 1652 | has-flag@4.0.0: {} 1653 | 1654 | is-docker@3.0.0: {} 1655 | 1656 | is-extglob@2.1.1: {} 1657 | 1658 | is-fullwidth-code-point@3.0.0: {} 1659 | 1660 | is-glob@4.0.3: 1661 | dependencies: 1662 | is-extglob: 2.1.1 1663 | 1664 | is-inside-container@1.0.0: 1665 | dependencies: 1666 | is-docker: 3.0.0 1667 | 1668 | is-number@7.0.0: {} 1669 | 1670 | is-wsl@3.1.0: 1671 | dependencies: 1672 | is-inside-container: 1.0.0 1673 | 1674 | isexe@2.0.0: {} 1675 | 1676 | jackspeak@3.4.3: 1677 | dependencies: 1678 | '@isaacs/cliui': 8.0.2 1679 | optionalDependencies: 1680 | '@pkgjs/parseargs': 0.11.0 1681 | 1682 | jake@10.8.5: 1683 | dependencies: 1684 | async: 3.2.4 1685 | chalk: 4.1.2 1686 | filelist: 1.0.4 1687 | minimatch: 3.1.2 1688 | 1689 | joycon@3.1.1: {} 1690 | 1691 | lilconfig@3.1.3: {} 1692 | 1693 | lines-and-columns@1.2.4: {} 1694 | 1695 | load-tsconfig@0.2.3: {} 1696 | 1697 | lodash.sortby@4.7.0: {} 1698 | 1699 | loupe@3.1.3: {} 1700 | 1701 | lru-cache@10.4.3: {} 1702 | 1703 | magic-string@0.30.17: 1704 | dependencies: 1705 | '@jridgewell/sourcemap-codec': 1.5.0 1706 | 1707 | merge2@1.4.1: {} 1708 | 1709 | micromatch@4.0.8: 1710 | dependencies: 1711 | braces: 3.0.3 1712 | picomatch: 2.3.1 1713 | 1714 | minimatch@3.1.2: 1715 | dependencies: 1716 | brace-expansion: 1.1.11 1717 | 1718 | minimatch@5.1.1: 1719 | dependencies: 1720 | brace-expansion: 2.0.1 1721 | 1722 | minimatch@9.0.5: 1723 | dependencies: 1724 | brace-expansion: 2.0.1 1725 | 1726 | minipass@7.1.2: {} 1727 | 1728 | mrmime@2.0.0: {} 1729 | 1730 | ms@2.1.3: {} 1731 | 1732 | mz@2.7.0: 1733 | dependencies: 1734 | any-promise: 1.3.0 1735 | object-assign: 4.1.1 1736 | thenify-all: 1.6.0 1737 | 1738 | nanoid@3.3.7: {} 1739 | 1740 | object-assign@4.1.1: {} 1741 | 1742 | open@10.1.0: 1743 | dependencies: 1744 | default-browser: 5.2.1 1745 | define-lazy-prop: 3.0.0 1746 | is-inside-container: 1.0.0 1747 | is-wsl: 3.1.0 1748 | 1749 | package-json-from-dist@1.0.1: {} 1750 | 1751 | path-key@3.1.1: {} 1752 | 1753 | path-scurry@1.11.1: 1754 | dependencies: 1755 | lru-cache: 10.4.3 1756 | minipass: 7.1.2 1757 | 1758 | pathe@2.0.2: {} 1759 | 1760 | pathval@2.0.0: {} 1761 | 1762 | picocolors@1.1.1: {} 1763 | 1764 | picomatch@2.3.1: {} 1765 | 1766 | picomatch@4.0.2: {} 1767 | 1768 | pirates@4.0.5: {} 1769 | 1770 | playwright-chromium@1.50.0: 1771 | dependencies: 1772 | playwright-core: 1.50.0 1773 | 1774 | playwright-core@1.50.0: {} 1775 | 1776 | postcss-load-config@6.0.1(postcss@8.4.49): 1777 | dependencies: 1778 | lilconfig: 3.1.3 1779 | optionalDependencies: 1780 | postcss: 8.4.49 1781 | 1782 | postcss@8.4.49: 1783 | dependencies: 1784 | nanoid: 3.3.7 1785 | picocolors: 1.1.1 1786 | source-map-js: 1.2.1 1787 | 1788 | punycode@2.1.1: {} 1789 | 1790 | queue-microtask@1.2.3: {} 1791 | 1792 | readdirp@4.1.1: {} 1793 | 1794 | resolve-from@5.0.0: {} 1795 | 1796 | reusify@1.0.4: {} 1797 | 1798 | rollup@4.30.1: 1799 | dependencies: 1800 | '@types/estree': 1.0.6 1801 | optionalDependencies: 1802 | '@rollup/rollup-android-arm-eabi': 4.30.1 1803 | '@rollup/rollup-android-arm64': 4.30.1 1804 | '@rollup/rollup-darwin-arm64': 4.30.1 1805 | '@rollup/rollup-darwin-x64': 4.30.1 1806 | '@rollup/rollup-freebsd-arm64': 4.30.1 1807 | '@rollup/rollup-freebsd-x64': 4.30.1 1808 | '@rollup/rollup-linux-arm-gnueabihf': 4.30.1 1809 | '@rollup/rollup-linux-arm-musleabihf': 4.30.1 1810 | '@rollup/rollup-linux-arm64-gnu': 4.30.1 1811 | '@rollup/rollup-linux-arm64-musl': 4.30.1 1812 | '@rollup/rollup-linux-loongarch64-gnu': 4.30.1 1813 | '@rollup/rollup-linux-powerpc64le-gnu': 4.30.1 1814 | '@rollup/rollup-linux-riscv64-gnu': 4.30.1 1815 | '@rollup/rollup-linux-s390x-gnu': 4.30.1 1816 | '@rollup/rollup-linux-x64-gnu': 4.30.1 1817 | '@rollup/rollup-linux-x64-musl': 4.30.1 1818 | '@rollup/rollup-win32-arm64-msvc': 4.30.1 1819 | '@rollup/rollup-win32-ia32-msvc': 4.30.1 1820 | '@rollup/rollup-win32-x64-msvc': 4.30.1 1821 | fsevents: 2.3.3 1822 | 1823 | run-applescript@7.0.0: {} 1824 | 1825 | run-parallel@1.2.0: 1826 | dependencies: 1827 | queue-microtask: 1.2.3 1828 | 1829 | shebang-command@2.0.0: 1830 | dependencies: 1831 | shebang-regex: 3.0.0 1832 | 1833 | shebang-regex@3.0.0: {} 1834 | 1835 | siginfo@2.0.0: {} 1836 | 1837 | signal-exit@4.1.0: {} 1838 | 1839 | sirv@3.0.0: 1840 | dependencies: 1841 | '@polka/url': 1.0.0-next.24 1842 | mrmime: 2.0.0 1843 | totalist: 3.0.0 1844 | 1845 | source-map-js@1.2.1: {} 1846 | 1847 | source-map@0.8.0-beta.0: 1848 | dependencies: 1849 | whatwg-url: 7.1.0 1850 | 1851 | stackback@0.0.2: {} 1852 | 1853 | std-env@3.8.0: {} 1854 | 1855 | string-width@4.2.3: 1856 | dependencies: 1857 | emoji-regex: 8.0.0 1858 | is-fullwidth-code-point: 3.0.0 1859 | strip-ansi: 6.0.1 1860 | 1861 | string-width@5.1.2: 1862 | dependencies: 1863 | eastasianwidth: 0.2.0 1864 | emoji-regex: 9.2.2 1865 | strip-ansi: 7.1.0 1866 | 1867 | strip-ansi@6.0.1: 1868 | dependencies: 1869 | ansi-regex: 5.0.1 1870 | 1871 | strip-ansi@7.1.0: 1872 | dependencies: 1873 | ansi-regex: 6.1.0 1874 | 1875 | sucrase@3.35.0: 1876 | dependencies: 1877 | '@jridgewell/gen-mapping': 0.3.8 1878 | commander: 4.1.1 1879 | glob: 10.4.5 1880 | lines-and-columns: 1.2.4 1881 | mz: 2.7.0 1882 | pirates: 4.0.5 1883 | ts-interface-checker: 0.1.13 1884 | 1885 | supports-color@7.2.0: 1886 | dependencies: 1887 | has-flag: 4.0.0 1888 | 1889 | thenify-all@1.6.0: 1890 | dependencies: 1891 | thenify: 3.3.1 1892 | 1893 | thenify@3.3.1: 1894 | dependencies: 1895 | any-promise: 1.3.0 1896 | 1897 | tinybench@2.9.0: {} 1898 | 1899 | tinyexec@0.3.2: {} 1900 | 1901 | tinyglobby@0.2.10: 1902 | dependencies: 1903 | fdir: 6.4.3(picomatch@4.0.2) 1904 | picomatch: 4.0.2 1905 | 1906 | tinypool@1.0.2: {} 1907 | 1908 | tinyrainbow@2.0.0: {} 1909 | 1910 | tinyspy@3.0.2: {} 1911 | 1912 | to-regex-range@5.0.1: 1913 | dependencies: 1914 | is-number: 7.0.0 1915 | 1916 | totalist@3.0.0: {} 1917 | 1918 | tr46@1.0.1: 1919 | dependencies: 1920 | punycode: 2.1.1 1921 | 1922 | tree-kill@1.2.2: {} 1923 | 1924 | ts-interface-checker@0.1.13: {} 1925 | 1926 | tsup@8.3.6(postcss@8.4.49)(typescript@5.7.3): 1927 | dependencies: 1928 | bundle-require: 5.1.0(esbuild@0.24.2) 1929 | cac: 6.7.14 1930 | chokidar: 4.0.3 1931 | consola: 3.4.0 1932 | debug: 4.4.0 1933 | esbuild: 0.24.2 1934 | joycon: 3.1.1 1935 | picocolors: 1.1.1 1936 | postcss-load-config: 6.0.1(postcss@8.4.49) 1937 | resolve-from: 5.0.0 1938 | rollup: 4.30.1 1939 | source-map: 0.8.0-beta.0 1940 | sucrase: 3.35.0 1941 | tinyexec: 0.3.2 1942 | tinyglobby: 0.2.10 1943 | tree-kill: 1.2.2 1944 | optionalDependencies: 1945 | postcss: 8.4.49 1946 | typescript: 5.7.3 1947 | transitivePeerDependencies: 1948 | - jiti 1949 | - supports-color 1950 | - tsx 1951 | - yaml 1952 | 1953 | typescript@5.7.3: {} 1954 | 1955 | undici-types@6.20.0: {} 1956 | 1957 | vite-node@3.0.4(@types/node@22.12.0): 1958 | dependencies: 1959 | cac: 6.7.14 1960 | debug: 4.4.0 1961 | es-module-lexer: 1.6.0 1962 | pathe: 2.0.2 1963 | vite: 6.0.11(@types/node@22.12.0) 1964 | transitivePeerDependencies: 1965 | - '@types/node' 1966 | - jiti 1967 | - less 1968 | - lightningcss 1969 | - sass 1970 | - sass-embedded 1971 | - stylus 1972 | - sugarss 1973 | - supports-color 1974 | - terser 1975 | - tsx 1976 | - yaml 1977 | 1978 | vite-plugin-inspect@10.1.0(vite@6.0.11(@types/node@22.12.0)): 1979 | dependencies: 1980 | debug: 4.4.0 1981 | error-stack-parser-es: 1.0.5 1982 | open: 10.1.0 1983 | picocolors: 1.1.1 1984 | sirv: 3.0.0 1985 | vite: 6.0.11(@types/node@22.12.0) 1986 | transitivePeerDependencies: 1987 | - supports-color 1988 | 1989 | vite@6.0.11(@types/node@22.12.0): 1990 | dependencies: 1991 | esbuild: 0.24.2 1992 | postcss: 8.4.49 1993 | rollup: 4.30.1 1994 | optionalDependencies: 1995 | '@types/node': 22.12.0 1996 | fsevents: 2.3.3 1997 | 1998 | vitest@3.0.4(@types/debug@4.1.12)(@types/node@22.12.0): 1999 | dependencies: 2000 | '@vitest/expect': 3.0.4 2001 | '@vitest/mocker': 3.0.4(vite@6.0.11(@types/node@22.12.0)) 2002 | '@vitest/pretty-format': 3.0.4 2003 | '@vitest/runner': 3.0.4 2004 | '@vitest/snapshot': 3.0.4 2005 | '@vitest/spy': 3.0.4 2006 | '@vitest/utils': 3.0.4 2007 | chai: 5.1.2 2008 | debug: 4.4.0 2009 | expect-type: 1.1.0 2010 | magic-string: 0.30.17 2011 | pathe: 2.0.2 2012 | std-env: 3.8.0 2013 | tinybench: 2.9.0 2014 | tinyexec: 0.3.2 2015 | tinypool: 1.0.2 2016 | tinyrainbow: 2.0.0 2017 | vite: 6.0.11(@types/node@22.12.0) 2018 | vite-node: 3.0.4(@types/node@22.12.0) 2019 | why-is-node-running: 2.3.0 2020 | optionalDependencies: 2021 | '@types/debug': 4.1.12 2022 | '@types/node': 22.12.0 2023 | transitivePeerDependencies: 2024 | - jiti 2025 | - less 2026 | - lightningcss 2027 | - msw 2028 | - sass 2029 | - sass-embedded 2030 | - stylus 2031 | - sugarss 2032 | - supports-color 2033 | - terser 2034 | - tsx 2035 | - yaml 2036 | 2037 | vue-router@4.5.0(vue@3.5.13(typescript@5.7.3)): 2038 | dependencies: 2039 | '@vue/devtools-api': 6.6.4 2040 | vue: 3.5.13(typescript@5.7.3) 2041 | 2042 | vue@3.5.13(typescript@5.7.3): 2043 | dependencies: 2044 | '@vue/compiler-dom': 3.5.13 2045 | '@vue/compiler-sfc': 3.5.13 2046 | '@vue/runtime-dom': 3.5.13 2047 | '@vue/server-renderer': 3.5.13(vue@3.5.13(typescript@5.7.3)) 2048 | '@vue/shared': 3.5.13 2049 | optionalDependencies: 2050 | typescript: 5.7.3 2051 | 2052 | webidl-conversions@4.0.2: {} 2053 | 2054 | whatwg-url@7.1.0: 2055 | dependencies: 2056 | lodash.sortby: 4.7.0 2057 | tr46: 1.0.1 2058 | webidl-conversions: 4.0.2 2059 | 2060 | which@2.0.2: 2061 | dependencies: 2062 | isexe: 2.0.0 2063 | 2064 | why-is-node-running@2.3.0: 2065 | dependencies: 2066 | siginfo: 2.0.0 2067 | stackback: 0.0.2 2068 | 2069 | wrap-ansi@7.0.0: 2070 | dependencies: 2071 | ansi-styles: 4.3.0 2072 | string-width: 4.2.3 2073 | strip-ansi: 6.0.1 2074 | 2075 | wrap-ansi@8.1.0: 2076 | dependencies: 2077 | ansi-styles: 6.2.1 2078 | string-width: 5.1.2 2079 | strip-ansi: 7.1.0 2080 | -------------------------------------------------------------------------------- /src/history-api/historyApiFallbackPlugin.ts: -------------------------------------------------------------------------------- 1 | import type { Connect, Plugin, ViteDevServer } from 'vite' 2 | import history from 'connect-history-api-fallback' 3 | import type { HistoryApiOptions, HistoryRewrites } from './types' 4 | 5 | // noinspection JSUnusedGlobalSymbols 6 | export const historyApiFallbackPlugin = (historyApiOptions: HistoryApiOptions): Plugin => { 7 | const { 8 | rewrites, 9 | usePreview 10 | } = historyApiOptions 11 | const configureServerHookName = usePreview ? 'configurePreviewServer' : 'configureServer' 12 | return { 13 | name: 'vite-plugin-virtual-html:history', 14 | [configureServerHookName](server: ViteDevServer) { 15 | if (rewrites) { 16 | buildHistoryApiFallback(server, rewrites) 17 | } 18 | }, 19 | } 20 | } 21 | 22 | /** 23 | * build a server 24 | * @param server 25 | * @param rewrites 26 | */ 27 | export function buildHistoryApiFallback(server: ViteDevServer, rewrites: Array) { 28 | server.middlewares.use(history({ 29 | disableDotRule: undefined, 30 | htmlAcceptHeaders: ['text/html', 'application/xhtml+xml'], 31 | rewrites: rewrites, 32 | }) as Connect.NextHandleFunction) 33 | } 34 | -------------------------------------------------------------------------------- /src/history-api/types.ts: -------------------------------------------------------------------------------- 1 | import type {Rewrite} from 'connect-history-api-fallback' 2 | export type HistoryRewrites = Rewrite 3 | 4 | export type HistoryApiOptions = { 5 | /** 6 | * option to connect-history-api-fallback's rewrites 7 | */ 8 | rewrites?: Array 9 | usePreview?: boolean 10 | } 11 | -------------------------------------------------------------------------------- /src/html/Base.ts: -------------------------------------------------------------------------------- 1 | import type { 2 | HtmlPluginOptions, 3 | InjectCode, 4 | PageObject, 5 | VirtualHtmlTemplateData, 6 | } from "./types"; 7 | import { 8 | Pages, 9 | POS, 10 | VirtualHtmlPage, 11 | VirtualHtmlTemplateRender, 12 | VirtualPageOptions, 13 | } from "./types"; 14 | import { createFilter, normalizePath, type UserConfig } from "vite"; 15 | import * as path from "path"; 16 | import * as fs from "fs"; 17 | import glob from "fast-glob"; 18 | import debug from "debug"; 19 | import { createRequire } from "node:module"; 20 | import MagicString from "magic-string"; 21 | 22 | const _require = 23 | import.meta.url !== undefined ? createRequire(import.meta.url) : require; 24 | 25 | const fsp = fs.promises; 26 | const DEFAULT_GLOB_PATTERN = [ 27 | "**/*.html", 28 | "!node_modules/**/*.html", 29 | "!.**/*.html", 30 | ]; 31 | 32 | const VIRTUAL_HTML_CONTENT = new MagicString(` 33 | 34 | 35 | 36 | 37 | #TITLE# 38 | 39 | 40 | 41 | #BODY# 42 | 43 | 44 | `); 45 | export const DEFAULT_INJECTCODE_ALL = "*"; 46 | 47 | export class Base { 48 | _config?: UserConfig; 49 | 50 | _pages: Pages; 51 | 52 | _indexPage: string; 53 | 54 | _globalRender: VirtualHtmlTemplateRender; 55 | 56 | _globalData: Record; 57 | 58 | _injectCode: Record; 59 | 60 | cwd = normalizePath(process.cwd()); 61 | logger = debug("vite-plugin-virtual-html"); 62 | _filter: (id: string | unknown) => boolean; 63 | 64 | constructor(virtualHtmlOptions: HtmlPluginOptions) { 65 | const { 66 | pages: pagesObj, 67 | indexPage = "index", 68 | render = this.defaultRender, 69 | data = {}, 70 | extraGlobPattern = [], 71 | injectCode = {}, 72 | cwd = normalizePath(process.cwd()) 73 | } = virtualHtmlOptions; 74 | if (pagesObj === true || pagesObj === undefined) { 75 | this._pages = this.findAllHtmlInProject(extraGlobPattern); 76 | } else { 77 | this._pages = pagesObj; 78 | } 79 | this._indexPage = indexPage; 80 | this._globalData = data; 81 | this._globalRender = render; 82 | this._injectCode = injectCode; 83 | this._filter = createFilter(/\.html|\/$/); 84 | this.cwd = cwd 85 | } 86 | 87 | /** 88 | * load html file 89 | * @param args 90 | */ 91 | _load = async (...args: [string, unknown]) => { 92 | const [id] = args; 93 | if (this._filter(id)) { 94 | let newId = this.getHtmlName(id, this._config?.root); 95 | const maybeIndexName1 = (newId + "/").replace("//", "/"); 96 | const maybeIndexName2 = (newId + "/index").replace("//", "/"); 97 | const maybeIndexName3 = newId.replace("index", "").replace("//", "/"); 98 | 99 | const pageOption: VirtualHtmlPage | VirtualPageOptions = 100 | this._pages[newId] || 101 | this._pages[maybeIndexName1] || 102 | this._pages[maybeIndexName2] || 103 | this._pages[maybeIndexName3]; 104 | if (pageOption !== undefined) { 105 | // string 106 | if (typeof pageOption === "string") { 107 | const page = await this.generatePageOptions( 108 | pageOption, 109 | this._globalData, 110 | this._globalRender 111 | ); 112 | // generate html template 113 | return await this.readHtml(page); 114 | } 115 | // PageObject 116 | if ("template" in pageOption) { 117 | const page = await this.generatePageOptions( 118 | pageOption, 119 | this._globalData, 120 | this._globalRender 121 | ); 122 | // generate html template 123 | return await this.readHtml(page); 124 | } 125 | // VirtualPageOptions 126 | if ("entry" in pageOption) { 127 | return await this.generateVirtualPage(pageOption); 128 | } 129 | } 130 | } 131 | return undefined; 132 | }; 133 | 134 | /** 135 | * transform code to inject some code into original code 136 | * @param args 137 | */ 138 | _transform = async ( 139 | ...args: [string, string, unknown] 140 | ): Promise => { 141 | const [code, id] = args; 142 | if (this._filter(id)) { 143 | const ids = id.split("/"); 144 | const key = ids[ids.length - 1]; 145 | let _code = code; 146 | if (DEFAULT_INJECTCODE_ALL in this._injectCode) { 147 | _code = this.generateInjectCode( 148 | this._injectCode[DEFAULT_INJECTCODE_ALL], 149 | _code 150 | ); 151 | } 152 | if (key in this._injectCode) { 153 | _code = this.generateInjectCode(this._injectCode[key], _code); 154 | } 155 | return _code; 156 | } 157 | return null; 158 | }; 159 | 160 | /** 161 | * get html file's name 162 | * @param id 163 | * @param root 164 | */ 165 | getHtmlName = (id: string, root?: string) => { 166 | const _root = (root ?? "").replace(this.cwd, ""); 167 | const _id = id.replace(this.cwd, ""); 168 | const result = _id 169 | .replace(".html", "") 170 | .replace(_root !== "" ? this.addTrailingSlash(_root) : "", ""); 171 | return result.startsWith("/") ? result.substring(1, result.length) : result; 172 | }; 173 | 174 | /** 175 | * add trailing slash on path 176 | * @param {string} path 177 | * @returns {string} 178 | */ 179 | addTrailingSlash = (path: string): string => { 180 | const _path = normalizePath(path.replace(this.cwd, "")); 181 | return _path.endsWith("/") ? _path : `${_path}/`; 182 | }; 183 | 184 | /** 185 | * generate URL 186 | * @param url 187 | */ 188 | generateUrl = (url?: string): string => { 189 | if (!url) { 190 | return "/"; 191 | } 192 | // url with parameters 193 | if (url.indexOf("?") > 0) { 194 | return url.split("?")[0]; 195 | } 196 | return url; 197 | }; 198 | 199 | /** 200 | * read HTML file from disk and generate code from template system(with render function) 201 | * @param template 202 | * @param data 203 | * @param render 204 | */ 205 | readHtml = async ({ 206 | template = "", 207 | data = {}, 208 | render = this.defaultRender, 209 | }: PageObject) => { 210 | const templatePath = path.resolve(this.cwd, `.${template}`); 211 | if (!fs.existsSync(templatePath)) { 212 | this.logger("[vite-plugin-virtual-html]: template file must exist!"); 213 | return ""; 214 | } 215 | return await this.renderTemplate(templatePath, render, data); 216 | }; 217 | 218 | /** 219 | * render template 220 | * @param templatePath 221 | * @param render 222 | * @param data 223 | */ 224 | renderTemplate = async ( 225 | templatePath: string, 226 | render: VirtualHtmlTemplateRender, 227 | data: VirtualHtmlTemplateData 228 | ) => { 229 | const code = await this.readTemplate(templatePath); 230 | return render( 231 | code, 232 | data, 233 | templatePath.substring(templatePath.lastIndexOf(path.sep) + 1) 234 | ); 235 | }; 236 | 237 | /** 238 | * read html file's content to render with render function 239 | * @param templatePath 240 | */ 241 | readTemplate = async (templatePath: string): Promise => { 242 | const result = await fsp.readFile(templatePath); 243 | return result.toString(); 244 | }; 245 | 246 | /** 247 | * generate page option from string/object to object 248 | * @param page 249 | * @param globalData 250 | * @param globalRender 251 | */ 252 | generatePageOptions = async ( 253 | page: PageObject | string, 254 | globalData: Record, 255 | globalRender: VirtualHtmlTemplateRender 256 | ): Promise => { 257 | if (typeof page === "string") { 258 | return { 259 | template: page, 260 | data: { 261 | ...globalData, 262 | }, 263 | render: globalRender, 264 | }; 265 | } 266 | const { data = {}, render, template } = page; 267 | return { 268 | template: template, 269 | data: { 270 | ...globalData, 271 | ...data, 272 | }, 273 | render: render ?? globalRender ?? this.defaultRender, 274 | }; 275 | }; 276 | 277 | /** 278 | * directly use find\replacement / replacement\find to replace find 279 | * @param {pos, find, replacement} 280 | * @param code 281 | */ 282 | generateInjectCode = ( 283 | { pos, find, replacement }: InjectCode, 284 | code: string 285 | ): string => { 286 | if (pos === POS.after) { 287 | return code.replace(find, `${find}\n${replacement}`); 288 | } 289 | if (pos === POS.before) { 290 | return code.replace(find, `\n${replacement}\n${find}`); 291 | } 292 | return code; 293 | }; 294 | 295 | /** 296 | * generate page from virtual page 297 | * @param vPages 298 | */ 299 | generateVirtualPage = async (vPages: VirtualPageOptions): Promise => { 300 | const { entry, title = "", body = '
' } = vPages; 301 | return VIRTUAL_HTML_CONTENT.replace("#ENTRY#", entry) 302 | .replace("#TITLE#", title) 303 | .replace("#BODY#", body) 304 | .toString(); 305 | }; 306 | 307 | /** 308 | * find all html file in project and return it as Pages 309 | */ 310 | findAllHtmlInProject = (extraGlobPattern: Array = []): Pages => { 311 | const pages: Pages = {}; 312 | let realPattern: Array = []; 313 | if (extraGlobPattern.length === 0) { 314 | realPattern = DEFAULT_GLOB_PATTERN; 315 | } else { 316 | const set: Set = new Set(); 317 | DEFAULT_GLOB_PATTERN.forEach((dg) => set.add(dg)); 318 | extraGlobPattern.forEach((dg) => set.add(dg)); 319 | for (let key of set.keys()) { 320 | realPattern.push(key); 321 | } 322 | } 323 | const files = glob.sync(realPattern); 324 | files.forEach((file) => { 325 | const filePathArr = file.split("/"); 326 | pages[ 327 | filePathArr[filePathArr.length - 1].replace(".html", "") 328 | ] = `/${file}`; 329 | }); 330 | return pages; 331 | }; 332 | 333 | defaultRender: VirtualHtmlTemplateRender = ( 334 | template: string, 335 | data: Record 336 | ) => { 337 | try { 338 | const resolved = _require.resolve("ejs"); 339 | return _require(resolved).render(template, data, { 340 | delimiter: "%", 341 | root: process.cwd(), 342 | }); 343 | } catch (e) { 344 | // 345 | } 346 | return template; 347 | }; 348 | } 349 | -------------------------------------------------------------------------------- /src/html/Build.ts: -------------------------------------------------------------------------------- 1 | import type { HtmlPluginOptions } from './types' 2 | import { VirtualHtmlPage, VirtualPageOptions } from './types' 3 | import type { UserConfig } from 'vite' 4 | import { normalizePath } from 'vite' 5 | import { Base } from './Base' 6 | import fs, { promises as fsp } from 'fs' 7 | import path from 'path' 8 | 9 | export class Build extends Base { 10 | 11 | _needRemove: Array = [] 12 | _distDir!: string 13 | 14 | constructor(virtualHtmlOptions: HtmlPluginOptions) { 15 | super(virtualHtmlOptions) 16 | } 17 | 18 | /** 19 | * check html file's parent directory 20 | * @param html 21 | * @param needRemove 22 | */ 23 | async checkVirtualPath(html: string, needRemove: Array, root: string) { 24 | const cwd = normalizePath(path.resolve(this.cwd, root)) 25 | const pathArr = html.split('/') 26 | const fileName = pathArr[pathArr.length - 1] 27 | const middlePath = html.replace(fileName, '').replace(cwd, '') 28 | const firstPath = middlePath.split('/')[1] 29 | if (!fs.existsSync(middlePath)) { 30 | needRemove.push(normalizePath(path.resolve(cwd, `./${firstPath}`))) 31 | await fsp.mkdir(path.resolve(cwd, `./${middlePath}`), { 32 | recursive: true 33 | }) 34 | } 35 | } 36 | 37 | async _buildConfig(config: UserConfig,) { 38 | this._config = config 39 | const pagesKey = Object.keys(this._pages) 40 | for (let i = 0; i < pagesKey.length; i++) { 41 | const key = pagesKey[i] 42 | const pageOption = this._pages[key] 43 | const vHtml = normalizePath(path.resolve(this.cwd, `./${config.root ? this.addTrailingSlash(config.root) : ''}${this.htmlNameAddIndex(key)}.html`)) 44 | if (!fs.existsSync(vHtml)) { 45 | this._needRemove.push(vHtml) 46 | await this.checkVirtualPath(vHtml, this._needRemove, config.root ?? '') 47 | if (typeof pageOption === 'string' || 'template' in pageOption) { 48 | const genPageOption = await this.generatePageOptions(pageOption, this._globalData, this._globalRender) 49 | await fsp.copyFile(path.resolve(this.cwd, `.${genPageOption.template}`), vHtml) 50 | } 51 | if (typeof pageOption !== 'string' && 'entry' in pageOption) { 52 | await fsp.writeFile(path.resolve(this.cwd, vHtml), await this.generateVirtualPage(pageOption)) 53 | } 54 | } 55 | } 56 | this.logger('[vite-plugin-virtual-html]: This plugin cannot use in library mode!') 57 | // get custom distDir config,if it is undefined use default config 'dist' 58 | this._distDir = config.build?.outDir ?? 'dist' 59 | // inject build.rollupOptions.input from pages directly. 60 | config.build = { 61 | ...config.build, 62 | rollupOptions: { 63 | ...config.build?.rollupOptions, 64 | input: { 65 | ...(config.build?.rollupOptions?.input as object), 66 | ...this.extractHtmlPath(this._pages), 67 | }, 68 | }, 69 | } 70 | } 71 | 72 | _closeBundle() { 73 | // remove files should not be under project root 74 | for (let vHtml of this._needRemove) { 75 | if (fs.existsSync(vHtml)) { 76 | fsp.rm(vHtml, { 77 | recursive: true, 78 | }).catch(() => { 79 | // ignore this warning 80 | }) 81 | } 82 | } 83 | } 84 | 85 | /** 86 | * use pages' key as html name 87 | * @param pages 88 | */ 89 | extractHtmlPath(pages: { 90 | [p: string]: VirtualHtmlPage | VirtualPageOptions 91 | }) { 92 | const newPages: { 93 | [key: string]: string 94 | } = {} 95 | Object.keys(pages).forEach(key => { 96 | newPages[key] = `/${this.htmlNameAddIndex(key)}.html` 97 | }) 98 | return newPages 99 | } 100 | 101 | htmlNameAddIndex(htmlName: string): string { 102 | return htmlName.endsWith('/') ? htmlName + 'index' : htmlName 103 | } 104 | 105 | } 106 | -------------------------------------------------------------------------------- /src/html/Serve.ts: -------------------------------------------------------------------------------- 1 | import type { HtmlPluginOptions, UrlTransformerFunction } from './types' 2 | import { Base } from './Base' 3 | import { buildHistoryApiFallback } from '../history-api/historyApiFallbackPlugin' 4 | import type { ViteDevServer } from 'vite' 5 | import { normalizePath, createFilter, } from 'vite' 6 | import type { HistoryApiOptions, HistoryRewrites } from '../history-api/types' 7 | 8 | const HTML_INCLUDE = [/\.html$/,/\/$/] 9 | const HTML_FILTER = createFilter(HTML_INCLUDE) 10 | 11 | export class Serve extends Base { 12 | _rewrites?: Array 13 | 14 | _urlTransformer?: UrlTransformerFunction 15 | 16 | constructor(virtualHtmlOptions: HtmlPluginOptions & HistoryApiOptions) { 17 | super(virtualHtmlOptions) 18 | this._rewrites = virtualHtmlOptions.rewrites 19 | this._urlTransformer = virtualHtmlOptions.urlTransformer 20 | } 21 | 22 | _configureServer = (server: ViteDevServer) => { 23 | if (this._rewrites) { 24 | buildHistoryApiFallback(server, this._rewrites) 25 | } 26 | // other html handled after vite's inner middlewares. 27 | return () => { 28 | server.middlewares.use(async (req, res, next) => { 29 | const originalUrl = req.originalUrl 30 | const reqUrl = req.url 31 | let url = decodeURI(this.generateUrl(originalUrl?.endsWith('/') ? originalUrl : reqUrl)) 32 | // allow user customize url transformer 33 | if (this._urlTransformer) { 34 | url = this._urlTransformer(url, req) 35 | } 36 | // if request is not html , directly return next() 37 | if (!HTML_FILTER(url) && url !== '/') { 38 | return next() 39 | } 40 | // request / means client requests an index page 41 | // load it with indexPage config 42 | let htmlCode: string|undefined 43 | if (url === '/' || url === '/index.html') { 44 | url = `/${this._indexPage}.html` 45 | } 46 | // load specify html file code 47 | // @ts-ignore 48 | htmlCode = await this._load(normalizePath(url)) 49 | if (htmlCode === undefined) { 50 | res.statusCode = 404 51 | res.end() 52 | return next() 53 | } 54 | // @ts-ignore 55 | const transformResult = await this._transform(htmlCode, url) 56 | if (transformResult === null) { 57 | return next() 58 | } 59 | res.end(await server.transformIndexHtml(url, transformResult)) 60 | next() 61 | }) 62 | } 63 | } 64 | } 65 | -------------------------------------------------------------------------------- /src/html/VirtualHtmlPlugin.ts: -------------------------------------------------------------------------------- 1 | import type { HtmlPluginOptions } from "./types"; 2 | import type { ConfigEnv, Plugin, UserConfig } from "vite"; 3 | import type { HistoryApiOptions } from "../history-api/types"; 4 | import { Serve } from "./Serve"; 5 | import { Build } from "./Build"; 6 | 7 | export const VirtualHtmlPlugin = ( 8 | virtualHtmlOptions: HtmlPluginOptions & HistoryApiOptions 9 | ): Plugin => { 10 | let _htmlOptions = virtualHtmlOptions; 11 | let _config: UserConfig; 12 | let _instance: Serve | Build | null = null; 13 | return { 14 | name: "vite-plugin-virtual-html", 15 | async config(config: UserConfig, { command }: ConfigEnv) { 16 | _config = config; 17 | if (command === "serve") { 18 | if (_htmlOptions.useCustom??true) { 19 | config.appType = "custom"; 20 | } 21 | _instance = new Serve(_htmlOptions); 22 | } else if (command === "build") { 23 | _instance = new Build(_htmlOptions); 24 | await _instance._buildConfig.call(_instance, config); 25 | } 26 | }, 27 | configureServer(server) { 28 | if ((_instance as Serve)._configureServer) { 29 | return (_instance as Serve)._configureServer(server); 30 | } 31 | }, 32 | async load(...args) { 33 | if (_instance?._load) { 34 | return await _instance._load(...args); 35 | } 36 | }, 37 | async transform(...args) { 38 | if (_instance?._transform) { 39 | return await _instance._transform(...args); 40 | } 41 | }, 42 | closeBundle() { 43 | if ((_instance as Build)._closeBundle) { 44 | return (_instance as Build)._closeBundle(); 45 | } 46 | }, 47 | }; 48 | }; 49 | -------------------------------------------------------------------------------- /src/html/types.ts: -------------------------------------------------------------------------------- 1 | import { type IncomingMessage } from 'http' 2 | 3 | export type PageObject = { 4 | template: string, 5 | data?: VirtualHtmlTemplateData, 6 | render?: VirtualHtmlTemplateRender, 7 | } 8 | /** 9 | * describe a page 10 | */ 11 | export type VirtualHtmlPage = string | PageObject | VirtualPageOptions 12 | /** 13 | * html template render 14 | */ 15 | export type VirtualHtmlTemplateRender = (template: string, data: Record, htmlName?: string) => string 16 | 17 | export type VirtualHtmlTemplateData = Record 18 | 19 | export type Pages = { 20 | [key: string]: VirtualHtmlPage 21 | } 22 | 23 | export type VirtualPageOptions = { 24 | entry: string, 25 | title?: string, 26 | body?: string, 27 | } 28 | 29 | export type UrlTransformerFunction = (resolvedUrl: string, req: IncomingMessage) => string 30 | 31 | /** 32 | * plugin config options 33 | */ 34 | export type HtmlPluginOptions = { 35 | /** 36 | * config html-entries' path 37 | * if it is true, plugin will use glob to find all the html page in project to generate a json like {a: /src/a/a.html,} 38 | */ 39 | pages?: Pages | true, 40 | /** 41 | * transform url to another url by user. 42 | * This is ONLY apply in dev mode. 43 | * @param url 44 | */ 45 | urlTransformer?: UrlTransformerFunction 46 | /** 47 | * define the index page,to replace default index.html 48 | * this page will trigger `transformIndexHtml` hook. 49 | */ 50 | indexPage?: string, 51 | /** 52 | * use for template. as global inject data 53 | */ 54 | data?: Record 55 | /** 56 | * function to render template 57 | */ 58 | render?: VirtualHtmlTemplateRender 59 | /** 60 | * when pages set to true, customize fast-glob's pattern 61 | * default value is ['**\\*.html', '!node_modules\\**\\*.html', '!.**\\*.html'] 62 | */ 63 | extraGlobPattern?: Array 64 | /** 65 | * inject code to html 66 | * key: html name, can be * 67 | */ 68 | injectCode?: Record 69 | /** 70 | * is set appType to custom? 71 | */ 72 | useCustom?: boolean 73 | cwd?: string 74 | } 75 | 76 | /** 77 | * inject code to tag's before or after 78 | */ 79 | export enum POS { 80 | before, after 81 | } 82 | 83 | /** 84 | * inject code config 85 | */ 86 | export type InjectCode = { 87 | pos: POS, 88 | find: string, 89 | replacement: string, 90 | } 91 | -------------------------------------------------------------------------------- /src/index.ts: -------------------------------------------------------------------------------- 1 | import type { Plugin, } from 'vite' 2 | import type { HtmlPluginOptions, } from './html/types' 3 | import { VirtualHtmlPlugin } from './html/VirtualHtmlPlugin' 4 | import type { HistoryApiOptions } from './history-api/types' 5 | 6 | export default (virtualHtmlOptions: HtmlPluginOptions & HistoryApiOptions): Plugin => { 7 | return VirtualHtmlPlugin(virtualHtmlOptions) 8 | } 9 | 10 | export { 11 | VirtualHtmlPlugin, 12 | } 13 | 14 | export * from './html/Build' 15 | 16 | export * from './html/Serve' 17 | 18 | export * from './history-api/historyApiFallbackPlugin' 19 | 20 | export * from './html/types' 21 | 22 | export * from './history-api/types' 23 | -------------------------------------------------------------------------------- /test/__snapshots__/pages.test.ts.snap: -------------------------------------------------------------------------------- 1 | // Vitest Snapshot v1, https://vitest.dev/guide/snapshot.html 2 | 3 | exports[`html_with_entry_and_template_config 1`] = ` 4 | " 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 |
13 | 14 | 15 | " 16 | `; 17 | 18 | exports[`html_with_entry_and_template_config 2`] = ` 19 | " 20 | 21 | 22 | 23 | Demo 24 | 25 | 26 | 27 |
28 | 29 | 30 | " 31 | `; 32 | 33 | exports[`html_with_entry_config 1`] = ` 34 | " 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 |
43 | 44 | 45 | " 46 | `; 47 | 48 | exports[`html_with_inject_code_all_page 1`] = ` 49 | " 50 | 51 | 52 | 53 | Demo 54 | 55 | 56 | 57 | 58 | 59 |
60 | 61 | 62 | " 63 | `; 64 | 65 | exports[`html_with_inject_code_all_page 2`] = ` 66 | " 67 | 68 | 69 | 70 | Demo 71 | 72 | 73 | 74 | 75 | 76 |
77 | 78 | 79 | " 80 | `; 81 | 82 | exports[`html_with_inject_code_demo1 1`] = ` 83 | " 84 | 85 | 86 | 87 | Demo 88 | 89 | 90 | 91 | 92 | 93 |
94 | 95 | 96 | " 97 | `; 98 | 99 | exports[`html_with_template_config1 1`] = ` 100 | " 101 | 102 | 103 | 104 | Demo 105 | 106 | 107 | 108 |
109 | 110 | 111 | " 112 | `; 113 | 114 | exports[`html_with_template_config2 1`] = ` 115 | " 116 | 117 | 118 | 119 | Demo 120 | 121 | 122 | 123 |
124 | 125 | 126 | " 127 | `; 128 | 129 | exports[`index 1`] = ` 130 | " 131 | 132 | 133 | 134 | Demo 135 | 136 | 137 | 138 |
139 | 140 | 141 | " 142 | `; 143 | 144 | exports[`index 2`] = ` 145 | " 146 | 147 | 148 | 149 | Demo 150 | 151 | 152 | 153 |
154 | 155 | 156 | " 157 | `; 158 | 159 | exports[`index with context 1 1`] = ` 160 | " 161 | 162 | 163 | 164 | Demo 165 | 166 | 167 | 168 |
169 | 170 | 171 | " 172 | `; 173 | 174 | exports[`index with context 1 2`] = ` 175 | " 176 | 177 | 178 | 179 | Demo 180 | 181 | 182 | 183 |
184 | 185 | 186 | " 187 | `; 188 | 189 | exports[`index_with_sub_folder1 1`] = ` 190 | " 191 | 192 | 193 | 194 | Demo 195 | 196 | 197 | 198 |
199 | 200 | 201 | " 202 | `; 203 | 204 | exports[`index_with_sub_folder1 2`] = ` 205 | " 206 | 207 | 208 | 209 | Demo 210 | 211 | 212 | 213 |
214 | 215 | 216 | " 217 | `; 218 | 219 | exports[`index_with_sub_folder2 1`] = ` 220 | " 221 | 222 | 223 | 224 | Demo 225 | 226 | 227 | 228 |
229 | 230 | 231 | " 232 | `; 233 | 234 | exports[`index_with_sub_folder2 2`] = ` 235 | " 236 | 237 | 238 | 239 | Demo 240 | 241 | 242 | 243 |
244 | 245 | 246 | " 247 | `; 248 | -------------------------------------------------------------------------------- /test/__snapshots__/template.test.ts.snap: -------------------------------------------------------------------------------- 1 | // Vitest Snapshot v1, https://vitest.dev/guide/snapshot.html 2 | 3 | exports[`template_ejs 1`] = ` 4 | " 5 | 6 | 7 | 8 | Demo1 9 | 10 | 11 | 12 |
13 |
a | b | c
14 | 15 | 16 | " 17 | `; 18 | 19 | exports[`template_ejs 2`] = ` 20 | " 21 | 22 | 23 | 24 | Demo2 25 | 26 | 27 | 28 |
29 |
a | b | c
30 | <p>I am a test partial</p> 31 | 32 | 33 | " 34 | `; 35 | 36 | exports[`template_ejs 3`] = ` 37 | " 38 | 39 | 40 | 41 | Demo3 42 | <script type="module" src="/test/demo/demo1/demo1.ts"></script> 43 | 44 | 45 |
46 | 47 | 48 | " 49 | `; 50 | -------------------------------------------------------------------------------- /test/demo/demo1/demo1.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Demo 6 | 7 | 8 | 9 |
10 | 11 | 12 | -------------------------------------------------------------------------------- /test/demo/demo1/demo1.ts: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/windsonR/vite-plugin-virtual-html/ec47c600e819cbb8ebcad1a45722d186399ac9ac/test/demo/demo1/demo1.ts -------------------------------------------------------------------------------- /test/demo/template/demo1.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Demo1 6 | 7 | 8 | 9 |
10 |
<%= users.join(" | "); %>
11 | 12 | 13 | -------------------------------------------------------------------------------- /test/demo/template/demo2.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Demo2 6 | 7 | 8 | 9 |
10 |
<%= users.join(" | "); %>
11 | <%= include('/test/demo/template/test') %> 12 | 13 | 14 | -------------------------------------------------------------------------------- /test/demo/template/demo3.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Demo3 6 | <%= script; %> 7 | 8 | 9 |
10 | 11 | 12 | -------------------------------------------------------------------------------- /test/demo/template/test.ejs: -------------------------------------------------------------------------------- 1 |

I am a test partial

-------------------------------------------------------------------------------- /test/pages.test.ts: -------------------------------------------------------------------------------- 1 | // noinspection DuplicatedCode 2 | 3 | import {expect, test} from 'vitest' 4 | import VirtualHtml, { POS } from '../src' 5 | import {createServer,} from 'vite' 6 | import {page} from '../vitestSetup' 7 | 8 | test('index', async () => { 9 | const server = await createServer({ 10 | configFile: false, 11 | plugins: [ 12 | VirtualHtml({ 13 | pages: { 14 | demo1: '/test/demo/demo1/demo1.html', 15 | }, 16 | indexPage: 'demo1', 17 | }) 18 | ] 19 | }) 20 | await server.listen() 21 | await page.goto(`http://localhost:${server.config.server.port}/`) 22 | const index = await page.content() 23 | expect(index).toMatchSnapshot() 24 | await page.goto(`http://localhost:${server.config.server.port}/demo1.html`) 25 | const demo1 = await page.content() 26 | expect(demo1).toMatchSnapshot() 27 | expect(index).toBe(demo1) 28 | await server.close() 29 | }) 30 | 31 | test('index with context 1', async () => { 32 | const context = '/demo/' 33 | const server = await createServer({ 34 | base: context, 35 | configFile: false, 36 | plugins: [ 37 | VirtualHtml({ 38 | pages: { 39 | demo1: '/test/demo/demo1/demo1.html', 40 | }, 41 | indexPage: 'demo1', 42 | urlTransformer(url){ 43 | return url.replace(context, '/') 44 | } 45 | }) 46 | ] 47 | }) 48 | await server.listen() 49 | await page.goto(`http://localhost:${server.config.server.port}${context}`) 50 | const index = await page.content() 51 | expect(index).toMatchSnapshot() 52 | await page.goto(`http://localhost:${server.config.server.port}${context}demo1.html`) 53 | const demo1 = await page.content() 54 | expect(demo1).toMatchSnapshot() 55 | expect(index).toBe(demo1) 56 | await server.close() 57 | }) 58 | 59 | test('index_with_sub_folder1', async () => { 60 | const server = await createServer({ 61 | configFile: false, 62 | plugins: [ 63 | VirtualHtml({ 64 | pages: { 65 | // should access with localhost/demo1/ or localhost/demo1/index.html 66 | 'demo1/index': '/test/demo/demo1/demo1.html', 67 | }, 68 | }) 69 | ] 70 | }) 71 | await server.listen() 72 | await page.goto(`http://localhost:${server.config.server.port}/demo1/`) 73 | const index = await page.content() 74 | expect(index).toMatchSnapshot() 75 | await page.goto(`http://localhost:${server.config.server.port}/demo1/index.html`) 76 | const demo1 = await page.content() 77 | expect(demo1).toMatchSnapshot() 78 | expect(index).toBe(demo1) 79 | await server.close() 80 | }) 81 | 82 | test('index_with_sub_folder2', async () => { 83 | const server = await createServer({ 84 | configFile: false, 85 | plugins: [ 86 | VirtualHtml({ 87 | pages: { 88 | // should access with localhost/demo1/ or localhost/demo1/index.html 89 | 'demo1/': '/test/demo/demo1/demo1.html', 90 | }, 91 | }) 92 | ] 93 | }) 94 | await server.listen() 95 | await page.goto(`http://localhost:${server.config.server.port}/demo1/`) 96 | const index = await page.content() 97 | expect(index).toMatchSnapshot() 98 | await page.goto(`http://localhost:${server.config.server.port}/demo1/index.html`) 99 | const demo1 = await page.content() 100 | expect(demo1).toMatchSnapshot() 101 | expect(index).toBe(demo1) 102 | await server.close() 103 | }) 104 | 105 | test('html_with_template_config1', async () => { 106 | const server = await createServer({ 107 | configFile: false, 108 | plugins: [ 109 | VirtualHtml({ 110 | pages: { 111 | demo1: '/test/demo/demo1/demo1.html', 112 | }, 113 | }) 114 | ] 115 | }) 116 | await server.listen() 117 | await page.goto(`http://localhost:${server.config.server.port}/demo1.html`) 118 | expect(await page.content()).toMatchSnapshot() 119 | await server.close() 120 | }) 121 | 122 | test('html_with_template_config2', async () => { 123 | const server = await createServer({ 124 | configFile: false, 125 | // root: path.resolve(__dirname, './'), 126 | plugins: [ 127 | VirtualHtml({ 128 | pages: { 129 | demo1: { 130 | template: '/test/demo/demo1/demo1.html' 131 | }, 132 | }, 133 | }) 134 | ] 135 | }) 136 | await server.listen() 137 | await page.goto(`http://localhost:${server.config.server.port}/demo1.html`) 138 | expect(await page.content()).toMatchSnapshot() 139 | await server.close() 140 | }) 141 | 142 | test('html_with_entry_config', async () => { 143 | const server = await createServer({ 144 | configFile: false, 145 | plugins: [ 146 | VirtualHtml({ 147 | pages: { 148 | demo1: { 149 | entry: '/test/demo/demo1/demo1.ts' 150 | }, 151 | }, 152 | }) 153 | ] 154 | }) 155 | await server.listen() 156 | await page.goto(`http://localhost:${server.config.server.port}/demo1.html`) 157 | expect(await page.content()).toMatchSnapshot() 158 | await server.close() 159 | }) 160 | 161 | test('html_with_entry_and_template_config', async () => { 162 | 163 | const server = await createServer({ 164 | configFile: false, 165 | plugins: [ 166 | VirtualHtml({ 167 | pages: { 168 | demo1: { 169 | entry: '/test/demo/demo1/demo1.ts' 170 | }, 171 | demo2: { 172 | template: '/test/demo/demo1/demo1.html', 173 | } 174 | }, 175 | }) 176 | ] 177 | }) 178 | await server.listen() 179 | await page.goto(`http://localhost:${server.config.server.port}/demo1.html`) 180 | expect(await page.content()).toMatchSnapshot() 181 | await page.goto(`http://localhost:${server.config.server.port}/demo2.html`) 182 | expect(await page.content()).toMatchSnapshot() 183 | await server.close() 184 | }) 185 | 186 | test('html_with_inject_code_demo1', async () => { 187 | const server = await createServer({ 188 | configFile: false, 189 | plugins: [ 190 | VirtualHtml({ 191 | pages: { 192 | demo1: '/test/demo/demo1/demo1.html', 193 | }, 194 | injectCode: { 195 | 'demo1.html': { 196 | pos: POS.before, 197 | find: '', 198 | replacement: '', 199 | } 200 | } 201 | }) 202 | ] 203 | }) 204 | await server.listen() 205 | await page.goto(`http://localhost:${server.config.server.port}/demo1.html`) 206 | expect(await page.content()).toMatchSnapshot() 207 | await server.close() 208 | }) 209 | 210 | test('html_with_inject_code_all_page', async () => { 211 | const server = await createServer({ 212 | configFile: false, 213 | plugins: [ 214 | VirtualHtml({ 215 | pages: { 216 | demo1: '/test/demo/demo1/demo1.html', 217 | demo2: { 218 | template: '/test/demo/demo1/demo1.html', 219 | } 220 | }, 221 | injectCode: { 222 | '*': { 223 | pos: POS.before, 224 | find: '', 225 | replacement: '', 226 | } 227 | } 228 | }) 229 | ] 230 | }) 231 | await server.listen() 232 | await page.goto(`http://localhost:${server.config.server.port}/demo1.html`) 233 | expect(await page.content()).toMatchSnapshot() 234 | await page.goto(`http://localhost:${server.config.server.port}/demo1.html`) 235 | expect(await page.content()).toMatchSnapshot() 236 | await server.close() 237 | }) 238 | -------------------------------------------------------------------------------- /test/template.test.ts: -------------------------------------------------------------------------------- 1 | // noinspection DuplicatedCode 2 | 3 | import {expect, test} from 'vitest' 4 | import VirtualHtml from '../src' 5 | import {createServer,} from 'vite' 6 | import {page} from '../vitestSetup' 7 | 8 | test('template_ejs', async () => { 9 | const server = await createServer({ 10 | configFile: false, 11 | plugins: [ 12 | VirtualHtml({ 13 | pages: { 14 | demo1: { 15 | template: '/test/demo/template/demo1.html', 16 | data: { 17 | users: ['a', 'b', 'c'] 18 | } 19 | }, 20 | demo2: { 21 | template: '/test/demo/template/demo2.html', 22 | data: { 23 | users: ['a', 'b', 'c'] 24 | } 25 | }, 26 | demo3: { 27 | template: '/test/demo/template/demo3.html', 28 | data: { 29 | script: '' 30 | } 31 | }, 32 | }, 33 | }) 34 | ] 35 | }) 36 | await server.listen() 37 | await page.goto(`http://localhost:${server.config.server.port}/demo1.html`) 38 | expect(await page.content()).toMatchSnapshot() 39 | await page.goto(`http://localhost:${server.config.server.port}/demo2.html`) 40 | expect(await page.content()).toMatchSnapshot() 41 | await page.goto(`http://localhost:${server.config.server.port}/demo3.html`) 42 | expect(await page.content()).toMatchSnapshot() 43 | await server.close() 44 | }) 45 | -------------------------------------------------------------------------------- /tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "target": "ES2020", 4 | "module": "ES2020", 5 | "moduleResolution": "node", 6 | "strict": true, 7 | "jsx": "preserve", 8 | "sourceMap": true, 9 | "resolveJsonModule": true, 10 | "esModuleInterop": true, 11 | "lib": [ 12 | "esnext", 13 | "dom" 14 | ], 15 | "types": [ 16 | "vite/client" 17 | ], 18 | "declaration": true, 19 | "skipLibCheck": true 20 | }, 21 | "include": [ 22 | "src/**/*.ts" 23 | ], 24 | "exclude": [ 25 | "vite.config.ts" 26 | ] 27 | } 28 | -------------------------------------------------------------------------------- /tsup.config.ts: -------------------------------------------------------------------------------- 1 | import { defineConfig } from 'tsup' 2 | 3 | export const tsup = defineConfig({ 4 | entry: [ 5 | 'src/index.ts', 6 | ], 7 | format: ['esm'], 8 | dts: true, 9 | splitting: false, 10 | clean: true, 11 | shims: false, 12 | minify: false, 13 | sourcemap: true, 14 | }) 15 | -------------------------------------------------------------------------------- /vite.config.ts: -------------------------------------------------------------------------------- 1 | import {defineConfig} from 'vite' 2 | import VirtualHtml from './src' 3 | import Vue from '@vitejs/plugin-vue' 4 | import Inspect from 'vite-plugin-inspect' 5 | // @ts-ignore 6 | import ejs from 'ejs' 7 | import {POS} from './src/html/types'; 8 | 9 | export default defineConfig({ 10 | resolve: { 11 | alias: { 12 | 'vue': 'vue/dist/vue.esm-bundler.js' 13 | } 14 | }, 15 | plugins: [ 16 | Inspect(), 17 | Vue(), 18 | VirtualHtml({ 19 | pages: { 20 | // demo1 is the html name you access in browser 21 | demo1: { 22 | template: '/demo/demo1/demo1.html', 23 | data: { 24 | users: ['a','b','c'] 25 | } 26 | }, 27 | 'demo1/index': '/demo/demoIndex1.html', 28 | 'demo2/': '/demo/demoIndex2.html', 29 | demo2: '/demo/demo2/demo2.html', 30 | demo3: { 31 | template: '/demo/demo3.html' 32 | }, 33 | demo4: { 34 | template: '/demo/template.html', 35 | data: { 36 | script: '' 37 | } 38 | }, 39 | 'demo1/demo2/demo5': { // multi-level directories, the last (demo5) will be the html name 40 | template: '/demo/template.html', 41 | data: { 42 | script: '' 43 | } 44 | }, 45 | demo6: { 46 | entry: '/demo/demo1/demo1.ts', 47 | }, 48 | demo7: { 49 | entry: '/demo/demo1/demo1.ts', 50 | title: 'demo7', 51 | body: '
' 52 | }, 53 | }, 54 | data: { 55 | users: ['a', 'b', 'c'], 56 | script: '' 57 | }, 58 | indexPage: 'demo1', 59 | // global render, from 0.3.0 it (this demo code) will auto configure in plugin, and you MUST install 'ejs' in your project to use it. 60 | // render(template,data){ 61 | // return ejs.render(template, data, {delimiter: '%', root: process.cwd()}) 62 | // }, 63 | // pages: true, // when pages set to true, plugin will use extraGlobPattern to glob html file 64 | // extraGlobPattern: [ 65 | // '**/*.html', 66 | // '!node_modules/**/*.html', 67 | // '!.**/*.html', 68 | // '!dist/**/*.html' 69 | // ], 70 | injectCode: { 71 | 'demo1.html': { 72 | pos: POS.after, 73 | find: '', 74 | replacement: '' 75 | }, 76 | '*': { 77 | pos: POS.after, 78 | find: '', 79 | replacement: '' 80 | }, 81 | }, 82 | // rewrites: [ 83 | // { 84 | // from: /.*/g, 85 | // to: '' 86 | // } 87 | // ] 88 | }), 89 | ], 90 | optimizeDeps: { 91 | force: true 92 | } 93 | }) 94 | -------------------------------------------------------------------------------- /vitestSetup.ts: -------------------------------------------------------------------------------- 1 | import {beforeAll} from 'vitest' 2 | import { chromium, Page } from 'playwright-chromium' 3 | 4 | export let page: Page = undefined! 5 | beforeAll(async ()=>{ 6 | const browser = await chromium.launch() 7 | const context = await browser.newContext() 8 | page = await context.newPage() 9 | page.once('load', () => console.log('Page loaded!')); 10 | }) 11 | --------------------------------------------------------------------------------