Skip to content

Commit d8557c8

Browse files
committed
fix: build metadata in renderChunk stage
1 parent 4ae621f commit d8557c8

File tree

2 files changed

+95
-79
lines changed

2 files changed

+95
-79
lines changed

src/index.js

Lines changed: 12 additions & 79 deletions
Original file line numberDiff line numberDiff line change
@@ -1,81 +1,25 @@
11
import { promises as fs } from 'fs';
2-
import { attachScopes } from '@rollup/pluginutils';
3-
import { walk } from 'estree-walker';
4-
import isReference from 'is-reference';
5-
6-
const gmAPIs = [
7-
'GM_info',
8-
'GM_getValue',
9-
'GM_setValue',
10-
'GM_deleteValue',
11-
'GM_listValues',
12-
'GM_addValueChangeListener',
13-
'GM_removeValueChangeListener',
14-
'GM_getResourceText',
15-
'GM_getResourceURL',
16-
'GM_addStyle',
17-
'GM_openInTab',
18-
'GM_registerMenuCommand',
19-
'GM_unregisterMenuCommand',
20-
'GM_notification',
21-
'GM_setClipboard',
22-
'GM_xmlhttpRequest',
23-
'GM_download',
24-
];
25-
const META_START = '// ==UserScript==';
26-
const META_END = '// ==/UserScript==';
2+
import { collectGmApi, getMetadata } from './util';
273

