1 | "format cjs";
|
2 | var esprima = require("esprima-next"),
|
3 | optionsNormalize = require("./options_normalize"),
|
4 | getAst = require("./get_ast"),
|
5 | types = require("ast-types"),
|
6 | n = types.namedTypes;
|
7 |
|
8 | var dirnameExp = /__dirname/;
|
9 | var globalExp = /global/;
|
10 |
|
11 | module.exports = function(load, options) {
|
12 | var ast = getAst(load);
|
13 |
|
14 | var source = load.source;
|
15 | var cjsOptions = {
|
16 | hasDirname: dirnameExp.test(source),
|
17 | hasGlobal: globalExp.test(source),
|
18 | duplicateCjsDependencies: options.duplicateCjsDependencies
|
19 | };
|
20 | cjsOptions.needsFunctionWrapper =
|
21 | cjsOptions.hasDirname || cjsOptions.hasGlobal;
|
22 |
|
23 | if (options && (options.normalizeMap || options.normalize)) {
|
24 | normalizeModuleIdentifiers(ast, load, options);
|
25 | }
|
26 |
|
27 | var normalizedName;
|
28 | if (options.namedDefines) {
|
29 | normalizedName = optionsNormalize(
|
30 | options,
|
31 | load.name,
|
32 | load.name,
|
33 | load.address
|
34 | );
|
35 | }
|
36 | ast = defineInsert(normalizedName, ast.body, cjsOptions);
|
37 |
|
38 | return ast;
|
39 | };
|
40 |
|
41 |
|
42 |
|
43 |
|
44 |
|
45 |
|
46 | function visitRequireArgument(ast, cb) {
|
47 | types.visit(ast, {
|
48 | visitCallExpression: function(path) {
|
49 | if (this.isRequireExpression(path.node)) {
|
50 | var arg = path.getValueProperty("arguments")[0];
|
51 |
|
52 | if (n.Literal.check(arg)) {
|
53 | cb(arg);
|
54 | }
|
55 | }
|
56 |
|
57 | this.traverse(path);
|
58 | },
|
59 |
|
60 | isRequireExpression(node) {
|
61 | return n.Identifier.check(node.callee) && node.callee.name === "require";
|
62 | }
|
63 | });
|
64 | }
|
65 |
|
66 |
|
67 |
|
68 |
|
69 | function normalizeModuleIdentifiers(ast, load, options) {
|
70 | visitRequireArgument(ast, function(argument) {
|
71 | var normalized = optionsNormalize(
|
72 | options,
|
73 | argument.value,
|
74 | load.name,
|
75 | load.address
|
76 | );
|
77 |
|
78 | argument.value = normalized;
|
79 | argument.raw = `"${normalized}"`;
|
80 | });
|
81 | }
|
82 |
|
83 |
|
84 |
|
85 |
|
86 |
|
87 |
|
88 | function collectDependenciesIds(ast) {
|
89 | var ids = [];
|
90 |
|
91 | visitRequireArgument(ast, function(argument) {
|
92 | ids.push(argument.value);
|
93 | });
|
94 |
|
95 | return ids;
|
96 | }
|
97 |
|
98 | function defineInsert(name, body, options) {
|
99 |
|
100 | var wrapper = defineWrapper(options);
|
101 |
|
102 | var code = makeDefineFunctionCode(
|
103 | name,
|
104 | options.duplicateCjsDependencies ? collectDependenciesIds(body) : [],
|
105 | wrapper
|
106 | );
|
107 |
|
108 | var ast = esprima.parse(code);
|
109 | var astBody = body || [];
|
110 |
|
111 | var innerFunctions = 0;
|
112 | var expectedFunctions = options.needsFunctionWrapper ? 2 : 1;
|
113 |
|
114 | types.visit(ast, {
|
115 | visitFunctionExpression: function(path) {
|
116 | if (this.isModuleFactory()) {
|
117 | var functionBody = path.getValueProperty("body").body;
|
118 |
|
119 | astBody.forEach(function(part) {
|
120 | functionBody.push(part);
|
121 | });
|
122 |
|
123 |
|
124 | this.abort();
|
125 | }
|
126 |
|
127 |
|
128 | this.traverse(path);
|
129 | },
|
130 |
|
131 | isModuleFactory: function() {
|
132 | innerFunctions += 1;
|
133 | return innerFunctions === expectedFunctions;
|
134 | }
|
135 | });
|
136 |
|
137 | return ast;
|
138 | }
|
139 |
|
140 | function makeDefineFunctionCode(name, cjsDeps, body) {
|
141 | var deps = "";
|
142 |
|
143 | if (cjsDeps.length) {
|
144 | deps = ["require", "exports", "module"]
|
145 | .concat(cjsDeps)
|
146 | .map(function(x) {
|
147 | return `"${x}"`;
|
148 | })
|
149 | .join(", ")
|
150 | }
|
151 |
|
152 | var defineArgs = [
|
153 | name ? `"${name}"` : null,
|
154 | deps ? `[ ${deps} ]` : null,
|
155 | `function(require, exports, module) { ${body} }`
|
156 | ];
|
157 |
|
158 | return `define(${defineArgs.filter(isTruthy).join(", ")});`;
|
159 | }
|
160 |
|
161 | function isTruthy(x) {
|
162 | return Boolean(x);
|
163 | }
|
164 |
|
165 | function defineWrapper(options) {
|
166 |
|
167 | var wrapper = "";
|
168 | if (options.needsFunctionWrapper) {
|
169 | wrapper += "(function(";
|
170 | if (options.hasGlobal) {
|
171 | wrapper += "global, ";
|
172 | }
|
173 | if (options.hasDirname) {
|
174 | wrapper += "__dirname, ";
|
175 | }
|
176 | wrapper += "require, exports, module";
|
177 | wrapper += "){\n";
|
178 | wrapper += "})(";
|
179 |
|
180 | if (options.hasGlobal) {
|
181 | wrapper += "function() { return this; }(), ";
|
182 | }
|
183 | if (options.hasDirname) {
|
184 | wrapper += '"/", ';
|
185 | }
|
186 | wrapper += "require, exports, module";
|
187 | wrapper += ");";
|
188 | }
|
189 |
|
190 | return wrapper;
|
191 | }
|