@firsthandjs/compiler 0.9.0 → 0.10.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.
@@ -0,0 +1,72 @@
1
+ /**
2
+ * The four things strict reactivity refuses to compile (ADR-0019).
3
+ *
4
+ * Every rule here stops a build, so every rule here is deliberately narrow:
5
+ * a false positive costs somebody a working program, and a false negative
6
+ * costs them the warning they would have got anyway the first time they looked
7
+ * at the screen. Where a shape could go either way, it is allowed.
8
+ *
9
+ * All four are *refusals*. Nothing in this module rewrites anything.
10
+ */
11
+ import type { NodePath } from '@babel/traverse';
12
+ import * as t from '@babel/types';
13
+ /**
14
+ * Rejects a value that is read once in a setup and then kept (ADR-0019).
15
+ *
16
+ * The setup runs one time per instance, so `const total = props.total` is a
17
+ * number from the moment it is read and will not move again. Nothing throws at
18
+ * runtime — the number is simply old — which is why this exists.
19
+ *
20
+ * Deliberately narrow, because a false positive here stops a build. Only a
21
+ * declaration whose initialiser is *nothing but* a read is reported:
22
+ * identifiers, member accesses, literals and the operators between them. A
23
+ * call is never reported, which leaves `signal(props.initial)`, `peek()`,
24
+ * `computed(...)` and every handler alone — including `snapshot(...)`, the way
25
+ * to say that reading once is the point.
26
+ */
27
+ export declare function checkKeptReads(setup: NodePath<t.Function>, name: string): void;
28
+ /**
29
+ * Rejects a view chosen once, in the setup, from something that changes.
30
+ *
31
+ * ```tsx
32
+ * // Decided while the component was built, and never again:
33
+ * return open.value ? <Form /> : <Button />;
34
+ *
35
+ * // A part, re-evaluated when `open` changes:
36
+ * return <>{open.value ? <Form /> : <Button />}</>;
37
+ * ```
38
+ *
39
+ * The two look the same and behave completely differently, because a setup
40
+ * runs once per instance: the first form freezes whichever branch was true at
41
+ * setup, and nothing throws — the screen is simply wrong, later, in a way that
42
+ * reads as a broken button.
43
+ *
44
+ * Only the **returned expression** is examined, and only when a signal decides
45
+ * which view it produces. A read *inside* JSX is a part and is left alone; so
46
+ * is a return with no JSX in it at all, which is somebody's helper rather than
47
+ * a view.
48
+ *
49
+ * Deliberately narrow, because a false positive here stops a build. A `.value`
50
+ * read is reported and a **prop read is not**: a signal exists in order to
51
+ * change, while a prop may be fixed for the life of an instance — a recursive
52
+ * `<Nested depth={props.depth - 1} />` chooses its shape once on purpose, and
53
+ * a rule that could not tell the difference would refuse it.
54
+ */
55
+ export declare function checkDecidedOnce(setup: NodePath<t.Function>, name: string): void;
56
+ /**
57
+ * Rejects the two things a render function cannot do.
58
+ *
59
+ * **Nothing persistent is made in a run.** A signal, a computed, an effect or
60
+ * a resource is a thing that outlives the moment it was made; a run happens
61
+ * again, so one made there would be made again, and the one before it thrown
62
+ * away. That is not a rule about order — it is the same rule as everywhere
63
+ * else in Firsthand, said once: persistent things are made in the setup.
64
+ *
65
+ * **Repeated markup carries a key.** A site is identified by where it stands,
66
+ * which answers for markup that appears once. Markup inside a loop appears
67
+ * many times from one place, and only a key can say which of them is which.
68
+ * Without one, a run would take the rows apart and build them again — silently,
69
+ * losing whatever they held.
70
+ */
71
+ export declare function checkRunBody(setup: NodePath<t.Function>, name: string): void;
72
+ //# sourceMappingURL=strict.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"strict.d.ts","sourceRoot":"","sources":["../src/strict.ts"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;AAEH,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,iBAAiB,CAAC;AAEhD,OAAO,KAAK,CAAC,MAAM,cAAc,CAAC;AAMlC;;;;;;;;;;;;;GAaG;AACH,wBAAgB,cAAc,CAAC,KAAK,EAAE,QAAQ,CAAC,CAAC,CAAC,QAAQ,CAAC,EAAE,IAAI,EAAE,MAAM,GAAG,IAAI,CA8B9E;AAED;;;;;;;;;;;;;;;;;;;;;;;;;;GA0BG;AACH,wBAAgB,gBAAgB,CAAC,KAAK,EAAE,QAAQ,CAAC,CAAC,CAAC,QAAQ,CAAC,EAAE,IAAI,EAAE,MAAM,GAAG,IAAI,CA+BhF;AAmCD;;;;;;;;;;;;;;GAcG;AACH,wBAAgB,YAAY,CAAC,KAAK,EAAE,QAAQ,CAAC,CAAC,CAAC,QAAQ,CAAC,EAAE,IAAI,EAAE,MAAM,GAAG,IAAI,CAoB5E"}
@@ -0,0 +1,11 @@
1
+ /**
2
+ * A host element: the markup that goes into the `<template>`, and the parts
3
+ * that write into a clone of it (ADR-0009).
4
+ *
5
+ * Where that clone lives between runs — built every time, or built once and
6
+ * written into — is `site.ts`.
7
+ */
8
+ import * as t from '@babel/types';
9
+ import { type Build, type State } from './state.js';
10
+ export declare function emitElement(node: t.JSXElement, build: Build, self: t.Identifier, state: State): void;
11
+ //# sourceMappingURL=template.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"template.d.ts","sourceRoot":"","sources":["../src/template.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAEH,OAAO,KAAK,CAAC,MAAM,cAAc,CAAC;AAmBlC,OAAO,EAIL,KAAK,KAAK,EAGV,KAAK,KAAK,EACX,MAAM,YAAY,CAAC;AAEpB,wBAAgB,WAAW,CACzB,IAAI,EAAE,CAAC,CAAC,UAAU,EAClB,KAAK,EAAE,KAAK,EACZ,IAAI,EAAE,CAAC,CAAC,UAAU,EAClB,KAAK,EAAE,KAAK,GACX,IAAI,CA2BN"}
@@ -10,92 +10,20 @@
10
10
  *