284
export default (metafile, transform) => {
295
const grantMap = new Map();
306
return {
31-
name: 'banner',
7+
name: 'userscript-metadata',
328
buildStart() {
339
this.addWatchFile(metafile);
3410
},
3511
transform(code, id) {
3612
const ast = this.parse(code);
37-
let scope = attachScopes(ast, 'scope');
38-
const grantSetPerFile = new Set();
39-
walk(ast, {
40-
enter(node, parent) {
41-
if (node.scope) scope = node.scope;
42-
if (node.type === 'Identifier' && isReference(node, parent) && !scope.contains(node.name)) {
43-
if (gmAPIs.includes(node.name)) {
44-
grantSetPerFile.add(node.name);
45-
}
46-
}
47-
},
48-
leave(node) {
49-
if (node.scope) scope = scope.parent;
50-
},
51-
});
13+
const grantSetPerFile = collectGmApi(ast);
5214
grantMap.set(id, grantSetPerFile);
5315
},
54-
async banner() {
55-
let meta = await fs.readFile(metafile, 'utf8');
56-
const lines = meta.split('\n').map(line => line.trim());
57-
const start = lines.indexOf(META_START);
58-
const end = lines.indexOf(META_END);
59-
if (start < 0 || end < 0) {
60-
console.warn('Invalid metadata block. For more details see https://violentmonkey.github.io/api/metadata-block/');
61-
return;
62-
}
16+
/**
17+
* Use `renderChunk` instead of `banner` to preserve the metadata after minimization.
18+
* Note that this plugin must be put after `@rollup/plugin-terser`.
19+
*/
20+
async renderChunk(code) {
21+
const meta = await fs.readFile(metafile, 'utf8');
6322
const grantSet = new Set();
64-
const items = lines.slice(start + 1, end)
65-
.map(line => {
66-
if (!line.startsWith('// ')) return;
67-
line = line.slice(3).trim();
68-
const i = line.indexOf(' ');
69-
if (i < 0) return;
70-
const key = line.slice(0, i);
71-
const value = line.slice(i + 1).trim();
72-
if (key === '@grant') {
73-
grantSet.add(value);
74-
return;
75-
}
76-
return [key, value];
77-
})
78-
.filter(Boolean);
7923
for (const id of this.getModuleIds()) {
8024
const grantSetPerFile = grantMap.get(id);
8125
if (grantSetPerFile) {
@@ -84,20 +28,9 @@ export default (metafile, transform) => {
8428
}
8529
}
8630
}
87-
const grantList = Array.from(grantSet);
88-
grantList.sort();
89-
for (const item of grantList) {
90-
items.push(['@grant', item]);
91-
}
92-
const maxKeyWidth = Math.max(...items.map(([key]) => key.length));
93-
meta = [
94-
META_START,
95-
...items.map(([key, value]) => `// ${key.padEnd(maxKeyWidth)} ${value}`),
96-
META_END,
97-
'',
98-
].join('\n');
99-
if (transform) meta = transform(meta);
100-
return meta;
31+
let metadata = getMetadata(meta, grantSet);
32+
if (transform) metadata = transform(metadata);
33+
return `${metadata}\n\n${code}`;
10134
},
10235
};
10336
};

src/util.js

Lines changed: 83 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,83 @@
1+
import { attachScopes } from '@rollup/pluginutils';
2+
import { walk } from 'estree-walker';
3+
import isReference from 'is-reference';
4+
5+
const gmAPIs = [
6+
'GM_info',
7+
'GM_getValue',
8+
'GM_setValue',
9+
'GM_deleteValue',
10+
'GM_listValues',
11+
'GM_addValueChangeListener',
12+
'GM_removeValueChangeListener',
13+
'GM_getResourceText',
14+
'GM_getResourceURL',
15+
'GM_addStyle',
16+
'GM_openInTab',
17+
'GM_registerMenuCommand',
18+
'GM_unregisterMenuCommand',
19+
'GM_notification',
20+
'GM_setClipboard',
21+
'GM_xmlhttpRequest',
22+
'GM_download',
23+
];
24+
const META_START = '// ==UserScript==';
25+
const META_END = '// ==/UserScript==';
26+
27+
export function collectGmApi(ast) {
28+
let scope = attachScopes(ast, 'scope');
29+
const grantSetPerFile = new Set();
30+
walk(ast, {
31+
enter(node, parent) {
32+
if (node.scope) scope = node.scope;
33+
if (node.type === 'Identifier' && isReference(node, parent) && !scope.contains(node.name)) {
34+
if (gmAPIs.includes(node.name)) {
35+
grantSetPerFile.add(node.name);
36+
}
37+
}
38+
},
39+
leave(node) {
40+
if (node.scope) scope = scope.parent;
41+
},
42+
});
43+
return grantSetPerFile;
44+
}
45+
46+
export function getMetadata(metaFileContent, additionalGrantList) {
47+
const lines = metaFileContent.split('\n').map(line => line.trim());
48+
const start = lines.indexOf(META_START);
49+
const end = lines.indexOf(META_END);
50+
if (start < 0 || end < 0) {
51+
throw new Error('Invalid metadata block. For more details see https://violentmonkey.github.io/api/metadata-block/');
52+
}
53+
const grantSet = new Set();
54+
const entries = lines.slice(start + 1, end)
55+
.map(line => {
56+
if (!line.startsWith('// ')) return;
57+
line = line.slice(3).trim();
58+
const i = line.indexOf(' ');
59+
if (i < 0) return;
60+
const key = line.slice(0, i);
61+
const value = line.slice(i + 1).trim();
62+
if (key === '@grant') {
63+
grantSet.add(value);
64+
return;
65+
}
66+
return [key, value];
67+
})
68+
.filter(Boolean);
69+
for (const item of additionalGrantList) {
70+
grantSet.add(item);
71+
}
72+
const grantList = Array.from(grantSet);
73+
grantList.sort();
74+
for (const item of grantList) {
75+
entries.push(['@grant', item]);
76+
}
77+
const maxKeyWidth = Math.max(...entries.map(([key]) => key.length));
78+
return [
79+
META_START,
80+
...entries.map(([key, value]) => `// ${key.padEnd(maxKeyWidth)} ${value}`),
81+
META_END,
82+
].join('\n');
83+
}

0 commit comments

Comments
 (0)