sinwan-compiler 0.2.3 → 0.2.5
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/README.md +37 -14
- package/dist/analyze.d.ts +0 -1
- package/dist/{chunk-w580fdrf.js → chunk-m4rk009c.js} +169 -219
- package/dist/cli.d.ts +2 -0
- package/dist/cli.js +5 -3
- package/dist/index.js +51 -40
- package/dist/reactive-wrap.d.ts +4 -0
- package/dist/transform.d.ts +3 -3
- package/docs/README.md +38 -19
- package/docs/analyzer.md +63 -52
- package/docs/api.md +77 -26
- package/docs/architecture.md +52 -49
- package/docs/cli.md +24 -17
- package/docs/plugins.md +35 -32
- package/docs/transform.md +109 -34
- package/package.json +2 -2
package/docs/architecture.md
CHANGED
|
@@ -1,75 +1,78 @@
|
|
|
1
1
|
# Architecture
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
This page is for people changing the compiler. App authors can stay on [Transform](transform.md) and [Plugins](plugins.md).
|
|
4
|
+
|
|
5
|
+
## Layout
|
|
4
6
|
|
|
5
7
|
```
|
|
6
8
|
src/
|
|
7
|
-
index.ts
|
|
8
|
-
|
|
9
|
-
reactive-wrap.ts
|
|
10
|
-
|
|
11
|
-
|
|
9
|
+
index.ts Public exports
|
|
10
|
+
auto-cc.ts Exported function → cc(...)
|
|
11
|
+
reactive-wrap.ts Imports, scopes, call graph, wrap
|
|
12
|
+
transform.ts Hoist + transformJSX
|
|
13
|
+
analyze.ts Project scan, resolution, AnalyzerCache
|
|
14
|
+
cli.ts analyze CLI
|
|
15
|
+
exports.ts collectExportedComponents
|
|
12
16
|
```
|
|
13
17
|
|
|
14
18
|
## Data flow
|
|
15
19
|
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
+
```text
|
|
21
|
+
transformJSX
|
|
22
|
+
autoWrapComponents
|
|
23
|
+
wrapReactiveExpressions ← analyze / analyzeMetadata / resolveImport
|
|
24
|
+
hoist JSXElement roots ← _$createTemplate + slots
|
|
25
|
+
|
|
26
|
+
analyze / AnalyzerCache.update
|
|
27
|
+
autoWrapComponents
|
|
28
|
+
analyzeModule ← scopes + local + imported call sites
|
|
29
|
+
buildProjectReactiveProps
|
|
30
|
+
propagateReactiveProps
|
|
31
|
+
```
|
|
20
32
|
|
|
21
|
-
|
|
33
|
+
Dev plugins call `cache.update` on each file, then pass `cache.reactiveProps` into `transformJSX` as `analyzeMetadata`. Production writes the same map to JSON (`analyze`) and the plugin sets `analyze: path`.
|
|
22
34
|
|
|
23
|
-
|
|
35
|
+
## Call graph
|
|
24
36
|
|
|
25
|
-
|
|
26
|
-
{
|
|
27
|
-
callee: t.Function;
|
|
28
|
-
props: { name: string; value: t.Expression }[];
|
|
29
|
-
spreads: t.Expression[];
|
|
30
|
-
}
|
|
31
|
-
```
|
|
37
|
+
A **local** site is `<Child />` where `Child` is `cc(...)` in the same file.
|
|
32
38
|
|
|
33
|
-
`spreads`
|
|
39
|
+
An **imported** site stores `{ caller, source, name, props, spreads }`. The project pass resolves `source` to a file and attaches the site to that file’s export (or `"default"`).
|
|
34
40
|
|
|
35
|
-
|
|
41
|
+
`spreads` are analyzed when the expression is an object literal or a local `const props = { … }` in the caller. Otherwise every known callee prop is marked reactive.
|
|
36
42
|
|
|
37
|
-
|
|
43
|
+
## Propagation
|
|
38
44
|
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
- local scopes
|
|
44
|
-
- local call graph
|
|
45
|
-
- imported call sites
|
|
45
|
+
1. Seed callee sets from values that are already reactive in the caller’s local scope.
|
|
46
|
+
2. Worklist over all component functions.
|
|
47
|
+
3. For each site, add prop names whose values are reactive in the caller’s **full** scope (local bindings + that function’s own reactive props).
|
|
48
|
+
4. Repeat until sets stop growing.
|
|
46
49
|
|
|
47
|
-
|
|
50
|
+
Cycles terminate because sets only grow.
|
|
48
51
|
|
|
49
|
-
|
|
50
|
-
- Maintains `reactiveProps: Map<filePath, Map<exportName, Set<propName>>>`.
|
|
51
|
-
- Maintains a reverse importer index for targeted incremental propagation.
|
|
52
|
-
- Serializes to JSON for persistence across process restarts.
|
|
52
|
+
## Import resolution
|
|
53
53
|
|
|
54
|
-
|
|
54
|
+
`createResolver` order:
|
|
55
55
|
|
|
56
|
-
1.
|
|
57
|
-
2.
|
|
58
|
-
3.
|
|
59
|
-
4.
|
|
60
|
-
5. If a callee's reactive set grows, add the callee to the worklist and repeat until stable.
|
|
56
|
+
1. `tsconfig` `paths`
|
|
57
|
+
2. `bunfig.toml` aliases
|
|
58
|
+
3. Workspace package name + subpath
|
|
59
|
+
4. Relative path + extensions / `index.*`
|
|
61
60
|
|
|
62
|
-
|
|
61
|
+
`AnalyzerCache` wraps that with an in-memory lookup so HMR can resolve `./Child` to a module that exists only in the cache.
|
|
62
|
+
|
|
63
|
+
## Cache persistence
|
|
64
|
+
|
|
65
|
+
Serialized modules store function **start/end** offsets. Restore re-parses the file and remaps those spans. If a span is gone (the file was edited), `restoreModule` returns `null` and that entry is dropped. The next `update` fills it in.
|
|
63
66
|
|
|
64
|
-
|
|
67
|
+
`remove(path)`:
|
|
65
68
|
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
69
|
+
1. Collect importers of `path` and modules `path` imported
|
|
70
|
+
2. Delete the module and importer index entries
|
|
71
|
+
3. `recomputeFor` that set
|
|
72
|
+
4. `save()` if `cachePath` is set
|
|
70
73
|
|
|
71
|
-
|
|
74
|
+
That second set matters: deleting a parent must let the child’s props become static again.
|
|
72
75
|
|
|
73
|
-
##
|
|
76
|
+
## Template protocol
|
|
74
77
|
|
|
75
|
-
|
|
78
|
+
`COMPILER_TEMPLATE_SLOT_PROTOCOL` in `transform.ts` is duplicated from the runtime (`sinwan` template protocol). A drift test in the `sinwan` package keeps the two in sync. Slot types: `child`, `attr`, `event`, `ref`.
|
package/docs/cli.md
CHANGED
|
@@ -1,46 +1,53 @@
|
|
|
1
1
|
# CLI
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
Production analysis is a single command. Dev servers do not need it; plugins keep a live cache.
|
|
4
4
|
|
|
5
|
-
|
|
5
|
+
The published binary name is `sinwan` (see `package.json` `"bin"`). After install you can also run the package name:
|
|
6
6
|
|
|
7
7
|
```bash
|
|
8
8
|
bunx sinwan-compiler analyze [root] [outFile] [options]
|
|
9
|
+
# same after a local install:
|
|
10
|
+
sinwan analyze [root] [outFile] [options]
|
|
9
11
|
```
|
|
10
12
|
|
|
11
|
-
|
|
12
|
-
|
|
13
|
+
| Argument | Default |
|
|
14
|
+
| -------- | ------- |
|
|
15
|
+
| `root` | Current working directory |
|
|
16
|
+
| `outFile` | `<root>/.sinwan/reactive-props.json` |
|
|
13
17
|
|
|
14
|
-
|
|
18
|
+
| Flag | Meaning |
|
|
19
|
+
| ---- | ------- |
|
|
20
|
+
| `--tsconfig`, `-t` | `tsconfig.json` for `paths` |
|
|
21
|
+
| `--bunfig`, `-b` | `bunfig.toml` for Bun aliases |
|
|
22
|
+
| `--workspaces`, `-w` | `package.json`, `pnpm-workspace.yaml`, or a package directory |
|
|
15
23
|
|
|
16
|
-
|
|
17
|
-
| -------------------- | --------------------------------------------------------------------------------- |
|
|
18
|
-
| `--tsconfig`, `-t` | Path to `tsconfig.json` for path alias resolution. |
|
|
19
|
-
| `--bunfig`, `-b` | Path to `bunfig.toml` for Bun alias resolution. |
|
|
20
|
-
| `--workspaces`, `-w` | Path to a `package.json` or `pnpm-workspace.yaml` to discover workspace packages. |
|
|
24
|
+
Anything other than `analyze` prints a short usage line and exits with code `1`.
|
|
21
25
|
|
|
22
26
|
## Example
|
|
23
27
|
|
|
24
28
|
```bash
|
|
25
|
-
bunx sinwan-compiler analyze ./src ./.sinwan/reactive-props.json
|
|
29
|
+
bunx sinwan-compiler analyze ./src ./.sinwan/reactive-props.json \
|
|
30
|
+
--tsconfig ./tsconfig.json \
|
|
31
|
+
--workspaces ../../package.json
|
|
26
32
|
```
|
|
27
33
|
|
|
28
|
-
## Output
|
|
34
|
+
## Output
|
|
29
35
|
|
|
30
|
-
|
|
36
|
+
JSON: absolute file path → export name → reactive prop names.
|
|
31
37
|
|
|
32
38
|
```json
|
|
33
39
|
{
|
|
34
40
|
"/project/src/Child.tsx": {
|
|
35
|
-
"Child": ["title"]
|
|
41
|
+
"Child": ["title"],
|
|
42
|
+
"default": ["label"]
|
|
36
43
|
}
|
|
37
44
|
}
|
|
38
45
|
```
|
|
39
46
|
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
Pass the generated JSON to the Bun or Vite plugin via the `analyze` option:
|
|
47
|
+
Pass that file to a plugin:
|
|
43
48
|
|
|
44
49
|
```ts
|
|
45
50
|
sinwan({ analyze: "./.sinwan/reactive-props.json" });
|
|
46
51
|
```
|
|
52
|
+
|
|
53
|
+
Programmatic equivalent: `analyze({ root, outFile, tsConfigPath, bunfigPath, workspaces })` from [API](api.md).
|
package/docs/plugins.md
CHANGED
|
@@ -1,60 +1,63 @@
|
|
|
1
|
-
# Bun and Vite
|
|
1
|
+
# Using the compiler from Bun and Vite
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
App projects should depend on **`bun-plugin-sinwan`** or **`vite-plugin-sinwan`**. Those packages import `sinwan-compiler` and pass the right options. You do not add `sinwan-compiler` unless you customize the pipeline.
|
|
4
4
|
|
|
5
|
-
## Bun
|
|
5
|
+
## Bun
|
|
6
6
|
|
|
7
7
|
```ts
|
|
8
8
|
import { sinwan } from "bun-plugin-sinwan";
|
|
9
9
|
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
workspaces: "../../package.json",
|
|
20
|
-
cachePath: "./.sinwan/cache.json",
|
|
21
|
-
},
|
|
22
|
-
}),
|
|
23
|
-
],
|
|
10
|
+
export default sinwan({
|
|
11
|
+
hoist: true,
|
|
12
|
+
cache: {
|
|
13
|
+
root: process.cwd(),
|
|
14
|
+
tsConfigPath: "./tsconfig.json",
|
|
15
|
+
bunfigPath: "./bunfig.toml",
|
|
16
|
+
workspaces: "../../package.json",
|
|
17
|
+
cachePath: "./.sinwan/cache.json",
|
|
18
|
+
},
|
|
24
19
|
});
|
|
25
20
|
```
|
|
26
21
|
|
|
27
|
-
|
|
22
|
+
`bunfig.toml` `[serve.static] plugins` can only load a **ready-made** plugin object. Scaffolded apps keep a small `sinwan-plugin.ts` that calls `sinwan({ cache: … })` and export that object.
|
|
23
|
+
|
|
24
|
+
The Bun plugin pins `sinwan` / `sinwan/*` to the **app** `node_modules` so a `file:` or `link:` package (for example `sinwan-router`) does not load a second runtime.
|
|
25
|
+
|
|
26
|
+
## Vite
|
|
28
27
|
|
|
29
28
|
```ts
|
|
30
29
|
import { sinwan } from "vite-plugin-sinwan";
|
|
30
|
+
import { defineConfig } from "vite";
|
|
31
31
|
|
|
32
|
-
export default {
|
|
32
|
+
export default defineConfig({
|
|
33
33
|
plugins: [
|
|
34
34
|
sinwan({
|
|
35
35
|
hoist: true,
|
|
36
36
|
cache: {
|
|
37
37
|
tsConfigPath: "./tsconfig.json",
|
|
38
|
-
bunfigPath: "./bunfig.toml",
|
|
39
|
-
workspaces: "../../package.json",
|
|
40
38
|
cachePath: "./.sinwan/cache.json",
|
|
41
39
|
},
|
|
42
40
|
}),
|
|
43
41
|
],
|
|
44
|
-
};
|
|
42
|
+
});
|
|
45
43
|
```
|
|
46
44
|
|
|
47
|
-
|
|
45
|
+
The Vite plugin sets `resolve.dedupe: ["sinwan"]` for the same duplicate-runtime problem.
|
|
48
46
|
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
|
52
|
-
|
|
|
53
|
-
| `
|
|
54
|
-
| `
|
|
55
|
-
| `
|
|
47
|
+
## Shared plugin options
|
|
48
|
+
|
|
49
|
+
| Option | Default | Role |
|
|
50
|
+
| ------ | ------- | ---- |
|
|
51
|
+
| `hoist` | `true` | Template hoisting |
|
|
52
|
+
| `explicitBindings` | `false` | `_$bind*` helpers instead of bare `() =>` |
|
|
53
|
+
| `analyze` | unset | Path to CLI JSON (production) |
|
|
54
|
+
| `cache` | `true` | Incremental analyzer in dev. `false` turns it off. An object sets `root`, `tsConfigPath`, `bunfigPath`, `workspaces`, `cachePath` |
|
|
55
|
+
| `fastRefresh` | `true` (Vite) | In-place component HMR in `vite serve` only |
|
|
56
56
|
|
|
57
57
|
## Dev vs production
|
|
58
58
|
|
|
59
|
-
|
|
60
|
-
|
|
59
|
+
**Dev** — leave `cache` on (the default). Each transformed file calls `AnalyzerCache.update`. No CLI step.
|
|
60
|
+
|
|
61
|
+
**Production** — run [the CLI](cli.md) (or `analyze()`) and set `analyze` to that JSON so every CI machine uses the same metadata.
|
|
62
|
+
|
|
63
|
+
If a file is deleted in HMR, the plugin should call `cache.remove(path)` so child components drop reactive props that only that caller needed. The compiler’s `remove` updates both importers and imported modules.
|
package/docs/transform.md
CHANGED
|
@@ -1,64 +1,139 @@
|
|
|
1
1
|
# JSX transform
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
`transformJSX(code, filename, options?)` rewrites one module so the Sinwan runtime can track reactive reads and reuse static markup.
|
|
4
4
|
|
|
5
|
-
|
|
5
|
+
You normally get this through a plugin. Call it yourself only in tests or custom tooling.
|
|
6
6
|
|
|
7
|
-
|
|
7
|
+
## Quick example
|
|
8
8
|
|
|
9
9
|
```tsx
|
|
10
|
-
|
|
10
|
+
import { cc } from "sinwan/component";
|
|
11
|
+
import { signal } from "sinwan/reactivity";
|
|
12
|
+
|
|
13
|
+
export const Counter = cc(() => {
|
|
14
|
+
const count = signal(0);
|
|
15
|
+
return <p>{count.value}</p>;
|
|
16
|
+
});
|
|
11
17
|
```
|
|
12
18
|
|
|
13
|
-
|
|
19
|
+
The compiler turns the child into a lazy getter:
|
|
14
20
|
|
|
15
21
|
```tsx
|
|
16
|
-
<p>{() =>
|
|
22
|
+
<p>{() => count.value}</p>
|
|
17
23
|
```
|
|
18
24
|
|
|
19
|
-
The runtime
|
|
25
|
+
The runtime runs that function inside an effect and updates the text when `count` changes.
|
|
20
26
|
|
|
21
|
-
##
|
|
27
|
+
## Automatic `cc()` wrapping
|
|
22
28
|
|
|
23
|
-
|
|
29
|
+
Exported functions that look like components are wrapped for you. You can write React-style components and still get a Sinwan instance and `displayName`.
|
|
24
30
|
|
|
25
|
-
|
|
26
|
-
- `signal` / `computed` from `sinwan/reactivity`
|
|
27
|
-
- `useState` from `sinwan/react-client`
|
|
31
|
+
A candidate must be:
|
|
28
32
|
|
|
29
|
-
|
|
33
|
+
- **Exported** (named or default)
|
|
34
|
+
- **Uppercase** (or an anonymous `export default function ()`)
|
|
35
|
+
- **0 or 1 parameter**
|
|
36
|
+
- **Returning JSX** in that function (not only in a nested helper)
|
|
37
|
+
- **Not already** `cc(...)` (or a local alias of `cc`)
|
|
30
38
|
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
39
|
+
```tsx
|
|
40
|
+
export function App() {
|
|
41
|
+
return <h1>Hello</h1>;
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
// becomes
|
|
45
|
+
import { cc } from "sinwan/component";
|
|
46
|
+
export const App = cc(function App() {
|
|
47
|
+
return <h1>Hello</h1>;
|
|
48
|
+
});
|
|
49
|
+
```
|
|
34
50
|
|
|
35
|
-
|
|
51
|
+
Not wrapped: lowercase helpers, generators, multi-argument functions, or functions that never return JSX. If `cc` is not imported yet, the compiler adds `import { cc } from "sinwan/component"`.
|
|
36
52
|
|
|
37
|
-
|
|
38
|
-
- Event handlers: `onClick={handleClick}`
|
|
39
|
-
- Static bindings that the analyzer proved are non-reactive
|
|
53
|
+
`cc` may be imported from `sinwan` or `sinwan/component`.
|
|
40
54
|
|
|
41
|
-
##
|
|
55
|
+
## What counts as reactive
|
|
42
56
|
|
|
43
|
-
|
|
57
|
+
Tracked imports:
|
|
44
58
|
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
59
|
+
| Module | Names |
|
|
60
|
+
| ------ | ----- |
|
|
61
|
+
| `sinwan/store` | `createMutable`, `createStore` |
|
|
62
|
+
| `sinwan/reactivity` | `signal`, `computed` |
|
|
63
|
+
| `sinwan/react` | `useState` |
|
|
64
|
+
| `sinwan/hook` | `useFetch` |
|
|
65
|
+
|
|
66
|
+
Reads the compiler wraps:
|
|
67
|
+
|
|
68
|
+
- Store / mutable fields: `state.name`, destructured `const { name } = state`
|
|
69
|
+
- Signals and computeds: `count.value`, including `count.value?.n` and `count.value!.n`
|
|
70
|
+
- `useState` getters: `count()`
|
|
71
|
+
- `useFetch` signal values: `f.data.value` (not the shell `f` or `f.data`)
|
|
72
|
+
- Calls to a **local** function whose body reads one of the above
|
|
73
|
+
|
|
74
|
+
Not wrapped:
|
|
75
|
+
|
|
76
|
+
- Literals and plain identifiers
|
|
77
|
+
- Event-handler functions (`onClick={fn}` or `onClick={() => …}`)
|
|
78
|
+
- Nested function *values* passed as props (`title={() => s.value}` stays a render prop)
|
|
79
|
+
- `useFetch` methods such as `abort` / `execute`
|
|
80
|
+
- Static values the [analyzer](analyzer.md) proved never change
|
|
81
|
+
|
|
82
|
+
## DOM vs component JSX
|
|
83
|
+
|
|
84
|
+
**Native elements** (`div`, `p`, `button`, …): wrap reactive children and attributes.
|
|
85
|
+
|
|
86
|
+
**User components** (`<Child title={…} />`): wrap a prop only if that prop is reactive for `Child` (local call graph, analyzer metadata, or a conservative fallback for exported components). Children forwarded to a user component stay unwrapped so the child can read `children` itself.
|
|
87
|
+
|
|
88
|
+
**Built-in control-flow** components wrap specific props, and they *do* wrap reactive expression children (those children render directly):
|
|
89
|
+
|
|
90
|
+
| Component | Reactive props |
|
|
91
|
+
| --------- | -------------- |
|
|
92
|
+
| `For`, `Index`, `Virtual` | `each` |
|
|
93
|
+
| `Show`, `Switch`, `Match`, `Key` | `when` |
|
|
94
|
+
| `Dynamic` | `component` |
|
|
95
|
+
| `Visible` | `when`, `style` |
|
|
96
|
+
| `Portal` | `mount` |
|
|
97
|
+
| `Activity` | `mode` |
|
|
98
|
+
|
|
99
|
+
`Suspense` is not in this list; its props are left alone.
|
|
100
|
+
|
|
101
|
+
## Template hoisting
|
|
102
|
+
|
|
103
|
+
Static native trees become a module-level template plus `_$createTemplate(tmpl, [dynamic…])`.
|
|
104
|
+
|
|
105
|
+
Slots:
|
|
106
|
+
|
|
107
|
+
| Type | When |
|
|
108
|
+
| ---- | ---- |
|
|
109
|
+
| `child` | `{expr}`, or a capitalized child component |
|
|
110
|
+
| `attr` | Dynamic attribute (not `on*`) |
|
|
111
|
+
| `event` | `onClick={…}` and other `on*` handlers |
|
|
112
|
+
| `ref` | `ref={fn}` or `ref={object}` |
|
|
113
|
+
|
|
114
|
+
Static `style` objects and strings are written into the HTML (`backgroundColor` → `background-color`). Quoted style strings that contain `${…}` log a warning in the compiler: use a template literal, `style={\`…${x}\`}`.
|
|
115
|
+
|
|
116
|
+
Hoisting is skipped (the JSX is left as JSX) when the root is a component, the tag is a member expression (`Icons.Star` as the root), or a spread is on a native element (`<div {...props}>`). In `dev: true`, skipped hoists log a warning.
|
|
117
|
+
|
|
118
|
+
Boolean attributes without a value are emitted as HTML flags: `<button disabled>`.
|
|
119
|
+
|
|
120
|
+
The slot marker format is `COMPILER_TEMPLATE_SLOT_PROTOCOL` (`s:0`, `s:1`, …). It must stay aligned with the runtime protocol in `sinwan`.
|
|
50
121
|
|
|
51
122
|
## Options
|
|
52
123
|
|
|
53
124
|
```ts
|
|
54
|
-
transformJSX(code,
|
|
55
|
-
hoist: true,
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
125
|
+
transformJSX(code, "src/App.tsx", {
|
|
126
|
+
hoist: true, // default true
|
|
127
|
+
dev: false, // warn when hoisting is skipped
|
|
128
|
+
explicitBindings: false, // emit _$bindText / _$bindAttr / …
|
|
129
|
+
analyze: "./.sinwan/reactive-props.json",
|
|
130
|
+
analyzeMetadata: cache.reactiveProps,
|
|
131
|
+
resolveImport: (source, fromFile) => absolutePathOrNull,
|
|
59
132
|
});
|
|
60
133
|
```
|
|
61
134
|
|
|
62
|
-
|
|
135
|
+
- **`analyze` / `analyzeMetadata`** — cross-file reactive props. Invalid JSON is ignored (transform still succeeds).
|
|
136
|
+
- **`resolveImport`** — maps `./Child` to an absolute file so imported components can use analyzer metadata. Without it, imported call sites are not wrapped at the parent.
|
|
137
|
+
- **`explicitBindings`** — wrap with `_$bindText` / `_$bindAttr` / `_$bindStyle` / `_$bindClass` from `sinwan/renderer` instead of a bare `() => …`.
|
|
63
138
|
|
|
64
|
-
|
|
139
|
+
Returns `{ code, map }` with a source map.
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "sinwan-compiler",
|
|
3
|
-
"version": "0.2.
|
|
3
|
+
"version": "0.2.5",
|
|
4
4
|
"description": "Shared compiler core for Sinwan plugins",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "./dist/index.js",
|
|
@@ -30,7 +30,7 @@
|
|
|
30
30
|
"sinwan": "./dist/cli.js"
|
|
31
31
|
},
|
|
32
32
|
"scripts": {
|
|
33
|
-
"test": "bun
|
|
33
|
+
"test": "bun run scripts/run-tests.ts",
|
|
34
34
|
"typecheck": "tsc --noEmit",
|
|
35
35
|
"build": "bun run build.ts",
|
|
36
36
|
"clean": "rm -rf dist",
|