@atlaspack/babel-plugin-transform-contextual-imports 2.14.1-canary.40 → 2.14.1-canary.401

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/src/index.ts CHANGED
@@ -1,9 +1,8 @@
1
- import type {PluginObj, NodePath, types as BabelTypes} from '@babel/core';
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
- /** @deprecated Statement types didn't work so using any */
15
- importNodes?: any[];
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 identifier name to set so we can mutate all import usages in the exit pass
311
- state.conditionalImportIdentifiers?.add(importId.name);
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
- Identifier: {
200
+ ReferencedIdentifier: {
318
201
  exit(path, state) {
319
- const identifier = state.conditionalImportIdentifiers?.has(
320
- path.node.name,
321
- );
322
- if (identifier && !state.visitedIdentifiers?.has(path.node)) {
323
- // Add load property to the import usage
324
- const newIdentifer = t.identifier(path.node.name);
325
- path.replaceWith(
326
- t.memberExpression(newIdentifer, t.identifier('load')),
327
- );
328
- state.visitedIdentifiers?.add(newIdentifer);
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.conditionalImportIdentifiers = new Set();
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
- "target": "es2016",
4
- "module": "commonjs",
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
  }