11
11
  * The transform emits calls against the published protocol in
12
12
  * `@firsthandjs/dom/internal` and has no privileged access to the runtime.
13
- */
14
- import type { PluginObject } from '@babel/core';
15
- import * as t from '@babel/types';
16
- type FirsthandState = {
17
- imports: Map<string, t.Identifier>;
18
- templates: t.VariableDeclarator[];
19
- counter: number;
20
- moduleId: string;
21
- /** Module-level functions markup was compiled into, in source order. */
22
- views: Map<string, t.Identifier>;
23
- /** Every function markup was written inside, for resolving tags locally. */
24
- viewNodes: Set<t.Node>;
25
- /** Whether this module is being compiled for a server render. */
26
- ssr: boolean;
27
- /** Whether this module's output has to be able to adopt server markup. */
28
- hydratable: boolean;
29
- /** Functions that run again as a whole, and where each keeps its sites. */
30
- runs: Map<t.Node, RunContext>;
31
- };
32
- /**
33
- * What a re-running function needs in order to keep its DOM.
34
13
  *
35
- * `store` is an array declared once per instance — in the setup, which runs
36
- * once and every site inside the run takes a numbered place in it. The index
37
- * is fixed at compile time, so a site inside an `if` keeps its own place
38
- * whether or not the branch was taken: nothing depends on the order the run
39
- * happens to reach things in, which is the rule React needs for hooks and this
40
- * does not.
14
+ * | Module | What it decides |
15
+ * | --------------- | --------------------------------------------------- |
16
+ * | `plugin.ts` | which pass runs when |
17
+ * | `components.ts` | what a `component(...)` call gains |
18
+ * | `strict.ts` | what will not compile at all (ADR-0019) |
19
+ * | `props.ts` | destructured props as live reads (ADR-0005) |
20
+ * | `runs.ts` | which function a piece of markup belongs to |
21
+ * | `lists.ts` | `.map` with a key as a keyed list part |
22
+ * | `jsx.ts` | component call, server markup, or template |
23
+ * | `template.ts` | the browser's `<template>` and the parts into it |
24
+ * | `markup.ts` | the server's string and the holes in it |
25
+ * | `attributes.ts` | one attribute, inlined or written |
41
26
  */
