diff --git a/__test__/basic.spec.ts b/__test__/basic.spec.ts
index d23d1b9..feea24f 100644
--- a/__test__/basic.spec.ts
+++ b/__test__/basic.spec.ts
@@ -28,8 +28,16 @@ vi.mock('fs-extra', async () => {
function initCache() {
const cache = new ManifestCache()
- cache.setCache({ key: 'name', value: 'value' })
- cache.setCache({ key: 'typescript', value: 'powerful' })
+ cache.set({
+ x: {
+ path: 'x.js',
+ _code: 'console.log("x")',
+ },
+ y: {
+ path: 'y.js',
+ _code: 'console.log("y")',
+ },
+ })
return cache
}
@@ -70,17 +78,17 @@ afterEach((ctx) => {
describe('manifestCache', () => {
test('should set cache and get cache right', ({ cache }) => {
- expect(cache.getCache('name')).toBe('value')
+ expect(cache.getByKey('x')).toStrictEqual({ path: 'x.js', _code: 'console.log("x")' })
})
test('should remove cache', ({ cache }) => {
- cache.removeCache('name')
+ cache.remove('x')
- expect(cache.getCache('name')).toBeFalsy()
+ expect(cache.getByKey('x')).toBeFalsy()
})
test('should get all', ({ cache }) => {
- const v = cache.getAll()
+ const v = cache.get()
expect(Object.keys(v).length === 2).toBe(true)
})
@@ -102,7 +110,7 @@ describe('manifestCache', () => {
{
const content = fs.readFileSync(manifestPath, 'utf-8')
const c = initCache()
- expect(eq(JSON.parse(content), c.getAll())).toBe(true)
+ expect(eq(JSON.parse(content), c.extractPath(c.get()))).toBe(true)
}
})
})
diff --git a/__test__/utils.spec.ts b/__test__/utils.spec.ts
index ac511b0..4e4ae96 100644
--- a/__test__/utils.spec.ts
+++ b/__test__/utils.spec.ts
@@ -9,7 +9,7 @@ import {
setEol,
validateOptions,
} from '../src/helper/utils'
-import { getGlobalConfig, setGlobalConfig } from '../src/helper/GlobalConfigBuilder'
+import { globalConfigBuilder } from '../src/helper/GlobalConfigBuilder'
describe('vite-plugin-public-typescript', () => {
it('should return true when filePath is a public typescript file', () => {
@@ -70,14 +70,8 @@ describe('vite-plugin-public-typescript', () => {
expect(a).toBe(b)
})
- test('should getGlobalConfig throw error', () => {
- expect(() => getGlobalConfig()).toThrowError('init')
- })
-
test('should get globalConfig', () => {
- // @ts-expect-error
- setGlobalConfig({ config: { publicDir: 'public' }, inputDir: 'publicTypescript' })
- expect(() => getGlobalConfig()).not.toThrowError()
+ expect(() => globalConfigBuilder.get()).not.toThrowError()
})
test('should extract hash', () => {
@@ -96,22 +90,9 @@ describe('vite-plugin-public-typescript', () => {
ssrBuild: false,
esbuildOptions: {},
sideEffects: false,
- }
+ destination: 'file',
+ } as const
expect(() => validateOptions(opts)).not.toThrowError()
})
-
- test('should validate options throw error', () => {
- const opts = {
- inputDir: '/publicTypescript/foo',
- outputDir: 'js',
- manifestName: 'manifest',
- hash: true,
- ssrBuild: false,
- esbuildOptions: {},
- sideEffects: false,
- }
-
- expect(() => validateOptions(opts)).toThrowError('dir')
- })
})
diff --git a/package.json b/package.json
index 5f59abe..29b5935 100644
--- a/package.json
+++ b/package.json
@@ -50,6 +50,7 @@
"esbuild": "^0.17.12",
"fs-extra": "^11.1.1",
"on-change": "^4.0.2",
+ "sirv": "^2.0.3",
"tiny-glob": "^0.2.9",
"watcher": "^2.2.2"
},
diff --git a/playground/spa/index.html b/playground/spa/index.html
index 74e6de5..2b6686b 100644
--- a/playground/spa/index.html
+++ b/playground/spa/index.html
@@ -11,6 +11,7 @@
+
diff --git a/playground/spa/package.json b/playground/spa/package.json
index 83b6c25..9f68017 100644
--- a/playground/spa/package.json
+++ b/playground/spa/package.json
@@ -5,7 +5,7 @@
"private": true,
"scripts": {
"dev": "vite",
- "debug": "cross-env DEBUG='*===>*' vite",
+ "debug": "cross-env DEBUG='MemoryCacheProcessor*,index*,ManifestCache*' vite",
"build": "vite build",
"preview": "vite preview"
},
diff --git a/playground/spa/publicTypescript/haha.ts b/playground/spa/public-typescript/haha.ts
similarity index 100%
rename from playground/spa/publicTypescript/haha.ts
rename to playground/spa/public-typescript/haha.ts
diff --git a/playground/spa/publicTypescript/index.ts b/playground/spa/public-typescript/index.ts
similarity index 100%
rename from playground/spa/publicTypescript/index.ts
rename to playground/spa/public-typescript/index.ts
diff --git a/playground/spa/public-typescript/manifest.json b/playground/spa/public-typescript/manifest.json
new file mode 100644
index 0000000..ad45b2a
--- /dev/null
+++ b/playground/spa/public-typescript/manifest.json
@@ -0,0 +1,5 @@
+{
+ "haha": "/js/haha.bdaaba63.js",
+ "index": "/js/index.cccf1b56.js",
+ "test": "/js/test.40879d01.js"
+}
diff --git a/playground/spa/public-typescript/test.ts b/playground/spa/public-typescript/test.ts
new file mode 100644
index 0000000..7d71fc8
--- /dev/null
+++ b/playground/spa/public-typescript/test.ts
@@ -0,0 +1 @@
+console.log('this is a')
diff --git a/playground/spa/publicTypescript/manifest.json b/playground/spa/publicTypescript/manifest.json
deleted file mode 100644
index 0967ef4..0000000
--- a/playground/spa/publicTypescript/manifest.json
+++ /dev/null
@@ -1 +0,0 @@
-{}
diff --git a/playground/spa/publicTypescript/test.ts b/playground/spa/publicTypescript/test.ts
deleted file mode 100644
index c793f10..0000000
--- a/playground/spa/publicTypescript/test.ts
+++ /dev/null
@@ -1 +0,0 @@
-console.log('this is test')
diff --git a/playground/spa/src/App.tsx b/playground/spa/src/App.tsx
index 7af4974..17a455c 100644
--- a/playground/spa/src/App.tsx
+++ b/playground/spa/src/App.tsx
@@ -1,5 +1,5 @@
import { useState } from 'react'
-import manifest from '../publicTypescript/manifest.json'
+import manifest from '../public-typescript/manifest.json'
import reactLogo from './assets/react.svg'
import './App.css'
diff --git a/playground/spa/temp/1.js b/playground/spa/temp/1.js
new file mode 100644
index 0000000..4560806
--- /dev/null
+++ b/playground/spa/temp/1.js
@@ -0,0 +1 @@
+console.log('temp')
diff --git a/playground/spa/tsconfig.json b/playground/spa/tsconfig.json
index d1b332c..b816984 100644
--- a/playground/spa/tsconfig.json
+++ b/playground/spa/tsconfig.json
@@ -18,7 +18,7 @@
"baseUrl": ".",
"types": ["vite/client"]
},
- "include": ["src", "publicTypescript"],
+ "include": ["src", "public-typescript"],
"references": [{ "path": "./tsconfig.node.json" }],
"exclude": ["**/*.js"]
}
diff --git a/playground/spa/tsconfig.node.json b/playground/spa/tsconfig.node.json
index 80b6f7b..3146625 100644
--- a/playground/spa/tsconfig.node.json
+++ b/playground/spa/tsconfig.node.json
@@ -7,5 +7,5 @@
"resolveJsonModule": true,
"baseUrl": "."
},
- "include": ["vite.config.ts", "./publicTypescript/manifest.json"]
+ "include": ["vite.config.ts", "public-typescript/manifest.json"]
}
diff --git a/playground/spa/vite.config.ts b/playground/spa/vite.config.ts
index c9774fd..d7f8efd 100644
--- a/playground/spa/vite.config.ts
+++ b/playground/spa/vite.config.ts
@@ -1,18 +1,11 @@
-import path from 'path'
import type { HtmlTagDescriptor } from 'vite'
import { defineConfig } from 'vite'
import { publicTypescript } from 'vite-plugin-public-typescript'
import react from '@vitejs/plugin-react'
-import glob from 'tiny-glob'
-import manifest from './publicTypescript/manifest.json'
+import manifest from './public-typescript/manifest.json'
// https://vitejs.dev/config/
export default defineConfig({
- build: {
- rollupOptions: {
- external: ['virtual:my-module'],
- },
- },
define: {
haha: JSON.stringify('custom define!'),
app: JSON.stringify({ hello: 'world' }),
@@ -20,26 +13,25 @@ export default defineConfig({
plugins: [
react(),
publicTypescript({
- inputDir: 'publicTypescript',
+ inputDir: 'public-typescript',
manifestName: 'manifest',
hash: true,
- outputDir: '/',
- buildDestination: 'memory',
+ outputDir: '/js',
+ destination: 'memory',
}),
{
name: 'add-script',
async transformIndexHtml(html) {
- const scripts = await glob('./public/*.js')
- const tags: HtmlTagDescriptor[] = scripts.map((s) => {
- return {
+ const tags: HtmlTagDescriptor[] = [
+ {
tag: 'script',
attrs: {
- src: manifest[path.parse(s).name.split('.')[0]],
+ src: manifest.test,
},
- injectTo: 'head-prepend',
- }
- })
+ injectTo: 'body',
+ },
+ ]
return {
html,
diff --git a/playground/ssr/public-typescript/manifest.json b/playground/ssr/public-typescript/manifest.json
new file mode 100644
index 0000000..78897e5
--- /dev/null
+++ b/playground/ssr/public-typescript/manifest.json
@@ -0,0 +1,3 @@
+{
+ "ssr": "/ssr.2507f2a9.js"
+}
diff --git a/playground/ssr/publicTypescript/ssr.ts b/playground/ssr/public-typescript/ssr.ts
similarity index 100%
rename from playground/ssr/publicTypescript/ssr.ts
rename to playground/ssr/public-typescript/ssr.ts
diff --git a/playground/ssr/public/ssr.4c5dba79.js b/playground/ssr/public/ssr.4c5dba79.js
deleted file mode 100644
index 2feb6e8..0000000
--- a/playground/ssr/public/ssr.4c5dba79.js
+++ /dev/null
@@ -1 +0,0 @@
-(()=>{console.log("this is ssr");})();
diff --git a/playground/ssr/publicTypescript/custom-manifest.json b/playground/ssr/publicTypescript/custom-manifest.json
deleted file mode 100644
index 5cbe07d..0000000
--- a/playground/ssr/publicTypescript/custom-manifest.json
+++ /dev/null
@@ -1,3 +0,0 @@
-{
- "ssr": "/ssr.4c5dba79.js"
-}
diff --git a/playground/ssr/server.js b/playground/ssr/server.js
index ccdfb1e..291f6fc 100644
--- a/playground/ssr/server.js
+++ b/playground/ssr/server.js
@@ -1,6 +1,6 @@
import fs from 'fs/promises'
import express from 'express'
-import manifest from './publicTypescript/custom-manifest.json' assert { type: 'json' }
+import manifest from './public-typescript/manifest.json' assert { type: 'json' }
// Constants
const isProduction = process.env.NODE_ENV === 'production'
diff --git a/playground/ssr/vite.config.ts b/playground/ssr/vite.config.ts
index 6e56d04..611b408 100644
--- a/playground/ssr/vite.config.ts
+++ b/playground/ssr/vite.config.ts
@@ -3,5 +3,5 @@ import { publicTypescript } from 'vite-plugin-public-typescript'
// https://vitejs.dev/config/
export default defineConfig({
- plugins: [publicTypescript()],
+ plugins: [publicTypescript({ destination: 'memory' })],
})
diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml
index 1e5b1c7..ca28b54 100644
--- a/pnpm-lock.yaml
+++ b/pnpm-lock.yaml
@@ -20,6 +20,9 @@ importers:
on-change:
specifier: ^4.0.2
version: 4.0.2
+ sirv:
+ specifier: ^2.0.3
+ version: 2.0.3
tiny-glob:
specifier: ^0.2.9
version: 0.2.9
@@ -796,7 +799,7 @@ packages:
/@types/body-parser@1.19.2:
resolution: {integrity: sha512-ALYone6pm6QmwZoAgeyNksccT9Q4AWZQ6PvfwR37GT6r6FWUPguq6sUmNGSMV2Wr761oQoBxwGGa6DR5o1DC9g==}
dependencies:
- '@types/connect': 3.4.35
+ '@types/connect': 3.4.36
'@types/node': 18.15.5
dev: true
@@ -810,8 +813,8 @@ packages:
resolution: {integrity: sha512-KnRanxnpfpjUTqTCXslZSEdLfXExwgNxYPdiO2WGUj8+HDjFi8R3k5RVKPeSCzLjCcshCAtVO2QBbVuAV4kTnw==}
dev: true
- /@types/connect@3.4.35:
- resolution: {integrity: sha512-cdeYyv4KWoEgpBISTxWvqYsVy444DOqehiF3fM3ne10AmJ62RSyNkUnxMJXHQWRQQX2eR94m5y1IZyDwBjV9FQ==}
+ /@types/connect@3.4.36:
+ resolution: {integrity: sha512-P63Zd/JUGq+PdrM1lv0Wv5SBYeA2+CORvbrXbngriYY0jzLUWfQMQQxOhjONEz/wlHOAxOdY7CY65rgQdTjq2w==}
dependencies:
'@types/node': 18.15.5
dev: true
@@ -3841,6 +3844,15 @@ packages:
totalist: 3.0.0
dev: false
+ /sirv@2.0.3:
+ resolution: {integrity: sha512-O9jm9BsID1P+0HOi81VpXPoDxYP374pkOLzACAoyUQ/3OUVndNpsz6wMnY2z+yOxzbllCKZrM+9QrWsv4THnyA==}
+ engines: {node: '>= 10'}
+ dependencies:
+ '@polka/url': 1.0.0-next.21
+ mrmime: 1.0.1
+ totalist: 3.0.0
+ dev: false
+
/sisteransi@1.0.5:
resolution: {integrity: sha512-bLGGlR1QxBcynn2d5YmDX4MGjlZvy2MRBDRNHLJ8VI6l6+9FUiyTFNJ0IveOSP0bcXgVDPRcfGqA0pjaqUpfVg==}
dev: true
diff --git a/src/helper/AbsCacheProcessor.ts b/src/helper/AbsCacheProcessor.ts
index 951c1a5..04ce1a1 100644
--- a/src/helper/AbsCacheProcessor.ts
+++ b/src/helper/AbsCacheProcessor.ts
@@ -1,3 +1,6 @@
+import { normalizePath } from 'vite'
+import type { TGlobalConfig } from './GlobalConfigBuilder'
+
export interface IDeleteFile {
fileName: string
jsFileName?: string
@@ -13,4 +16,28 @@ export interface IAddFile {
export abstract class AbsCacheProcessor {
abstract deleteOldJs(args: IDeleteFile): Promise
abstract addNewJs(args: IAddFile): Promise
+ setCache(args: IAddFile, globalConfig: TGlobalConfig) {
+ const { contentHash, code = '', fileName } = args
+ const { cache, outputDir } = globalConfig
+
+ function getOutputPath(p: string, hash?: string) {
+ hash = hash ? `.${hash}` : ''
+ return normalizePath(`${p}/${fileName}${hash}.js`)
+ }
+
+ let outPath = getOutputPath(outputDir)
+ if (contentHash) {
+ outPath = getOutputPath(outputDir, contentHash)
+ }
+
+ cache.set({
+ [fileName]: {
+ path: outPath,
+ _code: code,
+ _hash: contentHash,
+ },
+ })
+
+ return outPath
+ }
}
diff --git a/src/helper/FileCacheProcessor.ts b/src/helper/FileCacheProcessor.ts
index 5e04b4b..524484e 100644
--- a/src/helper/FileCacheProcessor.ts
+++ b/src/helper/FileCacheProcessor.ts
@@ -5,8 +5,8 @@ import { normalizePath } from 'vite'
import createDebug from 'debug'
import { assert } from './assert'
import { globalConfigBuilder } from './GlobalConfigBuilder'
-import type { IAddFile, IDeleteFile } from './AbsCacheProcessor'
import { AbsCacheProcessor } from './AbsCacheProcessor'
+import type { IAddFile, IDeleteFile } from './AbsCacheProcessor'
import { writeFile } from './utils'
const debug = createDebug('FileCacheProcessor ===> ')
@@ -24,6 +24,7 @@ export class FileCacheProcessor extends AbsCacheProcessor {
let oldFiles: string[] = []
try {
fs.ensureDirSync(path.join(publicDir, outputDir))
+
oldFiles = await glob(normalizePath(path.join(publicDir, `${outputDir}/${fileName}.?(*.)js`)))
} catch (e) {
console.error(e)
@@ -33,6 +34,8 @@ export class FileCacheProcessor extends AbsCacheProcessor {
assert(Array.isArray(oldFiles))
+ debug('cache:', cache.get())
+
if (oldFiles.length) {
for (const f of oldFiles) {
if (path.parse(f).name === jsFileName) {
@@ -40,8 +43,9 @@ export class FileCacheProcessor extends AbsCacheProcessor {
continue
} // skip repeat js file
if (fs.existsSync(f)) {
- if (cache.getCache(fileName) || force) {
- cache.removeCache(fileName)
+ debug('deleteOldJsFile - file exists:', f, fileName)
+ if (cache.getByKey(fileName) || force) {
+ cache.remove(fileName)
debug('deleteOldJsFile - cache removed:', fileName)
fs.remove(f)
debug('deleteOldJsFile -file removed:', f)
@@ -49,35 +53,23 @@ export class FileCacheProcessor extends AbsCacheProcessor {
}
}
} else if (force) {
- cache.removeCache(fileName)
+ cache.remove(fileName)
debug('cache force removed:', fileName)
}
}
async addNewJs(args: IAddFile): Promise {
- const { contentHash, code = '', fileName } = args
+ const { code = '' } = args
const {
- cache,
- outputDir,
config: { publicDir },
} = globalConfigBuilder.get()
- let outPath = normalizePath(`${outputDir}/${fileName}.js`)
- if (contentHash) {
- outPath = normalizePath(`${outputDir}/${fileName}.${contentHash}.js`)
- }
+ const outPath = this.setCache(args, globalConfigBuilder.get())
const fp = normalizePath(path.join(publicDir, outPath))
+
await fs.ensureDir(path.dirname(fp))
writeFile(fp, code)
-
- cache.setCache({
- [fileName]: {
- path: outPath,
- },
- })
-
- debug('addJsFile cache seted:', fileName, outPath)
}
}
diff --git a/src/helper/GlobalConfigBuilder.ts b/src/helper/GlobalConfigBuilder.ts
index 44b2e5b..534fd64 100644
--- a/src/helper/GlobalConfigBuilder.ts
+++ b/src/helper/GlobalConfigBuilder.ts
@@ -9,11 +9,10 @@ type UserConfig =
cache: ManifestCache
filesGlob: string[]
config: ResolvedConfig
- } & {
cacheProcessor: AbsCacheProcessor
} & Required
-type TGlobalConfig = UserConfig & {
+export type TGlobalConfig = UserConfig & {
absOutputDir: string
absInputDir: string
}
@@ -27,8 +26,8 @@ class GlobalConfigBuilder {
init(c: UserConfig) {
const root = c.config.root || process.cwd()
- const absOutputDir = path.resolve(root, c.config.publicDir)
- const absInputDir = path.resolve(root, c.inputDir)
+ const absOutputDir = path.join(root, c.outputDir)
+ const absInputDir = path.join(root, c.inputDir)
this.globalConfig = {
...c,
absOutputDir,
diff --git a/src/helper/ManifestCache.ts b/src/helper/ManifestCache.ts
index 947864a..e78f93e 100644
--- a/src/helper/ManifestCache.ts
+++ b/src/helper/ManifestCache.ts
@@ -12,12 +12,8 @@ export interface IManifestConstructor {
onChange?: (path: string, value: ValueType, previousValue: ValueType, applyData: ApplyData) => void
}
-type TCacheValue = {
- path: string
- _code?: string
-} & Partial<{ [_key: string]: string }>
-
/**
+ * @example
* {
* fileName: {
* path: '/some-path',
@@ -26,6 +22,12 @@ type TCacheValue = {
* }
* }
*/
+type TCacheValue = {
+ path: string
+ _code?: string
+ _hash?: string
+} & Partial<{ [_key: string]: string }>
+
type TDefaultCache = {
[fileName in string]: TCacheValue
}
@@ -47,11 +49,11 @@ export class ManifestCache {
}
}
- setCache(c: T, opts?: { disableWatch?: boolean }) {
+ set(c: T, opts?: { disableWatch?: boolean }) {
const keys = Object.keys(c)
keys.forEach((k) => {
- const cacheV = this.getCache(k)
+ const cacheV = this.getByKey(k)
if (cacheV !== c[k]) {
if (opts?.disableWatch) {
;(onChange.target(this.cache) as TDefaultCache)[k] = c[k]
@@ -64,18 +66,18 @@ export class ManifestCache {
return this
}
- getCache(k: keyof T) {
+ getByKey(k: keyof T) {
return this.cache[k]
}
- removeCache(k: keyof T) {
+ remove(k: keyof T) {
if (this.cache[k]) {
delete this.cache[k]
}
return this
}
- getAll() {
+ get() {
return Object.assign({}, this.cache)
}
@@ -96,7 +98,7 @@ export class ManifestCache {
return this.manifestPath
}
- private extractPath(c: T) {
+ extractPath(c: T) {
const cache = Object.assign({}, c)
const pathOnlyCache: Record = {}
for (const key in cache) {
@@ -107,16 +109,19 @@ export class ManifestCache {
async writeManifestJSON() {
const targetPath = this.getManifestPath()
- const cacheObj = this.extractPath(this.getAll())
- const orderdCache = Object.assign({}, cacheObj)
await fs.ensureDir(path.dirname(targetPath))
+ const cacheObj = this.extractPath(this.get())
+ const orderdCache = Object.assign({}, cacheObj)
+
const parsedCache = this.readManifestFromFile()
if (!isEmptyObject(parsedCache) && eq(parsedCache, orderdCache)) {
return
}
+
+ debug('write manifest json:', JSON.stringify(orderdCache || {}, null, 2))
writeFile(targetPath, JSON.stringify(orderdCache || {}, null, 2))
}
}
diff --git a/src/helper/MemoryCacheProcessor.ts b/src/helper/MemoryCacheProcessor.ts
index 968591d..d714140 100644
--- a/src/helper/MemoryCacheProcessor.ts
+++ b/src/helper/MemoryCacheProcessor.ts
@@ -1,37 +1,16 @@
-// import createDebug from 'debug'
-import type { IAddFile, IDeleteFile } from './AbsCacheProcessor'
import { AbsCacheProcessor } from './AbsCacheProcessor'
+import type { IAddFile, IDeleteFile } from './AbsCacheProcessor'
import { globalConfigBuilder } from './GlobalConfigBuilder'
-import { VIRTUAL } from './virtual'
-
-// const debug = createDebug('MemoryCacheProcessor ===> ')
export class MemoryCacheProcessor extends AbsCacheProcessor {
async deleteOldJs(args: IDeleteFile): Promise {
const { fileName } = args
const { cache } = globalConfigBuilder.get()
- cache.removeCache(fileName)
+ cache.remove(fileName)
}
async addNewJs(args: IAddFile): Promise {
- const { contentHash, code = '', fileName } = args
- const { cache } = globalConfigBuilder.get()
-
- // const getContentHash = () => {
- // if (contentHash) {
- // return `:${contentHash}`
- // }
- // return ''
- // }
-
- cache.setCache({
- [fileName]: {
- // path: 虚拟前缀-文件名
- path: `${VIRTUAL}:[${fileName}]`,
- _code: code,
- contentHash,
- },
- })
+ this.setCache(args, globalConfigBuilder.get())
}
}
diff --git a/src/helper/build.ts b/src/helper/build.ts
index e0bc2af..d9ac892 100644
--- a/src/helper/build.ts
+++ b/src/helper/build.ts
@@ -111,8 +111,6 @@ export async function build(options: { filePath: string }) {
const code = await esbuildTypescript({ filePath, ...globalConfig })
- debug('cacheManifest:', globalConfig.cache.getAll())
-
if (globalConfig.hash) {
contentHash = getContentHash(code, globalConfig.hash)
fileNameWithHash = `${fileName}.${contentHash}`
@@ -121,4 +119,6 @@ export async function build(options: { filePath: string }) {
await globalConfig.cacheProcessor.deleteOldJs({ fileName, jsFileName: fileNameWithHash })
await globalConfig.cacheProcessor.addNewJs({ code, fileName, contentHash })
+
+ debug('cacheManifest:', globalConfig.cache.get())
}
diff --git a/src/helper/processor.ts b/src/helper/processor.ts
index b72d986..ac433c2 100644
--- a/src/helper/processor.ts
+++ b/src/helper/processor.ts
@@ -3,13 +3,13 @@ import type { AbsCacheProcessor } from './AbsCacheProcessor'
import { FileCacheProcessor } from './FileCacheProcessor'
import { MemoryCacheProcessor } from './MemoryCacheProcessor'
-export function initCacheProcessor(destination: VPPTPluginOptions['buildDestination']): AbsCacheProcessor {
+export function initCacheProcessor(destination: VPPTPluginOptions['destination']): AbsCacheProcessor {
switch (destination) {
case 'file':
return new FileCacheProcessor()
case 'memory':
return new MemoryCacheProcessor()
default:
- return new FileCacheProcessor()
+ return new MemoryCacheProcessor()
}
}
diff --git a/src/helper/utils.ts b/src/helper/utils.ts
index be81b13..d35ea0b 100644
--- a/src/helper/utils.ts
+++ b/src/helper/utils.ts
@@ -133,25 +133,33 @@ export function extractHashFromFileName(filename: string, hash: VPPTPluginOption
}
export function validateOptions(options: Required) {
- const { outputDir } = options
+ let { outputDir } = options
// ensure outputDir is Dir
- if (!/^\/([a-zA-Z0-9]+\/)*[a-zA-Z0-9]*$/.test(outputDir)) {
- throw new Error(`outputDir must be a directory, but got ${outputDir}`)
+ if (!outputDir.startsWith('/')) {
+ outputDir = `/${outputDir}`
} else {
if (outputDir.length > 1 && outputDir.endsWith('/')) {
// remove last slash
options.outputDir = outputDir.replace(/\/$/, '')
}
}
+ options.outputDir = outputDir
// ensure inputDir is Dir
const { inputDir } = options
- if (!/^([a-zA-Z0-9]+\/)*[a-zA-Z0-9]*$/.test(inputDir)) {
- throw new Error(`inputDir must be a directory, but got ${inputDir}`)
- } else {
- if (inputDir.endsWith('/')) {
- // remove last slash
- options.inputDir = inputDir.replace(/\/$/, '')
- }
+ if (inputDir.endsWith('/')) {
+ // remove last slash
+ options.inputDir = inputDir.replace(/\/$/, '')
+ }
+}
+
+// normalize dir path
+export function normalizeDirPath(dir: string) {
+ if (dir.startsWith('/')) {
+ dir = dir.slice(1)
+ }
+ if (dir.endsWith('/')) {
+ dir = dir.slice(0, -1)
}
+ return dir
}
diff --git a/src/helper/virtual.ts b/src/helper/virtual.ts
deleted file mode 100644
index 154c2c6..0000000
--- a/src/helper/virtual.ts
+++ /dev/null
@@ -1,3 +0,0 @@
-export const VIRTUAL = 'virtual:public-typescript'
-
-export const RESOLVED_VIRTUAL_PREFIX = `\0${VIRTUAL}`
diff --git a/src/index.ts b/src/index.ts
index dfbd4e9..9fcf4a0 100644
--- a/src/index.ts
+++ b/src/index.ts
@@ -1,12 +1,20 @@
import path from 'path'
import type { PluginOption, ResolvedConfig } from 'vite'
-import { normalizePath } from 'vite'
+import { normalizePath, send } from 'vite'
import glob from 'tiny-glob'
import type { BuildOptions } from 'esbuild'
import Watcher from 'watcher'
import fs from 'fs-extra'
import createDebug from 'debug'
-import { TS_EXT, _isPublicTypescript, eq, isEmptyObject, reloadPage, validateOptions } from './helper/utils'
+import {
+ TS_EXT,
+ _isPublicTypescript,
+ eq,
+ isEmptyObject,
+ normalizeDirPath,
+ reloadPage,
+ validateOptions,
+} from './helper/utils'
import { build, esbuildTypescript } from './helper/build'
import { assert } from './helper/assert'
import { globalConfigBuilder } from './helper/GlobalConfigBuilder'
@@ -16,20 +24,21 @@ import { ManifestCache } from './helper/ManifestCache'
const debug = createDebug('index ===> ')
export interface VPPTPluginOptions {
- /**
- * @description vite ssrBuild
- * @see https://vitejs.dev/config/#conditional-config
- * @default false
- */
- ssrBuild?: boolean | undefined
/**
* @description input public typescript dir
- * @default 'publicTypescript'
+ * @default 'public-typescript'
*/
inputDir?: string
/**
- * @description output public javascript dir, relative to `publicDir`
- * @note outputDir should start with '/'
+ * @description output public javascript dir after build
+ * @note relative with vite.config.ts `publicDir`
+ * @example
+ * ```ts
+ * // vite.config.ts
+ * export default defineConfig({
+ * publicDir: 'some-public-dir', // outputDir will be '/some-public-dir'
+ * })
+ * ```
* @default '/'
*/
outputDir?: string
@@ -58,27 +67,30 @@ export interface VPPTPluginOptions {
* @default false
*/
sideEffects?: boolean
-
/**
- * @description build output type
- * @default 'file'
+ * @description vite ssrBuild
+ * @see https://vitejs.dev/config/#conditional-config
+ * @default false
*/
- buildDestination?: 'file' | 'memory'
+ ssrBuild?: boolean | undefined
+ destination?: 'file' | 'memory'
}
export const DEFAULT_OPTIONS: Required = {
- inputDir: 'publicTypescript',
+ inputDir: 'public-typescript',
outputDir: '/',
manifestName: 'manifest',
hash: true,
ssrBuild: false,
esbuildOptions: {},
sideEffects: false,
- buildDestination: 'file',
+ destination: 'memory',
}
let previousOpts: VPPTPluginOptions
+const cache = new ManifestCache({ watchMode: true })
+
export function publicTypescript(options: VPPTPluginOptions = {}) {
const opts = {
...DEFAULT_OPTIONS,
@@ -87,8 +99,6 @@ export function publicTypescript(options: VPPTPluginOptions = {}) {
validateOptions(opts)
- const cache = new ManifestCache({ watchMode: true })
-
debug('options:', opts)
let config: ResolvedConfig
@@ -96,21 +106,24 @@ export function publicTypescript(options: VPPTPluginOptions = {}) {
const plugins: PluginOption = [
{
name: 'vite:public-typescript',
+
async configResolved(c) {
config = c
+ const resolvedRoot = normalizePath(config.root ? path.resolve(config.root) : process.cwd())
+
function getInputDir(suffix = '') {
- return normalizePath(path.resolve(config.root, `${opts.inputDir}${suffix}`))
+ return normalizePath(path.resolve(resolvedRoot, `${opts.inputDir}${suffix}`))
}
fs.ensureDirSync(getInputDir())
const filesGlob = await glob(getInputDir(`/*${TS_EXT}`), {
- cwd: config.root,
+ cwd: resolvedRoot,
absolute: true,
})
- const cacheProcessor = initCacheProcessor(opts.buildDestination)
+ const cacheProcessor = initCacheProcessor(opts.destination)
globalConfigBuilder.init({
cache,
@@ -209,24 +222,40 @@ export function publicTypescript(options: VPPTPluginOptions = {}) {
debug('buildStart - filesGlob:', filesGlob)
debug('buildStart - fileNames:', fileNames)
- if (!isEmptyObject(parsedCacheJson)) {
- const keys = Object.keys(parsedCacheJson)
- keys.forEach((key) => {
- if (fileNames.includes(key)) {
- cache.setCache({ [key]: parsedCacheJson[key] }, { disableWatch: true })
- } else {
- cache.setCache({ [key]: parsedCacheJson[key] })
- globalConfigBuilder.get().cacheProcessor.deleteOldJs({ fileName: key, force: true })
- }
- })
+ if (opts.destination === 'file') {
+ if (!isEmptyObject(parsedCacheJson)) {
+ const keys = Object.keys(parsedCacheJson)
+ keys.forEach((key) => {
+ if (fileNames.includes(key)) {
+ cache.set({ [key]: parsedCacheJson[key] }, { disableWatch: true })
+ } else {
+ cache.set({ [key]: parsedCacheJson[key] })
+ globalConfigBuilder.get().cacheProcessor.deleteOldJs({ fileName: key, force: true })
+ }
+ })
+ }
}
- debug('buildStart - cache:', cache.getAll())
-
filesGlob.forEach((f) => {
build({ filePath: f })
})
},
+ generateBundle() {
+ if (opts.ssrBuild || config.build.ssr) {
+ return
+ }
+
+ if (opts.destination === 'memory') {
+ const c = cache.get()
+ Object.keys(c).forEach((key) => {
+ this.emitFile({
+ type: 'asset',
+ fileName: normalizeDirPath(`${c[key].path}`),
+ source: c[key]._code,
+ })
+ })
+ }
+ },
async handleHotUpdate(ctx) {
if (_isPublicTypescript(ctx.file)) {
debug('hmr:', ctx.file)
@@ -236,6 +265,35 @@ export function publicTypescript(options: VPPTPluginOptions = {}) {
}
},
},
+ {
+ name: 'vite:public-typescript-server',
+ enforce: 'post',
+ apply: 'serve',
+ configureServer(server) {
+ function addHeader(code: string) {
+ return `// gen via vite-plugin-public-typescript (only show in serve mode);
+ ${code}`
+ }
+ server.middlewares.use((req, res, next) => {
+ try {
+ if (req?.url?.endsWith('.js')) {
+ const c = cache.get()
+ const fileName = path.basename(req.url).split('.')[0]
+ if (fileName && c[fileName]) {
+ return send(req, res, addHeader(c[fileName]._code || ''), 'js', {
+ cacheControl: 'no-cache',
+ headers: server.config.server.headers,
+ map: null,
+ })
+ }
+ }
+ } catch (e) {
+ return next(e)
+ }
+ next()
+ })
+ },
+ },
]
// Return as `any` to avoid Plugin type mismatches when there are multiple Vite versions installed