-
Notifications
You must be signed in to change notification settings - Fork 84
/
features.js
81 lines (70 loc) · 2.31 KB
/
features.js
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
import {simple} from "acorn-walk";
import {syntaxError} from "./syntaxError.js";
export function findFeatures(node, references, input) {
const features = [];
simple(node, {
CallExpression(node) {
const {
callee,
arguments: args,
arguments: [arg]
} = node;
// Promote fetches with static literals to file attachment references.
if (isLocalFetch(node, references)) {
features.push({type: "FileAttachment", name: getStringLiteralValue(arg)});
return;
}
// Ignore function calls that are not references to the feature. For
// example, if there’s a local variable called Secret, that will mask the
// built-in Secret and won’t be considered a feature.
if (
callee.type !== "Identifier" ||
(callee.name !== "Secret" && callee.name !== "FileAttachment" && callee.name !== "DatabaseClient") ||
!references.includes(callee)
) {
return;
}
// Forbid dynamic calls.
if (args.length !== 1 || !isStringLiteral(arg)) {
throw syntaxError(`${callee.name} requires a single literal string argument`, node, input);
}
features.push({type: callee.name, name: getStringLiteralValue(arg)});
},
// Promote dynamic imports with static literals to file attachment references.
ImportExpression: findImport,
ImportDeclaration: findImport
});
function findImport(node) {
if (isStringLiteral(node.source)) {
const value = getStringLiteralValue(node.source);
if (value.startsWith("./")) {
features.push({type: "FileAttachment", name: value});
}
}
}
return features;
}
export function isLocalFetch(node, references) {
if (node.type !== "CallExpression") return false;
const {
callee,
arguments: [arg]
} = node;
return (
callee.type === "Identifier" &&
callee.name === "fetch" &&
!references.includes(callee) &&
arg &&
isStringLiteral(arg) &&
getStringLiteralValue(arg).startsWith("./")
);
}
export function isStringLiteral(node) {
return (
(node.type === "Literal" && /^['"]/.test(node.raw)) ||
(node.type === "TemplateLiteral" && node.expressions.length === 0)
);
}
export function getStringLiteralValue(node) {
return node.type === "Literal" ? node.value : node.quasis[0].value.cooked;
}