{"_id":"@aliyun-sls/vite-plugin-virtual-mpa","_rev":"2-eaa09efa9eab8d5990801fedbe270c1e","name":"@aliyun-sls/vite-plugin-virtual-mpa","dist-tags":{"latest":"1.12.1"},"versions":{"1.12.1":{"name":"@aliyun-sls/vite-plugin-virtual-mpa","version":"1.12.1","keywords":["vite","vite-plugin","virtual","virtual-html","html","html-mpa","html-template","mpa","multi","multi-page"],"author":{"name":"秦旭洋","email":"emosheep@qq.com"},"license":"MIT","_id":"@aliyun-sls/vite-plugin-virtual-mpa@1.12.1","maintainers":[{"name":"nosmilen","email":"1581024375@qq.com"},{"name":"ruiqi.hy","email":"ruiqi.hy@alibaba-inc.com"},{"name":"yuecjnadt","email":"yuecjn@gmail.com"}],"homepage":"https://github.com/emosheeep/vite-plugin-virtual-mpa#readme","bugs":{"url":"https://github.com/emosheeep/vite-plugin-virtual-mpa/issues"},"dist":{"shasum":"cc7f22d6f026549240cb63000624c625acf8e479","tarball":"https://registry.npmjs.org/@aliyun-sls/vite-plugin-virtual-mpa/-/vite-plugin-virtual-mpa-1.12.1.tgz","fileCount":9,"integrity":"sha512-EhvZD7aqWKCVJNan2F4UU7M11ttkDujUqDTFmnQjLyKL9vtRU09bHMzskyky/sMHNLhJYl17EZzG3M73Hv171Q==","signatures":[{"sig":"MEUCIHDS8K52U4QCfnehmTnVOmc019jE/p2O+LQBXdTSZIl5AiEAphiZiuebmoero3WIY3xdGW40/0I6zIKWSFWY9e9nRq0=","keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA"}],"unpackedSize":70446},"main":"./dist/index.js","types":"./dist/index.d.ts","module":"./dist/index.mjs","exports":{".":{"types":"./dist/index.d.ts","import":"./dist/index.mjs","require":"./dist/index.js"}},"gitHead":"5aed389c2fe8d137a093e30bc723b9a65a6cee27","scripts":{"pb":"npm run build && npm publish --access public --registry=https://registry.npmjs.org/ && tnpm sync @aliyun-sls/vite-plugin-virtual-mpa","lint":"eslint . --fix --ext .js,.ts","build":"tsup","watch":"tsup --watch","prepare":"simple-git-hooks","versions":"changeset version","changeset":"changeset","prepublishOnly":"npm run build"},"_npmUser":{"name":"yuecjnadt","email":"yuecjn@gmail.com"},"repository":{"url":"git+https://github.com/emosheeep/vite-plugin-virtual-mpa.git","type":"git"},"_npmVersion":"9.8.1","description":"Out-of-box MPA plugin for Vite, with html template engine and virtual files support.","directories":{},"_nodeVersion":"18.18.0","dependencies":{"ejs":"^3.1.10","tsup":"^8.2.4","vite":"^5.4.0","picocolors":"^1.0.1","html-minifier-terser":"^7.2.0","@types/html-minifier-terser":"^7.0.2","connect-history-api-fallback":"^2.0.0","@types/connect-history-api-fallback":"^1.5.4"},"_hasShrinkwrap":false,"packageManager":"pnpm@8.15.3","devDependencies":{"eslint":"^8.57.0","prettier":"^3.3.3","@types/ejs":"^3.1.5","typescript":"^5.5.4","lint-staged":"^15.2.8","@changesets/cli":"^2.27.7","@commitlint/cli":"^18.6.1","eslint-plugin-n":"^15.7.0","simple-git-hooks":"^2.11.1","eslint-plugin-vue":"^9.27.0","vue-eslint-parser":"^9.4.3","eslint-define-config":"^2.1.0","eslint-plugin-import":"^2.29.1","eslint-plugin-promise":"^6.6.0","eslint-config-prettier":"^9.1.0","eslint-config-standard":"^17.1.0","eslint-plugin-prettier":"^5.2.1","@typescript-eslint/parser":"^6.21.0","@vue/eslint-config-typescript":"^12.0.0","@commitlint/config-conventional":"^18.6.3","@typescript-eslint/eslint-plugin":"^6.21.0"},"peerDependencies":{"vite":">= 2.0.0"},"simple-git-hooks":{"commit-msg":"npx commitlint -e","pre-commit":"npx lint-staged && npx tsc --noEmit"},"_npmOperationalInternal":{"tmp":"tmp/vite-plugin-virtual-mpa_1.12.1_1723814137686_0.20071797914932699","host":"s3://npm-registry-packages"}}},"time":{"created":"2024-08-16T13:15:37.609Z","modified":"2025-04-07T06:32:57.691Z","1.12.1":"2024-08-16T13:15:38.039Z"},"bugs":{"url":"https://github.com/emosheeep/vite-plugin-virtual-mpa/issues"},"author":{"name":"秦旭洋","email":"emosheep@qq.com"},"license":"MIT","homepage":"https://github.com/emosheeep/vite-plugin-virtual-mpa#readme","keywords":["vite","vite-plugin","virtual","virtual-html","html","html-mpa","html-template","mpa","multi","multi-page"],"repository":{"url":"git+https://github.com/emosheeep/vite-plugin-virtual-mpa.git","type":"git"},"description":"Out-of-box MPA plugin for Vite, with html template engine and virtual files support.","maintainers":[{"email":"yuecjn@gmail.com","name":"yuecjnadt"},{"email":"ruiqi.hy@alibaba-inc.com","name":"ruiqi.hy"},{"email":"1581024375@qq.com","name":"nosmilen"},{"email":"qinghaotu@126.com","name":"7678tqh"}],"readme":"# vite-plugin-virtual-mpa ⚡\n\n[![npm version](https://img.shields.io/npm/v/vite-plugin-virtual-mpa)](https://npmjs.com/package/vite-plugin-virtual-mpa)\n[![awesome-vite](https://awesome.re/badge.svg)](https://github.com/vitejs/awesome-vite)\n![weekly downloads](https://img.shields.io/npm/dw/vite-plugin-virtual-mpa)\n![license](https://img.shields.io/npm/l/vite-plugin-virtual-mpa)\n[![install size](https://packagephobia.com/badge?p=vite-plugin-virtual-mpa)](https://packagephobia.com/result?p=vite-plugin-virtual-mpa)\n\n开箱即用的 Vite MPA插件 📦，支持HTML模板引擎和虚拟文件功能，能够使用一份模板生成多个文件。\n\n[English](./README.md) | 中文\n\n## 主要功能\n\n- 💡 EJS 模板渲染\n- 💡 完备的 TypeScript 类型提示支持，是一款小而美的插件\n- 🛠️ 自定义模板HTML文件的输出路径, 使用一份模板生成多份文件\n- 🛠️ 支持 MPA 多页面应用，为开发和预览服务器提供 History Fallback 支持.\n## 使用方式\n\n```sh\npnpm add -D vite-plugin-virtual-mpa # or npm/yarn\n```\n\n```ts\n// vite.config.ts\nimport { createMpaPlugin, createPages } from 'vite-plugin-virtual-mpa'\n\n// @see https://vitejs.dev/config/\nexport default defineConfig({\n  plugins: [\n    createMpaPlugin({\n      pages: [\n        // 你可以直接在这里书写页面配置，也可以单独使用 `createPages` 函数并将结果传递到这里。\n      ]\n    }),\n  ],\n})\n\n/**\n * 该函数仅仅是将参数转换为一个 pages 数组。\n * 它帮助你在插件之外创建页面配置，主要是为了能够拥有类型提示。\n * 同时在别处统一管理配置的方式可能也能帮助你简化 vite 的配置文件。\n */\nconst pages = createPages([\n  // 你可以传递一个 page 对象或一个 pages 数组。\n])\n\n// @see https://vitejs.dev/config/\nexport default defineConfig({\n  plugins: [\n    createMpaPlugin({\n      pages,\n    }),\n  ],\n})\n```\n\n## Options\n\n```ts\ntype FilterPattern = string | RegExp | (string | RegExp)[]\ntype RewriteRule = false | Rewrite[]\ninterface WatchHandler {\n  (ctx: {\n    server: ViteDevServer,\n    file: string,\n    type: Event\n    /**\n     * 可以调用这个方法更新页面配置\n     * @params pages MPA 页面核心配置，这将会替换默认的 `pages`\n     */\n    reloadPages: (pages: Page[]) => void\n  }): void\n}\n\ninterface MpaOptions {\n  /**\n   * 是否在控制台打印log\n   * @default true\n   */\n  verbose?: boolean,\n  /**\n   * 默认模板文件\n   * @default index.html\n   */\n  template?: `${string}.html`,\n  /**\n   * 为开发服务器配置 fallback rewrite rules，只会处理 accept=text/html 的文件请求。\n   * @see https://github.com/bripkens/connect-history-api-fallback\n   */\n  rewrites?: RewriteRule,\n  /**\n   * 为预览服务器配置重定向规则，配置方式同 rewrites。\n   * @see https://github.com/bripkens/connect-history-api-fallback\n   */\n  previewRewrites?: RewriteRule,\n  /**\n   * 处理模版 HTML 文件，继承自 `transformIndexHtml`。\n   * @see https://vitejs.dev/guide/api-plugin#transformindexhtml\n   */\n  transformHtml?: (\n    html: string,\n    ctx: IndexHtmlTransformContext & { page: Page },\n  ) => IndexHtmlTransformResult;\n  /**\n   * 用于扫描相似的目录结构，自动生成 pages 配置。\n   * 扫描到的配置会追加到 `pages` 中，具有相同 name 的 page 将被忽略\n   */\n  scanOptions?: {\n    /**\n     * 要扫描的目录，子目录名称将作为唯一的 page name。\n     */\n    scanDirs: string | string[];\n    /**\n     * 相对于扫描目录的入口文件路径\n     */\n    entryFile?: string;\n    /**\n     * 自定义虚拟文件的名称（输入文件名）\n     * @param name 子目录名称\n     */\n    filename?: (name: string) => string;\n  };\n  /**\n   * 当项目目录下有一些文件触发相应的事件如添加、删除、修改时，你可能想要重新加载 `pages` 配置 或 重启 ViteDevServer。\n   * 你可以通过设置 `watchOptions` 来实现这一目的。\n   */\n  watchOptions?: WatchHandler | {\n    /**\n     * 指定需要**包含**的文件，基于 `Rollup.createFilter` 过滤\n     * @see https://vitejs.dev/guide/api-plugin.html#filtering-include-exclude-pattern\n     */\n    include?: Exclude<FilterPattern, null>,\n    /**\n     * 指定需要**排除**的文件，基于 `Rollup.createFilter` 过滤\n     * @see https://vitejs.dev/guide/api-plugin.html#filtering-include-exclude-pattern\n     */\n    excluded?: Exclude<FilterPattern, null>,\n    /**\n     * 想要监听的文件事件\n     * @default ['add', 'unlink', 'change', 'unlinkDir', 'addDir']\n     */\n    events?: Event[],\n    /**\n     * 定义的文件事件触发后，执行自定义逻辑\n     */\n    handler: WatchHandler\n  },\n  pages: Array<{\n    /**\n     * 必填。该名称是一个不包含'/'的普通字符串，它用于生成默认的重定向规则。\n     * 如果你想自定义生成文件的路径，请使用filename选项，而不是name选项。\n     */\n    name: string;\n    /**\n     * 相对于`build.outDir`的路径，应该以html结尾\n     * @default `${name}.html`\n     */\n    filename?: `${string}.html`;\n    /**\n     * 更高优先级的模板文件，将会覆盖默认模板\n     */\n    template?: string;\n    /**\n     * 自动注入入口文件，如果设置了entry，需要移除模板文件中的entry\n     */\n    entry?: string;\n    /**\n     * 注入到模板文件的数据\n     */\n    data?: Record<string, any>,\n  }>,\n  /**\n   * 是否使用 html-minify-terser 压缩 html 文件\n   * @default false\n   * @see https://github.com/terser/html-minifier-terser\n   */\n  htmlMinify?: Options | boolean,\n}\n```\n## Examples\n\n点击链接 [codesandbox](https://codesandbox.io/p/sandbox/vite-plugin-virtual-mpa-0djylc) 快速体验\n\n```ts\n// vite.config.ts\nimport { normalizePath } from \"vite\";\nimport { createMpaPlugin } from \"vite-plugin-virtual-mpa\"\n\nconst base = \"/sites/\"\n\n// @see https://vitejs.dev/config/\nexport default defineConfig({\n  base,\n  plugins: [\n    createMpaPlugin({\n      htmlMinify: false,\n      pages: [\n        {\n          name: \"apple\",\n          /**\n           * 文件名是可选的，默认将会是`${name}.html`，这个路径是相对于`build.outDir`\n           */\n          filename: \"fruits/apple.html\", // 将会在编译时输出到sites/fruits/apple.html\n          entry: \"/src/fruits/apple/index.js\",\n          data: {\n            title: \"This is Apple page\"\n          }\n        },\n        {\n          name: \"banana\",\n          filename: \"fruits/banana.html\",\n          entry: \"/src/fruits/banana/index.js\",\n          data: {\n            title: \"This is Banana page\"\n          }\n        },\n        {\n          name: \"strawberries\",\n          filename: \"fruits/strawberries.html\",\n          entry: \"/src/fruits/strawberries/index.js\",\n          data: {\n            title: \"This is Strawberries page\"\n          }\n        }\n      ],\n      /**\n       * 以下示例的 scanOptions 配置可以替换上面的 pages 配置，除了 data 的注入。\n       */\n      scanOptions: {\n        scanDirs: 'src/fruits',\n        entryFile: 'index.js',\n        filename: name => `fruits/${name}.html`,\n        template: '../../template.html',\n      }\n      /**\n       * 通过该选项来配置 history fallback rewrite rules\n       * 如果你像上面这样配置页面的话，那下面的这份配置将会自动生成。\n       * 否则你需要自己编写重定向规则。\n       */\n      rewrites: [\n        {\n          from: new RegExp(normalizePath(`/${base}/(apple|banana|strawberries)`)),\n          to: (ctx) => normalizePath(`/fruits/${ctx.match[1]}.html`),\n        }\n      ],\n      /**\n       * 配置预览服务器的重定向规则，配置方式同 rewrites\n       */\n      previewRewrites: [\n        // 如果产物目录没有 index.html，你需要手动配置规则，以便服务器能正确找到入口文件。\n        { from: /.*/, to: '/home.html' },\n      ],\n      /** 自定义处理模板内容 */\n      transformHtml(html, ctx) {\n        return {\n          html,\n          tags: [\n            {\n              tag: 'div',\n              injectTo: 'body-prepend',\n              children: `[Auto Injected] Page name: ${ctx.page.name}`,\n            },\n          ],\n        };\n      },\n    }),\n  ],\n})\n```\n\n## 插件对比\n\n使用vite开发构建 **多页面应用(MPA)** 的时候，我们通常需要一个具备以下能力的插件：\n\n1. 具备模板引擎如ejs，能够使用一个模板生成多份文件，且能自定义构建时生成文件的路径。\n\n2. 自动配置 `rollupOptions.input`，并提供能力配置开发服务器的代理（主要是history fallback api）。\n\n市面上有非常多的关于vite的MPA插件，但他们却几乎没有能同时做到以上两点的。根据名称匹配度和下载量，我筛选到以下插件:\n\n1. [vite-plugin-mpa](https://github.com/IndexXuan/vite-plugin-mpa)：可以自动配置入口，并提供开发服务器代理配置入口（fallback rule），但必须按照约定调整目录结构，且不支持模板引擎和虚拟入口，也无法定义生成文件的路径。\n\n2. [vite-plugin-html-template](https://github.com/IndexXuan/vite-plugin-html-template)：这个插件的作者和vite-plugin-mpa是同一个人，算是作者推荐的配套插件，主要是和mpa插件组合使用以提供模板引擎功能，同样不支持虚拟入口。\n\n3. [vite-plugin-html](https://github.com/vbenjs/vite-plugin-html)：只支持模板引擎，且不支持虚拟入口。\n\n4. [vite-plugin-virtual-html](https://github.com/windsonR/vite-plugin-virtual-html)：支持虚拟入口，提供了渲染接口，可以定制模板引擎。但没有内置模板引擎，用起来有点麻烦还是。\n\n其中，**\"虚拟入口\"** 的意思是，通过一个模板文件，渲染出多个入口html文件。\n\n其他插件大同小异，他们各有所长，但用起来总不趁手。要么需要搭配使用，要么对现有项目结构的改动较多。有时候我也好奇，既然实现了模板引擎，却又需要多个模板文件，这样做岂不是失去了模板的优势。\n\n而这个插件便是为了解决这些问题，它同时具备上面提到的所有能力。通过结合虚拟入口和模板引擎，使得用户只需要一份模板就可以生成不同的入口html，且能自定义入口文件的输出路径（再也不用手动写脚本移动了！）。同时也提供了接口为开发服务器配置rewrite rules，以便开发时能够正确地请求到入口文件。\n\n如果你的项目正在使用vite工作流且为MPA应用，不妨尝试一下这个插件，它不限制技术栈，与你是否使用vue还是react或其他技术无关。\n\n## 默认重定向规则\n\n正如上面提到的👆🏻，如果你的配置遵循约定，插件将会自动生成一份重定向规则，这份配置会同时应用到开发和预览服务器，如下：\n```ts\n{\n  from: new RegExp(normalizePath(`/${base}/(${Object.keys(inputMap).join('|')})`)),\n  to: ctx => normalizePath(`/${base}/${inputMap[ctx.match[1]]}`),\n}\n```\n\n其中, **inputMap** 是一个`name`到对应虚拟文件的映射，结构如下:\n\n```ts\n{\n  apple: 'fruits/apple.html',\n  banana: 'fruits/banana.html',\n  strawberries: 'fruits/strawberries.html',\n}\n```\n\n请求Url`/sites/apple/xxx`将会被**默认重定向规则**处理并重定向到对应的url，也就是`/fruits/apple.html`(name `'apple'` 对应 `'fruits/apple.html'`, 其他同理)，重定向后的路径将会基于`viteConfig.base(这里是'/sites/')`去寻找目标文件，所以最终的Url会变成`/sites/fruits/apple.html`.\n\n## 关于虚拟入口文件\n\n通常在开发时，我们的文件都是写在本地的，我们通过DevServer的代理能够通过url访问到本地对应的文件。虚拟文件也是如此，只不过对应的文件没有写到文件系统中，而是保存在内存中而已。\n\n该插件通过模板系统生成了对应的虚拟文件，让你可以在开发时**通过代理访问到内存中的虚拟文件**，并在构建时生成到对应的目录下。\n\n你完全可以认为这些虚拟文件是真实存在的，这将有助于你在脑海中构建关于虚拟文件的直觉，以便能够正确地编写代理配置。\n\n## 关于 EJS 模板引擎\n\n插件使用 ejs 模板引擎进行数据注入，除页面配置中提供的 `data` 外，插件默认会将以 `'VITE_'` 开头的环境变量注入所有的页面配置中，更多信息可以查看官网 —— [envprefix](https://cn.vitejs.dev/config/shared-options.html#envprefix)。\n\n","readmeFilename":"README.zh_CN.md"}