Skip to content

Commit

Permalink
feat: new attributes outputFile (#13)
Browse files Browse the repository at this point in the history
* fix(test): index.html

* feat(option): outputFile: Generate a file with version tags

* feat(playground): outputFile

* test: outputFile

* docs: outputFile

* refactor: add Error Handling for File Operations

* test: ensure All Possible Cases for file Properties Are Handled

* refactor: simplify the type definition of outputFile for better readability

* chore: console.log→this.info
  • Loading branch information
peerless-hero authored Oct 19, 2024
1 parent 3e1798a commit 3887875
Show file tree
Hide file tree
Showing 9 changed files with 206 additions and 19 deletions.
19 changes: 19 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -98,6 +98,7 @@ Then you can use `vite-plugin-version-mark` ! 🎉
| ifGlobal | set a variable named *\`\_\_${APPNAME}\_VERSION\_\_\`* in the window<br/>[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 \<meta name="application-name" content="{APPNAME_VERSION}: {version}"> in the \<head> | `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`.<br/>Through `import { {APPNAME}_VERSION } from <your_library_name>` | `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`
Expand Down Expand Up @@ -142,6 +143,24 @@ git branch -r --contains <COMMIT_SHA>

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

Expand Down
22 changes: 21 additions & 1 deletion README_ZH.md
Original file line number Diff line number Diff line change
Expand Up @@ -58,6 +58,7 @@ export default defineConfig({
ifMeta: true,
ifLog: true,
ifGlobal: true,
// outputFile: true,
})
],
})
Expand All @@ -77,6 +78,7 @@ export default defineNuxtConfig({
ifMeta: true,
ifLog: true,
ifGlobal: true,
// outputFile: true,
}]
],
})
Expand All @@ -99,7 +101,7 @@ export default defineNuxtConfig({
| ifGlobal | 在window上定义变量 *\`\_\_${APPNAME}\_VERSION\_\_\`* <br/>[对于TypeScript使用者, 请确保您在 env.d.ts 或者 vite-env.d.ts 文件中定义该变量,以便通过类型检查。](https://vitejs.dev/config/shared-options.html#define) | `boolean` | true | `0.0.4+` |
| ifMeta |`<head>` 中添加 `<meta name="application-name" content="{APPNAME_VERSION}: {version}">` | `boolean` | true | `0.0.1+` |
| ifExport | 在入口文件导出版本字段。这在您使用vite构建 `library mode`时或许会用到。<br />通过 `import { {APPNAME}_VERSION} from <your_library_name>` | `boolean` | false | `0.0.11+` |

| outputFile | 构建时根据版本生成一个静态文件,具体配置详见下方的 `outputFile` 配置项说明 | `boolean`/`function` | false | `0.1.1+` |

> **版本字段**的取值优先级为: `command` > `ifShortSHA` > `ifGitSHA` > `version`
Expand Down Expand Up @@ -143,6 +145,24 @@ git branch -r --contains <COMMIT_SHA>

查看 [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

Expand Down
3 changes: 1 addition & 2 deletions __tests__/fixtures/vite/index.html
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,6 @@
<title>Vite + TS</title>
</head>
<body>
<div id="app"></div>
<script type="module" src="../src/main.ts"></script>
<div id="app"></div><script type="module" src="../src/main.ts"></script>
</body>
</html>
97 changes: 89 additions & 8 deletions __tests__/vite.test.ts
Original file line number Diff line number Diff line change
@@ -1,15 +1,16 @@
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'
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({
Expand All @@ -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
Expand Down Expand Up @@ -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', () => { })
Expand Down
1 change: 1 addition & 0 deletions playground/nuxt3-webapp/nuxt.config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ export default defineNuxtConfig({
ifMeta: true,
ifLog: true,
ifGlobal: true,
outputFile: true,
}],
],
})
5 changes: 3 additions & 2 deletions playground/vite-lib/vite.config.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import {defineConfig} from 'vite'
import {defineConfig, type Plugin} from 'vite'
import {vitePluginVersionMark} from '../../src/plugins/vite'

export default defineConfig({
Expand All @@ -17,6 +17,7 @@ export default defineConfig({
ifLog: true,
ifGlobal: true,
ifExport: true,
}),
outputFile: true,
}) as Plugin,
],
})
27 changes: 26 additions & 1 deletion src/plugins/core/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -54,17 +65,31 @@ export const analyticOptions: (options: VitePluginVersionMarkInput) => Promise<V
ifLog = true,
ifGlobal = true,
ifExport = false,
outputFile,
} = options
const finalCommand = command ?? (ifShortSHA ? 'git rev-parse --short HEAD' : ifGitSHA ? 'git rev-parse HEAD' : undefined)
const printVersion = (finalCommand ? await execCommand(finalCommand) : version) as string
const printName = `${name?.replace(/((?!\w).)/g, '_')?.toLocaleUpperCase?.()}_VERSION`
const printInfo = `${printName}: ${printVersion}`
const fileList = (() => {
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,
Expand Down
20 changes: 20 additions & 0 deletions src/plugins/nuxt3.ts
Original file line number Diff line number Diff line change
@@ -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'
Expand All @@ -12,6 +14,7 @@ const nuxt3Module: NuxtModule<ModuleOptions> = defineNuxtModule({
// https://github.com/nuxt/bridge/blob/main/packages/bridge/src/module.ts
async setup(options, nuxt) {
const {
fileList,
ifMeta,
ifLog,
ifGlobal,
Expand Down Expand Up @@ -41,6 +44,23 @@ const nuxt3Module: NuxtModule<ModuleOptions> = 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
}
}))
})
}
},
})

Expand Down
Loading

0 comments on commit 3887875

Please sign in to comment.