/
index.ts
119 lines (100 loc) 路 3.66 KB
/
index.ts
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
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
import { declare } from "@babel/helper-plugin-utils";
import type { types as t, File } from "@babel/core";
import syntaxImportAttributes from "@babel/plugin-syntax-import-attributes";
import {
importToPlatformApi,
buildParallelStaticImports,
type Pieces,
type Builders,
} from "@babel/helper-import-to-platform-api";
export default declare(api => {
const { types: t, template } = api;
api.assertVersion(REQUIRED_VERSION("^7.22.0"));
const targets = api.targets();
let helperESM: Builders;
let helperCJS: Builders;
const transformers: Pieces = {
webFetch: (fetch: t.Expression) =>
template.expression.ast`${fetch}.then(r => r.json())`,
nodeFsSync: (read: t.Expression) =>
template.expression.ast`JSON.parse(${read})`,
nodeFsAsync: () => template.expression.ast`JSON.parse`,
};
const getHelper = (file: File) => {
const modules = file.get("@babel/plugin-transform-modules-*");
if (modules === "commonjs") {
return (helperCJS ??= importToPlatformApi(targets, transformers, true));
}
if (modules == null) {
return (helperESM ??= importToPlatformApi(targets, transformers, false));
}
throw new Error(
`@babel/plugin-proposal-json-modules can only be used when not ` +
`compiling modules, or when compiling them to CommonJS.`,
);
};
function getAttributeKey({ key }: t.ImportAttribute): string {
return t.isIdentifier(key) ? key.name : key.value;
}
function hasTypeJson(attributes: t.ImportAttribute[]) {
return !!attributes?.some(
attr => getAttributeKey(attr) === "type" && attr.value.value === "json",
);
}
return {
name: "proposal-json-modules",
inherits: syntaxImportAttributes,
visitor: {
Program(path) {
if (path.node.sourceType !== "module") return;
const helper = getHelper(this.file);
const data = [];
for (const decl of path.get("body")) {
if (!decl.isImportDeclaration()) continue;
const attributes = decl.node.attributes || decl.node.assertions;
if (!hasTypeJson(attributes)) continue;
if (decl.node.phase != null) {
throw decl.buildCodeFrameError(
"JSON modules do not support phase modifiers.",
);
}
if (attributes.length > 1) {
const paths = decl.node.attributes
? decl.get("attributes")
: decl.get("assertions");
const index = getAttributeKey(attributes[0]) === "type" ? 1 : 0;
throw paths[index].buildCodeFrameError(
"Unknown attribute for JSON modules.",
);
}
let id: t.Identifier;
let needsNS = false;
for (const specifier of decl.get("specifiers")) {
if (specifier.isImportSpecifier()) {
throw specifier.buildCodeFrameError(
"JSON modules do not support named imports.",
);
}
id = specifier.node.local;
needsNS = specifier.isImportNamespaceSpecifier();
}
id ??= path.scope.generateUidIdentifier("_");
let fetch = helper.buildFetch(decl.node.source, path);
if (needsNS) {
if (helper.needsAwait) {
fetch = template.expression.ast`
${fetch}.then(j => ({ default: j }))
`;
} else {
fetch = template.expression.ast`{ default: ${fetch} }`;
}
}
data.push({ id, fetch });
decl.remove();
}
const decl = buildParallelStaticImports(data, helper.needsAwait);
if (decl) path.unshiftContainer("body", decl);
},
},
};
});