diff --git a/README.md b/README.md index be7da74..e60ed53 100644 --- a/README.md +++ b/README.md @@ -98,6 +98,7 @@ Then you can use `vite-plugin-version-mark` ! 🎉 | ifGlobal | set a variable named *\`\_\_${APPNAME}\_VERSION\_\_\`* in the window
[For TypeScript users, make sure to add the type declarations in the env.d.ts or vite-env.d.ts file to get type checks and Intellisense.](https://vitejs.dev/config/shared-options.html#define) | `boolean` | true | `0.0.4+` | | ifMeta | add \ in the \ | `boolean` | true | `0.0.1+` | | ifExport | export the version field in the entry file. This may be used when you use vite to build a `library mode`.
Through `import { {APPNAME}_VERSION } from ` | `boolean` | false | `0.0.11+` | +| outputFile | The build generates a static file based on the version, as described in the `outputFile` configuration below. | `boolean`/`function` | false | `0.1.1+` | > The **version field** takes precedence: `command` > `ifShortSHA` > `ifGitSHA` > `version` @@ -142,6 +143,24 @@ git branch -r --contains View [CHANGELOG](./CHANGELOG.md) +### outputFile Configuration Option + +If you want to enable it, you can set it to true, and it will create a file with the path “.well-known/version” and the content of the current version number in the relative build directory (dist for vite and .output/public for nuxt3). + +Alternatively, it can be set to a function that takes the version number as a parameter and returns an object. This allows you to define the content information generated, for example: + +```ts +// vite.config.ts +vitePluginVersionMark({ + // ...other options + outputFile: (version) => ({ + path: 'custom/version.json', + content: `{"version":"${version}"}`, + }) +}), +``` + +With this configuration, a file named "custom/version.json" will be generated, and its content will be {"version":"${current version number}"}. ## Star History diff --git a/README_ZH.md b/README_ZH.md index 1823a69..c793244 100644 --- a/README_ZH.md +++ b/README_ZH.md @@ -58,6 +58,7 @@ export default defineConfig({ ifMeta: true, ifLog: true, ifGlobal: true, + // outputFile: true, }) ], }) @@ -77,6 +78,7 @@ export default defineNuxtConfig({ ifMeta: true, ifLog: true, ifGlobal: true, + // outputFile: true, }] ], }) @@ -99,7 +101,7 @@ export default defineNuxtConfig({ | ifGlobal | 在window上定义变量 *\`\_\_${APPNAME}\_VERSION\_\_\`*
[对于TypeScript使用者, 请确保您在 env.d.ts 或者 vite-env.d.ts 文件中定义该变量,以便通过类型检查。](https://vitejs.dev/config/shared-options.html#define) | `boolean` | true | `0.0.4+` | | ifMeta | 在 `` 中添加 `` | `boolean` | true | `0.0.1+` | | ifExport | 在入口文件导出版本字段。这在您使用vite构建 `library mode`时或许会用到。
通过 `import { {APPNAME}_VERSION} from ` | `boolean` | false | `0.0.11+` | - +| outputFile | 构建时根据版本生成一个静态文件,具体配置详见下方的 `outputFile` 配置项说明 | `boolean`/`function` | false | `0.1.1+` | > **版本字段**的取值优先级为: `command` > `ifShortSHA` > `ifGitSHA` > `version` @@ -143,6 +145,24 @@ git branch -r --contains 查看 [CHANGELOG](./CHANGELOG.md) +### outputFile配置项说明 + +如需启用可设置为true,会在相对构建目录(vite默认为dist,nuxt3默认为.output/public)下创建路径为“.well-known/version”的文件,内容为当前版本号。 + +也可以设置为一个函数,该函数接收版本号作为参数,并返回一个对象,以便自行定义生成的内容信息,例如: + +```ts +// vite.config.ts +vitePluginVersionMark({ + // ...other options + outputFile: (version) => ({ + path: 'custom/version.json', + content: `{"version":"${version}"}`, + }) +}), +``` + +如此配置便可以生成一个名为“custom/version.json”的文件,内容为 `{"version":"${当前版本号}"}`。 ## Star History diff --git a/__tests__/fixtures/vite/index.html b/__tests__/fixtures/vite/index.html index bcd9e06..59bfc5b 100644 --- a/__tests__/fixtures/vite/index.html +++ b/__tests__/fixtures/vite/index.html @@ -7,7 +7,6 @@ Vite + TS -
- +
diff --git a/__tests__/vite.test.ts b/__tests__/vite.test.ts index 6305110..3b5dceb 100644 --- a/__tests__/vite.test.ts +++ b/__tests__/vite.test.ts @@ -1,7 +1,8 @@ import {resolve} from 'node:path' +import {existsSync,readFileSync} from 'node:fs' import {describe, test, expect} from 'vitest' -import {build} from 'vite' -import type {RollupOutput, OutputAsset, OutputChunk} from 'rollup' +import {build, type InlineConfig} from 'vite' +import type {RollupOutput} from 'rollup' import {vitePluginVersionMark, type VitePluginVersionMarkInput} from '../src' const entryPathForVite = './fixtures/vite' @@ -9,7 +10,7 @@ const entryFilenameForVite = 'index.html' const entryPathForLib = './fixtures/lib' const entryFilenameForLib = 'index.js' -async function buildVite(pluginConfig: VitePluginVersionMarkInput, entryPath, entryFilename, buildOptions = {}) { +async function buildVite(pluginConfig: VitePluginVersionMarkInput, entryPath: string, entryFilename: string, buildOptions: InlineConfig = {}) { const indexHtmlPath = resolve(__dirname, entryPath) const outputs = (await build({ @@ -23,11 +24,19 @@ async function buildVite(pluginConfig: VitePluginVersionMarkInput, entryPath, en const output = Array.isArray(outputs) ? outputs[0].output : outputs.output const file = output.find( item => item.fileName === entryFilename, - ) as OutputAsset | OutputChunk - - const codeStr = file.source || file.code - - return await codeStr.toString() + ) + if (!file) { + throw new Error(`File with name ${entryFilename} not found in output.`) + } + let codeStr = '' + if ('source' in file) { + codeStr = file.source.toString() + } else if ('code' in file) { + codeStr = file.code + } else { + throw new Error('The file object lacks both `source` and `code` properties.') + } + return codeStr } // https://github.com/Applelo/unplugin-inject-preload/blob/main/test/vite.test.ts#L24 @@ -135,8 +144,80 @@ describe('VitePlugin', () => { ) expect(output).toMatchSnapshot() }) + + test('output: File(true)', async () => { + const outDir = resolve(__dirname, entryPathForVite, 'dist') + await buildVite( + { + version: '1.0.0', + outputFile: true, + ifMeta: false, + ifLog: false, + ifGlobal: false, + ifExport: false, + }, + entryPathForLib, + entryFilenameForLib, + { + build: { + lib: { + entry: './index.ts', + name: 'index', + fileName: 'index', + formats: ['es'], + }, + outDir, + }, + }, + ) + const defaultFilePath = resolve(outDir, '.well-known/version') + // File should be created + expect(existsSync(defaultFilePath)).toBe(true) + // File should contain version + expect(readFileSync(defaultFilePath, 'utf-8')).toEqual('1.0.0') + }) + + test('output: File(custom function)', async () => { + const outDir = resolve(__dirname, entryPathForVite, 'dist') + const customPath = 'custom/version.json' + await buildVite( + { + version: '1.0.0', + outputFile(version){ + return { + path: customPath, + content: `{"version":"${version}"}`, + } + }, + ifMeta: false, + ifLog: false, + ifGlobal: false, + ifExport: false, + }, + entryPathForLib, + entryFilenameForLib, + { + build: { + lib: { + entry: './index.ts', + name: 'index', + fileName: 'index', + formats: ['es'], + }, + outDir, + }, + }, + ) + const customFilePath = resolve(outDir, customPath) + // custom file should be created + expect(existsSync(customFilePath)).toBe(true) + // custom file should contain version with json format + expect(readFileSync(customFilePath, 'utf-8')).toEqual(JSON.stringify({'version': '1.0.0'})) + }) }) + + // describe('longSHA', () => { }) // describe('shortSHA', () => { }) // describe('command', () => { }) diff --git a/playground/nuxt3-webapp/nuxt.config.ts b/playground/nuxt3-webapp/nuxt.config.ts index b816ac3..4cad2fb 100644 --- a/playground/nuxt3-webapp/nuxt.config.ts +++ b/playground/nuxt3-webapp/nuxt.config.ts @@ -10,6 +10,7 @@ export default defineNuxtConfig({ ifMeta: true, ifLog: true, ifGlobal: true, + outputFile: true, }], ], }) diff --git a/playground/vite-lib/vite.config.ts b/playground/vite-lib/vite.config.ts index e8b0f33..c0c3ffd 100644 --- a/playground/vite-lib/vite.config.ts +++ b/playground/vite-lib/vite.config.ts @@ -1,4 +1,4 @@ -import {defineConfig} from 'vite' +import {defineConfig, type Plugin} from 'vite' import {vitePluginVersionMark} from '../../src/plugins/vite' export default defineConfig({ @@ -17,6 +17,7 @@ export default defineConfig({ ifLog: true, ifGlobal: true, ifExport: true, - }), + outputFile: true, + }) as Plugin, ], }) diff --git a/src/plugins/core/index.ts b/src/plugins/core/index.ts index 0730cc9..cf67436 100644 --- a/src/plugins/core/index.ts +++ b/src/plugins/core/index.ts @@ -17,8 +17,19 @@ interface VitePluginVersionMarkCommandInput extends VitePluginVersionMarkBaseInp command?: string } -export type VitePluginVersionMarkInput = VitePluginVersionMarkGitInput & VitePluginVersionMarkCommandInput +interface OutputFile { + path: string; + content: string +} +type OutputFileFunction = (version: string) => OutputFile | OutputFile[] +interface VitePluginVersionMarkFileInput extends VitePluginVersionMarkBaseInput { + outputFile?: boolean | OutputFileFunction +} + +export type VitePluginVersionMarkInput = VitePluginVersionMarkGitInput & VitePluginVersionMarkCommandInput & VitePluginVersionMarkFileInput + export type VitePluginVersionMarkConfig = { + fileList: { path: string, content: string }[] ifMeta: boolean ifLog: boolean ifGlobal: boolean @@ -54,17 +65,31 @@ export const analyticOptions: (options: VitePluginVersionMarkInput) => Promise { + switch (typeof outputFile) { + case 'function': { + const res = outputFile(printVersion) + return Array.isArray(res) ? res : [res] + } + case 'boolean': + return outputFile ? [{path: '.well-known/version', content: printVersion}] : [] + default: + return [] + } + })() return { ifMeta, ifLog, ifGlobal, ifExport, + fileList, printVersion, printName, printInfo, diff --git a/src/plugins/nuxt3.ts b/src/plugins/nuxt3.ts index 5e79865..659567a 100644 --- a/src/plugins/nuxt3.ts +++ b/src/plugins/nuxt3.ts @@ -1,4 +1,6 @@ // https://github.com/nuxt-modules/google-adsense/blob/master/src/module.ts +import {mkdir, writeFile} from 'node:fs/promises' +import {resolve,dirname} from 'node:path' import {defineNuxtModule} from '@nuxt/kit' import type {NuxtModule} from '@nuxt/schema' import {VitePluginVersionMarkInput, analyticOptions} from './core' @@ -12,6 +14,7 @@ const nuxt3Module: NuxtModule = defineNuxtModule({ // https://github.com/nuxt/bridge/blob/main/packages/bridge/src/module.ts async setup(options, nuxt) { const { + fileList, ifMeta, ifLog, ifGlobal, @@ -41,6 +44,23 @@ const nuxt3Module: NuxtModule = defineNuxtModule({ children: `__${printName}__ = "${printVersion}"`, }) } + if (fileList.length > 0) { + nuxt.hook('nitro:build:public-assets', async ({options: {output: {publicDir}}}) => { + await Promise.all(fileList.map(async ({path, content = ''}) => { + const dir = dirname(path) + const fullDir = resolve(publicDir, dir) + const outputFilePath = resolve(publicDir, path) + try { + await mkdir(fullDir, {recursive: true}) + await writeFile(outputFilePath, content) + console.info(`Generated version file in ${outputFilePath}`) + } catch (error) { + console.error(`Failed to generate file at ${outputFilePath}:`, error) + throw error + } + })) + }) + } }, }) diff --git a/src/plugins/vite.ts b/src/plugins/vite.ts index 53a1fd9..75d1279 100644 --- a/src/plugins/vite.ts +++ b/src/plugins/vite.ts @@ -1,3 +1,5 @@ +import {mkdir, writeFile} from 'node:fs/promises' +import {resolve,dirname} from 'node:path' import {analyticOptions} from './core' import type {VitePluginVersionMarkInput, VitePluginVersionMarkConfig} from './core' import type {Plugin, IndexHtmlTransformResult} from 'vite' @@ -9,11 +11,12 @@ export const vitePluginVersionMark: (options?: VitePluginVersionMarkInput) => Pl if (!versionMarkConfig) versionMarkConfig = await analyticOptions(options) return versionMarkConfig } + let outDir: string return { name: 'vite-plugin-version-mark', - async config () { + async config() { const { ifGlobal, printName, @@ -21,7 +24,7 @@ export const vitePluginVersionMark: (options?: VitePluginVersionMarkInput) => Pl } = await getVersionMarkConfig() if (ifGlobal) { - const keyName = `__${printName}__` + const keyName = `__${printName}__` return { define: { [keyName]: JSON.stringify(printVersion), @@ -37,7 +40,7 @@ export const vitePluginVersionMark: (options?: VitePluginVersionMarkInput) => Pl printName, printVersion, } = await getVersionMarkConfig() - + let modifiedCode = code if (ifExport) modifiedCode += `\nexport const ${printName} = '${printVersion}';` @@ -45,7 +48,7 @@ export const vitePluginVersionMark: (options?: VitePluginVersionMarkInput) => Pl code: modifiedCode, map: null, } - } + } }, async transformIndexHtml() { @@ -86,5 +89,23 @@ export const vitePluginVersionMark: (options?: VitePluginVersionMarkInput) => Pl return els }, - } + configResolved(config) { + outDir = config.build.outDir + }, + async closeBundle() { + const {fileList} = await getVersionMarkConfig() + if (!fileList.length) return + await Promise.all(fileList.map(async ({path, content = ''}) => { + try { + const dir = dirname(path) + await mkdir(resolve(outDir, dir), {recursive: true}) + const outputFilePath = resolve(outDir, path) + await writeFile(outputFilePath, content) + this.info(`Generate version file in ${outputFilePath}`) + } catch (error) { + this.error(`Failed to generate version file at ${path}: ${(error as Error).message}`) + } + })) + }, + } as Plugin }