diff --git a/.github/workflows/pages.yml b/.github/workflows/pages.yml
index 4dc99b3..2b80979 100644
--- a/.github/workflows/pages.yml
+++ b/.github/workflows/pages.yml
@@ -11,7 +11,7 @@ concurrency:
jobs:
deploy:
- if: "contains(github.event.head_commit.message, 'deploy')"
+ if: contains(github.event.head_commit.message, 'release') || contains(github.event.head_commit.message, 'deploy')
runs-on: ubuntu-latest
steps:
- name: Checkout
diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml
index 4d82651..364245b 100644
--- a/.github/workflows/test.yml
+++ b/.github/workflows/test.yml
@@ -74,6 +74,10 @@ jobs:
key: ${{ runner.os }}-playwright-bin-v1
path: ${{ env.PLAYWRIGHT_BROWSERS_PATH }}
+ - name: Install Playwright
+ # does not need to explicitly set chromium after https://github.com/microsoft/playwright/issues/14862 is solved
+ run: pnpx playwright install chromium
+
- name: Build
run: pnpm run build
diff --git a/README.md b/README.md
index 2124498..f5380fd 100644
--- a/README.md
+++ b/README.md
@@ -2,7 +2,6 @@
![npm][npm-img]
-
**在vite的运行时或构建时编译指定目录下的typescript文件,供开发者独立使用**
@@ -26,12 +25,13 @@
## 功能
-- 运行时和构建时,把指定文件夹中的 `typescript` 文件编译为 `javascript`,供浏览器直接使用
- 输出带有 `hash` 的js文件,无需担心缓存
- 自定义编译选项,指定目标浏览器范围,无需担心兼容性
- 支持 `vite` 环境变量
- 支持 `HMR`
- 支持不同的输出方式(内存模式和文件模式)
+- 支持 `CSR` 和 `SSR` 应用
+
## 安装
@@ -41,16 +41,20 @@ pnpm add vite-plugin-public-typescript -D
## 配置项
-| 参数 | 类型 | 默认值 | 描述 |
-| -------------- | -------------- | ------------------- | ---------------------------------------------- |
-| inputDir | `string` | `public-typescript` | 存放需要编译的 `typescript` 的目录 |
-| outputDir | `string` | `/` | 输出公共 javascript 的目录,相对于 `publicDir` |
-| manifestName | `string` | `manifest` | `manifest` 的文件名 |
-| hash | `boolean` | `true` | 编译后的 `js` 是否生成 `hash ` |
-| esbuildOptions | `BuildOptions` | `{}` | esbuild 构建选项 |
-| ssrBuild | `boolean` | `false` | 当前打包环境是否是 ssr |
-| sideEffects | `boolean` | `false` | 若 `typescript` 文件中有导入第三方库,则开启 |
-| destination | `string` | `memory` | 输出模式:内存模式 \| 文件模式 |
+| 参数 | 类型 | 默认值 | 描述 |
+| -------------- | -------------- | --------------------------------------------- | ---------------------------------------------- |
+| inputDir | `string` | `public-typescript` | 存放需要编译的 `typescript` 的目录 |
+| outputDir | `string` | `/` | 输出公共 javascript 的目录,相对于 `publicDir` |
+| manifestName | `string` | `manifest` | `manifest` 的文件名 |
+| hash | `boolean` | `true` | 编译后的 `js` 是否生成 `hash ` |
+| esbuildOptions | `BuildOptions` | `{}` | esbuild 构建选项 |
+| ssrBuild | `boolean` | `false` | 当前打包环境是否是 ssr |
+| sideEffects | `boolean` | `true` | 是否编译三方库 |
+| destination | `string` | `memory` | 输出模式:内存模式 \| 文件模式 |
+| cacheDir | `string` | `node_modules/.vite-plugin-public-typescript` | 存放manifest缓存的目录 |
+| base | `string` | vite config 中的 `base` | 资源 base url |
+
+
## 用法
@@ -58,7 +62,6 @@ pnpm add vite-plugin-public-typescript -D
```typescript
import { defineConfig } from 'vite'
import { publicTypescript, injectScripts } from 'vite-plugin-public-typescript'
-import manifest from './public-typescript/manifest.json'
export default defineConfig({
plugins: [
@@ -69,7 +72,7 @@ export default defineConfig({
outputDir: '/out',
destination: 'memory',
}),
- injectScripts([
+ injectScripts((manifest) => [
{
attrs: {
src: manifest.script,
@@ -81,6 +84,14 @@ export default defineConfig({
})
```
+### 获取manifest
+
+```typescript
+import { manifest } from 'vite-plugin-public-typescript/client'
+
+console.log(manifest)
+```
+
### SPA
@@ -94,12 +105,11 @@ export default defineConfig({
import type { HtmlTagDescriptor } from 'vite'
import { defineConfig } from 'vite'
import { publicTypescript, injectScripts } from 'vite-plugin-public-typescript'
-import manifest from './public-typescript/manifest.json'
export default defineConfig({
plugins: [
publicTypescript(),
- injectScripts([
+ injectScripts((manifest) => [
{
attrs: {
src: manifest.spa,
@@ -133,11 +143,16 @@ export default defineConfig({
#### server.js
```js
-import manifest from './public-typescript/manifest.json'
-
-const html = template
- // inject js
- .replace('', ``)
+import { injectScriptsToHtml } from 'vite-plugin-public-typescript'
+
+html = injectScriptsToHtml(html, (manifest) => [
+ {
+ attrs: {
+ src: manifest.ssr,
+ },
+ injectTo: 'head-prepend',
+ },
+])
```
diff --git a/package.json b/package.json
index 2a2b73d..d9965a6 100644
--- a/package.json
+++ b/package.json
@@ -22,14 +22,26 @@
"README.md",
"dist"
],
- "main": "./dist/index.cjs",
- "module": "./dist/index.js",
- "types": "./dist/index.d.ts",
+ "main": "./dist/node/index.cjs",
+ "module": "./dist/node/index.js",
+ "types": "./dist/node/index.d.ts",
"exports": {
".": {
- "types": "./dist/index.d.ts",
- "require": "./dist/index.cjs",
- "import": "./dist/index.js"
+ "types": "./dist/node/index.d.ts",
+ "require": "./dist/node/index.cjs",
+ "import": "./dist/node/index.js"
+ },
+ "./client": {
+ "types": "./dist/client/index.d.ts",
+ "require": "./dist/client/index.cjs",
+ "import": "./dist/client/index.js"
+ }
+ },
+ "typesVersions": {
+ "*": {
+ "client": [
+ "./dist/client/index.d.ts"
+ ]
}
},
"scripts": {
@@ -57,14 +69,15 @@
"magic-string": "^0.30.5",
"on-change": "^4.0.2",
"parse5": "^7.1.2",
+ "picocolors": "^1.0.0",
"tiny-glob": "^0.2.9",
"watcher": "^2.3.0"
},
"devDependencies": {
"@commitlint/cli": "^18.0.0",
- "@minko-fe/commitlint-config": "^2.0.2",
- "@minko-fe/eslint-config": "^2.0.2",
- "@minko-fe/tsconfig": "^2.0.2",
+ "@minko-fe/commitlint-config": "^2.0.4",
+ "@minko-fe/eslint-config": "^2.0.4",
+ "@minko-fe/tsconfig": "^2.0.4",
"@types/debug": "^4.1.9",
"@types/fs-extra": "^11.0.2",
"@types/node": "^20.8.6",
@@ -75,7 +88,7 @@
"npm-run-all": "^4.1.5",
"simple-git-hooks": "^2.9.0",
"taze": "^0.11.4",
- "tsup": "^7.2.0",
+ "tsup": "^7.0.0",
"typescript": "^5.2.2",
"vite": "^4.4.11",
"vitest": "^0.34.5",
diff --git a/playground/spa-file-mode/public-typescript/manifest.json b/playground/spa-file-mode/public-typescript/manifest.json
deleted file mode 100644
index fc4ce77..0000000
--- a/playground/spa-file-mode/public-typescript/manifest.json
+++ /dev/null
@@ -1,5 +0,0 @@
-{
- "define": "/vite-plugin-public-typescript/out/define.6fc3aeb7.js",
- "env": "/vite-plugin-public-typescript/out/env.8bea83e0.js",
- "hmr": "/vite-plugin-public-typescript/out/hmr.c01ee876.js"
-}
diff --git a/playground/spa-file-mode/public/out/define.0852dc5a.js b/playground/spa-file-mode/public/out/define.0852dc5a.js
new file mode 100644
index 0000000..ac4b171
--- /dev/null
+++ b/playground/spa-file-mode/public/out/define.0852dc5a.js
@@ -0,0 +1 @@
+(()=>{var o={hello:"world"};window.VITE_DEFINE={};window.VITE_DEFINE["custom-define"]="custom define!";window.VITE_DEFINE["hello-world"]=o;})();
diff --git a/playground/spa-file-mode/public/out/env.e6648627.js b/playground/spa-file-mode/public/out/env.e6648627.js
new file mode 100644
index 0000000..b694714
--- /dev/null
+++ b/playground/spa-file-mode/public/out/env.e6648627.js
@@ -0,0 +1 @@
+(()=>{var e={VITE_ENV_FROM_ENVFILE:"imfromdotenv",BASE_URL:"/vite-plugin-public-typescript/",MODE:"development",DEV:!0,PROD:!1};window.VITE_ENV=e;})();
diff --git a/playground/spa-file-mode/public/out/hmr.c01ee876.js b/playground/spa-file-mode/public/out/hmr.c01ee876.js
new file mode 100644
index 0000000..cd5d92e
--- /dev/null
+++ b/playground/spa-file-mode/public/out/hmr.c01ee876.js
@@ -0,0 +1 @@
+(()=>{console.log("hmr");window.hmr="hmr original text";})();
diff --git a/playground/spa-file-mode/src/App.tsx b/playground/spa-file-mode/src/App.tsx
index bd2f4b8..3595d6e 100644
--- a/playground/spa-file-mode/src/App.tsx
+++ b/playground/spa-file-mode/src/App.tsx
@@ -1,12 +1,12 @@
import { type ReactNode, useEffect, useState } from 'react'
-import manifest from '../public-typescript/manifest.json'
+import { manifest } from 'vite-plugin-public-typescript/client'
import './App.css'
function formatManifst() {
return Object.keys(manifest).map((key) => (
文件 {key}.ts 的 js uri 是:
-
{(manifest as Record)[key]}
+
{manifest[key]}
))
}
diff --git a/playground/spa-file-mode/vite.config.ts b/playground/spa-file-mode/vite.config.ts
index b63f747..209f1ed 100644
--- a/playground/spa-file-mode/vite.config.ts
+++ b/playground/spa-file-mode/vite.config.ts
@@ -1,7 +1,6 @@
import react from '@vitejs/plugin-react'
import { defineConfig } from 'vite'
import { injectScripts, publicTypescript } from 'vite-plugin-public-typescript'
-import manifest from './public-typescript/manifest.json'
// https://vitejs.dev/config/
export default defineConfig(() => ({
@@ -19,7 +18,7 @@ export default defineConfig(() => ({
outputDir: 'out',
destination: 'file',
}),
- injectScripts([
+ injectScripts((manifest) => [
{
attrs: { src: manifest.hmr },
injectTo: 'body',
diff --git a/playground/spa/__tests__/spa.spec.ts b/playground/spa/__tests__/spa.spec.ts
index 052c88d..d64582f 100644
--- a/playground/spa/__tests__/spa.spec.ts
+++ b/playground/spa/__tests__/spa.spec.ts
@@ -16,6 +16,8 @@ import { beforeAll, describe, expect, test } from 'vitest'
const hmrOriginText = 'hmr original text'
+const manifestPath = 'node_modules/.vite-plugin-public-typescript/manifest.json'
+
describe('console', async () => {
test('should console hmr string', async () => {
await untilBrowserLogAfter(() => page.goto(viteTestUrl), 'hmr')
@@ -47,7 +49,7 @@ describe('manifest', () => {
let manifest: string
beforeAll(() => {
try {
- manifest = readFile('public-typescript/manifest.json')
+ manifest = readFile(manifestPath)
} catch {}
})
@@ -62,7 +64,7 @@ describe('manifest', () => {
})
test.runIf(isServe)('should not trigger vite server restart when manifest file changed', async () => {
- editFile('public-typescript/manifest.json', (content) => content)
+ editFile(manifestPath, (content) => content)
await withRetry(async () => {
expect(serverLogs).not.toEqual(expect.arrayContaining([expect.stringMatching('server restarted')]))
expect(serverLogs).not.toEqual(expect.arrayContaining([expect.stringMatching('error')]))
@@ -75,7 +77,7 @@ describe.skipIf(isServe)('build', () => {
let manifest: string
beforeAll(() => {
try {
- manifest = readFile('public-typescript/manifest.json')
+ manifest = readFile(manifestPath)
jsFiles = listFiles('dist/out')
} catch {}
})
diff --git a/playground/spa/public-typescript/manifest.json b/playground/spa/public-typescript/manifest.json
deleted file mode 100644
index 07b496a..0000000
--- a/playground/spa/public-typescript/manifest.json
+++ /dev/null
@@ -1,5 +0,0 @@
-{
- "define": "/vite-plugin-public-typescript/out/define.0852dc5a.js",
- "env": "/vite-plugin-public-typescript/out/env.e6648627.js",
- "hmr": "/vite-plugin-public-typescript/out/hmr.c01ee876.js"
-}
diff --git a/playground/spa/src/App.tsx b/playground/spa/src/App.tsx
index 24e42e6..be4a885 100644
--- a/playground/spa/src/App.tsx
+++ b/playground/spa/src/App.tsx
@@ -1,12 +1,12 @@
import { type ReactNode, useEffect, useState } from 'react'
-import manifest from '../public-typescript/manifest.json'
+import { manifest } from 'vite-plugin-public-typescript/client'
import './App.css'
function formatManifst() {
return Object.keys(manifest).map((key) => (
文件 {key}.ts 的 js uri 是:
-
{(manifest as Record)[key]}
+
{manifest[key]}
))
}
diff --git a/playground/spa/src/index.css b/playground/spa/src/index.css
index 917888c..fa868ec 100644
--- a/playground/spa/src/index.css
+++ b/playground/spa/src/index.css
@@ -3,11 +3,9 @@
font-size: 16px;
line-height: 24px;
font-weight: 400;
-
color-scheme: light dark;
color: rgba(255, 255, 255, 0.87);
background-color: #242424;
-
font-synthesis: none;
text-rendering: optimizeLegibility;
-webkit-font-smoothing: antialiased;
@@ -20,6 +18,7 @@ a {
color: #646cff;
text-decoration: inherit;
}
+
a:hover {
color: #535bf2;
}
@@ -48,9 +47,11 @@ button {
cursor: pointer;
transition: border-color 0.25s;
}
+
button:hover {
border-color: #646cff;
}
+
button:focus,
button:focus-visible {
outline: 4px auto -webkit-focus-ring-color;
@@ -61,9 +62,11 @@ button:focus-visible {
color: #213547;
background-color: #ffffff;
}
+
a:hover {
color: #747bff;
}
+
button {
background-color: #f9f9f9;
}
diff --git a/playground/spa/vite.config.ts b/playground/spa/vite.config.ts
index e524c9d..22989f9 100644
--- a/playground/spa/vite.config.ts
+++ b/playground/spa/vite.config.ts
@@ -1,7 +1,6 @@
import react from '@vitejs/plugin-react'
import { defineConfig } from 'vite'
import { injectScripts, publicTypescript } from 'vite-plugin-public-typescript'
-import manifest from './public-typescript/manifest.json'
// https://vitejs.dev/config/
export default defineConfig(() => ({
@@ -18,8 +17,9 @@ export default defineConfig(() => ({
hash: true,
outputDir: 'out',
destination: 'memory',
+ cacheDir: 'node_modules/.vite-plugin-public-typescript',
}),
- injectScripts([
+ injectScripts((manifest) => [
{
attrs: { src: manifest.hmr },
injectTo: 'body',
@@ -38,5 +38,5 @@ export default defineConfig(() => ({
},
]),
],
- clearScreen: true,
+ clearScreen: false,
}))
diff --git a/playground/ssr/public-typescript/manifest.json b/playground/ssr/public-typescript/manifest.json
deleted file mode 100644
index 78897e5..0000000
--- a/playground/ssr/public-typescript/manifest.json
+++ /dev/null
@@ -1,3 +0,0 @@
-{
- "ssr": "/ssr.2507f2a9.js"
-}
diff --git a/playground/ssr/server.js b/playground/ssr/server.js
index 2ff41f7..cadf414 100644
--- a/playground/ssr/server.js
+++ b/playground/ssr/server.js
@@ -1,16 +1,10 @@
import express from 'express'
import fs from 'node:fs'
-import { createRequire } from 'node:module'
import path from 'node:path'
import { fileURLToPath } from 'node:url'
import { injectScriptsToHtml } from 'vite-plugin-public-typescript'
-const require = createRequire(import.meta.url)
-
-const manifest = require('./public-typescript/manifest.json')
-
const __dirname = path.dirname(fileURLToPath(import.meta.url))
-
const isTest = process.env.VITEST
process.env.MY_CUSTOM_SECRET = 'API_KEY_qwertyuiop'
@@ -82,7 +76,7 @@ export async function createServer(root = process.cwd(), isProd = process.env.NO
let html = template.replace(``, appHtml)
- html = injectScriptsToHtml(html, [
+ html = injectScriptsToHtml(html, (manifest) => [
{
attrs: {
src: manifest.ssr,
@@ -101,3 +95,9 @@ export async function createServer(root = process.cwd(), isProd = process.env.NO
return { app, vite, hmrPort }
}
+
+// const port = process.env.PORT || 5173
+// const { app } = await createServer()
+// app.listen(port, () => {
+// console.log(`http://localhost:${port}`)
+// })
diff --git a/playground/ssr/src/App.tsx b/playground/ssr/src/App.tsx
index d9a1983..6ccfc5b 100644
--- a/playground/ssr/src/App.tsx
+++ b/playground/ssr/src/App.tsx
@@ -1,7 +1,18 @@
+import { manifest } from 'vite-plugin-public-typescript/client'
+
export default function App() {
+ function formatManifst() {
+ return Object.keys(manifest).map((key) => (
+
+
文件 {key}.ts 的 js uri 是:
+
{manifest[key]}
+
+ ))
+ }
return (
<>
this is app
+ {formatManifst()}
>
)
}
diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml
index 0550684..6271527 100644
--- a/pnpm-lock.yaml
+++ b/pnpm-lock.yaml
@@ -26,6 +26,9 @@ importers:
parse5:
specifier: ^7.1.2
version: 7.1.2
+ picocolors:
+ specifier: ^1.0.0
+ version: 1.0.0
tiny-glob:
specifier: ^0.2.9
version: 0.2.9
@@ -37,14 +40,14 @@ importers:
specifier: ^18.0.0
version: 18.0.0
'@minko-fe/commitlint-config':
- specifier: ^2.0.2
- version: 2.0.2
+ specifier: ^2.0.4
+ version: 2.0.4
'@minko-fe/eslint-config':
- specifier: ^2.0.2
- version: 2.0.2(eslint@8.51.0)
+ specifier: ^2.0.4
+ version: 2.0.4(eslint@8.51.0)
'@minko-fe/tsconfig':
- specifier: ^2.0.2
- version: 2.0.2(typescript@5.2.2)
+ specifier: ^2.0.4
+ version: 2.0.4(typescript@5.2.2)
'@types/debug':
specifier: ^4.1.9
version: 4.1.9
@@ -1135,18 +1138,18 @@ packages:
type-detect: 4.0.8
dev: true
- /@minko-fe/commitlint-config@2.0.2:
- resolution: {integrity: sha512-7q22WawonkD00+jJjhK36nuRAeystsTjd4Zj+kPLWys/g4mWlA4MlE05o0ucjklmDIwmx9FEhzwNmz2nI0j1QA==}
+ /@minko-fe/commitlint-config@2.0.4:
+ resolution: {integrity: sha512-ZtwKT0z6WR4c/1slE2bbM7w4sqKH6WDW8W548vkkmvlEB3MtQRIaXU6NKNgm4Rt3/0qO7/ZF+51nwwbRuvj/Tw==}
dependencies:
'@commitlint/config-conventional': 17.8.1
dev: true
- /@minko-fe/eslint-config@2.0.2(eslint@8.51.0):
- resolution: {integrity: sha512-GyBVygchSPWFXTXqLopjGWGbJjOnMWwowa17rnoN1mEy9FnJ/UNGiMzcVpvCjlv8PA/ryYB6R9d1mFyn1jwWDA==}
+ /@minko-fe/eslint-config@2.0.4(eslint@8.51.0):
+ resolution: {integrity: sha512-zTzYgGUKqm9tVVtvzneIDXZpumeZPtByEHOC+sZluPhR/9wientfn22RvI1C9Kw5t54ja0wbQfYlKH/omQxcRg==}
peerDependencies:
eslint: ^8.0.0
dependencies:
- '@minko-fe/prettier-config': 2.0.2(prettier@3.0.3)
+ '@minko-fe/prettier-config': 2.0.4(prettier@3.0.3)
'@typescript-eslint/eslint-plugin': 6.9.0(@typescript-eslint/parser@6.9.0)(eslint@8.51.0)(typescript@5.2.2)
'@typescript-eslint/parser': 6.9.0(eslint@8.51.0)(typescript@5.2.2)
eslint: 8.51.0
@@ -1154,6 +1157,7 @@ packages:
eslint-config-standard: 17.1.0(eslint-plugin-import@2.29.0)(eslint-plugin-n@16.2.0)(eslint-plugin-promise@6.1.1)(eslint@8.51.0)
eslint-define-config: 1.24.1
eslint-plugin-antfu: 1.0.0-beta.12(eslint@8.51.0)(typescript@5.2.2)
+ eslint-plugin-disable-autofix: 4.1.0(eslint@8.51.0)
eslint-plugin-eslint-comments: 3.2.0(eslint@8.51.0)
eslint-plugin-html: 7.1.0
eslint-plugin-i: 2.28.1(@typescript-eslint/parser@6.9.0)(eslint@8.51.0)
@@ -1189,16 +1193,16 @@ packages:
- vue-eslint-parser
dev: true
- /@minko-fe/prettier-config@2.0.2(prettier@3.0.3):
- resolution: {integrity: sha512-Cwot3poiKrs1nTUBIBhohPgfW7R7F1Len3BOuBtdJupNd/MTIQRTvzaeV4ScGNBAYQt/LYphINU79IOihj6/Tw==}
+ /@minko-fe/prettier-config@2.0.4(prettier@3.0.3):
+ resolution: {integrity: sha512-TsfteRXsUD17EjIMa7lkHiwmuIV96cYorzhdmWKQIHBQff6s6+7RO9REyqGHCETWybnOKWbIHUVj5h9cJOWYJg==}
peerDependencies:
prettier: '>=2.0.0'
dependencies:
prettier: 3.0.3
dev: true
- /@minko-fe/tsconfig@2.0.2(typescript@5.2.2):
- resolution: {integrity: sha512-DPb+atqs7PyDhD3VbZSkiNZ2GI0wCGwdrUOdZMUOyoDyiQDIGVLdWBrJsxM2cnHyJVddoieeowXDYXexw2tYlA==}
+ /@minko-fe/tsconfig@2.0.4(typescript@5.2.2):
+ resolution: {integrity: sha512-WHXhBCA+hWMUVdVp2d1aeIRem8otq50Nji7CxAu3qZHjKveHfGO7XNjCKf1hZlASsNML99SD788qeQlj7Xzmew==}
peerDependencies:
typescript: '>=4.0.0'
dependencies:
@@ -1594,14 +1598,6 @@ packages:
- supports-color
dev: true
- /@typescript-eslint/scope-manager@6.7.5:
- resolution: {integrity: sha512-GAlk3eQIwWOJeb9F7MKQ6Jbah/vx1zETSDw8likab/eFcqkjSD7BI75SDAeC5N2L0MmConMoPvTsmkrg71+B1A==}
- engines: {node: ^16.0.0 || >=18.0.0}
- dependencies:
- '@typescript-eslint/types': 6.7.5
- '@typescript-eslint/visitor-keys': 6.7.5
- dev: true
-
/@typescript-eslint/scope-manager@6.9.0:
resolution: {integrity: sha512-1R8A9Mc39n4pCCz9o79qRO31HGNDvC7UhPhv26TovDsWPBDx+Sg3rOZdCELIA3ZmNoWAuxaMOT7aWtGRSYkQxw==}
engines: {node: ^16.0.0 || >=18.0.0}
@@ -1630,37 +1626,11 @@ packages:
- supports-color
dev: true
- /@typescript-eslint/types@6.7.5:
- resolution: {integrity: sha512-WboQBlOXtdj1tDFPyIthpKrUb+kZf2VroLZhxKa/VlwLlLyqv/PwUNgL30BlTVZV1Wu4Asu2mMYPqarSO4L5ZQ==}
- engines: {node: ^16.0.0 || >=18.0.0}
- dev: true
-
/@typescript-eslint/types@6.9.0:
resolution: {integrity: sha512-+KB0lbkpxBkBSiVCuQvduqMJy+I1FyDbdwSpM3IoBS7APl4Bu15lStPjgBIdykdRqQNYqYNMa8Kuidax6phaEw==}
engines: {node: ^16.0.0 || >=18.0.0}
dev: true
- /@typescript-eslint/typescript-estree@6.7.5(typescript@5.2.2):
- resolution: {integrity: sha512-NhJiJ4KdtwBIxrKl0BqG1Ur+uw7FiOnOThcYx9DpOGJ/Abc9z2xNzLeirCG02Ig3vkvrc2qFLmYSSsaITbKjlg==}
- engines: {node: ^16.0.0 || >=18.0.0}
- peerDependencies:
- typescript: '*'
- peerDependenciesMeta:
- typescript:
- optional: true
- dependencies:
- '@typescript-eslint/types': 6.7.5
- '@typescript-eslint/visitor-keys': 6.7.5
- debug: 4.3.4
- globby: 11.1.0
- is-glob: 4.0.3
- semver: 7.5.4
- ts-api-utils: 1.0.3(typescript@5.2.2)
- typescript: 5.2.2
- transitivePeerDependencies:
- - supports-color
- dev: true
-
/@typescript-eslint/typescript-estree@6.9.0(typescript@5.2.2):
resolution: {integrity: sha512-NJM2BnJFZBEAbCfBP00zONKXvMqihZCrmwCaik0UhLr0vAgb6oguXxLX1k00oQyD+vZZ+CJn3kocvv2yxm4awQ==}
engines: {node: ^16.0.0 || >=18.0.0}
@@ -1682,25 +1652,6 @@ packages:
- supports-color
dev: true
- /@typescript-eslint/utils@6.7.5(eslint@8.51.0)(typescript@5.2.2):
- resolution: {integrity: sha512-pfRRrH20thJbzPPlPc4j0UNGvH1PjPlhlCMq4Yx7EGjV7lvEeGX0U6MJYe8+SyFutWgSHsdbJ3BXzZccYggezA==}
- engines: {node: ^16.0.0 || >=18.0.0}
- peerDependencies:
- eslint: ^7.0.0 || ^8.0.0
- dependencies:
- '@eslint-community/eslint-utils': 4.4.0(eslint@8.51.0)
- '@types/json-schema': 7.0.13
- '@types/semver': 7.5.2
- '@typescript-eslint/scope-manager': 6.7.5
- '@typescript-eslint/types': 6.7.5
- '@typescript-eslint/typescript-estree': 6.7.5(typescript@5.2.2)
- eslint: 8.51.0
- semver: 7.5.4
- transitivePeerDependencies:
- - supports-color
- - typescript
- dev: true
-
/@typescript-eslint/utils@6.9.0(eslint@8.51.0)(typescript@5.2.2):
resolution: {integrity: sha512-5Wf+Jsqya7WcCO8me504FBigeQKVLAMPmUzYgDbWchINNh1KJbxCgVya3EQ2MjvJMVeXl3pofRmprqX6mfQkjQ==}
engines: {node: ^16.0.0 || >=18.0.0}
@@ -1720,14 +1671,6 @@ packages:
- typescript
dev: true
- /@typescript-eslint/visitor-keys@6.7.5:
- resolution: {integrity: sha512-3MaWdDZtLlsexZzDSdQWsFQ9l9nL8B80Z4fImSpyllFC/KLqWQRdEcB+gGGO+N3Q2uL40EsG66wZLsohPxNXvg==}
- engines: {node: ^16.0.0 || >=18.0.0}
- dependencies:
- '@typescript-eslint/types': 6.7.5
- eslint-visitor-keys: 3.4.3
- dev: true
-
/@typescript-eslint/visitor-keys@6.9.0:
resolution: {integrity: sha512-dGtAfqjV6RFOtIP8I0B4ZTBRrlTT8NHHlZZSchQx3qReaoDeXhYM++M4So2AgFK9ZB0emRPA6JI1HkafzA2Ibg==}
engines: {node: ^16.0.0 || >=18.0.0}
@@ -1935,6 +1878,11 @@ packages:
picomatch: 2.3.1
dev: true
+ /app-root-path@3.1.0:
+ resolution: {integrity: sha512-biN3PwB2gUtjaYy/isrU3aNWI5w+fAfvHkSvCKeQGxhmYpwKFUxudR3Yya+KqVRHBmEDYh+/lTozYCFbmzX4nA==}
+ engines: {node: '>= 6.0.0'}
+ dev: true
+
/aproba@2.0.0:
resolution: {integrity: sha512-lYe4Gx7QT+MKGbDsA+Z+he/Wtef0BiwDOlK/XkBrdfsh9J/jPPXbX0tE9x9cl27Tmu5gg3QUbUrQYa/y+KOHPQ==}
dev: true
@@ -3015,7 +2963,7 @@ packages:
define-properties: 1.2.1
es-abstract: 1.22.2
es-set-tostringtag: 2.0.1
- function-bind: 1.1.1
+ function-bind: 1.1.2
get-intrinsic: 1.2.1
globalthis: 1.0.3
has-property-descriptors: 1.0.0
@@ -3171,7 +3119,7 @@ packages:
resolution: {integrity: sha512-WFj2isz22JahUv+B788TlO3N6zL3nNJGU8CcZbPZvVEkBPaJdCV4vy5wyghty5ROFbCRnm132v8BScu5/1BQ8g==}
dependencies:
debug: 3.2.7
- is-core-module: 2.13.0
+ is-core-module: 2.13.1
resolve: 1.22.6
transitivePeerDependencies:
- supports-color
@@ -3211,13 +3159,24 @@ packages:
peerDependencies:
eslint: '*'
dependencies:
- '@typescript-eslint/utils': 6.7.5(eslint@8.51.0)(typescript@5.2.2)
+ '@typescript-eslint/utils': 6.9.0(eslint@8.51.0)(typescript@5.2.2)
eslint: 8.51.0
transitivePeerDependencies:
- supports-color
- typescript
dev: true
+ /eslint-plugin-disable-autofix@4.1.0(eslint@8.51.0):
+ resolution: {integrity: sha512-D/fUPM1hTygcZhxM5rc2oiMhud2qgF/sEy3QWMKgSr0A3nbpek6gKG8r8Zk25bBTvAqxjMzqxGDxFN9B/bvrSw==}
+ peerDependencies:
+ eslint: '>= 7'
+ dependencies:
+ app-root-path: 3.1.0
+ eslint: 8.51.0
+ eslint-rule-composer: 0.3.0
+ lodash: 4.17.21
+ dev: true
+
/eslint-plugin-es-x@7.2.0(eslint@8.51.0):
resolution: {integrity: sha512-9dvv5CcvNjSJPqnS5uZkqb3xmbeqRLnvXKK7iI5+oK/yTusyc46zbBZKENGsOfojm/mKfszyZb+wNqNPAPeGXA==}
engines: {node: ^14.18.0 || >=16.0.0}
@@ -3341,7 +3300,7 @@ packages:
eslint-plugin-es-x: 7.2.0(eslint@8.51.0)
get-tsconfig: 4.7.2
ignore: 5.2.4
- is-core-module: 2.13.0
+ is-core-module: 2.13.1
minimatch: 3.1.2
resolve: 1.22.6
semver: 7.5.4
@@ -3365,7 +3324,7 @@ packages:
vue-eslint-parser:
optional: true
dependencies:
- '@typescript-eslint/utils': 6.7.5(eslint@8.51.0)(typescript@5.2.2)
+ '@typescript-eslint/utils': 6.9.0(eslint@8.51.0)(typescript@5.2.2)
eslint: 8.51.0
minimatch: 9.0.3
natural-compare-lite: 1.4.0
@@ -5841,7 +5800,6 @@ packages:
/picocolors@1.0.0:
resolution: {integrity: sha512-1fygroTLlHu66zi26VoTDv8yRgm0Fccecssto+MhsZ0D/DGW2sm8E8AjW7NU5VVTRt5GxbeZ5qBuJr+HyLYkjQ==}
- dev: true
/picomatch@2.3.1:
resolution: {integrity: sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==}
@@ -6261,7 +6219,7 @@ packages:
resolution: {integrity: sha512-iMDbmAWtfU+MHpxt/I5iWI7cY6YVEZUQ3MBgPQ++XD1PELuJHIl82xBmObyP2KyQmkNB2dsqF7seoQQiAn5yDQ==}
hasBin: true
dependencies:
- is-core-module: 2.13.0
+ is-core-module: 2.13.1
path-parse: 1.0.7
supports-preserve-symlinks-flag: 1.0.0
dev: true
diff --git a/src/client/index.ts b/src/client/index.ts
new file mode 100644
index 0000000..5d12f9f
--- /dev/null
+++ b/src/client/index.ts
@@ -0,0 +1 @@
+export * from './manifest'
diff --git a/src/client/manifest/index.ts b/src/client/manifest/index.ts
new file mode 100644
index 0000000..b2b3acd
--- /dev/null
+++ b/src/client/manifest/index.ts
@@ -0,0 +1,5 @@
+import virtualManifest from 'virtual:public-typescript:manifest'
+
+const manifest = virtualManifest
+
+export { manifest }
diff --git a/src/client/manifest/manifest.d.ts b/src/client/manifest/manifest.d.ts
new file mode 100644
index 0000000..cff0ed9
--- /dev/null
+++ b/src/client/manifest/manifest.d.ts
@@ -0,0 +1,3 @@
+declare module 'virtual:public-typescript:manifest' {
+ export default manifest as Record
+}
diff --git a/src/global-config/GlobalConfigBuilder.ts b/src/node/global-config/GlobalConfigBuilder.ts
similarity index 86%
rename from src/global-config/GlobalConfigBuilder.ts
rename to src/node/global-config/GlobalConfigBuilder.ts
index 2d3f9ae..3e00447 100644
--- a/src/global-config/GlobalConfigBuilder.ts
+++ b/src/node/global-config/GlobalConfigBuilder.ts
@@ -1,6 +1,6 @@
import path from 'node:path'
-import { type ResolvedConfig } from 'vite'
-import { type VPPTPluginOptions } from '..'
+import { type Logger, type ResolvedConfig } from 'vite'
+import { type OptionsTypeWithDefault } from '../helper/utils'
import { type CacheValue, type ManifestCache } from '../manifest-cache/ManifestCache'
import { type BaseCacheProcessor } from '../processor/BaseCacheProcessor'
@@ -9,7 +9,8 @@ export type UserConfig = {
originFilesGlob: string[]
viteConfig: ResolvedConfig
cacheProcessor: BaseCacheProcessor
-} & Required
+ logger: Logger
+} & Required
export type GlobalConfig = UserConfig & {
absOutputDir: string
@@ -27,6 +28,7 @@ export class GlobalConfigBuilder {
const root = c.viteConfig.root || process.cwd()
const absOutputDir = path.join(root, c.outputDir)
const absInputDir = path.join(root, c.inputDir)
+
this._globalConfig = {
...c,
absInputDir,
diff --git a/src/global-config/index.ts b/src/node/global-config/index.ts
similarity index 100%
rename from src/global-config/index.ts
rename to src/node/global-config/index.ts
diff --git a/src/helper/build.ts b/src/node/helper/build.ts
similarity index 95%
rename from src/helper/build.ts
rename to src/node/helper/build.ts
index 641c926..cf83596 100644
--- a/src/helper/build.ts
+++ b/src/node/helper/build.ts
@@ -2,11 +2,9 @@ import createDebug from 'debug'
import { type BuildResult, type Plugin, build as esbuild } from 'esbuild'
import path from 'node:path'
import { type ResolvedConfig } from 'vite'
-import { type VPPTPluginOptions } from '..'
-import { name } from '../../package.json'
import { globalConfig } from '../global-config'
import { type BaseCacheProcessor } from '../processor/BaseCacheProcessor'
-import { getContentHash } from './utils'
+import { type OptionsTypeWithDefault, getContentHash, pkgName } from './utils'
const debug = createDebug('vite-plugin-public-typescript:build ===> ')
@@ -57,7 +55,7 @@ function transformEnvToDefine(viteConfig: ResolvedConfig) {
type IBuildOptions = {
filePath: string
viteConfig: ResolvedConfig
-} & Required
+} & OptionsTypeWithDefault
export async function esbuildTypescript(buildOptions: IBuildOptions) {
const { filePath, esbuildOptions, sideEffects, viteConfig } = buildOptions
@@ -90,7 +88,7 @@ export async function esbuildTypescript(buildOptions: IBuildOptions) {
debug('esbuild success:', filePath)
} catch (error) {
- console.error(`[${name}]`, error)
+ console.error(`[${pkgName}]`, error)
return
}
diff --git a/src/helper/file-watcher.ts b/src/node/helper/file-watcher.ts
similarity index 51%
rename from src/helper/file-watcher.ts
rename to src/node/helper/file-watcher.ts
index 1dafecc..8f7e9e8 100644
--- a/src/helper/file-watcher.ts
+++ b/src/node/helper/file-watcher.ts
@@ -3,36 +3,41 @@ import path from 'node:path'
import Watcher from 'watcher'
import { globalConfig } from '../global-config'
import { build } from './build'
-import { _isPublicTypescript } from './utils'
+import { _isPublicTypescript, type HmrFile } from './utils'
const debug = createDebug('vite-plugin-public-typescript:file-watcher ===> ')
-export async function handleUnlink(filePath: string, cb: () => void) {
+export async function handleUnlink(filePath: string, cb?: () => void) {
if (_isPublicTypescript(filePath)) {
const fileName = path.parse(filePath).name
debug('unlink:', fileName)
await globalConfig.get().cacheProcessor.deleteOldJs({ originFileName: fileName })
- cb()
+ cb?.()
}
}
-export async function handleFileAdded(filePath: string, cb: () => void) {
+export async function handleFileAdded(filePath: string, cb?: () => void) {
if (_isPublicTypescript(filePath)) {
debug('file added:', filePath)
await build({ filePath }, (...args) => globalConfig.get().cacheProcessor.onTsBuildEnd(...args))
- cb()
+ cb?.()
}
}
-export async function handleFileRenamed(filePath: string, filePathNext: string, cb: () => void) {
+export async function handleFileRenamed(filePath: string, filePathNext: string, cb?: () => void) {
if (_isPublicTypescript(filePath)) {
debug('file renamed:', filePath, '==>', filePathNext)
- await handleUnlink(filePath, cb)
- await handleFileAdded(filePathNext, cb)
+ await handleUnlink(filePath)
+ await handleFileAdded(filePathNext)
+ cb?.()
}
}
-export function initWatcher(cb: () => void) {
+async function handleFileChange(filePath: string, cb?: () => void) {
+ handleFileAdded(filePath, cb)
+}
+
+export function initWatcher(cb: (file: HmrFile) => void) {
try {
const watcher = new Watcher(globalConfig.get().absInputDir, {
debounce: 0,
@@ -42,12 +47,16 @@ export function initWatcher(cb: () => void) {
renameTimeout: 0,
})
- watcher.on('unlink', (filePath) => handleUnlink(filePath, cb))
+ watcher.on('unlink', (filePath: string) => handleUnlink(filePath, () => cb({ path: filePath, event: 'deleted' })))
- watcher.on('add', (filePath) => handleFileAdded(filePath, cb))
+ watcher.on('add', (filePath: string) => handleFileAdded(filePath, () => cb({ path: filePath, event: 'added' })))
+
+ watcher.on('rename', async (f: string, fNext: string) => {
+ handleFileRenamed(f, fNext, () => cb({ path: fNext, event: `renamed` }))
+ })
- watcher.on('rename', async (f, fNext) => {
- await handleFileRenamed(f, fNext, cb)
+ watcher.on('change', (filePath: string) => {
+ handleFileChange(filePath, () => cb({ path: filePath, event: 'changed' }))
})
} catch (error) {
console.error(error)
diff --git a/src/helper/html.ts b/src/node/helper/html.ts
similarity index 100%
rename from src/helper/html.ts
rename to src/node/helper/html.ts
diff --git a/src/node/helper/server.ts b/src/node/helper/server.ts
new file mode 100644
index 0000000..8570923
--- /dev/null
+++ b/src/node/helper/server.ts
@@ -0,0 +1,40 @@
+import colors from 'picocolors'
+import { type ResolvedConfig, type WebSocketServer } from 'vite'
+import { globalConfig } from '../global-config'
+import { fileRelativeRootPath, isInTest } from './utils'
+
+export function addCodeHeader(code: string) {
+ return `// Generated via vite-plugin-public-typescript (This line print in serve mode only)\n${code}`
+}
+
+export type HmrFile = {
+ path: string
+ event: string
+}
+export function reloadPage(ws: WebSocketServer, file: HmrFile) {
+ const { logger } = globalConfig.get()
+
+ if (!isInTest()) {
+ logger.info(
+ colors.green(`page reload `) + colors.gray(`file ${file.event} `) + colors.dim(fileRelativeRootPath(file.path)),
+ {
+ clear: false,
+ timestamp: true,
+ },
+ )
+ }
+
+ ws.send({
+ path: '*',
+ type: 'full-reload',
+ })
+}
+
+export function disableManifestHmr(config: ResolvedConfig, manifestPath: string) {
+ if (config.command === 'serve') {
+ const index = config.configFileDependencies.indexOf(manifestPath)
+ if (index !== -1) {
+ config.configFileDependencies.splice(index, 1)
+ }
+ }
+}
diff --git a/src/helper/utils.ts b/src/node/helper/utils.ts
similarity index 82%
rename from src/helper/utils.ts
rename to src/node/helper/utils.ts
index e878747..f9b0d32 100644
--- a/src/helper/utils.ts
+++ b/src/node/helper/utils.ts
@@ -3,21 +3,37 @@ import fs from 'fs-extra'
import { createHash } from 'node:crypto'
import path from 'node:path'
import glob from 'tiny-glob'
-import { type ResolvedConfig, type WebSocketServer, normalizePath } from 'vite'
+import { type ResolvedConfig, createLogger, normalizePath } from 'vite'
import { type VPPTPluginOptions } from '..'
+import { name as pkgName } from '../../../package.json'
import { globalConfig } from '../global-config'
import { manifestCache } from '../manifest-cache'
import { initCacheProcessor } from '../processor/processor'
+import { disableManifestHmr } from './server'
const debug = createDebug('vite-plugin-public-typescript:util ===> ')
-export function reloadPage(ws: WebSocketServer) {
- ws.send({
- path: '*',
- type: 'full-reload',
+type PartialExclude = Omit & Partial>
+
+export type OptionsTypeWithDefault = PartialExclude, 'base'>
+
+export { pkgName }
+
+export function createInternalLogger(allowClearScreen?: boolean) {
+ return createLogger('info', {
+ allowClearScreen: !!allowClearScreen,
+ prefix: `[${pkgName}]`,
})
}
+export function fileRelativeRootPath(filePath: string) {
+ return normalizePath(`/${path.relative(globalConfig.get().viteConfig.root, filePath)}`)
+}
+
+export function isInTest() {
+ return process.env.VITEST || process.env.CI
+}
+
export function isPublicTypescript(args: { filePath: string; inputDir: string; root: string }) {
const { filePath, root, inputDir } = args
@@ -35,6 +51,10 @@ export function _isPublicTypescript(filePath: string) {
})
}
+export function isManifestFile(filePath: string) {
+ return filePath === manifestCache.manifestPath
+}
+
export function isWindows() {
return typeof process != 'undefined' && process.platform === 'win32'
}
@@ -141,7 +161,7 @@ export function extractHashFromFileName(filename: string, hash: VPPTPluginOption
return ''
}
-export function validateOptions(options: Required) {
+export function validateOptions(options: OptionsTypeWithDefault) {
let { outputDir } = options
// ensure outputDir is Dir
if (!outputDir.startsWith('/')) {
@@ -165,10 +185,6 @@ export function normalizeAssetsDirPath(dir: string) {
return dir.replaceAll(/^\/|\/$/g, '')
}
-export function addCodeHeader(code: string) {
- return `// gen via vite-plugin-public-typescript (show in serve mode only)\n${code}`
-}
-
export function getInputDir(resolvedRoot: string, originInputDir: string, suffix = '') {
return normalizePath(path.resolve(resolvedRoot, `${originInputDir}${suffix}`))
}
@@ -198,24 +214,17 @@ export function removeOldJsFiles(oldFiles: string[]) {
}
}
-export function disableManifestHmr(config: ResolvedConfig, manifestPath: string) {
- if (config.command === 'serve') {
- const index = config.configFileDependencies.indexOf(manifestPath)
- if (index !== -1) {
- config.configFileDependencies.splice(index, 1)
- }
- }
-}
-
// NOTE: remmember call this before write compiled js file to disk
export function removeBase(filePath: string, base: string): string {
const devBase = base.at(-1) === '/' ? base : `${base}/`
return filePath.startsWith(devBase) ? filePath.slice(devBase.length - 1) : filePath
}
-export async function setupGlobalConfig(viteConfig: ResolvedConfig, opts: Required) {
+export async function setupGlobalConfig(viteConfig: ResolvedConfig, opts: OptionsTypeWithDefault) {
const resolvedRoot = normalizePath(viteConfig.root ? path.resolve(viteConfig.root) : process.cwd())
+ opts.base = opts.base ?? viteConfig.base
+
fs.ensureDirSync(getInputDir(resolvedRoot, opts.inputDir))
const originFilesGlob = await glob(getInputDir(resolvedRoot, opts.inputDir, `/*.ts`), {
@@ -225,24 +234,28 @@ export async function setupGlobalConfig(viteConfig: ResolvedConfig, opts: Requir
const cacheProcessor = initCacheProcessor(opts, manifestCache)
+ const logger = createInternalLogger(viteConfig.clearScreen)
+
globalConfig.init({
cacheProcessor,
manifestCache,
originFilesGlob,
viteConfig,
- ...opts,
+ logger,
+ ...(opts as Required),
})
return globalConfig.get()
}
-export async function setupManifestCache(viteConfig: ResolvedConfig, opts: Required) {
- manifestCache.setManifestPath(normalizePath(`${globalConfig.get().absInputDir}/${opts.manifestName}.json`))
+export async function setupManifestCache(viteConfig: ResolvedConfig, opts: OptionsTypeWithDefault) {
+ const cacheDir = path.resolve(viteConfig.root, opts.cacheDir)
+ manifestCache.setManifestPath(normalizePath(`${cacheDir}/${opts.manifestName}.json`))
// no need to set `_pathToDisk` manually anymore
manifestCache.beforeSet = (value) => {
if (value?.path) {
- value._pathToDisk = removeBase(value.path, viteConfig.base)
+ value._pathToDisk = removeBase(value.path, opts.base!)
}
return value
}
diff --git a/src/node/helper/virtual.ts b/src/node/helper/virtual.ts
new file mode 100644
index 0000000..5cbb3cb
--- /dev/null
+++ b/src/node/helper/virtual.ts
@@ -0,0 +1,2 @@
+export const virtualModuleId = 'virtual:public-typescript:manifest'
+export const resolvedVirtualModuleId = `\0${virtualModuleId}`
diff --git a/src/index.ts b/src/node/index.ts
similarity index 57%
rename from src/index.ts
rename to src/node/index.ts
index 54c25b0..0e5ada6 100644
--- a/src/index.ts
+++ b/src/node/index.ts
@@ -1,27 +1,28 @@
import createDebug from 'debug'
import { type BuildOptions } from 'esbuild'
import fs from 'fs-extra'
-import MagicString from 'magic-string'
import path from 'node:path'
-import { type PluginOption, type ResolvedConfig, send } from 'vite'
+import { type PluginOption, type ResolvedConfig } from 'vite'
import { globalConfig } from './global-config'
-import { build, buildAllOnce, esbuildTypescript } from './helper/build'
+import { buildAllOnce, esbuildTypescript } from './helper/build'
import { initWatcher } from './helper/file-watcher'
-import { getScriptInfo, nodeIsElement, traverseHtml } from './helper/html'
+import { reloadPage } from './helper/server'
import {
_isPublicTypescript,
- addCodeHeader,
+ type OptionsTypeWithDefault,
eq,
findAllOldJsFile,
isEmptyObject,
+ isManifestFile,
normalizeAssetsDirPath,
- reloadPage,
removeOldJsFiles,
setupGlobalConfig,
setupManifestCache,
validateOptions,
} from './helper/utils'
import { manifestCache } from './manifest-cache'
+import { pluginServer } from './plugins/server'
+import { pluginVirtual } from './plugins/virtual'
const debug = createDebug('vite-plugin-public-typescript:index ===> ')
@@ -66,24 +67,38 @@ export interface VPPTPluginOptions {
/**
* @description treat `input` as sideEffect or not
* @see https://esbuild.github.io/api/#tree-shaking-and-side-effects
- * @default false
+ * @default true
*/
sideEffects?: boolean
/**
* @description build-out destination
* @default 'memory'
+ * @version v1.5.0 introduced
*/
destination?: 'memory' | 'file'
+ /**
+ * @description manifest cache dir
+ * @default `node_modules/.vite-plugin-public-typescript`
+ * @version v2.0.0 introduced
+ */
+ cacheDir?: string
+ /**
+ * @description base path for all files
+ * @default vite.config.ts `base`
+ * @version v2.0.0 introduced
+ */
+ base?: string
}
-export const DEFAULT_OPTIONS: Required = {
+export const DEFAULT_OPTIONS: OptionsTypeWithDefault = {
destination: 'memory',
esbuildOptions: {},
hash: true,
inputDir: 'public-typescript',
manifestName: 'manifest',
outputDir: '/',
- sideEffects: false,
+ sideEffects: true,
+ cacheDir: 'node_modules/.vite-plugin-public-typescript',
}
let previousOpts: VPPTPluginOptions
@@ -112,13 +127,9 @@ export default function publicTypescript(options: VPPTPluginOptions = {}) {
await setupManifestCache(viteConfig, opts)
},
configureServer(server) {
- if (process.env.VITEST || process.env.CI) {
- return
- }
-
const { ws } = server
- initWatcher(() => reloadPage(ws))
+ initWatcher((file) => reloadPage(ws, file))
},
async buildStart() {
// skip server restart when options not changed
@@ -166,7 +177,7 @@ export default function publicTypescript(options: VPPTPluginOptions = {}) {
}
}
- buildAllOnce(originFilesGlob)
+ await buildAllOnce(originFilesGlob)
},
generateBundle() {
if (opts.destination === 'memory') {
@@ -181,100 +192,17 @@ export default function publicTypescript(options: VPPTPluginOptions = {}) {
}
},
async handleHotUpdate(ctx) {
- const { file, server } = ctx
+ const { file } = ctx
- if (_isPublicTypescript(file)) {
+ if (_isPublicTypescript(file) || isManifestFile(file)) {
debug('hmr:', file)
- await build({ filePath: file }, (...args) => globalConfig.get().cacheProcessor.onTsBuildEnd(...args))
-
- reloadPage(server.ws)
-
return []
}
},
- transformIndexHtml: {
- order: 'post',
- async handler(html, { filename }) {
- const s = new MagicString(html)
-
- await traverseHtml(html, filename, (node) => {
- if (!nodeIsElement(node)) {
- return
- }
- // script tags
- if (node.nodeName === 'script') {
- const { src, vppt } = getScriptInfo(node)
-
- if (vppt?.name && src?.value) {
- const c = manifestCache.get()
- let cacheItem = manifestCache.findCacheItemByPath(src.value)
-
- if (!cacheItem) {
- const fileName = path.basename(src.value).split('.')[0]
- cacheItem = c[fileName]
- }
-
- if (cacheItem) {
- const attrs = node.attrs
- .reduce((acc, attr) => {
- if (attr.name === src.name) {
- acc += ` ${attr.name}="${cacheItem!.path}"`
- return acc
- }
- acc += attr.value ? ` ${attr.name}="${attr.value}"` : ` ${attr.name}`
- return acc
- }, '')
- .trim()
-
- s.update(
- node.sourceCodeLocation!.startOffset,
- node.sourceCodeLocation!.endOffset,
- ``,
- )
- } else {
- s.remove(node.sourceCodeLocation!.startOffset, node.sourceCodeLocation!.endOffset)
- }
- }
- }
- })
- return s.toString()
- },
- },
- },
- {
- name: 'vite:public-typescript:server',
- apply: 'serve',
- enforce: 'post',
- load(id) {
- const cacheItem = manifestCache.findCacheItemByPath(id)
- if (cacheItem) {
- return {
- code: '',
- map: null,
- }
- }
- },
- async configureServer(server) {
- server.middlewares.use((req, res, next) => {
- try {
- if (req?.url?.startsWith('/') && req?.url?.endsWith('.js')) {
- const cacheItem = manifestCache.findCacheItemByPath(req.url)
- if (cacheItem) {
- return send(req, res, addCodeHeader(cacheItem._code || ''), 'js', {
- cacheControl: 'max-age=31536000,immutable',
- headers: server.config.server.headers,
- map: null,
- })
- }
- }
- } catch (error) {
- return next(error)
- }
- next()
- })
- },
},
+ pluginServer(),
+ pluginVirtual(),
]
return plugins
diff --git a/src/manifest-cache/ManifestCache.ts b/src/node/manifest-cache/ManifestCache.ts
similarity index 100%
rename from src/manifest-cache/ManifestCache.ts
rename to src/node/manifest-cache/ManifestCache.ts
diff --git a/src/manifest-cache/index.ts b/src/node/manifest-cache/index.ts
similarity index 100%
rename from src/manifest-cache/index.ts
rename to src/node/manifest-cache/index.ts
diff --git a/src/plugins/inject-script.ts b/src/node/plugins/inject-script.ts
similarity index 73%
rename from src/plugins/inject-script.ts
rename to src/node/plugins/inject-script.ts
index 29a04a6..c4022d9 100644
--- a/src/plugins/inject-script.ts
+++ b/src/node/plugins/inject-script.ts
@@ -1,10 +1,12 @@
import { type HtmlTagDescriptor, type PluginOption } from 'vite'
import { VPPT_DATA_ATTR, injectTagsToHtml } from '../helper/html'
+import { manifestCache } from '../manifest-cache'
-export type Scripts = Omit[]
+export type Scripts = (manifest: Record) => Omit[]
export function generateScriptTags(scripts: Scripts) {
- const tags: HtmlTagDescriptor[] = scripts.map((s) => ({
+ const _scripts = scripts(manifestCache.getManifestJson())
+ const tags: HtmlTagDescriptor[] = _scripts.map((s) => ({
...s,
attrs: {
crossorigin: true,
@@ -23,6 +25,7 @@ export function injectScriptsToHtml(html: string, scripts: Scripts) {
export function injectScripts(scripts: Scripts) {
const plugin: PluginOption = {
name: 'vite:public-typescript:inject-script',
+ enforce: 'post',
transformIndexHtml: {
async handler(html) {
return {
diff --git a/src/node/plugins/server.ts b/src/node/plugins/server.ts
new file mode 100644
index 0000000..20c53dd
--- /dev/null
+++ b/src/node/plugins/server.ts
@@ -0,0 +1,92 @@
+import MagicString from 'magic-string'
+import path from 'node:path'
+import { type PluginOption, send } from 'vite'
+import { getScriptInfo, nodeIsElement, traverseHtml } from '../helper/html'
+import { addCodeHeader } from '../helper/server'
+import { manifestCache } from '../manifest-cache'
+
+export function pluginServer() {
+ const plugin: PluginOption = {
+ name: 'vite:public-typescript:server',
+ apply: 'serve',
+ enforce: 'post',
+ load(id) {
+ const cacheItem = manifestCache.findCacheItemByPath(id)
+ if (cacheItem) {
+ return {
+ code: '',
+ map: null,
+ }
+ }
+ },
+ async configureServer(server) {
+ server.middlewares.use((req, res, next) => {
+ try {
+ if (req?.url?.startsWith('/') && req?.url?.endsWith('.js')) {
+ const cacheItem = manifestCache.findCacheItemByPath(req.url)
+ if (cacheItem) {
+ return send(req, res, addCodeHeader(cacheItem._code || ''), 'js', {
+ cacheControl: 'max-age=31536000,immutable',
+ headers: server.config.server.headers,
+ map: null,
+ })
+ }
+ }
+ } catch (error) {
+ return next(error)
+ }
+ next()
+ })
+ },
+ transformIndexHtml: {
+ order: 'post',
+ async handler(html, { filename }) {
+ const s = new MagicString(html)
+
+ await traverseHtml(html, filename, (node) => {
+ if (!nodeIsElement(node)) {
+ return
+ }
+ // script tags
+ if (node.nodeName === 'script') {
+ const { src, vppt } = getScriptInfo(node)
+
+ if (vppt?.name && src?.value) {
+ const c = manifestCache.get()
+ let cacheItem = manifestCache.findCacheItemByPath(src.value)
+
+ if (!cacheItem) {
+ const fileName = path.basename(src.value).split('.')[0]
+ cacheItem = c[fileName]
+ }
+
+ if (cacheItem) {
+ const attrs = node.attrs
+ .reduce((acc, attr) => {
+ if (attr.name === src.name) {
+ acc += ` ${attr.name}="${cacheItem!.path}"`
+ return acc
+ }
+ acc += attr.value ? ` ${attr.name}="${attr.value}"` : ` ${attr.name}`
+ return acc
+ }, '')
+ .trim()
+
+ s.update(
+ node.sourceCodeLocation!.startOffset,
+ node.sourceCodeLocation!.endOffset,
+ ``,
+ )
+ } else {
+ s.remove(node.sourceCodeLocation!.startOffset, node.sourceCodeLocation!.endOffset)
+ }
+ }
+ }
+ })
+ return s.toString()
+ },
+ },
+ }
+
+ return plugin
+}
diff --git a/src/node/plugins/virtual.ts b/src/node/plugins/virtual.ts
new file mode 100644
index 0000000..89739d8
--- /dev/null
+++ b/src/node/plugins/virtual.ts
@@ -0,0 +1,27 @@
+import { type PluginOption } from 'vite'
+import { resolvedVirtualModuleId, virtualModuleId } from '../helper/virtual'
+import { manifestCache } from '../manifest-cache'
+
+export function pluginVirtual() {
+ const plugin: PluginOption = {
+ name: 'vite:public-typescript:virtual',
+ enforce: 'post',
+ config: () => ({
+ optimizeDeps: {
+ exclude: [virtualModuleId],
+ },
+ }),
+ async resolveId(id: string) {
+ if (id === virtualModuleId) {
+ return resolvedVirtualModuleId
+ }
+ },
+ async load(id) {
+ if (id === resolvedVirtualModuleId) {
+ return `export default ${JSON.stringify(manifestCache.getManifestJson())}`
+ }
+ },
+ }
+
+ return plugin
+}
diff --git a/src/processor/BaseCacheProcessor.ts b/src/node/processor/BaseCacheProcessor.ts
similarity index 100%
rename from src/processor/BaseCacheProcessor.ts
rename to src/node/processor/BaseCacheProcessor.ts
diff --git a/src/processor/FileCacheProcessor.ts b/src/node/processor/FileCacheProcessor.ts
similarity index 100%
rename from src/processor/FileCacheProcessor.ts
rename to src/node/processor/FileCacheProcessor.ts
diff --git a/src/processor/ManifestCacheProcessor.ts b/src/node/processor/ManifestCacheProcessor.ts
similarity index 95%
rename from src/processor/ManifestCacheProcessor.ts
rename to src/node/processor/ManifestCacheProcessor.ts
index 6e01d5d..b91c206 100644
--- a/src/processor/ManifestCacheProcessor.ts
+++ b/src/node/processor/ManifestCacheProcessor.ts
@@ -37,10 +37,7 @@ export abstract class ManifestCacheProcessor extends BaseCacheProcessor, manifestCache: ManifestCache) {
+export function initCacheProcessor(options: OptionsTypeWithDefault, manifestCache: ManifestCache) {
const { destination } = options
const processorContainer: Record = {
diff --git a/tests/fixtures/demo/vite.config.ts b/tests/fixtures/demo/vite.config.ts
index 0ca361c..55ba058 100644
--- a/tests/fixtures/demo/vite.config.ts
+++ b/tests/fixtures/demo/vite.config.ts
@@ -1,5 +1,5 @@
import { defineConfig } from 'vite'
-import { publicTypescript } from '../../../src'
+import { publicTypescript } from '../../../src/node'
// https://vitejs.dev/config/
export default defineConfig(() => ({
diff --git a/tests/utils.test.ts b/tests/utils.test.ts
index ed7361b..444bcf8 100644
--- a/tests/utils.test.ts
+++ b/tests/utils.test.ts
@@ -1,6 +1,6 @@
import path from 'node:path'
import { describe, expect, test } from 'vitest'
-import { globalConfig } from '../src/global-config'
+import { globalConfig } from '../src/node/global-config'
import {
eq,
extractHashFromFileName,
@@ -9,9 +9,9 @@ import {
linebreak,
setEol,
validateOptions,
-} from '../src/helper/utils'
+} from '../src/node/helper/utils'
-describe.skip('unit test', () => {
+describe('unit test', () => {
test('should return true when filePath is a public typescript file', () => {
const filePath = 'src/foo/bar.ts'
const root = 'src'
@@ -31,14 +31,14 @@ describe.skip('unit test', () => {
const tsFile = 'hello.ts'
const otherFile = 'hello.js'
const res1 = isPublicTypescript({
- filePath: path.resolve(root, `publicTypescript/${tsFile}`),
- inputDir: 'publicTypescript',
+ filePath: path.resolve(root, `public-typescript/${tsFile}`),
+ inputDir: 'public-typescript',
root,
})
const res2 = isPublicTypescript({
- filePath: path.resolve(root, `publicTypescript/${otherFile}`),
- inputDir: 'publicTypescript',
+ filePath: path.resolve(root, `public-typescript/${otherFile}`),
+ inputDir: 'public-typescript',
root,
})
@@ -91,6 +91,7 @@ describe.skip('unit test', () => {
outputDir: '/',
sideEffects: false,
ssrBuild: false,
+ cacheDir: 'node_modules/.vite-plugin-public-typescript',
} as const
expect(() => validateOptions(opts)).not.toThrowError()
diff --git a/tests/vitestSetup.ts b/tests/vitestSetup.ts
index b6a0154..dc6fb24 100644
--- a/tests/vitestSetup.ts
+++ b/tests/vitestSetup.ts
@@ -1,10 +1,10 @@
import path from 'node:path'
import { type InlineConfig, type ResolvedConfig, resolveConfig } from 'vite'
import { beforeEach } from 'vitest'
-import { DEFAULT_OPTIONS } from '../src'
-import { type GlobalConfig } from '../src/global-config/GlobalConfigBuilder'
-import { setupGlobalConfig } from '../src/helper/utils'
-import { type CacheValueEx } from '../src/manifest-cache'
+import { DEFAULT_OPTIONS } from '../src/node'
+import { type GlobalConfig } from '../src/node/global-config/GlobalConfigBuilder'
+import { setupGlobalConfig } from '../src/node/helper/utils'
+import { type CacheValueEx } from '../src/node/manifest-cache'
const config: InlineConfig = {
configFile: path.resolve(__dirname, './fixtures/demo/vite.config.ts'),
diff --git a/tsup.config.ts b/tsup.config.ts
index f25b73d..7187559 100644
--- a/tsup.config.ts
+++ b/tsup.config.ts
@@ -3,28 +3,43 @@ import { type Options, defineConfig } from 'tsup'
const commonConfig = (option: Options): Options => {
return {
clean: false,
- minify: false,
- platform: 'node',
sourcemap: !!option.watch,
define: {
'import.meta.vitest': 'undefined',
},
tsconfig: option.watch ? './tsconfig.dev.json' : './tsconfig.json',
- target: 'node16',
+ dts: true,
+ minify: false,
+ external: [/^virtual:.*/],
}
}
export const tsup = defineConfig((option) => [
{
- entry: ['src/index.ts'],
- dts: true,
+ entry: {
+ 'node/index': './src/node/index.ts',
+ },
format: ['esm'],
+ target: 'node16',
+ platform: 'node',
...commonConfig(option),
},
{
- entry: ['src/index.ts'],
+ entry: {
+ 'node/index': './src/node/index.ts',
+ },
noExternal: ['on-change', 'watcher'],
format: ['cjs'],
+ target: 'node16',
+ platform: 'node',
+ ...commonConfig(option),
+ },
+ {
+ entry: {
+ 'client/index': './src/client/index.ts',
+ },
+ format: ['esm', 'cjs'],
+ platform: 'neutral',
...commonConfig(option),
},
])
diff --git a/vitest.config.e2e.ts b/vitest.config.e2e.ts
index f99e0bc..bf32d9c 100644
--- a/vitest.config.e2e.ts
+++ b/vitest.config.e2e.ts
@@ -11,7 +11,7 @@ export default defineConfig({
},
},
test: {
- include: ['./playground/**/*.spec.[tj]s'],
+ include: ['./playground/spa/**/*.spec.[tj]s'],
reporters: 'dot',
coverage: {
provider: undefined,