42
- type RunContext = {
43
- /** The function whose body re-runs. Bindings inside it belong to one run. */
44
- node: t.Node;
45
- store: t.Identifier;
46
- next: () => number;
47
- };
48
- declare module '@babel/core' {
49
- interface PluginPass {
50
- firsthand: FirsthandState;
51
- }
52
- }
53
- export type FirsthandPluginOptions = {
54
- /** Package name used when hashing stable component ids (ADR-0004). */
55
- packageName?: string;
56
- /**
57
- * Refuse to compile a value that is read once in a setup and then kept.
58
- *
59
- * **On by default.** The rule only sees declarations whose initialiser is
60
- * nothing but a read, which is the shape that is almost always a mistake;
61
- * anything containing a call is left alone. `false` turns it off for a
62
- * codebase that has such a read on purpose and would rather not mark it with
63
- * `snapshot()` — see `checkKeptReads` (ADR-0019).
64
- */
65
- strictReactivity?: boolean;
66
- /**
67
- * Name the cells a module creates, for devtools.
68
- *
69
- * A runtime cannot see that `const count = signal(0)` is called `count`, and
70
- * `new Error().stack` reports a position in the *compiled* module — the
71
- * browser does not apply source maps to `error.stack`, so the line it names
72
- * is not the line that was written. The compiler knows both, so it says so.
73
- *
74
- * Off by default and turned on by the Vite plugin while serving: a
75
- * production build emits nothing.
76
- */
77
- devtools?: boolean;
78
- /**
79
- * Compile for a server render.
80
- *
81
- * The same source, emitted against `@firsthandjs/server/internal` instead of
82
- * `@firsthandjs/dom/internal`: markup is built as a string rather than as
83
- * nodes, and the things a server cannot do — listeners, refs, retained
84
- * sites — are not emitted at all.
85
- *
86
- * The Vite plugin sets this from the bundler's own `ssr` flag, so an
87
- * application configures nothing.
88
- */
89
- ssr?: boolean;
90
- /**
91
- * Emit navigation that can walk server markup.
92
- *
93
- * An application that hydrates needs it; one that does not should leave it
94
- * off, because it turns two property reads per dynamic position into two
95
- * calls. The Vite plugin sets it for a project that has a server build.
96
- */
97
- hydratable?: boolean;
98
- };
99
- export default function firsthandPlugin(_api: unknown, options?: FirsthandPluginOptions): PluginObject;
100
- export {};
27
+ export { default } from './plugin.js';
28
+ export type { FirsthandPluginOptions } from './options.js';
101
29
  //# sourceMappingURL=transform.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"transform.d.ts","sourceRoot":"","sources":["../src/transform.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;GAYG;AAEH,OAAO,KAAK,EAAE,YAAY,EAAc,MAAM,aAAa,CAAC;AAE5D,OAAO,KAAK,CAAC,MAAM,cAAc,CAAC;AAgBlC,KAAK,cAAc,GAAG;IACpB,OAAO,EAAE,GAAG,CAAC,MAAM,EAAE,CAAC,CAAC,UAAU,CAAC,CAAC;IACnC,SAAS,EAAE,CAAC,CAAC,kBAAkB,EAAE,CAAC;IAClC,OAAO,EAAE,MAAM,CAAC;IAChB,QAAQ,EAAE,MAAM,CAAC;IACjB,wEAAwE;IACxE,KAAK,EAAE,GAAG,CAAC,MAAM,EAAE,CAAC,CAAC,UAAU,CAAC,CAAC;IACjC,4EAA4E;IAC5E,SAAS,EAAE,GAAG,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC;IACvB,iEAAiE;IACjE,GAAG,EAAE,OAAO,CAAC;IACb,0EAA0E;IAC1E,UAAU,EAAE,OAAO,CAAC;IACpB,2EAA2E;IAC3E,IAAI,EAAE,GAAG,CAAC,CAAC,CAAC,IAAI,EAAE,UAAU,CAAC,CAAC;CAC/B,CAAC;AAEF;;;;;;;;;GASG;AACH,KAAK,UAAU,GAAG;IAChB,6EAA6E;IAC7E,IAAI,EAAE,CAAC,CAAC,IAAI,CAAC;IACb,KAAK,EAAE,CAAC,CAAC,UAAU,CAAC;IACpB,IAAI,EAAE,MAAM,MAAM,CAAC;CACpB,CAAC;AAyBF,OAAO,QAAQ,aAAa,CAAC;IAC3B,UAAU,UAAU;QAClB,SAAS,EAAE,cAAc,CAAC;KAC3B;CACF;AA2BD,MAAM,MAAM,sBAAsB,GAAG;IACnC,sEAAsE;IACtE,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB;;;;;;;;OAQG;IACH,gBAAgB,CAAC,EAAE,OAAO,CAAC;IAC3B;;;;;;;;;;OAUG;IACH,QAAQ,CAAC,EAAE,OAAO,CAAC;IACnB;;;;;;;;;;OAUG;IACH,GAAG,CAAC,EAAE,OAAO,CAAC;IACd;;;;;;OAMG;IACH,UAAU,CAAC,EAAE,OAAO,CAAC;CACtB,CAAC;AAEF,MAAM,CAAC,OAAO,UAAU,eAAe,CACrC,IAAI,EAAE,OAAO,EACb,OAAO,GAAE,sBAA2B,GACnC,YAAY,CAiFd"}
1
+ {"version":3,"file":"transform.d.ts","sourceRoot":"","sources":["../src/transform.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;GAyBG;AAEH,OAAO,EAAE,OAAO,EAAE,MAAM,aAAa,CAAC;AAEtC,YAAY,EAAE,sBAAsB,EAAE,MAAM,cAAc,CAAC"}
package/dist/vite.js CHANGED
@@ -1,6 +1,6 @@
1
1
  import {
2
2
  compileModule
3
- } from "./chunk-ACKKPAGI.js";
3
+ } from "./chunk-JEIWCE46.js";
4
4
 
5
5
  // packages/compiler/src/vite.ts
6
6
  function firsthand(options = {}) {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@firsthandjs/compiler",
3
- "version": "0.9.0",
3
+ "version": "0.10.0",
4
4
  "description": "Build-time TSX transform for Firsthand: static markup into templates, dynamic expressions into DOM parts.",
5
5
  "license": "MIT",
6
6
  "type": "module",