@nodegx/export 0.0.0-stage → 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 +59 -2
- package/dist/analyze/appState.d.ts +296 -0
- package/dist/analyze/plan.d.ts +3757 -0
- package/dist/analyze/querySettings.d.ts +75 -0
- package/dist/analyze/reach.d.ts +41 -0
- package/dist/analyze/scriptUses.d.ts +29 -0
- package/dist/catalog.d.ts +129 -0
- package/dist/cli/args.d.ts +90 -0
- package/dist/cli/exitCodes.d.ts +125 -0
- package/dist/cli/run.d.ts +30 -0
- package/dist/cli.inputs.json +174 -0
- package/dist/cli.mjs +53746 -0
- package/dist/cli.mjs.map +7 -0
- package/dist/emit/animateLib.d.ts +43 -0
- package/dist/emit/component.d.ts +144 -0
- package/dist/emit/dateLib.d.ts +45 -0
- package/dist/emit/emitApp.d.ts +38 -0
- package/dist/emit/idLib.d.ts +68 -0
- package/dist/emit/kits.d.ts +85 -0
- package/dist/emit/mediaLib.d.ts +75 -0
- package/dist/emit/naming.d.ts +76 -0
- package/dist/emit/parseLib.d.ts +106 -0
- package/dist/emit/preflight.d.ts +174 -0
- package/dist/emit/readme.d.ts +51 -0
- package/dist/emit/report.d.ts +309 -0
- package/dist/emit/scaffold.d.ts +67 -0
- package/dist/emit/statesLib.d.ts +52 -0
- package/dist/emit/style.d.ts +165 -0
- package/dist/emit/utilLib.d.ts +78 -0
- package/dist/errorMessage.d.ts +28 -0
- package/dist/index.cjs +52848 -0
- package/dist/index.cjs.map +7 -0
- package/dist/index.d.ts +37 -0
- package/dist/index.mjs +52769 -0
- package/dist/index.mjs.map +7 -0
- package/dist/ir/types.d.ts +567 -0
- package/dist/ledger.cjs +971 -0
- package/dist/ledger.cjs.map +7 -0
- package/dist/ledger.d.ts +75 -0
- package/dist/ledger.mjs +948 -0
- package/dist/ledger.mjs.map +7 -0
- package/dist/node-catalog.json +65333 -0
- package/dist/parse/kitSource.d.ts +135 -0
- package/dist/parse/parseModules.d.ts +42 -0
- package/dist/parse/parseProject.d.ts +13 -0
- package/dist/scriptFiles.d.ts +30 -0
- package/dist/structurePorts.d.ts +70 -0
- package/dist/write/writeExport.d.ts +67 -0
- package/package.json +53 -4
|
@@ -0,0 +1,135 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Reading a custom node kit's `index.js` for what it *declares* — EXP-010's parse half.
|
|
3
|
+
*
|
|
4
|
+
* A kit is a plain `<script>`-shaped file that calls `Noodl.defineModule({ reactNodes: [...] })`.
|
|
5
|
+
* Its node definitions are the only statement anywhere of a custom node's ports: there is no
|
|
6
|
+
* catalog entry, no manifest field, nothing on disk beside the code. `projectmodules.ts` says the
|
|
7
|
+
* same thing from the other side — "what a kit registers is known only once its `index.js` has
|
|
8
|
+
* executed". So the export runs it.
|
|
9
|
+
*
|
|
10
|
+
* 🔴 **This is the fourth evaluator of kit source in the repo, and that is deliberate rather than
|
|
11
|
+
* missed.** The other three cannot answer this question, which was checked rather than assumed:
|
|
12
|
+
*
|
|
13
|
+
* - `noodl-editor/src/shared/utils/projectmodules.ts` → `verifyKitSource` returns node *names* and
|
|
14
|
+
* a pass/fail outcome. EXP-010 needs the ports — `inputProps`, `outputProps`, `outputs` — which
|
|
15
|
+
* that function reads past and discards. It is also TypeScript inside the editor package.
|
|
16
|
+
* - `noodl-mcp/src/kitExtract/entry.js` is an esbuild entry point for a child process; there is no
|
|
17
|
+
* function to import.
|
|
18
|
+
* - `noodl-viewer-react/static/ssr/kit-modules.js` takes a *deploy's `index.html`* and reads script
|
|
19
|
+
* tags out of it. There is no deploy at export time.
|
|
20
|
+
*
|
|
21
|
+
* What is reused is the shape of the question and the shim: the recursive-noop `Proxy` on `Noodl`
|
|
22
|
+
* with a collecting `defineModule` is `kitExtract/entry.js`'s, adopted rather than reinvented,
|
|
23
|
+
* because kit code touches other members of the `Noodl` global at module scope.
|
|
24
|
+
*
|
|
25
|
+
* ⚠️ **`React` is a recursive noop here, so this reads declarations and not behaviour.** A kit's
|
|
26
|
+
* components are *values* the export never calls — Route B renders them by running the kit's own
|
|
27
|
+
* code in the browser (`src/kits/`), and what this file extracts is the port map that decides how
|
|
28
|
+
* the graph's parameters and wires reach them. A kit that computed its `inputProps` from a React
|
|
29
|
+
* call at module scope would come back with fewer ports than it has; no kit on this machine does,
|
|
30
|
+
* and a kit that registers *no* usable ports is reported by name rather than assumed empty.
|
|
31
|
+
*
|
|
32
|
+
* ⚠️ **This is a shape read, not a security boundary.** A `vm` context is not a sandbox in the
|
|
33
|
+
* security sense — `vm`'s own documentation says so. Exporting a project already means running the
|
|
34
|
+
* project's own code on the machine that authored it, but no caller may describe a kit that
|
|
35
|
+
* survives this as safe.
|
|
36
|
+
*
|
|
37
|
+
* 🔴 **Nothing from the sandbox escapes.** Every value returned is rebuilt as plain JSON-shaped
|
|
38
|
+
* data by `plainValue`; the definitions hold functions closed over the vm realm and handing one
|
|
39
|
+
* back would put a foreign-realm callable in the IR, which is supposed to be serialisable.
|
|
40
|
+
*/
|
|
41
|
+
/**
|
|
42
|
+
* What running a kit's source turned out to be.
|
|
43
|
+
*
|
|
44
|
+
* Named after `projectmodules.ts`'s `KitVerifyOutcome` and carrying the same distinctions, because
|
|
45
|
+
* the two are read by people looking at the same kit — the Kits panel and the export report must
|
|
46
|
+
* not disagree about why a kit produced nothing.
|
|
47
|
+
*
|
|
48
|
+
* ⚠️ `'unreadable'` is the caller's to report: this function is handed source, so "there is no
|
|
49
|
+
* code" is a fact about the file read, not about the run.
|
|
50
|
+
*/
|
|
51
|
+
export type KitRunOutcome =
|
|
52
|
+
/** Ran, called `Noodl.defineModule`, and defined at least one named node. */
|
|
53
|
+
'defines-nodes'
|
|
54
|
+
/** Ran cleanly and never called `Noodl.defineModule` — a library, not a kit. */
|
|
55
|
+
| 'no-define-module'
|
|
56
|
+
/** Called `defineModule` with no `nodes` and no `reactNodes` carrying a name. */
|
|
57
|
+
| 'defines-no-nodes'
|
|
58
|
+
/** An ES-module build; a `<script>` tag cannot load it, so neither can the exported app. */
|
|
59
|
+
| 'es-module'
|
|
60
|
+
/** A CommonJS (Node) build; same. */
|
|
61
|
+
| 'commonjs'
|
|
62
|
+
/** Threw while running. It would register nothing in the running app either. */
|
|
63
|
+
| 'threw';
|
|
64
|
+
/** One port a kit node declares. Plain data — no sandbox values, no functions. */
|
|
65
|
+
export interface KitPort {
|
|
66
|
+
name: string;
|
|
67
|
+
/** Declared port type name ("string", "number", "color", "signal", …) or undefined. */
|
|
68
|
+
type?: string;
|
|
69
|
+
/** The port type's `defaultUnit` ("px"), when the type is a units type. */
|
|
70
|
+
defaultUnit?: string;
|
|
71
|
+
displayName?: string;
|
|
72
|
+
group?: string;
|
|
73
|
+
/** Present only when the definition declares one. JSON-shaped. */
|
|
74
|
+
default?: unknown;
|
|
75
|
+
}
|
|
76
|
+
/** One node a kit defines. */
|
|
77
|
+
export interface KitNodeDefinition {
|
|
78
|
+
/** The node type name the graph stores — `nodegx.cashflow.Pill`. */
|
|
79
|
+
type: string;
|
|
80
|
+
displayName?: string;
|
|
81
|
+
docs?: string;
|
|
82
|
+
/** `allowChildren` — whether the node accepts visual children. */
|
|
83
|
+
allowChildren: boolean;
|
|
84
|
+
/**
|
|
85
|
+
* True when the definition has a `getReactComponent`. A kit may also define non-visual nodes
|
|
86
|
+
* (`nodes:` rather than `reactNodes:`); those are logic and this export does not render them.
|
|
87
|
+
*/
|
|
88
|
+
visual: boolean;
|
|
89
|
+
/**
|
|
90
|
+
* Every input the node declares, from **both** places a definition declares them, tagged with
|
|
91
|
+
* how the runtime delivers it.
|
|
92
|
+
*
|
|
93
|
+
* - `via: 'prop'` — an `inputProps` entry. The value becomes a React prop of that name
|
|
94
|
+
* (`react-component-node.ts:945`), which is the only shape a visual kit node normally uses.
|
|
95
|
+
* - `via: 'node'` — an `inputs` entry, whose `set` function the node runs. `rename-kit`'s
|
|
96
|
+
* logic node `Source` declares its only input this way, and reading `inputProps` alone
|
|
97
|
+
* reports it as having none — which then reads as "the author set a parameter no port
|
|
98
|
+
* accepts" for a port that exists and works.
|
|
99
|
+
*/
|
|
100
|
+
inputs: Array<KitPort & {
|
|
101
|
+
via: 'prop' | 'node';
|
|
102
|
+
}>;
|
|
103
|
+
/**
|
|
104
|
+
* Every output the node publishes, from **both** places a definition declares them, tagged with
|
|
105
|
+
* which one — the two are wired differently and collapsing them loses the difference.
|
|
106
|
+
*
|
|
107
|
+
* - `via: 'prop'` — an `outputProps` entry. The runtime *gives the component a callback prop*
|
|
108
|
+
* of this name (`react-component-node.ts:addPrimitiveOutputPropHandler`), and the component
|
|
109
|
+
* calling it is what fires the port. A `Money Pill` fires `onClick` this way.
|
|
110
|
+
* - `via: 'node'` — an `outputs` entry. The node itself publishes it: a value output through
|
|
111
|
+
* `get()` after `flagOutputDirty(name)`, a signal through `sendSignalOnOutput(name)`. `Money
|
|
112
|
+
* Pill`'s `liveDay`, `dropped` and `dragStarted` are all this shape, and `dropped` is fired
|
|
113
|
+
* from `initialize` with no `outputProps` entry at all — read only `outputProps` and it
|
|
114
|
+
* disappears.
|
|
115
|
+
*/
|
|
116
|
+
outputs: Array<KitPort & {
|
|
117
|
+
kind: 'value' | 'signal';
|
|
118
|
+
via: 'prop' | 'node';
|
|
119
|
+
}>;
|
|
120
|
+
}
|
|
121
|
+
export interface KitRunResult {
|
|
122
|
+
outcome: KitRunOutcome;
|
|
123
|
+
/** Human-facing, and on failure it names the likely cause. Never a bare "false". */
|
|
124
|
+
message: string;
|
|
125
|
+
/** Definition order across every `defineModule` call. Empty on every failure. */
|
|
126
|
+
nodes: KitNodeDefinition[];
|
|
127
|
+
}
|
|
128
|
+
/**
|
|
129
|
+
* Run a kit's source and read every node definition it registers.
|
|
130
|
+
*
|
|
131
|
+
* Never throws. Every failure mode is an outcome with a message, because a project with one broken
|
|
132
|
+
* kit must still export the rest of itself (AC4) — an exception here would take the whole app down
|
|
133
|
+
* over a folder the author may not even remember adding.
|
|
134
|
+
*/
|
|
135
|
+
export declare function runKitSource(code: string): KitRunResult;
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* `noodl_modules/` → `ModuleIR[]` — EXP-010's parse half.
|
|
3
|
+
*
|
|
4
|
+
* Until this file existed, `parseProject` contained **zero references to `noodl_modules`**: the
|
|
5
|
+
* directory was never opened. Every node from a custom kit therefore resolved to `catalogRef:
|
|
6
|
+
* null`, fell through `renderRole`'s default arm, and left the emitted JSX with a hole in it. The
|
|
7
|
+
* export was silently dropping the one part of a project the author had written themselves.
|
|
8
|
+
*
|
|
9
|
+
* 🔴 **The scan is `@nodegx/module-inject`'s, not this file's.** LIB-003 merged two
|
|
10
|
+
* `noodl_modules` readers because they had drifted, and CN-001 moved the merged core into a
|
|
11
|
+
* no-build package precisely so a fourth caller could reuse it instead of writing a third. What
|
|
12
|
+
* this file adds on top is the export's own questions — which modules carry nodes, what those
|
|
13
|
+
* nodes' ports are, and which files have to travel with the app.
|
|
14
|
+
*
|
|
15
|
+
* ⚠️ **Parsing now runs project code.** `runKitSource` executes each kit's `index.js` in a `vm`
|
|
16
|
+
* context, because a custom node's ports are declared nowhere else (see `kitSource.ts`). That is a
|
|
17
|
+
* real change to what `parseProject` does and it is stated here rather than buried: exporting a
|
|
18
|
+
* project runs that project's kits on the machine doing the export.
|
|
19
|
+
*
|
|
20
|
+
* ⚠️ **Never throws on content**, the same contract as the rest of parse. A kit that is missing,
|
|
21
|
+
* unreadable, an ES-module build or a thrower comes back as a `ModuleIR` with a `status` and a
|
|
22
|
+
* `message` saying so, and the rest of the app exports around it (AC4).
|
|
23
|
+
*/
|
|
24
|
+
import { ModuleIR } from '../ir/types';
|
|
25
|
+
/**
|
|
26
|
+
* P109-S17-EXPORTKITDEPS — the kit dependencies the deployed page loads, in the order it loads them.
|
|
27
|
+
*
|
|
28
|
+
* The export copied every dependency to `public/` and nothing referenced it, so a kit that reads `window.THREE` threw
|
|
29
|
+
* in the exported app and drew in the deployed one. 🔴 **The injector's own `toInjectModules`, not a transcription**:
|
|
30
|
+
* it owns the path rule (`http…` verbatim, anything else under the module folder), the module order (kits by index
|
|
31
|
+
* path, then the rest, then `requires`) and the refusals (a refused kit has no dependencies). What is left is
|
|
32
|
+
* `buildInjectionTags`' loop: browser modules only, each dependency once.
|
|
33
|
+
*/
|
|
34
|
+
export declare function parseModuleDependencies(projectDir: string): string[];
|
|
35
|
+
/**
|
|
36
|
+
* Every module in a project, in directory order.
|
|
37
|
+
*
|
|
38
|
+
* The `kind` split is `projectmodules.ts`'s, measured there across the 29 real projects on this
|
|
39
|
+
* machine and adopted rather than re-derived: there is no `kind: 'node-kit'` marker, so a kit is
|
|
40
|
+
* *subtractively* a module with a `main` that is not an iconset and not an ERG-002 library.
|
|
41
|
+
*/
|
|
42
|
+
export declare function parseModules(projectDir: string): ModuleIR[];
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* v2 files → Graph IR. The faithful half of the pipeline: this stage records what the project
|
|
3
|
+
* says, resolves it against the catalog, and refuses to guess. Decisions (dispositions, names,
|
|
4
|
+
* file plans) belong to analysis, not here.
|
|
5
|
+
*
|
|
6
|
+
* Parsing never drops and never fails on content: unknown node types get catalogRef null,
|
|
7
|
+
* statically-unknowable port sets get portKnowledge 'unknown'/'partial', script parameters are
|
|
8
|
+
* captured verbatim. See EXP-002-IR-DESIGN.md.
|
|
9
|
+
*/
|
|
10
|
+
import { Catalog } from '../catalog';
|
|
11
|
+
import { ExportIR } from '../ir/types';
|
|
12
|
+
export declare const EXPORTER_VERSION = "0.2.5";
|
|
13
|
+
export declare function parseProject(projectDir: string, catalog: Catalog): ExportIR;
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* P109 ISL-005 — script files, as the export reads them: code a project keeps once, in
|
|
3
|
+
* `scripts/**.js`, that any Function lists under its `uses` parameter and reaches as
|
|
4
|
+
* `Scripts.<name>`. Inside a file another file is reached with `use('engine/world')`, and what a
|
|
5
|
+
* file offers is what it puts on `exports`.
|
|
6
|
+
*
|
|
7
|
+
* 🔴 **Mirrors, not imports.** This package installs on its own (HLS-001), so it cannot reach
|
|
8
|
+
* `noodl-runtime/src/scriptfiles.ts`. The four readers below are that file's, transcribed; the
|
|
9
|
+
* spec `tests/isl-005-script-files.test.ts` runs both over the same table so the two cannot drift.
|
|
10
|
+
* The runtime is the definition: a path the runtime resolves, this resolves the same way.
|
|
11
|
+
*/
|
|
12
|
+
/** A file's path as the project names it: relative to `scripts/`, with `.js`. */
|
|
13
|
+
export type ScriptPath = string;
|
|
14
|
+
/**
|
|
15
|
+
* `engine/world`, `./engine/world.js`, `/engine/world.js` and `scripts/engine/world.js` are all
|
|
16
|
+
* `engine/world.js`. Undefined for a path that leaves `scripts/` (`..`) or names nothing.
|
|
17
|
+
*/
|
|
18
|
+
export declare function normalizeScriptPath(path: unknown): ScriptPath | undefined;
|
|
19
|
+
/**
|
|
20
|
+
* A Function's `uses` parameter as a list of paths: the editor's comma string, or an array. Empty
|
|
21
|
+
* entries and repeats are dropped; an entry that is not a script path is kept as written (trimmed),
|
|
22
|
+
* so the caller can name it.
|
|
23
|
+
*/
|
|
24
|
+
export declare function scriptUsesOf(value: unknown): string[];
|
|
25
|
+
/** The files a script reaches with `use('…')`, read from its text. */
|
|
26
|
+
export declare function scriptUses(source: string): ScriptPath[];
|
|
27
|
+
/** The name a Function's code reaches a file by: `engine/world.js` → `world`. */
|
|
28
|
+
export declare function scriptAlias(path: ScriptPath): string;
|
|
29
|
+
/** What the runtime compiles a file with (`scriptfiles.ts` `PARAMS`); the three app names arrive `undefined`. */
|
|
30
|
+
export declare const SCRIPT_FILE_PARAMS: readonly ["use", "exports", "module", "Noodl", "window", "document"];
|
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The export's defer tables, and the type → render-role map above them.
|
|
3
|
+
*
|
|
4
|
+
* 🔴 **These live outside `analyze/plan.ts` because a second reader needs them.** FLD-013 (#37):
|
|
5
|
+
* an agent choosing nodes for an app it intends to export asks `get_node_type` *before* it
|
|
6
|
+
* designs, and the only honest answer to *"will this node export?"* is per **parameter source**,
|
|
7
|
+
* not per type. `Circle` is `status: "translated"` in the coverage ledger and still refuses on a
|
|
8
|
+
* wire into any of thirteen ports — so the ledger status alone, which is what #37 asked for, would
|
|
9
|
+
* have read green on the exact node that produced the twenty-three refusals it was filed about.
|
|
10
|
+
*
|
|
11
|
+
* `noodl-mcp` reads these through `@nodegx/export`'s index; `plan.ts` imports them back and
|
|
12
|
+
* remains their only writer. **Moved out of `plan.ts`, not copied into a second file** — this repo
|
|
13
|
+
* has already paid for a second copy of a table that drifted.
|
|
14
|
+
*/
|
|
15
|
+
import type { RenderRole } from './analyze/plan';
|
|
16
|
+
/**
|
|
17
|
+
* Ports whose value shapes the emitted *structure* (tracks, options, marks, initial state) —
|
|
18
|
+
* a wire into one means the node's static translation would lie, so the node defers whole
|
|
19
|
+
* (VISUALS-TARGET). Ports that merely carry content (src, label text) stay bindable.
|
|
20
|
+
*/
|
|
21
|
+
export declare const STRUCTURE_PORTS: Partial<Record<RenderRole, string[]>>;
|
|
22
|
+
/**
|
|
23
|
+
* Ports that carry *content* into a control — text, bounds, increments. A wire into one does
|
|
24
|
+
* not move the rendered structure: `label` is the single text child of `<label>`
|
|
25
|
+
* (Checkbox.tsx:191, RadioButton.tsx:200) and `min`/`max`/`step` are plain attributes the
|
|
26
|
+
* emitter already orders (CONTENT_ATTR_ORDER), with nothing in the emitted CSS derived from
|
|
27
|
+
* them (style.ts's `range` rule reads `thumbColor` and `width` only).
|
|
28
|
+
*
|
|
29
|
+
* They still defer, because omitting an unknown bound renders a 0–100 slider where the running
|
|
30
|
+
* app renders the row's — wrong output, confidently emitted. But the wall is the *source*, not
|
|
31
|
+
* the port, and every one of them in the corpus resolves to one of the two walls already on the
|
|
32
|
+
* list: a `Model2` row property, or a component-record property only a runtime script writes.
|
|
33
|
+
* Naming the source is what lets the census group them there instead of inventing a third wall
|
|
34
|
+
* (RECORD-VERBS §19).
|
|
35
|
+
*/
|
|
36
|
+
export declare const CONTENT_BOUND_PORTS: Partial<Record<RenderRole, string[]>>;
|
|
37
|
+
/**
|
|
38
|
+
* Type name → render role, for every type whose role is fixed by its name.
|
|
39
|
+
*
|
|
40
|
+
* 🔴 **This is `renderRole`'s switch, moved.** It was a `switch (node.type)` inside `plan.ts` and
|
|
41
|
+
* nothing outside the exporter could read it, which left the MCP server with no way to say which
|
|
42
|
+
* ports a *named* type refuses on. `plan.ts` now looks the answer up here, so there is one table
|
|
43
|
+
* and not two. The three cases the switch decides before consulting a name — a component instance
|
|
44
|
+
* (`/`-prefixed), a kit node, and the `catalog.isVisual` fallback — stay in `renderRole`, because
|
|
45
|
+
* they are decided by the graph and the catalog rather than by the type name.
|
|
46
|
+
*
|
|
47
|
+
* `null` is a real answer and not a miss: a `Router` has a role of none, exactly as a `Variable`
|
|
48
|
+
* does. Callers must distinguish `null` (this type draws nothing) from `undefined` (this table
|
|
49
|
+
* does not decide this type).
|
|
50
|
+
*/
|
|
51
|
+
export declare const ROLE_OF_TYPE: Readonly<Partial<Record<string, RenderRole | null>>>;
|
|
52
|
+
/**
|
|
53
|
+
* The ports of a named type that shape its emitted structure — *"a wire into any of these and this
|
|
54
|
+
* node is left out of the export"*.
|
|
55
|
+
*
|
|
56
|
+
* Always an array, never `undefined`, for a type this table decides: **an empty list and an absent
|
|
57
|
+
* field are different answers**, and a caller that cannot tell them apart learns nothing from the
|
|
58
|
+
* empty one.
|
|
59
|
+
*/
|
|
60
|
+
export declare function structurePortsOf(typeName: string): string[];
|
|
61
|
+
/**
|
|
62
|
+
* The ports of a named type that carry *content* into a control and still refuse it.
|
|
63
|
+
*
|
|
64
|
+
* 🔴 **Reported apart from {@link structurePortsOf} because they are not the same claim.** A wire
|
|
65
|
+
* into `Range.min` does not move the rendered structure — the node is left out because the bound
|
|
66
|
+
* is not statically known and a 0–100 slider is the wrong output, confidently emitted. Folding
|
|
67
|
+
* these into a field called `structurePorts` would tell an agent something about this product that
|
|
68
|
+
* is not true; the consequence is identical, the reason is not.
|
|
69
|
+
*/
|
|
70
|
+
export declare function contentPortsOf(typeName: string): string[];
|
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The half of the code export that touches the disk — every file written, every asset copied,
|
|
3
|
+
* and the one refusal that has to happen before any of it.
|
|
4
|
+
*
|
|
5
|
+
* `emitApp` is pure: it returns generated strings (`files`) and a list of project assets to copy
|
|
6
|
+
* byte-for-byte (`copies`), and writes nothing. This module is the caller that writes. It takes
|
|
7
|
+
* `fs` as an argument rather than importing it, which is what lets the same code run under the
|
|
8
|
+
* editor's renderer, under a plain-Node CLI, and against a real temp directory in a spec.
|
|
9
|
+
*
|
|
10
|
+
* ## Why it lives in the package (HLS-002)
|
|
11
|
+
*
|
|
12
|
+
* It was written for EXP-012 inside `noodl-editor`, as the disk half of the editor's menu item.
|
|
13
|
+
* HLS-002 gives the export a second front door — `nodegx export` — and the whole point of that
|
|
14
|
+
* task is that the two doors produce the *same* export. Two copies of the write loop is the
|
|
15
|
+
* cheapest way to make that untrue, and the copy that would drift is the one nobody clicks:
|
|
16
|
+
* `scripts/emit-app.ts` had its own write loop and dropped `copies` on the floor for a whole
|
|
17
|
+
* phase (P18 §19.6) while every gate stayed green. So there is one write loop now, here, and
|
|
18
|
+
* both doors call it.
|
|
19
|
+
*
|
|
20
|
+
* 🔴 **`copies` is a second channel.** `files` are strings and `copies` are bytes — a kit's
|
|
21
|
+
* script, an icon set's `.woff2`, Inter's four `.ttf`. They are separate precisely *because* a
|
|
22
|
+
* font is not a string: reading one into UTF-8 to put it in `files` corrupts it silently, and the
|
|
23
|
+
* failure renders as blank glyphs rather than as an error. Both counts are reported so a caller
|
|
24
|
+
* can say what it did.
|
|
25
|
+
*
|
|
26
|
+
* 🔴 **The target may not be inside the project.** The editor's loader reads every file under the
|
|
27
|
+
* project directory with no skip list (P82: `readBundleDirectory`), and the MCP server's scanner
|
|
28
|
+
* walks it too — an export dropped into the project would be read back as project content on the
|
|
29
|
+
* next open. `checkTarget` refuses that before anything is written.
|
|
30
|
+
*/
|
|
31
|
+
import type * as fsType from 'fs';
|
|
32
|
+
/** The shape `emitApp` returns; structural, so this module does not import the package. */
|
|
33
|
+
export interface ExportOutput {
|
|
34
|
+
/** Output-relative path → generated content. */
|
|
35
|
+
files: Record<string, string>;
|
|
36
|
+
/** Project-relative source → output-relative destination, copied byte-for-byte. */
|
|
37
|
+
copies: ReadonlyArray<{
|
|
38
|
+
from: string;
|
|
39
|
+
to: string;
|
|
40
|
+
}>;
|
|
41
|
+
}
|
|
42
|
+
export type FsLike = Pick<typeof fsType, 'existsSync' | 'readdirSync' | 'mkdirSync' | 'writeFileSync' | 'copyFileSync' | 'statSync'>;
|
|
43
|
+
export type TargetVerdict = {
|
|
44
|
+
ok: true; /** Entries already in the folder; > 0 means the caller should confirm. */
|
|
45
|
+
existing: number;
|
|
46
|
+
} | {
|
|
47
|
+
ok: false;
|
|
48
|
+
reason: string;
|
|
49
|
+
};
|
|
50
|
+
/**
|
|
51
|
+
* Whether `outDir` is a folder the export may write into. Refuses the project directory and
|
|
52
|
+
* anything under it; otherwise reports how many entries the folder already holds so the caller
|
|
53
|
+
* can ask before overwriting.
|
|
54
|
+
*/
|
|
55
|
+
export declare function checkTarget(projectDir: string, outDir: string, fs: FsLike): TargetVerdict;
|
|
56
|
+
export interface WriteResult {
|
|
57
|
+
files: number;
|
|
58
|
+
copies: number;
|
|
59
|
+
}
|
|
60
|
+
/**
|
|
61
|
+
* Writes an emitted app into `outDir`: every generated file, then every asset copy. Existing
|
|
62
|
+
* files with the same names are overwritten; nothing else in the folder is touched.
|
|
63
|
+
*
|
|
64
|
+
* Throws on the first failed write — the caller reports it as a part-way failure rather than a
|
|
65
|
+
* success with a smaller number, because a folder that is half an app builds nothing.
|
|
66
|
+
*/
|
|
67
|
+
export declare function writeExport(projectDir: string, outDir: string, output: ExportOutput, fs: FsLike): WriteResult;
|
package/package.json
CHANGED
|
@@ -1,6 +1,55 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@nodegx/export",
|
|
3
|
-
"version": "0.
|
|
4
|
-
"
|
|
5
|
-
"
|
|
6
|
-
|
|
3
|
+
"version": "0.2.5",
|
|
4
|
+
"description": "Code export for NodeGX projects: v2 files → IR → analysis → generated React. EXP-002's pipeline; the IR is the shared contract with EXP-003/005 (dev-docs/tasks/phase-18-code-export-v2/EXP-002-IR-DESIGN.md).",
|
|
5
|
+
"author": "The Low Code Foundation <contact@thelowcodefoundation.com>",
|
|
6
|
+
"license": "GPL-3.0",
|
|
7
|
+
"repository": {
|
|
8
|
+
"type": "git",
|
|
9
|
+
"url": "git+https://github.com/The-Low-Code-Foundation/NodeGX.git",
|
|
10
|
+
"directory": "packages/nodegx-export"
|
|
11
|
+
},
|
|
12
|
+
"bin": {
|
|
13
|
+
"nodegx": "./dist/cli.mjs"
|
|
14
|
+
},
|
|
15
|
+
"main": "./dist/index.cjs",
|
|
16
|
+
"module": "./dist/index.mjs",
|
|
17
|
+
"types": "./dist/index.d.ts",
|
|
18
|
+
"sideEffects": false,
|
|
19
|
+
"exports": {
|
|
20
|
+
".": {
|
|
21
|
+
"types": "./dist/index.d.ts",
|
|
22
|
+
"import": "./dist/index.mjs",
|
|
23
|
+
"require": "./dist/index.cjs"
|
|
24
|
+
},
|
|
25
|
+
"./ledger": {
|
|
26
|
+
"types": "./dist/ledger.d.ts",
|
|
27
|
+
"import": "./dist/ledger.mjs",
|
|
28
|
+
"require": "./dist/ledger.cjs"
|
|
29
|
+
},
|
|
30
|
+
"./package.json": "./package.json"
|
|
31
|
+
},
|
|
32
|
+
"files": [
|
|
33
|
+
"dist",
|
|
34
|
+
"README.md"
|
|
35
|
+
],
|
|
36
|
+
"engines": {
|
|
37
|
+
"node": ">=22"
|
|
38
|
+
},
|
|
39
|
+
"scripts": {
|
|
40
|
+
"build": "node build.mjs",
|
|
41
|
+
"test": "jest",
|
|
42
|
+
"typecheck": "tsc --noEmit"
|
|
43
|
+
},
|
|
44
|
+
"devDependencies": {
|
|
45
|
+
"@nodegx/module-inject": "file:../nodegx-module-inject",
|
|
46
|
+
"@nodegx/project-contract": "file:../nodegx-project-contract",
|
|
47
|
+
"@noodl/backend-contract": "file:../nodegx-backend-contract",
|
|
48
|
+
"@types/jest": "^29.0.1",
|
|
49
|
+
"jest": "^29.7.0",
|
|
50
|
+
"ts-jest": "^29.4.1"
|
|
51
|
+
},
|
|
52
|
+
"publishConfig": {
|
|
53
|
+
"access": "public"
|
|
54
|
+
}
|
|
55
|
+
}
|