1 | "use strict";
|
2 |
|
3 | var _interopRequireDefault = require("@babel/runtime/helpers/interopRequireDefault");
|
4 |
|
5 | var _toArray2 = _interopRequireDefault(require("@babel/runtime/helpers/toArray"));
|
6 |
|
7 | const t = require('@babel/types');
|
8 |
|
9 | const Path = require('path');
|
10 |
|
11 | const fs = require('fs');
|
12 |
|
13 | const template = require('@babel/template').default;
|
14 |
|
15 | const logger = require('@parcel/logger');
|
16 |
|
17 | const bufferTemplate = template('Buffer(CONTENT, ENC)');
|
18 | module.exports = {
|
19 | AssignmentExpression(path) {
|
20 | if (!isRequire(path.node.right, 'fs', 'readFileSync')) {
|
21 | return;
|
22 | }
|
23 |
|
24 | for (let name in path.getBindingIdentifiers()) {
|
25 | const binding = path.scope.getBinding(name);
|
26 | if (!binding) continue;
|
27 | binding.path.setData('__require', path.node);
|
28 | }
|
29 | },
|
30 |
|
31 | CallExpression(path, asset) {
|
32 | if (referencesImport(path, 'fs', 'readFileSync')) {
|
33 | let vars = {
|
34 | __dirname: Path.dirname(asset.name),
|
35 | __filename: asset.basename
|
36 | };
|
37 | let filename, args, res;
|
38 |
|
39 | try {
|
40 | var _path$get$map = path.get('arguments').map(arg => evaluate(arg, vars));
|
41 |
|
42 | var _path$get$map2 = (0, _toArray2.default)(_path$get$map);
|
43 |
|
44 | filename = _path$get$map2[0];
|
45 | args = _path$get$map2.slice(1);
|
46 | filename = Path.resolve(filename);
|
47 | res = fs.readFileSync(filename, ...args);
|
48 | } catch (err) {
|
49 | if (err instanceof NodeNotEvaluatedError) {
|
50 |
|
51 | err.fileName = asset.name;
|
52 | asset.generateErrorMessage(err);
|
53 | logger.warn(err);
|
54 | return;
|
55 | }
|
56 |
|
57 |
|
58 | err.loc = path.node.arguments.length > 0 ? path.node.arguments[0].loc.start : path.node.loc.start;
|
59 | throw err;
|
60 | }
|
61 |
|
62 | let replacementNode;
|
63 |
|
64 | if (Buffer.isBuffer(res)) {
|
65 | replacementNode = bufferTemplate({
|
66 | CONTENT: t.stringLiteral(res.toString('base64')),
|
67 | ENC: t.stringLiteral('base64')
|
68 | });
|
69 | } else {
|
70 | replacementNode = t.stringLiteral(res);
|
71 | }
|
72 |
|
73 | asset.addDependency(filename, {
|
74 | includedInParent: true
|
75 | });
|
76 | path.replaceWith(replacementNode);
|
77 | asset.isAstDirty = true;
|
78 | }
|
79 | }
|
80 |
|
81 | };
|
82 |
|
83 | function isRequire(node, name, method) {
|
84 |
|
85 | if (t.isMemberExpression(node) && node.property.name === method) {
|
86 | node = node.object;
|
87 | }
|
88 |
|
89 | if (!t.isCallExpression(node)) {
|
90 | return false;
|
91 | }
|
92 |
|
93 | let _node = node,
|
94 | callee = _node.callee,
|
95 | args = _node.arguments;
|
96 | let isRequire = t.isIdentifier(callee) && callee.name === 'require' && args.length === 1 && t.isStringLiteral(args[0]);
|
97 |
|
98 | if (!isRequire) {
|
99 | return false;
|
100 | }
|
101 |
|
102 | if (name && args[0].value !== name) {
|
103 | return false;
|
104 | }
|
105 |
|
106 | return true;
|
107 | }
|
108 |
|
109 | function referencesImport(path, name, method) {
|
110 | let callee = path.node.callee;
|
111 | let bindingPath;
|
112 |
|
113 | if (t.isIdentifier(callee)) {
|
114 | bindingPath = getBindingPath(path, callee.name);
|
115 | } else if (t.isMemberExpression(callee)) {
|
116 | if (callee.property.name !== method) {
|
117 | return false;
|
118 | }
|
119 |
|
120 |
|
121 | if (t.isIdentifier(callee.object)) {
|
122 | bindingPath = getBindingPath(path, callee.object.name);
|
123 | } else if (isRequire(callee.object, name)) {
|
124 | return true;
|
125 | }
|
126 | } else {
|
127 | return false;
|
128 | }
|
129 |
|
130 | if (!bindingPath) {
|
131 | return;
|
132 | }
|
133 |
|
134 | let bindingNode = bindingPath.getData('__require') || bindingPath.node;
|
135 | let parent = bindingPath.parentPath;
|
136 |
|
137 | if (parent.isImportDeclaration()) {
|
138 | if (bindingPath.isImportSpecifier() && bindingPath.node.imported.name !== method) {
|
139 | return false;
|
140 | }
|
141 |
|
142 | return parent.node.source.value === name;
|
143 | } else if (t.isVariableDeclarator(bindingNode) || t.isAssignmentExpression(bindingNode)) {
|
144 | let left = bindingNode.id || bindingNode.left;
|
145 | let right = bindingNode.init || bindingNode.right;
|
146 |
|
147 | if (t.isObjectPattern(left)) {
|
148 | let prop = left.properties.find(p => p.value.name === callee.name);
|
149 |
|
150 | if (!prop || prop.key.name !== method) {
|
151 | return false;
|
152 | }
|
153 | } else if (!t.isIdentifier(left)) {
|
154 | return false;
|
155 | }
|
156 |
|
157 | return isRequire(right, name, method);
|
158 | }
|
159 |
|
160 | return false;
|
161 | }
|
162 |
|
163 | function getBindingPath(path, name) {
|
164 | let binding = path.scope.getBinding(name);
|
165 | return binding && binding.path;
|
166 | }
|
167 |
|
168 | function NodeNotEvaluatedError(node) {
|
169 | this.message = 'Cannot statically evaluate fs argument';
|
170 | this.node = node;
|
171 | this.loc = node.loc.start;
|
172 | }
|
173 |
|
174 | function evaluate(path, vars) {
|
175 |
|
176 | path.traverse({
|
177 | Identifier: function Identifier(ident) {
|
178 | let key = ident.node.name;
|
179 |
|
180 | if (key in vars) {
|
181 | ident.replaceWith(t.valueToNode(vars[key]));
|
182 | }
|
183 | }
|
184 | });
|
185 | let res = path.evaluate();
|
186 |
|
187 | if (!res.confident) {
|
188 | throw new NodeNotEvaluatedError(path.node);
|
189 | }
|
190 |
|
191 | return res.value;
|
192 | } |
\ | No newline at end of file |