Skip to content

Commit 0fe1573

Browse files
authored
Merge pull request #13 from crashmax-dev/meta-banner
feat: add meta header for update and download userscript
2 parents 014e866 + e82ad24 commit 0fe1573

File tree

4 files changed

+78
-34
lines changed

4 files changed

+78
-34
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
}

test/__snapshots__/banner.test.ts.snap

Lines changed: 12 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
// Vitest Snapshot v1
22

3-
exports[`banner snapshot 1`] = `
3+
exports[`banner default snapshot 1`] = `
44
"// ==UserScript==
55
// @name vitest
66
// @version 1.0.0
@@ -52,3 +52,14 @@ exports[`banner snapshot 1`] = `
5252
// @run-at document-start
5353
// ==/UserScript=="
5454
`;
55+
56+
exports[`banner meta snapshot 1`] = `
57+
"// ==UserScript==
58+
// @name vitest
59+
// @version 1.0.0
60+
// @match https://example.com
61+
// @homepage https://crashmax-dev.github.io/jsx/
62+
// @updateURL https://crashmax-dev.github.io/jsx/vitest.meta.js
63+
// @downloadURL https://crashmax-dev.github.io/jsx/vitest.user.js
64+
// ==/UserScript=="
65+
`;

test/banner.test.ts

Lines changed: 18 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,9 @@
11
import { expect, test } from 'vitest'
2-
import { banner } from '../src/banner.js'
2+
import { Banner } from '../src/banner.js'
33
import { grants } from '../src/constants.js'
4-
import type { Grants, MetadataConfig } from '../src/types.js'
4+
import type { Grants, HeaderConfig } from '../src/types.js'
55

6-
const metadataConfig: MetadataConfig = {
6+
const defaultBanner: HeaderConfig = {
77
name: 'vitest',
88
version: '1.0.0',
99
author: 'John Doe',
@@ -27,7 +27,19 @@ const metadataConfig: MetadataConfig = {
2727
'run-at': 'document-start'
2828
}
2929

30-
test('banner snapshot', () => {
31-
const defaultBanner = banner(metadataConfig)
32-
expect(defaultBanner).toMatchSnapshot()
30+
test('banner default snapshot', () => {
31+
const banner = new Banner(defaultBanner).generate()
32+
expect(banner).toMatchSnapshot()
33+
})
34+
35+
const metaBanner: HeaderConfig = {
36+
name: 'vitest',
37+
version: '1.0.0',
38+
match: 'https://example.com',
39+
homepage: 'https://crashmax-dev.github.io/jsx/'
40+
}
41+
42+
test('banner meta snapshot', () => {
43+
const banner = new Banner(metaBanner).generate()
44+
expect(banner).toMatchSnapshot()
3345
})

0 commit comments

Comments
 (0)