@litsx/babel-preset-litsx 0.3.0 → 0.4.0

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.
@@ -8,82 +8,11 @@ function createThisMemberExpression(propName) {
8
8
  return t.memberExpression(t.thisExpression(), t.identifier(propName));
9
9
  }
10
10
 
11
- function getBoundPropName(bindingInfo) {
12
- if (typeof bindingInfo === "string") {
13
- return bindingInfo;
14
- }
15
-
16
- if (bindingInfo && typeof bindingInfo === "object") {
17
- return bindingInfo.bindKey ?? null;
18
- }
19
-
20
- return null;
21
- }
22
-
23
- function isPropBackedCallee(node, localNames) {
24
- if (t.isIdentifier(node)) {
25
- return localNames.includes(node.name);
26
- }
27
-
28
- if (
29
- t.isMemberExpression(node) &&
30
- !node.computed &&
31
- t.isIdentifier(node.object)
32
- ) {
33
- return localNames.includes(node.object.name) && node.object.name === "props";
34
- }
35
-
36
- return false;
37
- }
38
-
39
- function getPropBackedCalleeReplacement(node, bindings) {
40
- if (t.isIdentifier(node)) {
41
- const propName = getBoundPropName(bindings.get(node.name));
42
- return propName ? createThisMemberExpression(propName) : node;
43
- }
44
-
45
- if (
46
- t.isMemberExpression(node) &&
47
- !node.computed &&
48
- t.isIdentifier(node.object)
49
- ) {
50
- const propName = getBoundPropName(bindings.get(node.object.name));
51
- if (!propName) {
52
- return node;
53
- }
54
-
55
- return t.memberExpression(
56
- createThisMemberExpression(propName),
57
- t.cloneNode(node.property),
58
- false
59
- );
60
- }
61
-
62
- return node;
63
- }
64
-
65
11
  export function transformJSXExpressions(jsxPath, bindings, state = null) {
66
12
  const localNames = Array.from(bindings.keys());
67
13
 
68
14
  jsxPath.traverse({
69
15
  JSXExpressionContainer(expressionPath) {
70
- if (t.isCallExpression(expressionPath.node.expression)) {
71
- const { callee, arguments: args } = expressionPath.node.expression;
72
- if (isPropBackedCallee(callee, localNames)) {
73
- if (state) {
74
- state.__litsxNeedsRendererCallImport = true;
75
- }
76
- expressionPath.node.expression = t.callExpression(
77
- t.identifier("renderRendererCall"),
78
- [
79
- getPropBackedCalleeReplacement(callee, bindings),
80
- ...args,
81
- ]
82
- );
83
- return;
84
- }
85
- }
86
-
87
16
  if (t.isIdentifier(expressionPath.node.expression)) {
88
17
  const name = expressionPath.node.expression.name;
89
18
  if (localNames.includes(name)) {
@@ -0,0 +1,113 @@
1
+ import {
2
+ createComponentInstanceRefSyncStatement,
3
+ hasRefProp,
4
+ lowerForwardedElementRefs,
5
+ } from "./transform-litsx-refs.js";
6
+ import {
7
+ replaceParamReferences,
8
+ transformJSXExpressions,
9
+ } from "./transform-litsx-param-rewrites.js";
10
+ import { transformJSXRendererCalls } from "./transform-litsx-renderer-calls.js";
11
+
12
+ let t;
13
+
14
+ export function setRenderBodyBabelTypes(nextTypes) {
15
+ t = nextTypes;
16
+ }
17
+
18
+ function createThisMemberExpression(propName) {
19
+ return t.memberExpression(t.thisExpression(), t.identifier(propName));
20
+ }
21
+
22
+ function createNestedInitializerStatement(pattern, root, defaultValue) {
23
+ const rootAccess = createThisMemberExpression(root);
24
+ let sourceExpression = rootAccess;
25
+
26
+ if (defaultValue) {
27
+ sourceExpression = t.logicalExpression(
28
+ "??",
29
+ t.cloneNode(rootAccess),
30
+ t.cloneNode(defaultValue)
31
+ );
32
+ }
33
+
34
+ return t.variableDeclaration("const", [
35
+ t.variableDeclarator(t.cloneNode(pattern), sourceExpression),
36
+ ]);
37
+ }
38
+
39
+ function collectReturnStatement(functionPath, bindings, state) {
40
+ let returnStatement = null;
41
+
42
+ functionPath.traverse({
43
+ ReturnStatement(returnPath) {
44
+ if (t.isJSXElement(returnPath.node.argument)) {
45
+ returnStatement = returnPath.node;
46
+ transformJSXRendererCalls(returnPath, bindings, state);
47
+ transformJSXExpressions(returnPath, bindings, state);
48
+ }
49
+ },
50
+ });
51
+
52
+ return returnStatement;
53
+ }
54
+
55
+ export function prepareComponentRender(functionPath, node, propertyNames, bindings, nestedInitializers, options = {}) {
56
+ const returnStatement = collectReturnStatement(
57
+ functionPath,
58
+ bindings,
59
+ options.state ?? null
60
+ );
61
+
62
+ if (!returnStatement) {
63
+ return null;
64
+ }
65
+
66
+ const capturedPropAliasStatements = replaceParamReferences(
67
+ functionPath,
68
+ bindings,
69
+ propertyNames
70
+ );
71
+ const prefixStatements = [];
72
+
73
+ const forwardRefOptions = options.forwardRef || null;
74
+ const resolvedRefPropName =
75
+ forwardRefOptions?.propName ||
76
+ (propertyNames.has("ref") || hasRefProp(functionPath) ? "ref" : null);
77
+ let needsCallbackRef = false;
78
+
79
+ if (resolvedRefPropName) {
80
+ prefixStatements.push(
81
+ ...lowerForwardedElementRefs(functionPath, resolvedRefPropName)
82
+ );
83
+ needsCallbackRef =
84
+ prefixStatements.some(
85
+ (statement) =>
86
+ t.isExpressionStatement(statement) &&
87
+ t.isCallExpression(statement.expression) &&
88
+ t.isIdentifier(statement.expression.callee, { name: "useCallbackRef" })
89
+ ) || needsCallbackRef;
90
+ }
91
+
92
+ if (resolvedRefPropName && !forwardRefOptions) {
93
+ prefixStatements.push(createComponentInstanceRefSyncStatement());
94
+ needsCallbackRef = true;
95
+ }
96
+
97
+ if (capturedPropAliasStatements.length > 0) {
98
+ prefixStatements.push(...capturedPropAliasStatements);
99
+ }
100
+
101
+ if (nestedInitializers.length > 0) {
102
+ const initializerStatements = nestedInitializers.map(({ pattern, root, defaultValue }) =>
103
+ createNestedInitializerStatement(pattern, root, defaultValue)
104
+ );
105
+ prefixStatements.push(...initializerStatements);
106
+ }
107
+
108
+ return {
109
+ needsCallbackRef,
110
+ prefixStatements,
111
+ returnStatement,
112
+ };
113
+ }
@@ -0,0 +1,92 @@
1
+ let t;
2
+
3
+ export function setRendererCallsBabelTypes(nextTypes) {
4
+ t = nextTypes;
5
+ }
6
+
7
+ function createThisMemberExpression(propName) {
8
+ return t.memberExpression(t.thisExpression(), t.identifier(propName));
9
+ }
10
+
11
+ function getBoundPropName(bindingInfo) {
12
+ if (typeof bindingInfo === "string") {
13
+ return bindingInfo;
14
+ }
15
+
16
+ if (bindingInfo && typeof bindingInfo === "object") {
17
+ return bindingInfo.bindKey ?? null;
18
+ }
19
+
20
+ return null;
21
+ }
22
+
23
+ function isPropBackedCallee(node, localNames) {
24
+ if (t.isIdentifier(node)) {
25
+ return localNames.includes(node.name);
26
+ }
27
+
28
+ if (
29
+ t.isMemberExpression(node) &&
30
+ !node.computed &&
31
+ t.isIdentifier(node.object)
32
+ ) {
33
+ return localNames.includes(node.object.name) && node.object.name === "props";
34
+ }
35
+
36
+ return false;
37
+ }
38
+
39
+ function getPropBackedCalleeReplacement(node, bindings) {
40
+ if (t.isIdentifier(node)) {
41
+ const propName = getBoundPropName(bindings.get(node.name));
42
+ return propName ? createThisMemberExpression(propName) : node;
43
+ }
44
+
45
+ if (
46
+ t.isMemberExpression(node) &&
47
+ !node.computed &&
48
+ t.isIdentifier(node.object)
49
+ ) {
50
+ const propName = getBoundPropName(bindings.get(node.object.name));
51
+ if (!propName) {
52
+ return node;
53
+ }
54
+
55
+ return t.memberExpression(
56
+ createThisMemberExpression(propName),
57
+ t.cloneNode(node.property),
58
+ false
59
+ );
60
+ }
61
+
62
+ return node;
63
+ }
64
+
65
+ export function transformJSXRendererCalls(jsxPath, bindings, state = null) {
66
+ const localNames = Array.from(bindings.keys());
67
+
68
+ jsxPath.traverse({
69
+ JSXExpressionContainer(expressionPath) {
70
+ if (!t.isCallExpression(expressionPath.node.expression)) {
71
+ return;
72
+ }
73
+
74
+ const { callee, arguments: args } = expressionPath.node.expression;
75
+ if (!isPropBackedCallee(callee, localNames)) {
76
+ return;
77
+ }
78
+
79
+ if (state) {
80
+ state.__litsxNeedsRendererCallImport = true;
81
+ }
82
+
83
+ expressionPath.node.expression = t.callExpression(
84
+ t.identifier("renderRendererCall"),
85
+ [
86
+ getPropBackedCalleeReplacement(callee, bindings),
87
+ ...args,
88
+ ]
89
+ );
90
+ },
91
+ });
92
+ }
@@ -1,5 +1,6 @@
1
1
  import jsxSyntaxPlugin from "@babel/plugin-syntax-jsx";
2
2
  import { decodeVirtualAttributeName } from "@litsx/jsx-authoring";
3
+ import { importedBindingNeedsRendererContext } from "./transform-litsx-element-candidates.js";
3
4
 
4
5
  let t;
5
6
 
@@ -238,7 +239,7 @@ function expressionNeedsRendererContext(node, scope, seenBindings = new Set()) {
238
239
  return false;
239
240
  }
240
241
 
241
- function isBindableFunctionReference(expressionPath) {
242
+ function isBindableFunctionReference(expressionPath, options = {}) {
242
243
  const expression = unwrapExpression(expressionPath.node);
243
244
  if (
244
245
  t.isArrowFunctionExpression(expression) ||
@@ -251,7 +252,12 @@ function isBindableFunctionReference(expressionPath) {
251
252
  const binding = expressionPath.scope.getBinding(expression.name);
252
253
  const functionNode = getFunctionNodeFromBinding(binding);
253
254
  if (!functionNode) {
254
- return false;
255
+ const programPath = expressionPath.findParent((entry) => entry.isProgram?.());
256
+ return importedBindingNeedsRendererContext(
257
+ programPath,
258
+ expression.name,
259
+ options
260
+ );
255
261
  }
256
262
  return functionBodyNeedsRendererContext(functionNode.body, binding.path.scope, new Set([binding]));
257
263
  }
@@ -259,7 +265,7 @@ function isBindableFunctionReference(expressionPath) {
259
265
  return false;
260
266
  }
261
267
 
262
- function shouldBindRendererContext(attributePath, rawName, expressionPath) {
268
+ function shouldBindRendererContext(attributePath, rawName, expressionPath, options = {}) {
263
269
  if (typeof rawName !== "string" || rawName[0] !== ".") {
264
270
  return false;
265
271
  }
@@ -274,7 +280,7 @@ function shouldBindRendererContext(attributePath, rawName, expressionPath) {
274
280
  return false;
275
281
  }
276
282
 
277
- return isBindableFunctionReference(expressionPath);
283
+ return isBindableFunctionReference(expressionPath, options);
278
284
  }
279
285
 
280
286
  function ensureRendererBindingImport(programPath) {
@@ -342,7 +348,9 @@ export default function transformLitsxRendererProps(api) {
342
348
  return;
343
349
  }
344
350
 
345
- if (!shouldBindRendererContext(path, rawName, expressionPath)) {
351
+ if (!shouldBindRendererContext(path, rawName, expressionPath, {
352
+ filename: state.file?.opts?.filename || "",
353
+ })) {
346
354
  return;
347
355
  }
348
356
 
package/src/pipeline.js CHANGED
@@ -11,6 +11,7 @@ const NATIVE_TRANSFORM_OPTION_KEYS = [
11
11
  "inMemoryFiles",
12
12
  "typescriptSession",
13
13
  "suppressNativeClassNameWarning",
14
+ "__litsxCompilationSession",
14
15
  ];
15
16
 
16
17
  const HOOK_FEATURE_PATTERN = /\b(?:useOnConnect|useAfterUpdate|useOnCommit|useMemoValue|useStableCallback|useEvent|useEmit|usePrevious|useReducedState|useState|useControlledState|useAsyncState|useOptimistic|useExpose|useExternalStore|useHost|useHostContent|useSlot|useTextContent|useTransition|useDeferredValue|useStyle|useRef|useCallbackRef)\b/;