Skip to content

Commit a64b030

Browse files
committed
feat: support automatic granting of GM.* and unsafeWindow
1 parent 9699f79 commit a64b030

File tree

2 files changed

+45
-28
lines changed

2 files changed

+45
-28
lines changed

src/index.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
import { readFile } from 'fs/promises';
22
import MagicString from 'magic-string';
33
import type { Plugin } from 'rollup';
4-
import { collectGmApi, getMetadata } from './util';
4+
import { collectGrants, getMetadata } from './util';
55

66
const suffix = '?userscript-metadata';
77

@@ -25,7 +25,7 @@ export default (transform?: (metadata: string) => string): Plugin => {
2525
},
2626
transform(code, id) {
2727
const ast = this.parse(code);
28-
const grantSetPerFile = collectGmApi(ast);
28+
const grantSetPerFile = collectGrants(ast);
2929
grantMap.set(id, grantSetPerFile);
3030
},
3131
/**

src/util.ts

Lines changed: 43 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -2,46 +2,63 @@ import { AttachedScope, attachScopes } from '@rollup/pluginutils';
22
import { Node, walk } from 'estree-walker';
33
import isReference from 'is-reference';
44
import type { AstNode } from 'rollup';
5+
import type { MemberExpression } from 'estree';
56

6-
const gmAPIs = [
7-
'GM_info',
8-
'GM_getValue',
9-
'GM_getValues',
10-
'GM_setValue',
11-
'GM_setValues',
12-
'GM_deleteValue',
13-
'GM_deleteValues',
14-
'GM_listValues',
15-
'GM_addValueChangeListener',
16-
'GM_removeValueChangeListener',
17-
'GM_getResourceText',
18-
'GM_getResourceURL',
19-
'GM_addElement',
20-
'GM_addStyle',
21-
'GM_openInTab',
22-
'GM_registerMenuCommand',
23-
'GM_unregisterMenuCommand',
24-
'GM_notification',
25-
'GM_setClipboard',
26-
'GM_xmlhttpRequest',
27-
'GM_download',
28-
];
297
const META_START = '// ==UserScript==';
308
const META_END = '// ==/UserScript==';
9+
const GRANTS_REGEXP = /^unsafeWindow|GM[._][a-zA-Z0-9_]+/;
3110

32-
export function collectGmApi(ast: AstNode) {
11+
export function collectGrants(ast: AstNode) {
3312
let scope = attachScopes(ast, 'scope');
3413
const grantSetPerFile = new Set();
3514
walk(ast as Node, {
3615
enter(node: Node & { scope: AttachedScope }, parent) {
3716
if (node.scope) scope = node.scope;
17+
18+
if (
19+
node.type === 'MemberExpression' &&
20+
isReference(node, parent)
21+
) {
22+
function getMemberExpressionFullNameRecursive(astNode: MemberExpression): string | null {
23+
if (astNode.property.type !== 'Identifier') {
24+
return null;
25+
}
26+
27+
switch (astNode.object.type) {
28+
case 'MemberExpression': {
29+
const nameSoFar = getMemberExpressionFullNameRecursive(astNode.object);
30+
if (nameSoFar == null) {
31+
return null;
32+
}
33+
34+
return `${nameSoFar}.${astNode.property.name}`
35+
}
36+
case 'Identifier': {
37+
return `${astNode.object.name}.${astNode.property.name}`;
38+
}
39+
default: {
40+
return null;
41+
}
42+
}
43+
}
44+
45+
const fullName = getMemberExpressionFullNameRecursive(node);
46+
const match = GRANTS_REGEXP.exec(fullName);
47+
if (match) {
48+
grantSetPerFile.add(match[0]);
49+
50+
this.skip();
51+
}
52+
}
53+
3854
if (
3955
node.type === 'Identifier' &&
4056
isReference(node, parent) &&
4157
!scope.contains(node.name)
4258
) {
43-
if (gmAPIs.includes(node.name)) {
44-
grantSetPerFile.add(node.name);
59+
const match = GRANTS_REGEXP.exec(node.name);
60+
if (match) {
61+
grantSetPerFile.add(match[0]);
4562
}
4663
}
4764
},

0 commit comments

Comments
 (0)