sinwan-compiler 0.1.0 → 0.2.1

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.
@@ -17,12 +17,32 @@ var traverse = typeof _traverse === "function" ? _traverse : _traverse.default ?
17
17
  var REACTIVE_SOURCE_MODULES = {
18
18
  "sinwan/store": new Set(["createMutable", "createStore"]),
19
19
  "sinwan/reactivity": new Set(["signal", "computed"]),
20
+ "sinwan/react": new Set(["useState"]),
20
21
  "sinwan/react-client": new Set(["useState"])
21
22
  };
22
23
  var COMPONENT_FACTORY_MODULES = {
23
24
  sinwan: new Set(["cc"]),
24
25
  "sinwan/component": new Set(["cc"])
25
26
  };
27
+ var BUILTIN_REACTIVE_PROPS = new Map([
28
+ ["For", { reactiveProps: new Set(["each"]) }],
29
+ ["Show", { reactiveProps: new Set(["when"]) }],
30
+ ["Switch", { reactiveProps: new Set(["when"]) }],
31
+ ["Match", { reactiveProps: new Set(["when"]) }],
32
+ ["Index", { reactiveProps: new Set(["each"]) }],
33
+ ["Key", { reactiveProps: new Set(["when"]) }],
34
+ ["Dynamic", { reactiveProps: new Set(["component"]) }],
35
+ ["Visible", { reactiveProps: new Set(["when", "style"]) }],
36
+ ["Portal", { reactiveProps: new Set(["mount"]) }],
37
+ ["Virtual", { reactiveProps: new Set(["each"]) }],
38
+ ["Activity", { reactiveProps: new Set(["mode"]) }]
39
+ ]);
40
+ function isBuiltinReactiveProp(componentName, attrName) {
41
+ const entry = BUILTIN_REACTIVE_PROPS.get(componentName);
42
+ if (!entry)
43
+ return false;
44
+ return entry.reactiveProps.has(attrName);
45
+ }
26
46
  function trackReactiveImports(ast) {
27
47
  const names = {
28
48
  createMutable: new Set,
@@ -370,23 +390,31 @@ function containsReactiveRead(expr, scope) {
370
390
  visit(expr);
371
391
  return found;
372
392
  }
373
- function isComponentExpression(exprPath) {
393
+ function getComponentExpressionInfo(exprPath) {
374
394
  const parent = exprPath.parentPath;
375
- if (!parent)
376
- return false;
395
+ if (!parent) {
396
+ return { isComponent: false, componentName: null, attributeName: null };
397
+ }
377
398
  let elementPath = null;
399
+ let attributeName = null;
378
400
  if (parent.isJSXAttribute && parent.isJSXAttribute()) {
401
+ const attrName = parent.node.name;
402
+ if (t.isJSXIdentifier(attrName)) {
403
+ attributeName = attrName.name;
404
+ }
379
405
  const openingElementPath = parent.parentPath;
380
406
  elementPath = openingElementPath?.parentPath;
381
407
  } else if (parent.isJSXElement && parent.isJSXElement()) {
382
408
  elementPath = parent;
383
409
  }
384
410
  if (!elementPath || !elementPath.isJSXElement || !elementPath.isJSXElement()) {
385
- return false;
411
+ return { isComponent: false, componentName: null, attributeName: null };
386
412
  }
387
413
  const name = elementPath.node.openingElement.name;
388
414
  let firstChar = null;
415
+ let componentName = null;
389
416
  if (t.isJSXIdentifier(name)) {
417
+ componentName = name.name;
390
418
  firstChar = name.name?.[0] ?? null;
391
419
  } else if (t.isJSXMemberExpression(name)) {
392
420
  let current = name;
@@ -394,12 +422,29 @@ function isComponentExpression(exprPath) {
394
422
  current = current.object;
395
423
  }
396
424
  if (t.isJSXIdentifier(current)) {
425
+ componentName = current.name;
397
426
  firstChar = current.name?.[0] ?? null;
398
427
  }
399
428
  }
400
- if (!firstChar)
429
+ if (!firstChar || firstChar !== firstChar.toUpperCase() || !/[A-Z]/.test(firstChar)) {
430
+ return { isComponent: false, componentName: null, attributeName: null };
431
+ }
432
+ return { isComponent: true, componentName, attributeName };
433
+ }
434
+ function isReactiveComponentProp(info, componentNames, reactiveProps) {
435
+ if (!info.isComponent || !info.componentName || !info.attributeName) {
401
436
  return false;
402
- return firstChar === firstChar.toUpperCase() && /[A-Z]/.test(firstChar);
437
+ }
438
+ if (isBuiltinReactiveProp(info.componentName, info.attributeName)) {
439
+ return true;
440
+ }
441
+ const componentFn = componentNames.get(info.componentName);
442
+ if (!componentFn)
443
+ return false;
444
+ const props = reactiveProps.get(componentFn);
445
+ if (!props)
446
+ return false;
447
+ return props.has(info.attributeName);
403
448
  }
404
449
  function shouldWrap(expr, scope) {
405
450
  if (t.isArrowFunctionExpression(expr) || t.isFunctionExpression(expr))
@@ -720,8 +765,12 @@ function wrapReactiveExpressions(ast, options = {}) {
720
765
  path2.traverse({
721
766
  JSXExpressionContainer(exprPath) {
722
767
  const expr = exprPath.node.expression;
723
- if (isComponentExpression(exprPath))
724
- return;
768
+ const compInfo = getComponentExpressionInfo(exprPath);
769
+ if (compInfo.isComponent) {
770
+ if (!isReactiveComponentProp(compInfo, componentNames, reactiveProps)) {
771
+ return;
772
+ }
773
+ }
725
774
  if (shouldWrap(expr, scope)) {
726
775
  exprPath.replaceWith(t.jsxExpressionContainer(createBindingDescriptor(expr, exprPath, options)));
727
776
  }
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-tvnem5mj.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-tvnem5mj.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.1",
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",