sinwan-compiler 0.1.0 → 0.2.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.
@@ -23,6 +23,25 @@ var COMPONENT_FACTORY_MODULES = {
23
23
  sinwan: new Set(["cc"]),
24
24
  "sinwan/component": new Set(["cc"])
25
25
  };
26
+ var BUILTIN_REACTIVE_PROPS = new Map([
27
+ ["For", { reactiveProps: new Set(["each"]) }],
28
+ ["Show", { reactiveProps: new Set(["when"]) }],
29
+ ["Switch", { reactiveProps: new Set(["when"]) }],
30
+ ["Match", { reactiveProps: new Set(["when"]) }],
31
+ ["Index", { reactiveProps: new Set(["each"]) }],
32
+ ["Key", { reactiveProps: new Set(["when"]) }],
33
+ ["Dynamic", { reactiveProps: new Set(["component"]) }],
34
+ ["Visible", { reactiveProps: new Set(["when", "style"]) }],
35
+ ["Portal", { reactiveProps: new Set(["mount"]) }],
36
+ ["Virtual", { reactiveProps: new Set(["each"]) }],
37
+ ["Activity", { reactiveProps: new Set(["mode"]) }]
38
+ ]);
39
+ function isBuiltinReactiveProp(componentName, attrName) {
40
+ const entry = BUILTIN_REACTIVE_PROPS.get(componentName);
41
+ if (!entry)
42
+ return false;
43
+ return entry.reactiveProps.has(attrName);
44
+ }
26
45
  function trackReactiveImports(ast) {
27
46
  const names = {
28
47
  createMutable: new Set,
@@ -370,23 +389,31 @@ function containsReactiveRead(expr, scope) {
370
389
  visit(expr);
371
390
  return found;
372
391
  }
373
- function isComponentExpression(exprPath) {
392
+ function getComponentExpressionInfo(exprPath) {
374
393
  const parent = exprPath.parentPath;
375
- if (!parent)
376
- return false;
394
+ if (!parent) {
395
+ return { isComponent: false, componentName: null, attributeName: null };
396
+ }
377
397
  let elementPath = null;
398
+ let attributeName = null;
378
399
  if (parent.isJSXAttribute && parent.isJSXAttribute()) {
400
+ const attrName = parent.node.name;
401
+ if (t.isJSXIdentifier(attrName)) {
402
+ attributeName = attrName.name;
403
+ }
379
404
  const openingElementPath = parent.parentPath;
380
405
  elementPath = openingElementPath?.parentPath;
381
406
  } else if (parent.isJSXElement && parent.isJSXElement()) {
382
407
  elementPath = parent;
383
408
  }
384
409
  if (!elementPath || !elementPath.isJSXElement || !elementPath.isJSXElement()) {
385
- return false;
410
+ return { isComponent: false, componentName: null, attributeName: null };
386
411
  }
387
412
  const name = elementPath.node.openingElement.name;
388
413
  let firstChar = null;
414
+ let componentName = null;
389
415
  if (t.isJSXIdentifier(name)) {
416
+ componentName = name.name;
390
417
  firstChar = name.name?.[0] ?? null;
391
418
  } else if (t.isJSXMemberExpression(name)) {
392
419
  let current = name;
@@ -394,12 +421,29 @@ function isComponentExpression(exprPath) {
394
421
  current = current.object;
395
422
  }
396
423
  if (t.isJSXIdentifier(current)) {
424
+ componentName = current.name;
397
425
  firstChar = current.name?.[0] ?? null;
398
426
  }
399
427
  }
400
- if (!firstChar)
428
+ if (!firstChar || firstChar !== firstChar.toUpperCase() || !/[A-Z]/.test(firstChar)) {
429
+ return { isComponent: false, componentName: null, attributeName: null };
430
+ }
431
+ return { isComponent: true, componentName, attributeName };
432
+ }
433
+ function isReactiveComponentProp(info, componentNames, reactiveProps) {
434
+ if (!info.isComponent || !info.componentName || !info.attributeName) {
401
435
  return false;
402
- return firstChar === firstChar.toUpperCase() && /[A-Z]/.test(firstChar);
436
+ }
437
+ if (isBuiltinReactiveProp(info.componentName, info.attributeName)) {
438
+ return true;
439
+ }
440
+ const componentFn = componentNames.get(info.componentName);
441
+ if (!componentFn)
442
+ return false;
443
+ const props = reactiveProps.get(componentFn);
444
+ if (!props)
445
+ return false;
446
+ return props.has(info.attributeName);
403
447
  }
404
448
  function shouldWrap(expr, scope) {
405
449
  if (t.isArrowFunctionExpression(expr) || t.isFunctionExpression(expr))
@@ -720,8 +764,12 @@ function wrapReactiveExpressions(ast, options = {}) {
720
764
  path2.traverse({
721
765
  JSXExpressionContainer(exprPath) {
722
766
  const expr = exprPath.node.expression;
723
- if (isComponentExpression(exprPath))
724
- return;
767
+ const compInfo = getComponentExpressionInfo(exprPath);
768
+ if (compInfo.isComponent) {
769
+ if (!isReactiveComponentProp(compInfo, componentNames, reactiveProps)) {
770
+ return;
771
+ }
772
+ }
725
773
  if (shouldWrap(expr, scope)) {
726
774
  exprPath.replaceWith(t.jsxExpressionContainer(createBindingDescriptor(expr, exprPath, options)));
727
775
  }
package/dist/cli.js CHANGED
@@ -2,7 +2,7 @@
2
2
  // @bun
3
3
  import {
4
4
  runAnalyzeCli
5
- } from "./chunk-x75bhdr1.js";
5
+ } from "./chunk-ba0546ea.js";
6
6
  export {
7
7
  runAnalyzeCli
8
8
  };
package/dist/index.d.ts CHANGED
@@ -7,4 +7,4 @@
7
7
  export { transformJSX, type TransformOptions } from "./transform";
8
8
  export { analyze, analyzeProject, analyzeModule, loadMetadata, AnalyzerCache, type AnalyzeOptions, type ModuleAnalysis, type ProjectAnalysis, type ImportedCallSite, type ImportInfo, type WorkspacesConfig, } from "./analyze";
9
9
  export { runAnalyzeCli } from "./cli";
10
- export { wrapReactiveExpressions, trackReactiveImports, collectComponentFunctions, computeLocalScopes, collectComponentCallGraph, propagateReactiveProps, buildFullScope, containsReactiveValue, getAllPropNames, isReactiveValue, type ReactiveScope, type Binding, type CallSite, type ImportNames, } from "./reactive-wrap";
10
+ export { wrapReactiveExpressions, trackReactiveImports, collectComponentFunctions, computeLocalScopes, collectComponentCallGraph, propagateReactiveProps, buildFullScope, containsReactiveValue, getAllPropNames, isReactiveValue, type ReactiveScope, type Binding, type CallSite, type ImportNames, type ComponentExpressionInfo, } from "./reactive-wrap";
package/dist/index.js CHANGED
@@ -16,7 +16,7 @@ import {
16
16
  runAnalyzeCli,
17
17
  trackReactiveImports,
18
18
  wrapReactiveExpressions
19
- } from "./chunk-x75bhdr1.js";
19
+ } from "./chunk-ba0546ea.js";
20
20
 
21
21
  // src/transform.ts
22
22
  import { parse } from "@babel/parser";
@@ -49,6 +49,11 @@ export interface ReactiveScope {
49
49
  export declare function getAllPropNames(params: t.Node | undefined): Set<string>;
50
50
  export declare function isReactiveValue(node: t.Identifier | t.MemberExpression, scope: ReactiveScope): boolean;
51
51
  export declare function containsReactiveValue(expr: t.Expression, scope: ReactiveScope): boolean;
52
+ export interface ComponentExpressionInfo {
53
+ isComponent: boolean;
54
+ componentName: string | null;
55
+ attributeName: string | null;
56
+ }
52
57
  export declare function collectComponentFunctions(ast: t.Node, ccNames: Set<string>): {
53
58
  functions: Set<t.Function>;
54
59
  names: Map<string, t.Function>;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "sinwan-compiler",
3
- "version": "0.1.0",
3
+ "version": "0.2.0",
4
4
  "description": "Shared compiler core for Sinwan plugins",
5
5
  "type": "module",
6
6
  "main": "./dist/index.js",
@@ -30,7 +30,11 @@
30
30
  "sinwan": "./dist/cli.js"
31
31
  },
32
32
  "scripts": {
33
- "build": "bun run build.ts"
33
+ "test": "bun test",
34
+ "typecheck": "tsc --noEmit",
35
+ "build": "bun run build.ts",
36
+ "clean": "rm -rf dist",
37
+ "prepublishOnly": "bun run clean && bun run typecheck && bun run test && bun run build"
34
38
  },
35
39
  "devDependencies": {
36
40
  "@types/babel__generator": "^7.27.0",