@atlaspack/babel-plugin-transform-contextual-imports 2.14.1-canary.39 → 2.14.1-canary.392
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +46 -0
- package/dist/index.js +116 -0
- package/lib/index.js +107 -149
- package/lib/types/index.d.ts +16 -0
- package/package.json +7 -3
- package/src/index.ts +41 -145
- package/test/babel-plugin-transform-contextual-imports.test.ts +162 -0
- package/tsconfig.json +4 -9
- package/tsconfig.tsbuildinfo +1 -0
- package/lib/index.js.map +0 -1
- package/test/babel-plugin-transform-contextual-imports.test.js +0 -72
package/src/index.ts
CHANGED
|
@@ -1,9 +1,8 @@
|
|
|
1
|
-
import type {PluginObj,
|
|
1
|
+
import type {PluginObj, types as BabelTypes} from '@babel/core';
|
|
2
|
+
import type {Binding} from '@babel/traverse';
|
|
2
3
|
import {declare} from '@babel/helper-plugin-utils';
|
|
3
4
|
|
|
4
5
|
interface Opts {
|
|
5
|
-
/** @deprecated Use "node" instead */
|
|
6
|
-
server?: boolean;
|
|
7
6
|
/** Use node safe import cond syntax */
|
|
8
7
|
node?: boolean;
|
|
9
8
|
}
|
|
@@ -11,18 +10,12 @@ interface Opts {
|
|
|
11
10
|
interface State {
|
|
12
11
|
/** Plugin options */
|
|
13
12
|
opts: Opts;
|
|
14
|
-
/**
|
|
15
|
-
|
|
16
|
-
/** Set of identifier names that need to be mutated after import was transformed */
|
|
17
|
-
conditionalImportIdentifiers?: Set<string>;
|
|
13
|
+
/** Set of bindings that need to be mutated after import was transformed */
|
|
14
|
+
conditionalImportBindings?: Set<Binding>;
|
|
18
15
|
/** Set of identifiers that have been visited in the exit pass, to avoid adding the load property multiple times */
|
|
19
|
-
visitedIdentifiers?: Set<BabelTypes.Identifier>;
|
|
16
|
+
visitedIdentifiers?: Set<BabelTypes.Identifier | BabelTypes.JSXIdentifier>;
|
|
20
17
|
}
|
|
21
18
|
|
|
22
|
-
const isServer = (opts: Opts) => {
|
|
23
|
-
return 'server' in opts && opts.server;
|
|
24
|
-
};
|
|
25
|
-
|
|
26
19
|
const isNode = (opts: Opts): boolean => !!('node' in opts && opts.node);
|
|
27
20
|
|
|
28
21
|
export default declare((api): PluginObj<State> => {
|
|
@@ -157,124 +150,11 @@ export default declare((api): PluginObj<State> => {
|
|
|
157
150
|
),
|
|
158
151
|
];
|
|
159
152
|
|
|
160
|
-
const buildServerObject = (
|
|
161
|
-
identUid: string,
|
|
162
|
-
cond: BabelTypes.StringLiteral,
|
|
163
|
-
ifTrue: BabelTypes.StringLiteral,
|
|
164
|
-
ifFalse: BabelTypes.StringLiteral,
|
|
165
|
-
) => [
|
|
166
|
-
// Create object containing imports
|
|
167
|
-
t.variableDeclaration('const', [
|
|
168
|
-
t.variableDeclarator(
|
|
169
|
-
t.identifier(identUid),
|
|
170
|
-
t.objectExpression([
|
|
171
|
-
t.objectProperty(
|
|
172
|
-
t.identifier('ifTrue'),
|
|
173
|
-
t.memberExpression(
|
|
174
|
-
t.callExpression(t.identifier('require'), [ifTrue]),
|
|
175
|
-
t.identifier('default'),
|
|
176
|
-
),
|
|
177
|
-
),
|
|
178
|
-
t.objectProperty(
|
|
179
|
-
t.identifier('ifFalse'),
|
|
180
|
-
t.memberExpression(
|
|
181
|
-
t.callExpression(t.identifier('require'), [ifFalse]),
|
|
182
|
-
t.identifier('default'),
|
|
183
|
-
),
|
|
184
|
-
),
|
|
185
|
-
]),
|
|
186
|
-
),
|
|
187
|
-
]),
|
|
188
|
-
|
|
189
|
-
// Create lazy getter via the load property on the object
|
|
190
|
-
t.expressionStatement(
|
|
191
|
-
t.callExpression(
|
|
192
|
-
t.memberExpression(
|
|
193
|
-
t.identifier('Object'),
|
|
194
|
-
t.identifier('defineProperty'),
|
|
195
|
-
),
|
|
196
|
-
[
|
|
197
|
-
t.identifier(identUid),
|
|
198
|
-
t.stringLiteral('load'),
|
|
199
|
-
t.objectExpression([
|
|
200
|
-
t.objectProperty(
|
|
201
|
-
t.identifier('get'),
|
|
202
|
-
t.arrowFunctionExpression(
|
|
203
|
-
[],
|
|
204
|
-
t.conditionalExpression(
|
|
205
|
-
t.logicalExpression(
|
|
206
|
-
'&&',
|
|
207
|
-
t.memberExpression(
|
|
208
|
-
t.identifier('globalThis'),
|
|
209
|
-
t.identifier('__MCOND'),
|
|
210
|
-
),
|
|
211
|
-
t.callExpression(
|
|
212
|
-
t.memberExpression(
|
|
213
|
-
t.identifier('globalThis'),
|
|
214
|
-
t.identifier('__MCOND'),
|
|
215
|
-
),
|
|
216
|
-
[cond],
|
|
217
|
-
),
|
|
218
|
-
),
|
|
219
|
-
t.memberExpression(
|
|
220
|
-
t.identifier(identUid),
|
|
221
|
-
t.identifier('ifTrue'),
|
|
222
|
-
),
|
|
223
|
-
t.memberExpression(
|
|
224
|
-
t.identifier(identUid),
|
|
225
|
-
t.identifier('ifFalse'),
|
|
226
|
-
),
|
|
227
|
-
),
|
|
228
|
-
),
|
|
229
|
-
),
|
|
230
|
-
]),
|
|
231
|
-
],
|
|
232
|
-
),
|
|
233
|
-
),
|
|
234
|
-
];
|
|
235
|
-
|
|
236
|
-
const checkIsServer = (
|
|
237
|
-
path: NodePath<BabelTypes.CallExpression>,
|
|
238
|
-
state: State,
|
|
239
|
-
) => {
|
|
240
|
-
if (
|
|
241
|
-
path.node.callee.type === 'Identifier' &&
|
|
242
|
-
path.node.callee.name === 'importCond'
|
|
243
|
-
) {
|
|
244
|
-
if (
|
|
245
|
-
path.node.arguments.length == 3 &&
|
|
246
|
-
path.node.arguments.every((arg) => arg.type === 'StringLiteral')
|
|
247
|
-
) {
|
|
248
|
-
const [cond, ifTrue, ifFalse] = path.node.arguments;
|
|
249
|
-
|
|
250
|
-
if (isServer(state.opts)) {
|
|
251
|
-
// Make module pass lazy in ssr
|
|
252
|
-
const identUid = path.scope.generateUid(
|
|
253
|
-
`${cond.value}$${ifTrue.value}$${ifFalse.value}`,
|
|
254
|
-
);
|
|
255
|
-
|
|
256
|
-
state.importNodes ??= [];
|
|
257
|
-
state.importNodes.push(
|
|
258
|
-
...buildServerObject(identUid, cond, ifTrue, ifFalse),
|
|
259
|
-
);
|
|
260
|
-
|
|
261
|
-
// Replace call expression with reference to lazy object getter
|
|
262
|
-
path.replaceWith(
|
|
263
|
-
t.memberExpression(t.identifier(identUid), t.identifier('load')),
|
|
264
|
-
);
|
|
265
|
-
}
|
|
266
|
-
}
|
|
267
|
-
}
|
|
268
|
-
};
|
|
269
|
-
|
|
270
153
|
return {
|
|
271
154
|
name: '@atlaspack/babel-plugin-transform-contextual-imports',
|
|
272
155
|
visitor: {
|
|
273
156
|
CallExpression: {
|
|
274
157
|
enter(path, state) {
|
|
275
|
-
// Preserve server behaviour in deletable code
|
|
276
|
-
checkIsServer(path, state);
|
|
277
|
-
|
|
278
158
|
const node = path.node;
|
|
279
159
|
if (isImportCondCallExpression(node)) {
|
|
280
160
|
const [cond, ifTrue, ifFalse] = node.arguments;
|
|
@@ -307,39 +187,55 @@ export default declare((api): PluginObj<State> => {
|
|
|
307
187
|
buildNodeObject(importId, cond, ifTrue, ifFalse),
|
|
308
188
|
);
|
|
309
189
|
|
|
310
|
-
// Add
|
|
311
|
-
|
|
190
|
+
// Add the binding to set so we can mutate all references to this binding in the exit pass
|
|
191
|
+
const binding = path.scope.getBinding(importId.name);
|
|
192
|
+
if (binding) {
|
|
193
|
+
state.conditionalImportBindings?.add(binding);
|
|
194
|
+
}
|
|
312
195
|
}
|
|
313
196
|
}
|
|
314
197
|
}
|
|
315
198
|
},
|
|
316
199
|
},
|
|
317
|
-
|
|
200
|
+
ReferencedIdentifier: {
|
|
318
201
|
exit(path, state) {
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
202
|
+
if (!isNode(state.opts)) {
|
|
203
|
+
return;
|
|
204
|
+
}
|
|
205
|
+
|
|
206
|
+
if (path.parentPath.isTSType()) {
|
|
207
|
+
return;
|
|
208
|
+
}
|
|
209
|
+
|
|
210
|
+
if (state.visitedIdentifiers?.has(path.node)) {
|
|
211
|
+
return;
|
|
212
|
+
}
|
|
213
|
+
|
|
214
|
+
const binding = path.scope.getBinding(path.node.name);
|
|
215
|
+
if (binding && state.conditionalImportBindings?.has(binding)) {
|
|
216
|
+
if (path.isJSXIdentifier()) {
|
|
217
|
+
// Add load property to the import usage
|
|
218
|
+
const newIdentifer = t.jsxIdentifier(path.node.name);
|
|
219
|
+
path.replaceWith(
|
|
220
|
+
t.jsxMemberExpression(newIdentifer, t.jsxIdentifier('load')),
|
|
221
|
+
);
|
|
222
|
+
state.visitedIdentifiers?.add(newIdentifer);
|
|
223
|
+
} else {
|
|
224
|
+
// Add load property to the import usage
|
|
225
|
+
const newIdentifer = t.identifier(path.node.name);
|
|
226
|
+
path.replaceWith(
|
|
227
|
+
t.memberExpression(newIdentifer, t.identifier('load')),
|
|
228
|
+
);
|
|
229
|
+
state.visitedIdentifiers?.add(newIdentifer);
|
|
230
|
+
}
|
|
329
231
|
}
|
|
330
232
|
},
|
|
331
233
|
},
|
|
332
234
|
Program: {
|
|
333
235
|
enter(_, state) {
|
|
334
|
-
state.
|
|
236
|
+
state.conditionalImportBindings = new Set();
|
|
335
237
|
state.visitedIdentifiers = new Set();
|
|
336
238
|
},
|
|
337
|
-
exit(path, state) {
|
|
338
|
-
if (state.importNodes) {
|
|
339
|
-
// If there's an import node, add it to the top of the body
|
|
340
|
-
path.unshiftContainer('body', state.importNodes);
|
|
341
|
-
}
|
|
342
|
-
},
|
|
343
239
|
},
|
|
344
240
|
},
|
|
345
241
|
};
|
|
@@ -0,0 +1,162 @@
|
|
|
1
|
+
import * as babel from '@babel/core';
|
|
2
|
+
import assert from 'assert';
|
|
3
|
+
|
|
4
|
+
const plugin = require.resolve('../src/index.ts');
|
|
5
|
+
|
|
6
|
+
describe('@atlaspack/babel-plugin-transform-contextual-imports', () => {
|
|
7
|
+
it('should transform importCond to inline requires', () => {
|
|
8
|
+
const input = `
|
|
9
|
+
const Imported = importCond('CONDITION', 'IF_TRUE', 'IF_FALSE');
|
|
10
|
+
`;
|
|
11
|
+
const result = babel.transformSync(input, {
|
|
12
|
+
configFile: false,
|
|
13
|
+
presets: [],
|
|
14
|
+
plugins: [plugin],
|
|
15
|
+
});
|
|
16
|
+
|
|
17
|
+
assert.equal(
|
|
18
|
+
result?.code,
|
|
19
|
+
"const Imported = globalThis.__MCOND && globalThis.__MCOND('CONDITION') ? require('IF_TRUE').default : require('IF_FALSE').default;",
|
|
20
|
+
);
|
|
21
|
+
});
|
|
22
|
+
|
|
23
|
+
it('should transform importCond to node lazy code', () => {
|
|
24
|
+
const input = `
|
|
25
|
+
const Imported = importCond('CONDITION', 'IF_TRUE', 'IF_FALSE');
|
|
26
|
+
|
|
27
|
+
console.log(Imported, Imported.someProperty);
|
|
28
|
+
`;
|
|
29
|
+
const result = babel.transformSync(input, {
|
|
30
|
+
configFile: false,
|
|
31
|
+
presets: [],
|
|
32
|
+
plugins: [[plugin, {node: true}]],
|
|
33
|
+
});
|
|
34
|
+
|
|
35
|
+
assert.equal(
|
|
36
|
+
result?.code,
|
|
37
|
+
`const Imported = {
|
|
38
|
+
ifTrue: require('IF_TRUE').default,
|
|
39
|
+
ifFalse: require('IF_FALSE').default
|
|
40
|
+
};
|
|
41
|
+
Object.defineProperty(Imported, "load", {
|
|
42
|
+
get: () => globalThis.__MCOND && globalThis.__MCOND('CONDITION') ? Imported.ifTrue : Imported.ifFalse
|
|
43
|
+
});
|
|
44
|
+
console.log(Imported.load, Imported.load.someProperty);`,
|
|
45
|
+
);
|
|
46
|
+
});
|
|
47
|
+
|
|
48
|
+
it('should transform shadowed identifiers correctly', () => {
|
|
49
|
+
const input = `const JqlUtils = importCond('CONDITION', 'IF_TRUE', 'IF_FALSE');
|
|
50
|
+
|
|
51
|
+
const myObject = {
|
|
52
|
+
JqlUtils: 'some-value',
|
|
53
|
+
other: JqlUtils,
|
|
54
|
+
};
|
|
55
|
+
|
|
56
|
+
const jsx = <MyComponent JqlUtils={JqlUtils} />;`;
|
|
57
|
+
const result = babel.transformSync(input, {
|
|
58
|
+
configFile: false,
|
|
59
|
+
presets: [],
|
|
60
|
+
plugins: [[plugin, {node: true}]],
|
|
61
|
+
parserOpts: {
|
|
62
|
+
plugins: ['jsx'],
|
|
63
|
+
},
|
|
64
|
+
});
|
|
65
|
+
|
|
66
|
+
assert.equal(
|
|
67
|
+
result?.code,
|
|
68
|
+
`const JqlUtils = {
|
|
69
|
+
ifTrue: require('IF_TRUE').default,
|
|
70
|
+
ifFalse: require('IF_FALSE').default
|
|
71
|
+
};
|
|
72
|
+
Object.defineProperty(JqlUtils, "load", {
|
|
73
|
+
get: () => globalThis.__MCOND && globalThis.__MCOND('CONDITION') ? JqlUtils.ifTrue : JqlUtils.ifFalse
|
|
74
|
+
});
|
|
75
|
+
const myObject = {
|
|
76
|
+
JqlUtils: 'some-value',
|
|
77
|
+
other: JqlUtils.load
|
|
78
|
+
};
|
|
79
|
+
const jsx = <MyComponent JqlUtils={JqlUtils.load} />;`,
|
|
80
|
+
);
|
|
81
|
+
});
|
|
82
|
+
|
|
83
|
+
it('should transform importCond components from legacy runtime transformed JSX', () => {
|
|
84
|
+
const input = `const JqlUtils = importCond('CONDITION', 'IF_TRUE', 'IF_FALSE');
|
|
85
|
+
|
|
86
|
+
const jsx = React.createElement(JqlUtils, null);`;
|
|
87
|
+
const result = babel.transformSync(input, {
|
|
88
|
+
configFile: false,
|
|
89
|
+
presets: [],
|
|
90
|
+
plugins: [[plugin, {node: true}]],
|
|
91
|
+
parserOpts: {
|
|
92
|
+
plugins: ['jsx'],
|
|
93
|
+
},
|
|
94
|
+
});
|
|
95
|
+
|
|
96
|
+
assert.equal(
|
|
97
|
+
result?.code,
|
|
98
|
+
`const JqlUtils = {
|
|
99
|
+
ifTrue: require('IF_TRUE').default,
|
|
100
|
+
ifFalse: require('IF_FALSE').default
|
|
101
|
+
};
|
|
102
|
+
Object.defineProperty(JqlUtils, "load", {
|
|
103
|
+
get: () => globalThis.__MCOND && globalThis.__MCOND('CONDITION') ? JqlUtils.ifTrue : JqlUtils.ifFalse
|
|
104
|
+
});
|
|
105
|
+
const jsx = React.createElement(JqlUtils.load, null);`,
|
|
106
|
+
);
|
|
107
|
+
});
|
|
108
|
+
|
|
109
|
+
it('should transform importCond components from preserved JSX', () => {
|
|
110
|
+
const input = `const JqlUtils = importCond('CONDITION', 'IF_TRUE', 'IF_FALSE');
|
|
111
|
+
|
|
112
|
+
const jsx = <JqlUtils />;`;
|
|
113
|
+
const result = babel.transformSync(input, {
|
|
114
|
+
configFile: false,
|
|
115
|
+
presets: [],
|
|
116
|
+
plugins: [[plugin, {node: true}]],
|
|
117
|
+
parserOpts: {
|
|
118
|
+
plugins: ['jsx'],
|
|
119
|
+
},
|
|
120
|
+
});
|
|
121
|
+
|
|
122
|
+
assert.equal(
|
|
123
|
+
result?.code,
|
|
124
|
+
`const JqlUtils = {
|
|
125
|
+
ifTrue: require('IF_TRUE').default,
|
|
126
|
+
ifFalse: require('IF_FALSE').default
|
|
127
|
+
};
|
|
128
|
+
Object.defineProperty(JqlUtils, "load", {
|
|
129
|
+
get: () => globalThis.__MCOND && globalThis.__MCOND('CONDITION') ? JqlUtils.ifTrue : JqlUtils.ifFalse
|
|
130
|
+
});
|
|
131
|
+
const jsx = <JqlUtils.load />;`,
|
|
132
|
+
);
|
|
133
|
+
});
|
|
134
|
+
|
|
135
|
+
it('should not transform type references', () => {
|
|
136
|
+
const input = `const JqlUtils = importCond<typeof import('IF_TRUE'),typeof import('IF_FALSE')>('CONDITION', 'IF_TRUE', 'IF_FALSE');
|
|
137
|
+
|
|
138
|
+
type Props = ComponentProps<typeof JqlUtils>;
|
|
139
|
+
const jsx = <JqlUtils />;`;
|
|
140
|
+
const result = babel.transformSync(input, {
|
|
141
|
+
configFile: false,
|
|
142
|
+
presets: [],
|
|
143
|
+
plugins: [[plugin, {node: true}]],
|
|
144
|
+
parserOpts: {
|
|
145
|
+
plugins: ['jsx', 'typescript'],
|
|
146
|
+
},
|
|
147
|
+
});
|
|
148
|
+
|
|
149
|
+
assert.equal(
|
|
150
|
+
result?.code,
|
|
151
|
+
`const JqlUtils = {
|
|
152
|
+
ifTrue: require('IF_TRUE').default,
|
|
153
|
+
ifFalse: require('IF_FALSE').default
|
|
154
|
+
};
|
|
155
|
+
Object.defineProperty(JqlUtils, "load", {
|
|
156
|
+
get: () => globalThis.__MCOND && globalThis.__MCOND('CONDITION') ? JqlUtils.ifTrue : JqlUtils.ifFalse
|
|
157
|
+
});
|
|
158
|
+
type Props = ComponentProps<typeof JqlUtils>;
|
|
159
|
+
const jsx = <JqlUtils.load />;`,
|
|
160
|
+
);
|
|
161
|
+
});
|
|
162
|
+
});
|
package/tsconfig.json
CHANGED
|
@@ -1,12 +1,7 @@
|
|
|
1
1
|
{
|
|
2
|
+
"extends": "../../../tsconfig.base.json",
|
|
3
|
+
"include": ["src"],
|
|
2
4
|
"compilerOptions": {
|
|
3
|
-
"
|
|
4
|
-
|
|
5
|
-
"esModuleInterop": true,
|
|
6
|
-
"forceConsistentCasingInFileNames": true,
|
|
7
|
-
"strict": true,
|
|
8
|
-
"skipLibCheck": true
|
|
9
|
-
},
|
|
10
|
-
"include": ["src/*"],
|
|
11
|
-
"exclude": ["node_modules"]
|
|
5
|
+
"composite": true
|
|
6
|
+
}
|
|
12
7
|
}
|