Skip to content

Commit 4ca820f

Browse files
committed
feat: add meta header for update and download userscript
1 parent 014e866 commit 4ca820f

File tree

2 files changed

+48
-27
lines changed

2 files changed

+48
-27
lines changed

src/banner.ts

Lines changed: 38 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -1,33 +1,50 @@
11
import type { HeaderConfig } from './types.js'
22

3-
export function banner(config: HeaderConfig): string {
4-
const header: string[] = []
5-
const configKeys = Object.keys(config)
6-
const maxKeyLength = Math.max(...configKeys.map((key) => key.length)) + 1
3+
export class Banner {
4+
private header: string[] = []
5+
private configKeys: string[]
6+
private maxKeyLength: number
77

8-
const addSpaces = (str: string): string => {
9-
return ' '.repeat(maxKeyLength - str.length)
8+
constructor(private readonly config: HeaderConfig) {
9+
this.downloadMeta()
10+
this.configKeys = Object.keys(this.config)
11+
this.maxKeyLength =
12+
Math.max(...this.configKeys.map((key) => key.length)) + 1
1013
}
1114

12-
const addMetadata = (key: string, value: string | number | boolean): void => {
15+
private downloadMeta(): void {
16+
const { name, homepage, updateURL, downloadURL } = this.config
17+
if (homepage && !updateURL && !downloadURL) {
18+
this.config.updateURL = new URL(`${name}.meta.js`, homepage).href
19+
this.config.downloadURL = new URL(`${name}.user.js`, homepage).href
20+
}
21+
}
22+
23+
private addSpaces(str: string): string {
24+
return ' '.repeat(this.maxKeyLength - str.length)
25+
}
26+
27+
private addMetadata(key: string, value: string | number | boolean): void {
1328
const isBoolean = typeof value === 'boolean'
1429
if (isBoolean && !value) return
15-
value = !isBoolean ? addSpaces(key) + value.toString() : ''
16-
header.push(`// @${key}${value}`)
30+
value = !isBoolean ? `${this.addSpaces(key)}${value}` : ''
31+
this.header.push(`// @${key}${value}`)
1732
}
1833

19-
for (const [key, value] of Object.entries(config)) {
20-
if (Array.isArray(value)) {
21-
value.forEach((value) => addMetadata(key, value))
22-
} else {
23-
if (value === undefined) continue
24-
addMetadata(key, value)
34+
generate(): string {
35+
for (const [key, value] of Object.entries(this.config)) {
36+
if (Array.isArray(value)) {
37+
value.forEach((value) => this.addMetadata(key, value))
38+
} else {
39+
if (value === undefined) continue
40+
this.addMetadata(key, value)
41+
}
2542
}
26-
}
2743

28-
return [
29-
'// ==UserScript==',
30-
...header,
31-
'// ==/UserScript=='
32-
].join('\n')
44+
return [
45+
'// ==UserScript==',
46+
...this.header,
47+
'// ==/UserScript=='
48+
].join('\n')
49+
}
3350
}

src/index.ts

Lines changed: 10 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@ import serveHandler from 'serve-handler'
1010
import { PluginOption, ResolvedConfig, createLogger } from 'vite'
1111
import { server } from 'websocket'
1212
import type { connection } from 'websocket'
13-
import { banner } from './banner.js'
13+
import { Banner } from './banner.js'
1414
import { userConfig } from './config.js'
1515
import { grants, regexpScripts, regexpStyles } from './constants.js'
1616
import css from './css.js'
@@ -104,17 +104,19 @@ export default function UserscriptPlugin(
104104
},
105105
async writeBundle(_, bundle) {
106106
const { open, port } = config.server!
107+
const userFilename = `${config.header.name}.user.js`
107108
const proxyFilename = `${config.header.name}.proxy.user.js`
109+
const metaFilename = `${config.header.name}.meta.js`
108110

109111
for (const [fileName] of Object.entries(bundle)) {
110112
if (regexpScripts.test(fileName)) {
111113
const rootDir = pluginConfig.root
112114
const outDir = pluginConfig.build.outDir
113-
const userFilename = `${config.header.name}.user.js`
114115

115116
const outPath = resolve(rootDir, outDir, fileName)
116-
const proxyFilePath = resolve(rootDir, outDir, proxyFilename)
117117
const userFilePath = resolve(rootDir, outDir, userFilename)
118+
const proxyFilePath = resolve(rootDir, outDir, proxyFilename)
119+
const metaFilePath = resolve(rootDir, outDir, metaFilename)
118120
const hotReloadPath = resolve(
119121
dirname(fileURLToPath(import.meta.url)),
120122
`hot-reload-${config.header.name}.js`
@@ -141,14 +143,14 @@ export default function UserscriptPlugin(
141143
writeFileSync(hotReloadPath, hotReloadScript)
142144
writeFileSync(
143145
proxyFilePath,
144-
banner({
146+
new Banner({
145147
...config.header,
146148
require: [
147149
...config.header.require!,
148150
'file://' + hotReloadPath,
149151
'file://' + outPath
150152
]
151-
})
153+
}).generate()
152154
)
153155
}
154156

@@ -165,8 +167,10 @@ export default function UserscriptPlugin(
165167
: [...defineGrants(source), ...(config.header.grant ?? [])]
166168
)
167169

170+
const banner = new Banner(config.header).generate()
168171
writeFileSync(outPath, source)
169-
writeFileSync(userFilePath, `${banner(config.header)}\n\n${source}`)
172+
writeFileSync(userFilePath, `${banner}\n\n${source}`)
173+
writeFileSync(metaFilePath, banner)
170174
} catch (err) {
171175
console.log(err)
172176
}

0 commit comments

Comments
 (0)