From 78e6ecb3fc4ee8256ff3051bf77b9185d5564d23 Mon Sep 17 00:00:00 2001 From: "Jesse.Feng" Date: Sun, 27 Apr 2025 14:54:53 +0800 Subject: [PATCH 1/2] =?UTF-8?q?docs(vitepress):=20=E4=BF=AE=E6=94=B9?= =?UTF-8?q?=E6=96=87=E6=A1=A3?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../en/plugins/vite-plugin-combine/options.md | 70 ++++++++++++++----- docs/en/plugins/vite-plugin-combine/usage.md | 2 +- .../zh/plugins/vite-plugin-combine/options.md | 49 +++++++++---- docs/zh/plugins/vite-plugin-combine/usage.md | 2 +- examples/config/vite.combine.1.mts | 14 ++-- examples/config/vite.combine.2.mts | 14 ++-- examples/config/vite.combine.3.mts | 14 ++-- examples/config/vite.combine.4.mts | 14 ++-- examples/config/vite.combine.5.mts | 14 ++-- examples/config/vite.combine.6.mts | 14 ++-- examples/vite3-demo/package.json | 1 - examples/vite4-demo/package.json | 1 - examples/vite5-demo/package.json | 1 - examples/vite6-demo/package.json | 1 - 14 files changed, 114 insertions(+), 97 deletions(-) diff --git a/docs/en/plugins/vite-plugin-combine/options.md b/docs/en/plugins/vite-plugin-combine/options.md index 481fd47..20171cc 100644 --- a/docs/en/plugins/vite-plugin-combine/options.md +++ b/docs/en/plugins/vite-plugin-combine/options.md @@ -3,56 +3,94 @@ ## TypeScript Definitions ```typescript +import type { NullValue } from 'rollup'; +import type { ConfigEnv, UserConfig } from 'vite'; +import type { PluginOptions as DtsPluginOptions } from 'vite-plugin-dts'; +import type { LogLevel } from 'vp-runtime-helper'; + +export type { LogLevel }; + export type NameExport = (name: string, filePath: string) => string; export interface Options { /** * Path to the files to be combined, supports glob patterns. + * + * 需要合并的文件路径,支持 glob 模式。 */ src: string | string[]; - /** * Path to the target file after combination. + * + * 合并后的目标文件虚拟路径。 + * * @default 'index.js' */ target: string; /** - * Whether to overwrite the existing target file. - * @default false - */ - overwrite?: boolean; - - /** - * Custom function or boolean value for controlling export name generation. + * Custom function or boolean value for controlling the generation of export names. + * + * 自定义导出名称的函数或布尔值,用于控制导出名称的生成方式。 */ nameExport?: NameExport | boolean; /** * Exported module types. + * + * 导出的模块类型 + * * @default 'named' */ - exports?: 'named' | 'default' | 'both' | 'none'; + exports?: 'named' | 'default' | 'both' | 'all' | 'none'; /** - * Plugin execution order. 'pre' runs before others, 'post' after. - * @see [Vite Plugin Ordering](https://cn.vitejs.dev/guide/api-plugin.html#plugin-ordering) + * The value of enforce can be either `"pre"` or `"post"`, see more at https://vitejs.dev/guide/api-plugin.html#plugin-ordering. + * + * 强制执行顺序,`pre` 前,`post` 后,参考 https://cn.vitejs.dev/guide/api-plugin.html#plugin-ordering。 */ enforce?: 'pre' | 'post'; /** - * Logging level configuration. + * Apply the plugin only for serve or build, or on certain conditions. + * + * 应用插件仅在 serve 或 build 时,或满足某些条件的情况下。 */ - logLevel?: LogLevel; + apply?: 'serve' | 'build' | ((this: void, config: UserConfig, env: ConfigEnv) => boolean); /** - * Current working directory. + * Current Working Directory. + * + * 当前工作目录 */ cwd?: string; /** - * Process code before writing to the file. + * Log level + * + * 输出日志等级 + */ + logLevel?: LogLevel; + + /** + * Handle code before writing to the file. + * + * 写入文件前处理代码字符串 + */ + beforeWrite?: (code: string) => string | NullValue; + + /** + * Whether to output the banner + * + * 是否输出 banner + */ + enableBanner?: boolean; + + /** + * Whether to generate d.ts file + * + * 是否生成 d.ts 文件 */ - beforeWrite?: (code: string) => string | void | undefined | null; + dts?: boolean | DtsPluginOptions; } ``` \ No newline at end of file diff --git a/docs/en/plugins/vite-plugin-combine/usage.md b/docs/en/plugins/vite-plugin-combine/usage.md index 5779cd7..4f4e4cc 100644 --- a/docs/en/plugins/vite-plugin-combine/usage.md +++ b/docs/en/plugins/vite-plugin-combine/usage.md @@ -38,7 +38,7 @@ export default defineConfig({ This will generate the following files: `dist/index.mjs` -```mjs +```js export { default as default2 } from './Button'; export { default as default3 } from './Input'; export { default as default4 } from './Select'; diff --git a/docs/zh/plugins/vite-plugin-combine/options.md b/docs/zh/plugins/vite-plugin-combine/options.md index 46ca8cf..8647122 100644 --- a/docs/zh/plugins/vite-plugin-combine/options.md +++ b/docs/zh/plugins/vite-plugin-combine/options.md @@ -3,6 +3,13 @@ ## TypeScript 定义 ```ts +import type { NullValue } from 'rollup'; +import type { ConfigEnv, UserConfig } from 'vite'; +import type { PluginOptions as DtsPluginOptions } from 'vite-plugin-dts'; +import type { LogLevel } from 'vp-runtime-helper'; + +export type { LogLevel }; + export type NameExport = (name: string, filePath: string) => string; export interface Options { @@ -15,21 +22,12 @@ export interface Options { /** * Path to the target file after combination. * - * 合并后的目标文件路径。 + * 合并后的目标文件虚拟路径。 * * @default 'index.js' */ target: string; - /** - * Whether to overwrite the existing target file。 - * - * 是否覆盖已存在的目标文件。 - * - * @default false - */ - overwrite?: boolean; - /** * Custom function or boolean value for controlling the generation of export names. * @@ -44,7 +42,7 @@ export interface Options { * * @default 'named' */ - exports?: 'named' | 'default' | 'both' | 'none'; + exports?: 'named' | 'default' | 'both' | 'all' | 'none'; /** * The value of enforce can be either `"pre"` or `"post"`, see more at https://vitejs.dev/guide/api-plugin.html#plugin-ordering. @@ -54,11 +52,11 @@ export interface Options { enforce?: 'pre' | 'post'; /** - * Log level + * Apply the plugin only for serve or build, or on certain conditions. * - * 输出日志等级 + * 应用插件仅在 serve 或 build 时,或满足某些条件的情况下。 */ - logLevel?: LogLevel; + apply?: 'serve' | 'build' | ((this: void, config: UserConfig, env: ConfigEnv) => boolean); /** * Current Working Directory. @@ -67,11 +65,32 @@ export interface Options { */ cwd?: string; + /** + * Log level + * + * 输出日志等级 + */ + logLevel?: LogLevel; + /** * Handle code before writing to the file. * * 写入文件前处理代码字符串 */ - beforeWrite?: (code: string) => string | void | undefined | null; + beforeWrite?: (code: string) => string | NullValue; + + /** + * Whether to output the banner + * + * 是否输出 banner + */ + enableBanner?: boolean; + + /** + * Whether to generate d.ts file + * + * 是否生成 d.ts 文件 + */ + dts?: boolean | DtsPluginOptions; } ``` \ No newline at end of file diff --git a/docs/zh/plugins/vite-plugin-combine/usage.md b/docs/zh/plugins/vite-plugin-combine/usage.md index b062e90..9d90274 100644 --- a/docs/zh/plugins/vite-plugin-combine/usage.md +++ b/docs/zh/plugins/vite-plugin-combine/usage.md @@ -39,7 +39,7 @@ export default defineConfig({ 输出的文件内容: `dist/index.mjs` -```mjs +```js export { default as default2 } from './Button'; export { default as default3 } from './Input'; export { default as default4 } from './Select'; diff --git a/examples/config/vite.combine.1.mts b/examples/config/vite.combine.1.mts index 79f3e67..2ac3327 100644 --- a/examples/config/vite.combine.1.mts +++ b/examples/config/vite.combine.1.mts @@ -1,6 +1,5 @@ -import { defineConfig, Plugin } from 'vite'; +import { defineConfig, PluginOption } from 'vite'; import vitePluginCombine from 'vite-plugin-combine'; -import ts from 'vite-plugin-dts'; // https://vitejs.dev/config/ export default defineConfig({ @@ -9,14 +8,9 @@ export default defineConfig({ logLevel: 'TRACE', src: ['src/util/*.ts', '!src/util/typings.ts'], target: 'src/combine.1.ts', - nameExport: true - }), - ts({ - tsconfigPath: './tsconfig.build.json', - compilerOptions: { - declarationDir: 'dist/combine/1' - } - }) as unknown as Plugin + nameExport: true, + dts: true + }) as unknown as PluginOption ], build: { outDir: 'dist/combine/1', diff --git a/examples/config/vite.combine.2.mts b/examples/config/vite.combine.2.mts index 717f33d..769086f 100644 --- a/examples/config/vite.combine.2.mts +++ b/examples/config/vite.combine.2.mts @@ -1,6 +1,5 @@ -import { defineConfig, Plugin } from 'vite'; +import { defineConfig, PluginOption } from 'vite'; import vitePluginCombine from 'vite-plugin-combine'; -import ts from 'vite-plugin-dts'; // https://vitejs.dev/config/ export default defineConfig({ @@ -9,14 +8,9 @@ export default defineConfig({ logLevel: 'TRACE', src: ['src/util/*.ts', '!src/util/typings.ts'], target: 'src/combine.2.ts', - exports: 'default' - }), - ts({ - tsconfigPath: './tsconfig.build.json', - compilerOptions: { - declarationDir: 'dist/combine/2' - } - }) as Plugin + exports: 'default', + dts: true + }) as unknown as PluginOption ], build: { outDir: 'dist/combine/2', diff --git a/examples/config/vite.combine.3.mts b/examples/config/vite.combine.3.mts index 80d7850..7ce9449 100644 --- a/examples/config/vite.combine.3.mts +++ b/examples/config/vite.combine.3.mts @@ -1,6 +1,5 @@ -import { defineConfig, Plugin } from 'vite'; +import { defineConfig, PluginOption } from 'vite'; import vitePluginCombine from 'vite-plugin-combine'; -import ts from 'vite-plugin-dts'; // https://vitejs.dev/config/ export default defineConfig({ @@ -9,14 +8,9 @@ export default defineConfig({ logLevel: 'TRACE', src: ['src/util/*.ts', '!src/util/typings.ts'], target: 'src/combine.3.ts', - exports: 'both' - }), - ts({ - tsconfigPath: './tsconfig.build.json', - compilerOptions: { - declarationDir: 'dist/combine/3' - } - }) as Plugin + exports: 'both', + dts: true + }) as unknown as PluginOption ], build: { outDir: 'dist/combine/3', diff --git a/examples/config/vite.combine.4.mts b/examples/config/vite.combine.4.mts index d486c3f..a234e30 100644 --- a/examples/config/vite.combine.4.mts +++ b/examples/config/vite.combine.4.mts @@ -1,6 +1,5 @@ -import { defineConfig, Plugin } from 'vite'; +import { defineConfig, PluginOption } from 'vite'; import vitePluginCombine from 'vite-plugin-combine'; -import ts from 'vite-plugin-dts'; // https://vitejs.dev/config/ export default defineConfig({ @@ -9,14 +8,9 @@ export default defineConfig({ logLevel: 'TRACE', src: ['src/util/*.ts', '!src/util/typings.ts'], target: 'src/combine.4.ts', - nameExport: (name) => `my${name}` - }), - ts({ - tsconfigPath: './tsconfig.build.json', - compilerOptions: { - declarationDir: 'dist/combine/4' - } - }) as Plugin + nameExport: (name) => `my${name}`, + dts: true + }) as unknown as PluginOption ], build: { outDir: 'dist/combine/4', diff --git a/examples/config/vite.combine.5.mts b/examples/config/vite.combine.5.mts index 1839b38..a2e32e3 100644 --- a/examples/config/vite.combine.5.mts +++ b/examples/config/vite.combine.5.mts @@ -1,7 +1,6 @@ -import { defineConfig, Plugin } from 'vite'; +import { defineConfig, PluginOption } from 'vite'; import vitePluginCombine from 'vite-plugin-combine'; -import ts from 'vite-plugin-dts'; // https://vitejs.dev/config/ export default defineConfig({ @@ -12,14 +11,9 @@ export default defineConfig({ target: 'src/combine.5.ts', beforeWrite(code) { return `${code}export * from './util/typings';`; - } - }), - ts({ - tsconfigPath: './tsconfig.build.json', - compilerOptions: { - declarationDir: 'dist/combine/5' - } - }) as Plugin + }, + dts: true + }) as unknown as PluginOption ], build: { outDir: 'dist/combine/5', diff --git a/examples/config/vite.combine.6.mts b/examples/config/vite.combine.6.mts index 921c6b8..7cac45b 100644 --- a/examples/config/vite.combine.6.mts +++ b/examples/config/vite.combine.6.mts @@ -1,6 +1,5 @@ -import { defineConfig, Plugin } from 'vite'; +import { defineConfig, PluginOption } from 'vite'; import vitePluginCombine from 'vite-plugin-combine'; -import ts from 'vite-plugin-dts'; // https://vitejs.dev/config/ export default defineConfig({ @@ -9,14 +8,9 @@ export default defineConfig({ logLevel: 'TRACE', src: ['src/util/*.ts', '!src/util/typings.ts'], target: 'src/combine.6.ts', - exports: 'none' - }), - ts({ - tsconfigPath: './tsconfig.build.json', - compilerOptions: { - declarationDir: 'dist/combine/6' - } - }) as Plugin + exports: 'none', + dts: true + }) as unknown as PluginOption ], build: { outDir: 'dist/combine/6', diff --git a/examples/vite3-demo/package.json b/examples/vite3-demo/package.json index b9f8dab..a56f1f7 100644 --- a/examples/vite3-demo/package.json +++ b/examples/vite3-demo/package.json @@ -51,7 +51,6 @@ "vite": "^3.1.0", "vite-plugin-combine": "workspace:^", "vite-plugin-cp": "workspace:^", - "vite-plugin-dts": "^4.5.3", "vite-plugin-external": "workspace:^", "vite-plugin-hook-use": "workspace:^", "vite-plugin-include-css": "workspace:^", diff --git a/examples/vite4-demo/package.json b/examples/vite4-demo/package.json index aed65e8..2428750 100644 --- a/examples/vite4-demo/package.json +++ b/examples/vite4-demo/package.json @@ -49,7 +49,6 @@ "vite": "^4.5.0", "vite-plugin-combine": "workspace:^", "vite-plugin-cp": "workspace:^", - "vite-plugin-dts": "^4.5.3", "vite-plugin-external": "workspace:^", "vite-plugin-hook-use": "workspace:^", "vite-plugin-include-css": "workspace:^", diff --git a/examples/vite5-demo/package.json b/examples/vite5-demo/package.json index ddb74dd..97b1ea2 100644 --- a/examples/vite5-demo/package.json +++ b/examples/vite5-demo/package.json @@ -49,7 +49,6 @@ "vite": "^5.0.0", "vite-plugin-combine": "workspace:^", "vite-plugin-cp": "workspace:^", - "vite-plugin-dts": "^4.5.3", "vite-plugin-external": "workspace:^", "vite-plugin-hook-use": "workspace:^", "vite-plugin-include-css": "workspace:^", diff --git a/examples/vite6-demo/package.json b/examples/vite6-demo/package.json index 1dab2e7..3d01035 100644 --- a/examples/vite6-demo/package.json +++ b/examples/vite6-demo/package.json @@ -51,7 +51,6 @@ "vite": "^6.0.0", "vite-plugin-combine": "workspace:^", "vite-plugin-cp": "workspace:^", - "vite-plugin-dts": "^4.5.3", "vite-plugin-external": "workspace:^", "vite-plugin-hook-use": "workspace:^", "vite-plugin-include-css": "workspace:^", From c51a98e44e9f0722ca0418f02303d40b03b529f5 Mon Sep 17 00:00:00 2001 From: "Jesse.Feng" Date: Sun, 27 Apr 2025 15:23:16 +0800 Subject: [PATCH 2/2] =?UTF-8?q?feat(vite-plugin-combine):=20=E6=B7=BB?= =?UTF-8?q?=E5=8A=A0tds?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- package.json | 2 +- .../docs/.vitepress/config/zh.mts | 2 +- packages/vite-plugin-combine/package.json | 17 +- packages/vite-plugin-combine/src/common.ts | 144 ++++++++ packages/vite-plugin-combine/src/index.ts | 311 +++--------------- .../src/{typings.ts => types.ts} | 14 +- .../vite-plugin-combine/tsconfig.build.json | 2 +- packages/vite-plugin-combine/vite.config.mts | 7 +- packages/vite-plugin-external/package.json | 2 +- packages/vite-plugin-include-css/README.md | 2 +- packages/vite-plugin-include-css/package.json | 3 +- packages/vite-plugin-include-css/src/index.ts | 3 +- .../src/index.ts | 4 +- 13 files changed, 217 insertions(+), 296 deletions(-) create mode 100644 packages/vite-plugin-combine/src/common.ts rename packages/vite-plugin-combine/src/{typings.ts => types.ts} (91%) diff --git a/package.json b/package.json index 2923d9e..eb47bdb 100644 --- a/package.json +++ b/package.json @@ -43,7 +43,7 @@ "@commitlint/cli": "^17.6.1", "@commitlint/config-conventional": "^17.6.1", "@types/node": "^20.17.28", - "eslint": "^8.48.0", + "eslint": "^8.57.1", "eslint-config-fe": "^2.1.3", "eslint-plugin-react": "^7.24.0", "eslint-plugin-react-hooks": "^4.2.0", diff --git a/packages/create-vite-lib-starter/docs/.vitepress/config/zh.mts b/packages/create-vite-lib-starter/docs/.vitepress/config/zh.mts index 72e1b28..efb37b0 100644 --- a/packages/create-vite-lib-starter/docs/.vitepress/config/zh.mts +++ b/packages/create-vite-lib-starter/docs/.vitepress/config/zh.mts @@ -9,7 +9,7 @@ export const zh = defineConfig({ // https://vitepress.dev/reference/default-theme-config nav: [ { - text: '指南', + text: '指引', link: '/zh/guide/introduction', activeMatch: '/zh/guide/' }, diff --git a/packages/vite-plugin-combine/package.json b/packages/vite-plugin-combine/package.json index 6714d50..41bd74d 100644 --- a/packages/vite-plugin-combine/package.json +++ b/packages/vite-plugin-combine/package.json @@ -1,10 +1,10 @@ { "name": "vite-plugin-combine", - "version": "6.1.3", + "version": "6.2.0", "description": "Combines multiple module files into a single target file. It supports four modes: named exports, default exports, automatic exports, and no exports, and can auto-generate corresponding import statements based on configuration.", - "types": "./dist/index.d.ts", - "module": "./dist/index.mjs", - "main": "./dist/index.js", + "types": "dist/index.d.ts", + "module": "dist/index.mjs", + "main": "dist/index.js", "exports": { ".": { "types": "./dist/index.d.ts", @@ -13,7 +13,7 @@ } }, "engines": { - "node": ">=14.18.0", + "node": ">=16.0.0", "vite": ">=3.1.0" }, "scripts": { @@ -28,6 +28,7 @@ "directory": "packages/vite-plugin-combine" }, "keywords": [ + "vite", "vite-plugin", "vite-plugin-combine" ], @@ -38,15 +39,15 @@ }, "homepage": "https://fengxinming.github.io/vite-plugins/plugins/vite-plugin-combine/quick-start", "dependencies": { - "es-toolkit": "^1.34.1", + "@types/fs-extra": "^11.0.4", + "camel-kit": "^1.0.0", "fast-replaceall": "^2.0.1", "fs-extra": "^11.1.1", "tinyglobby": "^0.2.12", + "vite-plugin-dts": "^4.5.3", "vp-runtime-helper": "workspace:^" }, "devDependencies": { - "@rollup/plugin-typescript": "^12.1.2", - "@types/fs-extra": "^11.0.4", "vite": "^6.1.0" }, "files": [ diff --git a/packages/vite-plugin-combine/src/common.ts b/packages/vite-plugin-combine/src/common.ts new file mode 100644 index 0000000..9931f80 --- /dev/null +++ b/packages/vite-plugin-combine/src/common.ts @@ -0,0 +1,144 @@ +import { EOL } from 'node:os'; +import { dirname, join, parse, relative } from 'node:path'; + +import { camelize } from 'camel-kit'; +import type { InputOption } from 'rollup'; + +import { NameExport } from './types'; + +export function handleExport(name: string, filePath: string, nameExport?: NameExport | boolean): string { + if (nameExport) { + switch (typeof nameExport) { + case 'boolean': + return camelize(name); + case 'function': + return nameExport(name, filePath); + } + } + return name; +} + +// function namedExport(files: string[], target: string, nameExport?: NameExport | boolean): string { +// return files +// .map((file) => { +// const { name, dir } = parse(file); +// const exportName = handleExport(name, file, nameExport); +// const relativeDir = relative(dirname(target), dir); +// return `export { default as ${exportName} } from '${`./${join(relativeDir, name)}`}';`; +// }) +// .join(EOL) +// .concat(EOL); +// } + +export function spliceCode( + files: string[], + target: string, + exportsType: string, + nameExport?: NameExport | boolean +): string { + const importDeclare: string[] = []; + const exportNames: string[] = []; + + const handles = { + named: { + collect(exportName: string, relativePath: string) { + importDeclare.push(`export { default as ${exportName} } from '${relativePath}';`); + }, + end(code: string) { + return code; + } + }, + default: { + collect(exportName: string, relativePath: string) { + importDeclare.push(`import ${exportName} from '${relativePath}';`); + exportNames.push(exportName); + }, + end(code: string) { + return `${code}export default { ${exportNames.join(', ')} };${EOL}`; + } + }, + both: { + collect(exportName: string, relativePath: string) { + importDeclare.push(`import ${exportName} from '${relativePath}';`); + exportNames.push(exportName); + }, + end(code: string) { + code += `export { ${exportNames.join(', ')} };${EOL}`; + return `${code}export default { ${exportNames.join(', ')} };${EOL}`; + } + }, + all: { + collect(exportName: string, relativePath: string) { + importDeclare.push(`export * from '${relativePath}';`); + }, + end(code: string) { + return code; + } + }, + none: { + collect(exportName: string, relativePath: string) { + importDeclare.push(`import '${relativePath}';`); + }, + end(code: string) { + return `${code}export {};${EOL}`; + } + } + }; + + const fns = handles[exportsType]; + if (!fns) { + return ''; + } + + const make = fns.collect; + + for (const file of files) { + const { name, dir } = parse(file); + const exportName = handleExport(name, file, nameExport); + + const relativeDir = relative(dirname(target), dir); + const relativePath = `./${join(relativeDir, name)}`; + + make(exportName, relativePath); + } + + return fns.end(importDeclare.join(EOL) + EOL); +} + +export function makeESModuleCode( + files: string[], + absTarget: string, + opts +): string { + // 导出类型 + const exportsType = opts.exports || 'named'; + const { nameExport, beforeWrite } = opts; + + let mainCode = spliceCode(files, absTarget, exportsType, nameExport); + + if (typeof beforeWrite === 'function') { + const code = beforeWrite(mainCode); + if (typeof code === 'string') { + mainCode = code; + } + } + return mainCode; +} + +export function rebuildInput(input: InputOption | undefined, files: string[]): InputOption { + const whatType = typeof input; + if (whatType === 'string') { + return [input as string].concat(files); + } + else if (Array.isArray(input)) { + return input.concat(files); + } + else if (whatType === 'object' && input !== null) { + return files.reduce((prev, cur) => { + const obj = parse(cur); + prev[obj.name] = cur; + return prev; + }, input as Record); + } + return files; +} diff --git a/packages/vite-plugin-combine/src/index.ts b/packages/vite-plugin-combine/src/index.ts index 488b2d0..c71c097 100644 --- a/packages/vite-plugin-combine/src/index.ts +++ b/packages/vite-plugin-combine/src/index.ts @@ -1,202 +1,18 @@ import { existsSync, unlink, writeFileSync } from 'node:fs'; import { EOL } from 'node:os'; -import { dirname, isAbsolute, join, parse, relative } from 'node:path'; -import { camelCase } from 'es-toolkit'; import type { InputOption } from 'rollup'; import { globSync } from 'tinyglobby'; -import type { Plugin } from 'vite'; -import { normalizePath } from 'vite'; -import { banner } from 'vp-runtime-helper'; +import type { Plugin, PluginOption } from 'vite'; +import dts from 'vite-plugin-dts'; +import { banner, toAbsolutePath } from 'vp-runtime-helper'; +import { makeESModuleCode, rebuildInput } from './common'; import { logger, PLUGIN_NAME } from './logger'; -import { NameExport, Options } from './typings'; -export * from './typings'; +import type { Options } from './types'; +export * from './types'; -function onExit(listener: (...args: any[]) => any): void { - process.on('exit', listener); - process.on('SIGHUP', listener); - process.on('SIGINT', listener); - process.on('SIGTERM', listener); - process.on('SIGBREAK', listener); - process.on('uncaughtException', listener); - process.on('unhandledRejection', listener); -} - -function offExit(listener: (...args: any[]) => any): void { - process.off('exit', listener); - process.off('SIGHUP', listener); - process.off('SIGINT', listener); - process.off('SIGTERM', listener); - process.off('SIGBREAK', listener); - process.off('uncaughtException', listener); - process.off('unhandledRejection', listener); -} - -function handleExport(name: string, filePath: string, nameExport?: NameExport | boolean): string { - if (nameExport) { - switch (typeof nameExport) { - case 'boolean': - return camelCase(name); - case 'function': - return nameExport(name, filePath); - } - } - return name; -} - -// function namedExport(files: string[], target: string, nameExport?: NameExport | boolean): string { -// return files -// .map((file) => { -// const { name, dir } = parse(file); -// const exportName = handleExport(name, file, nameExport); -// const relativeDir = relative(dirname(target), dir); -// return `export { default as ${exportName} } from '${`./${join(relativeDir, name)}`}';`; -// }) -// .join(EOL) -// .concat(EOL); -// } - -function spliceCode( - files: string[], - target: string, - exportsType: string, - nameExport?: NameExport | boolean -): string { - const importDeclare: string[] = []; - const exportNames: string[] = []; - - const handles = { - named: { - collect(exportName: string, relativePath: string) { - importDeclare.push(`export { default as ${exportName} } from '${relativePath}';`); - }, - end(code: string) { - return code; - } - }, - default: { - collect(exportName: string, relativePath: string) { - importDeclare.push(`import ${exportName} from '${relativePath}';`); - exportNames.push(exportName); - }, - end(code: string) { - return `${code}export default { ${exportNames.join(', ')} };${EOL}`; - } - }, - both: { - collect(exportName: string, relativePath: string) { - importDeclare.push(`import ${exportName} from '${relativePath}';`); - exportNames.push(exportName); - }, - end(code: string) { - code += `export { ${exportNames.join(', ')} };${EOL}`; - return `${code}export default { ${exportNames.join(', ')} };${EOL}`; - } - }, - all: { - collect(exportName: string, relativePath: string) { - importDeclare.push(`export * from '${relativePath}';`); - }, - end(code: string) { - return code; - } - }, - none: { - collect(exportName: string, relativePath: string) { - importDeclare.push(`import '${relativePath}';`); - }, - end(code: string) { - return `${code}export {};${EOL}`; - } - } - }; - - const fns = handles[exportsType]; - if (!fns) { - return ''; - } - - const make = fns.collect; - - for (const file of files) { - const { name, dir } = parse(file); - const exportName = handleExport(name, file, nameExport); - - const relativeDir = relative(dirname(target), dir); - const relativePath = `./${join(relativeDir, name)}`; - - make(exportName, relativePath); - } - - return fns.end(importDeclare.join(EOL) + EOL); -} - -function makeESModuleCode( - files: string[], - absTarget: string, - opts -): string { - // 导出类型 - const exportsType = opts.exports || 'named'; - const { nameExport, beforeWrite } = opts; - - let mainCode = spliceCode(files, absTarget, exportsType, nameExport); - - if (typeof beforeWrite === 'function') { - const code = beforeWrite(mainCode); - if (typeof code === 'string') { - mainCode = code; - } - } - logger.trace(`Result:${EOL}${mainCode}`); - return mainCode; -} - -function rebuildInput(input: InputOption | undefined, files: string[]): InputOption { - const whatType = typeof input; - if (whatType === 'string') { - return [input as string].concat(files); - } - else if (Array.isArray(input)) { - return input.concat(files); - } - else if (whatType === 'object' && input !== null) { - return files.reduce((prev, cur) => { - const obj = parse(cur); - prev[obj.name] = cur; - return prev; - }, input as Record); - } - return files; -} - -function normalizeTarget(cwd: string, target: string) { - let absTarget = target; - - if (!isAbsolute(absTarget)) { - absTarget = join(cwd, absTarget); - } - absTarget = normalizePath(absTarget); - return absTarget; -} - -function getFiles(src: string | string[], cwd: string, prefix: string): string[] { - return globSync(src, { cwd, absolute: true }).filter((f) => { - if (f.includes(prefix)) { - unlink(f, (e) => { - if (e) { - return; - } - logger.trace(`'${f}' has been deleted.`); - }); - return false; - } - return true; - }); -} - -export default function pluginCombine(opts: Options) { +export default function pluginCombine(opts: Options): PluginOption { if (!opts) { opts = {} as Options; } @@ -212,9 +28,7 @@ export default function pluginCombine(opts: Options) { // 当前工作目录 const cwd = opts.cwd || process.cwd(); - - const prefix = `${PLUGIN_NAME}-temp-`; - const files = getFiles(src, cwd, prefix); + const files = globSync(src, { cwd, absolute: true }); if (!files.length) { logger.warn(`No files found in '${src}'.`); @@ -227,49 +41,29 @@ export default function pluginCombine(opts: Options) { const target = opts.target || 'index.js'; // target 绝对地址 - const absTarget = normalizeTarget(cwd, target); + const absTarget = toAbsolutePath(target, cwd); if (existsSync(absTarget)) { throw new Error(`File '${absTarget}' already exists.`); } - // 生成临时文件 - writeFileSync(absTarget, makeESModuleCode(files, absTarget, opts)); - logger.trace(`Target file '${absTarget}' has been created.`); - - const clearTemp = (err?: any) => { - offExit(clearTemp); - - unlink(absTarget, (e) => { - if (e) { - return; - } - logger.trace(`'${absTarget}' has been deleted.`); - }); - - if (err !== void 0) { - logger.debug('Exit event received:', err); - } - }; - onExit(clearTemp); - - // const version = getRuntimeVersion(); - - let isWatching; - let outDir; - - return { + const viteCombine = { name: PLUGIN_NAME, enforce: ('enforce' in opts) ? opts.enforce : 'post', apply: ('apply' in opts) ? opts.apply : 'build', async config(config) { + const combinedCode = makeESModuleCode(files, absTarget, opts); + logger.debug(`Result:${EOL}${combinedCode}`); + + writeFileSync(absTarget, combinedCode, 'utf-8'); + const inputs = files.concat(absTarget); const { build } = config; + let entry: InputOption | undefined; if (build) { - const { lib, rollupOptions } = build; - let entry: InputOption; + const { lib } = build; // 库模式 if (lib && typeof lib === 'object') { @@ -278,62 +72,45 @@ export default function pluginCombine(opts: Options) { entry = rebuildInput(entry, inputs); logger.debug('New `lib.entry`:', entry); - - return { - build: { - lib: { - entry - } - } - }; - } - - // 可能配置了 input - if (rollupOptions && typeof rollupOptions === 'object') { - entry = rollupOptions.input as InputOption; - logger.debug('Original `rollupOptions.input`:', entry); - - rebuildInput(entry, inputs); - logger.debug('New `rollupOptions.input`:', entry); - - return { - build: { - rollupOptions: { - input: entry - } - } - }; } } - logger.debug('Entry:', inputs); + entry = entry || inputs; + logger.debug('Entry:', entry); + return { build: { lib: { - entry: inputs + entry } } }; }, - configResolved({ root, build, command }) { - outDir = join(root, build.outDir); - logger.debug('OutDir:', outDir); - - const isBuild = command === 'build'; - isWatching = isBuild && !!build.watch; - }, - - buildStart() { - this.meta.watchMode = isWatching; - }, - buildEnd() { - this.meta.watchMode = isWatching; - }, - - closeBundle() { - setTimeout(clearTemp, opts.clearInDelay || 1000); + unlink(absTarget, (err) => { + if (err) { + return; + } + logger.debug(`'${absTarget}' has been removed.`); + }); } } as Plugin; + + let dtsOpts = opts.dts; + if (!dtsOpts) { + return viteCombine; + } + if (dtsOpts === true) { + dtsOpts = {}; + } + + return [ + viteCombine, + dts( + Object.assign({}, dtsOpts, { + include: files.concat(absTarget) + }) + ) + ]; } diff --git a/packages/vite-plugin-combine/src/typings.ts b/packages/vite-plugin-combine/src/types.ts similarity index 91% rename from packages/vite-plugin-combine/src/typings.ts rename to packages/vite-plugin-combine/src/types.ts index 22ad13b..98f7a90 100644 --- a/packages/vite-plugin-combine/src/typings.ts +++ b/packages/vite-plugin-combine/src/types.ts @@ -1,5 +1,6 @@ import type { NullValue } from 'rollup'; import type { ConfigEnv, UserConfig } from 'vite'; +import type { PluginOptions as DtsPluginOptions } from 'vite-plugin-dts'; import type { LogLevel } from 'vp-runtime-helper'; export type { LogLevel }; @@ -74,17 +75,16 @@ export interface Options { beforeWrite?: (code: string) => string | NullValue; /** - * Clear the cache file after the specified time. - * - * 在指定时间后清除缓存文件 + * Whether to output the banner * + * 是否输出 banner */ - clearInDelay?: number; + enableBanner?: boolean; /** - * Whether to output the banner + * Whether to generate d.ts file * - * 是否输出 banner + * 是否生成 d.ts 文件 */ - enableBanner?: boolean; + dts?: boolean | DtsPluginOptions; } diff --git a/packages/vite-plugin-combine/tsconfig.build.json b/packages/vite-plugin-combine/tsconfig.build.json index cb2c71a..e219cb2 100644 --- a/packages/vite-plugin-combine/tsconfig.build.json +++ b/packages/vite-plugin-combine/tsconfig.build.json @@ -6,7 +6,7 @@ "declarationDir": "dist" }, "include": [ - "src", + "src/**/*.ts", "./node_modules/vite/client.d.ts" ], "exclude": [ diff --git a/packages/vite-plugin-combine/vite.config.mts b/packages/vite-plugin-combine/vite.config.mts index 9ef9c80..a3b7d59 100644 --- a/packages/vite-plugin-combine/vite.config.mts +++ b/packages/vite-plugin-combine/vite.config.mts @@ -1,5 +1,5 @@ -import ts from '@rollup/plugin-typescript'; import { defineConfig } from 'vite'; +import dts from 'vite-plugin-dts'; import pluginExternal from 'vite-plugin-external'; import pkg from './package.json'; @@ -7,8 +7,9 @@ import pkg from './package.json'; // https://vitejs.dev/config/ export default defineConfig({ plugins: [ - ts({ - tsconfig: './tsconfig.build.json' + dts({ + entryRoot: 'src', + include: 'src/*.ts' }), pluginExternal({ nodeBuiltins: true, diff --git a/packages/vite-plugin-external/package.json b/packages/vite-plugin-external/package.json index 737bcef..d07a2ec 100644 --- a/packages/vite-plugin-external/package.json +++ b/packages/vite-plugin-external/package.json @@ -39,13 +39,13 @@ }, "homepage": "https://fengxinming.github.io/vite-plugins/plugins/vite-plugin-external/quick-start", "dependencies": { + "@types/fs-extra": "^11.0.4", "fs-extra": "^11.1.1", "is-what-type": "^1.1.1", "vp-runtime-helper": "workspace:^" }, "devDependencies": { "@rollup/plugin-typescript": "^12.1.2", - "@types/fs-extra": "^11.0.4", "vite": "^6.1.0" }, "files": [ diff --git a/packages/vite-plugin-include-css/README.md b/packages/vite-plugin-include-css/README.md index 5aea3ab..824e674 100644 --- a/packages/vite-plugin-include-css/README.md +++ b/packages/vite-plugin-include-css/README.md @@ -2,7 +2,7 @@ [![npm package](https://nodei.co/npm/vite-plugin-include-css.png?downloads=true&downloadRank=true&stars=true)](https://www.npmjs.com/package/vite-plugin-include-css) -> When `cssCodeSplit: false` is enabled, all CSS will be bundled into a single JavaScript file. +> Bundles all CSS into a single JavaScript file when `cssCodeSplit: false` is enabled. [![NPM version](https://img.shields.io/npm/v/vite-plugin-include-css.svg?style=flat)](https://npmjs.org/package/vite-plugin-include-css) [![NPM Downloads](https://img.shields.io/npm/dm/vite-plugin-include-css.svg?style=flat)](https://npmjs.org/package/vite-plugin-include-css) diff --git a/packages/vite-plugin-include-css/package.json b/packages/vite-plugin-include-css/package.json index b031757..1047ccc 100644 --- a/packages/vite-plugin-include-css/package.json +++ b/packages/vite-plugin-include-css/package.json @@ -1,7 +1,7 @@ { "name": "vite-plugin-include-css", "version": "6.0.0", - "description": "When `cssCodeSplit: false` is enabled, all CSS will be bundled into a single JavaScript file.", + "description": "Bundles all CSS into a single JavaScript file when `cssCodeSplit: false` is enabled.", "main": "./dist/index.js", "module": "./dist/index.mjs", "types": "./dist/index.d.ts", @@ -45,7 +45,6 @@ "dist" ], "dependencies": { - "fast-replaceall": "^2.0.1", "magic-string": "^0.30.17", "terser": "^5.39.0", "vp-runtime-helper": "workspace:^" diff --git a/packages/vite-plugin-include-css/src/index.ts b/packages/vite-plugin-include-css/src/index.ts index 34b00ca..43c81bc 100644 --- a/packages/vite-plugin-include-css/src/index.ts +++ b/packages/vite-plugin-include-css/src/index.ts @@ -1,6 +1,5 @@ import { EOL } from 'node:os'; -import replaceAll from 'fast-replaceall'; import MagicString from 'magic-string'; import { minify } from 'terser'; import type { Plugin, ResolvedConfig } from 'vite'; @@ -33,7 +32,7 @@ document.head.appendChild(__vite_style__);${EOL}`; })).code || ''; if (jsCode.includes('"use strict";')) { - return replaceAll(jsCode, '"use strict";', `"use strict";${styleCode}`); + return jsCode.replace('"use strict";', `"use strict";${styleCode}`); } return closure(tryCatch(styleCode) + jsCode); diff --git a/packages/vite-plugin-separate-importer/src/index.ts b/packages/vite-plugin-separate-importer/src/index.ts index a514914..b1d228e 100644 --- a/packages/vite-plugin-separate-importer/src/index.ts +++ b/packages/vite-plugin-separate-importer/src/index.ts @@ -163,7 +163,7 @@ function processLibs( function pluginSeparateImporter( opts: Options = {} ): Plugin | undefined { - const { enforce, libs = [], logLevel, enableBanner } = opts; + const { enforce, libs = [], logLevel } = opts; if (!Array.isArray(libs) || libs.length === 0) { logger.warn('No libs specified.'); @@ -174,7 +174,7 @@ function pluginSeparateImporter( logger.level = logLevel; } - if (enableBanner) { + if (opts.enableBanner) { banner(PLUGIN_NAME); }