1 | "use strict";
|
2 |
|
3 | Object.defineProperty(exports, "__esModule", {
|
4 | value: true
|
5 | });
|
6 | exports.default = void 0;
|
7 | exports.ensure = ensure;
|
8 | exports.get = get;
|
9 | exports.getDependencies = getDependencies;
|
10 | exports.list = void 0;
|
11 | exports.minVersion = minVersion;
|
12 | var _traverse = require("@babel/traverse");
|
13 | var _t = require("@babel/types");
|
14 | var _helpers = require("./helpers.js");
|
15 | const {
|
16 | assignmentExpression,
|
17 | cloneNode,
|
18 | expressionStatement,
|
19 | file,
|
20 | identifier
|
21 | } = _t;
|
22 | function makePath(path) {
|
23 | const parts = [];
|
24 | for (; path.parentPath; path = path.parentPath) {
|
25 | parts.push(path.key);
|
26 | if (path.inList) parts.push(path.listKey);
|
27 | }
|
28 | return parts.reverse().join(".");
|
29 | }
|
30 | let FileClass = undefined;
|
31 | function getHelperMetadata(file) {
|
32 | const globals = new Set();
|
33 | const localBindingNames = new Set();
|
34 | const dependencies = new Map();
|
35 | let exportName;
|
36 | let exportPath;
|
37 | const exportBindingAssignments = [];
|
38 | const importPaths = [];
|
39 | const importBindingsReferences = [];
|
40 | const dependencyVisitor = {
|
41 | ImportDeclaration(child) {
|
42 | const name = child.node.source.value;
|
43 | if (!_helpers.default[name]) {
|
44 | throw child.buildCodeFrameError(`Unknown helper ${name}`);
|
45 | }
|
46 | if (child.get("specifiers").length !== 1 || !child.get("specifiers.0").isImportDefaultSpecifier()) {
|
47 | throw child.buildCodeFrameError("Helpers can only import a default value");
|
48 | }
|
49 | const bindingIdentifier = child.node.specifiers[0].local;
|
50 | dependencies.set(bindingIdentifier, name);
|
51 | importPaths.push(makePath(child));
|
52 | },
|
53 | ExportDefaultDeclaration(child) {
|
54 | const decl = child.get("declaration");
|
55 | if (!decl.isFunctionDeclaration() || !decl.node.id) {
|
56 | throw decl.buildCodeFrameError("Helpers can only export named function declarations");
|
57 | }
|
58 | exportName = decl.node.id.name;
|
59 | exportPath = makePath(child);
|
60 | },
|
61 | ExportAllDeclaration(child) {
|
62 | throw child.buildCodeFrameError("Helpers can only export default");
|
63 | },
|
64 | ExportNamedDeclaration(child) {
|
65 | throw child.buildCodeFrameError("Helpers can only export default");
|
66 | },
|
67 | Statement(child) {
|
68 | if (child.isImportDeclaration() || child.isExportDeclaration()) return;
|
69 | child.skip();
|
70 | }
|
71 | };
|
72 | const referenceVisitor = {
|
73 | Program(path) {
|
74 | const bindings = path.scope.getAllBindings();
|
75 | Object.keys(bindings).forEach(name => {
|
76 | if (name === exportName) return;
|
77 | if (dependencies.has(bindings[name].identifier)) return;
|
78 | localBindingNames.add(name);
|
79 | });
|
80 | },
|
81 | ReferencedIdentifier(child) {
|
82 | const name = child.node.name;
|
83 | const binding = child.scope.getBinding(name);
|
84 | if (!binding) {
|
85 | if (name !== "arguments" || child.scope.path.isProgram()) {
|
86 | globals.add(name);
|
87 | }
|
88 | } else if (dependencies.has(binding.identifier)) {
|
89 | importBindingsReferences.push(makePath(child));
|
90 | }
|
91 | },
|
92 | AssignmentExpression(child) {
|
93 | const left = child.get("left");
|
94 | if (!(exportName in left.getBindingIdentifiers())) return;
|
95 | if (!left.isIdentifier()) {
|
96 | throw left.buildCodeFrameError("Only simple assignments to exports are allowed in helpers");
|
97 | }
|
98 | const binding = child.scope.getBinding(exportName);
|
99 | if (binding != null && binding.scope.path.isProgram()) {
|
100 | exportBindingAssignments.push(makePath(child));
|
101 | }
|
102 | }
|
103 | };
|
104 | (0, _traverse.default)(file.ast, dependencyVisitor, file.scope);
|
105 | (0, _traverse.default)(file.ast, referenceVisitor, file.scope);
|
106 | if (!exportPath) throw new Error("Helpers must have a default export.");
|
107 | exportBindingAssignments.reverse();
|
108 | return {
|
109 | globals: Array.from(globals),
|
110 | localBindingNames: Array.from(localBindingNames),
|
111 | dependencies,
|
112 | exportBindingAssignments,
|
113 | exportPath,
|
114 | exportName,
|
115 | importBindingsReferences,
|
116 | importPaths
|
117 | };
|
118 | }
|
119 | function permuteHelperAST(file, metadata, id, localBindings, getDependency) {
|
120 | if (localBindings && !id) {
|
121 | throw new Error("Unexpected local bindings for module-based helpers.");
|
122 | }
|
123 | if (!id) return;
|
124 | const {
|
125 | localBindingNames,
|
126 | dependencies,
|
127 | exportBindingAssignments,
|
128 | exportPath,
|
129 | exportName,
|
130 | importBindingsReferences,
|
131 | importPaths
|
132 | } = metadata;
|
133 | const dependenciesRefs = {};
|
134 | dependencies.forEach((name, id) => {
|
135 | dependenciesRefs[id.name] = typeof getDependency === "function" && getDependency(name) || id;
|
136 | });
|
137 | const toRename = {};
|
138 | const bindings = new Set(localBindings || []);
|
139 | if (id.type === "Identifier") bindings.add(id.name);
|
140 | localBindingNames.forEach(name => {
|
141 | let newName = name;
|
142 | while (bindings.has(newName)) newName = "_" + newName;
|
143 | if (newName !== name) toRename[name] = newName;
|
144 | });
|
145 | if (id.type === "Identifier" && exportName !== id.name) {
|
146 | toRename[exportName] = id.name;
|
147 | }
|
148 | const {
|
149 | path
|
150 | } = file;
|
151 | const exp = path.get(exportPath);
|
152 | const imps = importPaths.map(p => path.get(p));
|
153 | const impsBindingRefs = importBindingsReferences.map(p => path.get(p));
|
154 | const decl = exp.get("declaration");
|
155 | if (id.type === "Identifier") {
|
156 | exp.replaceWith(decl);
|
157 | } else if (id.type === "MemberExpression") {
|
158 | exportBindingAssignments.forEach(assignPath => {
|
159 | const assign = path.get(assignPath);
|
160 | assign.replaceWith(assignmentExpression("=", id, assign.node));
|
161 | });
|
162 | exp.replaceWith(decl);
|
163 | path.pushContainer("body", expressionStatement(assignmentExpression("=", id, identifier(exportName))));
|
164 | } else {
|
165 | throw new Error("Unexpected helper format.");
|
166 | }
|
167 | Object.keys(toRename).forEach(name => {
|
168 | path.scope.rename(name, toRename[name]);
|
169 | });
|
170 | for (const path of imps) path.remove();
|
171 | for (const path of impsBindingRefs) {
|
172 | const node = cloneNode(dependenciesRefs[path.node.name]);
|
173 | path.replaceWith(node);
|
174 | }
|
175 | }
|
176 | const helperData = Object.create(null);
|
177 | function loadHelper(name) {
|
178 | if (!helperData[name]) {
|
179 | const helper = _helpers.default[name];
|
180 | if (!helper) {
|
181 | throw Object.assign(new ReferenceError(`Unknown helper ${name}`), {
|
182 | code: "BABEL_HELPER_UNKNOWN",
|
183 | helper: name
|
184 | });
|
185 | }
|
186 | const fn = () => {
|
187 | {
|
188 | if (!FileClass) {
|
189 | const fakeFile = {
|
190 | ast: file(helper.ast()),
|
191 | path: null
|
192 | };
|
193 | (0, _traverse.default)(fakeFile.ast, {
|
194 | Program: path => (fakeFile.path = path).stop()
|
195 | });
|
196 | return fakeFile;
|
197 | }
|
198 | }
|
199 | return new FileClass({
|
200 | filename: `babel-helper://${name}`
|
201 | }, {
|
202 | ast: file(helper.ast()),
|
203 | code: "[internal Babel helper code]",
|
204 | inputMap: null
|
205 | });
|
206 | };
|
207 | let metadata = null;
|
208 | helperData[name] = {
|
209 | minVersion: helper.minVersion,
|
210 | build(getDependency, id, localBindings) {
|
211 | const file = fn();
|
212 | metadata || (metadata = getHelperMetadata(file));
|
213 | permuteHelperAST(file, metadata, id, localBindings, getDependency);
|
214 | return {
|
215 | nodes: file.ast.program.body,
|
216 | globals: metadata.globals
|
217 | };
|
218 | },
|
219 | getDependencies() {
|
220 | metadata || (metadata = getHelperMetadata(fn()));
|
221 | return Array.from(metadata.dependencies.values());
|
222 | }
|
223 | };
|
224 | }
|
225 | return helperData[name];
|
226 | }
|
227 | function get(name, getDependency, id, localBindings) {
|
228 | return loadHelper(name).build(getDependency, id, localBindings);
|
229 | }
|
230 | function minVersion(name) {
|
231 | return loadHelper(name).minVersion;
|
232 | }
|
233 | function getDependencies(name) {
|
234 | return loadHelper(name).getDependencies();
|
235 | }
|
236 | function ensure(name, newFileClass) {
|
237 | FileClass || (FileClass = newFileClass);
|
238 | loadHelper(name);
|
239 | }
|
240 | const list = exports.list = Object.keys(_helpers.default).map(name => name.replace(/^_/, ""));
|
241 | var _default = exports.default = get;
|
242 |
|
243 | //# sourceMappingURL=index.js.map
|