@confect/cli 9.0.0-next.5 → 9.0.0-next.7
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/CHANGELOG.md +165 -0
- package/dist/CodegenError.mjs +22 -9
- package/dist/CodegenError.mjs.map +1 -1
- package/dist/LeafModule.mjs +2 -2
- package/dist/LeafModule.mjs.map +1 -1
- package/dist/SpecAssemblyNode.mjs +1 -19
- package/dist/SpecAssemblyNode.mjs.map +1 -1
- package/dist/TableModule.mjs +90 -0
- package/dist/TableModule.mjs.map +1 -0
- package/dist/confect/codegen.mjs +175 -78
- package/dist/confect/codegen.mjs.map +1 -1
- package/dist/confect/dev.mjs +9 -7
- package/dist/confect/dev.mjs.map +1 -1
- package/dist/log.mjs +4 -3
- package/dist/log.mjs.map +1 -1
- package/dist/package.mjs +1 -1
- package/dist/templates.mjs +123 -34
- package/dist/templates.mjs.map +1 -1
- package/package.json +41 -42
- package/src/CodegenError.ts +90 -28
- package/src/LeafModule.ts +1 -1
- package/src/SpecAssemblyNode.ts +0 -36
- package/src/TableModule.ts +153 -0
- package/src/confect/codegen.ts +323 -132
- package/src/confect/dev.ts +25 -7
- package/src/log.ts +4 -2
- package/src/templates.ts +200 -59
package/src/confect/dev.ts
CHANGED
|
@@ -49,8 +49,14 @@ import { ProjectRoot } from "../ProjectRoot";
|
|
|
49
49
|
import { generateAuthConfig, generateCrons, generateHttp } from "../utils";
|
|
50
50
|
import { codegenHandler, loadPreviousFunctionPaths } from "./codegen";
|
|
51
51
|
|
|
52
|
-
const
|
|
53
|
-
|
|
52
|
+
const GENERATED_DIRNAME = "_generated";
|
|
53
|
+
|
|
54
|
+
const GENERATED_SPEC_PATH = Effect.andThen(Path.Path, (path) =>
|
|
55
|
+
path.join(GENERATED_DIRNAME, "spec.ts"),
|
|
56
|
+
);
|
|
57
|
+
const GENERATED_NODE_SPEC_PATH = Effect.andThen(Path.Path, (path) =>
|
|
58
|
+
path.join(GENERATED_DIRNAME, "nodeSpec.ts"),
|
|
59
|
+
);
|
|
54
60
|
|
|
55
61
|
// Quiescence window: the sync loop waits this long for further signals
|
|
56
62
|
// after each batch. One user edit fires `onEnd` on every esbuild
|
|
@@ -99,7 +105,7 @@ const changeChar = (change: "Added" | "Removed" | "Modified") =>
|
|
|
99
105
|
Match.value(change).pipe(
|
|
100
106
|
Match.when("Added", () => ({ char: "+", color: Ansi.green })),
|
|
101
107
|
Match.when("Removed", () => ({ char: "-", color: Ansi.red })),
|
|
102
|
-
Match.when("Modified", () => ({ char: "~", color: Ansi.
|
|
108
|
+
Match.when("Modified", () => ({ char: "~", color: Ansi.magenta })),
|
|
103
109
|
Match.exhaustive,
|
|
104
110
|
);
|
|
105
111
|
|
|
@@ -414,10 +420,18 @@ const discoverEntryPoints = Effect.gen(function* () {
|
|
|
414
420
|
});
|
|
415
421
|
});
|
|
416
422
|
|
|
423
|
+
const generatedSpecPath = yield* GENERATED_SPEC_PATH;
|
|
424
|
+
const generatedNodeSpecPath = yield* GENERATED_NODE_SPEC_PATH;
|
|
425
|
+
|
|
417
426
|
const fixedEntryOptions = yield* Effect.all([
|
|
418
|
-
tryEntry(
|
|
419
|
-
tryEntry(
|
|
420
|
-
|
|
427
|
+
tryEntry(generatedSpecPath, "specDirty"),
|
|
428
|
+
tryEntry(generatedNodeSpecPath, "specDirty"),
|
|
429
|
+
// `confect/schema.ts` is no longer user-authored; the runtime
|
|
430
|
+
// `DatabaseSchema` lives at `_generated/schema.ts` (codegen-written,
|
|
431
|
+
// so not an entry point — wiring it through esbuild would form a
|
|
432
|
+
// codegen→write→onEnd→codegen loop). Updates to `confect/tables/*.ts`
|
|
433
|
+
// still reach this dev loop via the impl entry points' import graphs;
|
|
434
|
+
// brand-new tables are caught by the Create-event safety net below.
|
|
421
435
|
tryEntry("http.ts", "httpDirty"),
|
|
422
436
|
tryEntry("crons.ts", "cronsDirty"),
|
|
423
437
|
tryEntry("auth.ts", "authDirty"),
|
|
@@ -703,6 +717,10 @@ const handleConfectChange = ({
|
|
|
703
717
|
);
|
|
704
718
|
}
|
|
705
719
|
|
|
720
|
+
// A stray `confect/schema.ts` (now codegen-owned at
|
|
721
|
+
// `_generated/schema.ts`) shouldn't exist; flagging it here ensures the
|
|
722
|
+
// next codegen pass surfaces the migration error
|
|
723
|
+
// (`LegacySchemaFileError`) instead of silently ignoring the file.
|
|
706
724
|
if (relativePath === "schema.ts") {
|
|
707
725
|
return flipDirtyAndSignal(
|
|
708
726
|
pendingRef,
|
|
@@ -723,7 +741,7 @@ const handleConfectChange = ({
|
|
|
723
741
|
);
|
|
724
742
|
}
|
|
725
743
|
|
|
726
|
-
// Any other `.ts` under `confect/` (helpers like `tables/
|
|
744
|
+
// Any other `.ts` under `confect/` (helpers like `tables/notes.ts`).
|
|
727
745
|
// Updates to such files are handled by the esbuild watcher for whichever
|
|
728
746
|
// entry point imports them — its onEnd flips the right dirty flag.
|
|
729
747
|
// Creates are our safety net: when a previously-missing import is added,
|
package/src/log.ts
CHANGED
|
@@ -57,7 +57,7 @@ export const logFileAdded = logFile("+", Ansi.green);
|
|
|
57
57
|
|
|
58
58
|
export const logFileRemoved = logFile("-", Ansi.red);
|
|
59
59
|
|
|
60
|
-
export const logFileModified = logFile("~", Ansi.
|
|
60
|
+
export const logFileModified = logFile("~", Ansi.magenta);
|
|
61
61
|
|
|
62
62
|
// --- Function subline logs ---
|
|
63
63
|
|
|
@@ -101,4 +101,6 @@ export const logSuccess = logStatus("✔︎", Ansi.green);
|
|
|
101
101
|
|
|
102
102
|
export const logFailure = logStatus("✘", Ansi.red);
|
|
103
103
|
|
|
104
|
-
export const logPending = logStatus("⭘", Ansi.
|
|
104
|
+
export const logPending = logStatus("⭘", Ansi.cyan);
|
|
105
|
+
|
|
106
|
+
export const logWarn = logStatus("⚠", Ansi.yellow);
|
package/src/templates.ts
CHANGED
|
@@ -2,7 +2,6 @@ import { Array, Effect, Option } from "effect";
|
|
|
2
2
|
import { CodeBlockWriter } from "./CodeBlockWriter";
|
|
3
3
|
import {
|
|
4
4
|
collectImportBindings,
|
|
5
|
-
collectLeafPaths,
|
|
6
5
|
type SpecAssemblyNode,
|
|
7
6
|
} from "./SpecAssemblyNode";
|
|
8
7
|
|
|
@@ -36,15 +35,197 @@ export const functions = ({
|
|
|
36
35
|
return yield* cbw.toString();
|
|
37
36
|
});
|
|
38
37
|
|
|
39
|
-
|
|
38
|
+
/**
|
|
39
|
+
* Emit `convex/schema.ts` as a one-line re-export of the codegen-emitted
|
|
40
|
+
* deploy schema in `confect/_generated/convexSchema.ts`. Deploy-time
|
|
41
|
+
* consumers (the Convex CLI, `convex-test`) keep reading
|
|
42
|
+
* `convex/schema.ts`; the runtime `DatabaseSchema` in
|
|
43
|
+
* `confect/_generated/schema.ts` is untouched by this file.
|
|
44
|
+
*/
|
|
45
|
+
export const schema = ({
|
|
46
|
+
convexSchemaImportPath,
|
|
47
|
+
}: {
|
|
48
|
+
convexSchemaImportPath: string;
|
|
49
|
+
}) =>
|
|
40
50
|
Effect.gen(function* () {
|
|
41
51
|
const cbw = new CodeBlockWriter({ indentNumberOfSpaces: 2 });
|
|
42
52
|
|
|
43
|
-
yield* cbw.writeLine(`import schemaDefinition from "${schemaImportPath}";`);
|
|
44
|
-
yield* cbw.newLine();
|
|
45
53
|
yield* cbw.writeLine(
|
|
46
|
-
`export default
|
|
54
|
+
`export { default } from "${convexSchemaImportPath}";`,
|
|
55
|
+
);
|
|
56
|
+
|
|
57
|
+
return yield* cbw.toString();
|
|
58
|
+
});
|
|
59
|
+
|
|
60
|
+
interface TableModuleBinding {
|
|
61
|
+
readonly importPath: string;
|
|
62
|
+
readonly tableName: string;
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
/**
|
|
66
|
+
* Emit `confect/_generated/schema.ts` — the runtime `DatabaseSchema` used
|
|
67
|
+
* by impls and the per-group registries (and downstream by per-function
|
|
68
|
+
* bundles for codec lookup). Every table wrapper at
|
|
69
|
+
* `confect/_generated/tables/<name>.ts` is imported statically and
|
|
70
|
+
* registered as a value entry on the `DatabaseSchema.make({...})` call.
|
|
71
|
+
* Per-table laziness lives inside each `Table`: its `Fields`, `Doc`, and
|
|
72
|
+
* `tableDefinition` are lazy memoised getters that only evaluate the
|
|
73
|
+
* user-supplied field-schema callback on first access, so unused tables in
|
|
74
|
+
* a function bundle never pay schema-construction cost despite the
|
|
75
|
+
* static import.
|
|
76
|
+
*
|
|
77
|
+
* The `DatabaseSchema` import is aliased to `$DatabaseSchema` because each
|
|
78
|
+
* table is imported under its own (filename-derived) name; a table named
|
|
79
|
+
* `DatabaseSchema` would otherwise collide with the library import and emit
|
|
80
|
+
* a duplicate-binding file. The leading `$` makes the alias collision-proof:
|
|
81
|
+
* `validateConfectTableIdentifier` requires names to match
|
|
82
|
+
* `/^[a-zA-Z][a-zA-Z0-9_]*$/`, which forbids `$`, so no valid table import
|
|
83
|
+
* can ever shadow it.
|
|
84
|
+
*/
|
|
85
|
+
export const runtimeSchema = ({
|
|
86
|
+
tableModules,
|
|
87
|
+
}: {
|
|
88
|
+
tableModules: ReadonlyArray<TableModuleBinding>;
|
|
89
|
+
}) =>
|
|
90
|
+
Effect.gen(function* () {
|
|
91
|
+
const cbw = new CodeBlockWriter({ indentNumberOfSpaces: 2 });
|
|
92
|
+
|
|
93
|
+
yield* cbw.writeLine(
|
|
94
|
+
`import { DatabaseSchema as $DatabaseSchema } from "@confect/server";`,
|
|
95
|
+
);
|
|
96
|
+
|
|
97
|
+
if (tableModules.length > 0) {
|
|
98
|
+
yield* cbw.blankLine();
|
|
99
|
+
yield* Effect.forEach(tableModules, ({ tableName, importPath }) =>
|
|
100
|
+
cbw.writeLine(`import ${tableName} from "${importPath}";`),
|
|
101
|
+
);
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
yield* cbw.blankLine();
|
|
105
|
+
|
|
106
|
+
if (tableModules.length === 0) {
|
|
107
|
+
yield* cbw.writeLine(`export default $DatabaseSchema.make({});`);
|
|
108
|
+
} else {
|
|
109
|
+
yield* cbw.writeLine(`export default $DatabaseSchema.make({`);
|
|
110
|
+
yield* cbw.indent(
|
|
111
|
+
Effect.gen(function* () {
|
|
112
|
+
for (const { tableName } of tableModules) {
|
|
113
|
+
yield* cbw.writeLine(`${tableName},`);
|
|
114
|
+
}
|
|
115
|
+
}),
|
|
116
|
+
);
|
|
117
|
+
yield* cbw.writeLine(`});`);
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
return yield* cbw.toString();
|
|
121
|
+
});
|
|
122
|
+
|
|
123
|
+
/**
|
|
124
|
+
* Emit `confect/_generated/convexSchema.ts` — the Convex deploy-time
|
|
125
|
+
* `SchemaDefinition`. Imports every table from its generated wrapper at
|
|
126
|
+
* `_generated/tables/<name>` and calls `defineSchema({...})` exactly once.
|
|
127
|
+
* The file deliberately avoids any `@confect/server` import so that the
|
|
128
|
+
* deploy artifact's import graph stays decoupled from the runtime
|
|
129
|
+
* `DatabaseSchema` machinery.
|
|
130
|
+
*
|
|
131
|
+
* The `defineSchema` import is aliased to `$defineSchema` because each table
|
|
132
|
+
* is imported under its own (filename-derived) name; a table named
|
|
133
|
+
* `defineSchema` would otherwise collide with the library import and emit a
|
|
134
|
+
* duplicate-binding file. The leading `$` makes the alias collision-proof:
|
|
135
|
+
* `validateConfectTableIdentifier` requires names to match
|
|
136
|
+
* `/^[a-zA-Z][a-zA-Z0-9_]*$/`, which forbids `$`, so no valid table import
|
|
137
|
+
* can ever shadow it.
|
|
138
|
+
*/
|
|
139
|
+
export const convexSchema = ({
|
|
140
|
+
tableModules,
|
|
141
|
+
}: {
|
|
142
|
+
tableModules: ReadonlyArray<TableModuleBinding>;
|
|
143
|
+
}) =>
|
|
144
|
+
Effect.gen(function* () {
|
|
145
|
+
const cbw = new CodeBlockWriter({ indentNumberOfSpaces: 2 });
|
|
146
|
+
|
|
147
|
+
yield* cbw.writeLine(
|
|
148
|
+
`import { defineSchema as $defineSchema } from "convex/server";`,
|
|
149
|
+
);
|
|
150
|
+
|
|
151
|
+
if (tableModules.length > 0) {
|
|
152
|
+
yield* cbw.blankLine();
|
|
153
|
+
yield* Effect.forEach(tableModules, ({ tableName, importPath }) =>
|
|
154
|
+
cbw.writeLine(`import ${tableName} from "${importPath}";`),
|
|
155
|
+
);
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
yield* cbw.blankLine();
|
|
159
|
+
|
|
160
|
+
if (tableModules.length === 0) {
|
|
161
|
+
yield* cbw.writeLine(`export default $defineSchema({});`);
|
|
162
|
+
} else {
|
|
163
|
+
yield* cbw.writeLine(`export default $defineSchema({`);
|
|
164
|
+
yield* cbw.indent(
|
|
165
|
+
Effect.gen(function* () {
|
|
166
|
+
for (const { tableName } of tableModules) {
|
|
167
|
+
yield* cbw.writeLine(`${tableName}: ${tableName}.tableDefinition,`);
|
|
168
|
+
}
|
|
169
|
+
}),
|
|
170
|
+
);
|
|
171
|
+
yield* cbw.writeLine(`});`);
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
return yield* cbw.toString();
|
|
175
|
+
});
|
|
176
|
+
|
|
177
|
+
/**
|
|
178
|
+
* Emit `confect/_generated/id.ts` — a type-constrained `Id` constructor and
|
|
179
|
+
* a `TableNames` union derived from the user's `confect/tables/*.ts`
|
|
180
|
+
* filenames. User-authored table modules import `Id` from this file to
|
|
181
|
+
* declare cross-table id references without typing the destination name as
|
|
182
|
+
* a free string (and without ever importing each other transitively).
|
|
183
|
+
*
|
|
184
|
+
* When the table directory is empty the `TableNames` union resolves to
|
|
185
|
+
* `never`, which still lets the file typecheck against an empty workspace.
|
|
186
|
+
*/
|
|
187
|
+
export const id = ({ tableNames }: { tableNames: ReadonlyArray<string> }) =>
|
|
188
|
+
Effect.gen(function* () {
|
|
189
|
+
const cbw = new CodeBlockWriter({ indentNumberOfSpaces: 2 });
|
|
190
|
+
|
|
191
|
+
yield* cbw.writeLine(`import { GenericId } from "@confect/core";`);
|
|
192
|
+
yield* cbw.blankLine();
|
|
193
|
+
|
|
194
|
+
const union =
|
|
195
|
+
tableNames.length === 0
|
|
196
|
+
? "never"
|
|
197
|
+
: tableNames.map((n) => `"${n}"`).join(" | ");
|
|
198
|
+
yield* cbw.writeLine(`export type TableNames = ${union};`);
|
|
199
|
+
yield* cbw.blankLine();
|
|
200
|
+
|
|
201
|
+
yield* cbw.writeLine(
|
|
202
|
+
`export const Id = <const TableName extends TableNames>(`,
|
|
47
203
|
);
|
|
204
|
+
yield* cbw.indent(cbw.writeLine(`tableName: TableName,`));
|
|
205
|
+
yield* cbw.writeLine(`) => GenericId.GenericId(tableName);`);
|
|
206
|
+
|
|
207
|
+
return yield* cbw.toString();
|
|
208
|
+
});
|
|
209
|
+
|
|
210
|
+
/**
|
|
211
|
+
* Emit `confect/_generated/tables/<tableName>.ts` — a two-line wrapper that
|
|
212
|
+
* imports the user-authored `UnnamedTable` and binds the file basename to
|
|
213
|
+
* it, producing the fully-named `Table` value that downstream consumers
|
|
214
|
+
* (schema, specs, impls) read.
|
|
215
|
+
*/
|
|
216
|
+
export const tableWrapper = ({
|
|
217
|
+
tableName,
|
|
218
|
+
unnamedImportPath,
|
|
219
|
+
}: {
|
|
220
|
+
tableName: string;
|
|
221
|
+
unnamedImportPath: string;
|
|
222
|
+
}) =>
|
|
223
|
+
Effect.gen(function* () {
|
|
224
|
+
const cbw = new CodeBlockWriter({ indentNumberOfSpaces: 2 });
|
|
225
|
+
|
|
226
|
+
yield* cbw.writeLine(`import unnamed from "${unnamedImportPath}";`);
|
|
227
|
+
yield* cbw.blankLine();
|
|
228
|
+
yield* cbw.writeLine(`export default unnamed("${tableName}");`);
|
|
48
229
|
|
|
49
230
|
return yield* cbw.toString();
|
|
50
231
|
});
|
|
@@ -110,54 +291,15 @@ export const refs = ({
|
|
|
110
291
|
return yield* cbw.toString();
|
|
111
292
|
});
|
|
112
293
|
|
|
113
|
-
export const
|
|
294
|
+
export const registeredFunctionsForGroup = ({
|
|
114
295
|
schemaImportPath,
|
|
115
296
|
specImportPath,
|
|
116
|
-
}: {
|
|
117
|
-
schemaImportPath: string;
|
|
118
|
-
specImportPath: string;
|
|
119
|
-
}) =>
|
|
120
|
-
Effect.gen(function* () {
|
|
121
|
-
const cbw = new CodeBlockWriter({ indentNumberOfSpaces: 2 });
|
|
122
|
-
|
|
123
|
-
yield* cbw.writeLine(`import { Api } from "@confect/server";`);
|
|
124
|
-
yield* cbw.writeLine(`import schema from "${schemaImportPath}";`);
|
|
125
|
-
yield* cbw.writeLine(`import spec from "${specImportPath}";`);
|
|
126
|
-
yield* cbw.blankLine();
|
|
127
|
-
yield* cbw.writeLine(`export default Api.make(schema, spec);`);
|
|
128
|
-
|
|
129
|
-
return yield* cbw.toString();
|
|
130
|
-
});
|
|
131
|
-
|
|
132
|
-
export const nodeApi = ({
|
|
133
|
-
schemaImportPath,
|
|
134
|
-
nodeSpecImportPath,
|
|
135
|
-
}: {
|
|
136
|
-
schemaImportPath: string;
|
|
137
|
-
nodeSpecImportPath: string;
|
|
138
|
-
}) =>
|
|
139
|
-
Effect.gen(function* () {
|
|
140
|
-
const cbw = new CodeBlockWriter({ indentNumberOfSpaces: 2 });
|
|
141
|
-
|
|
142
|
-
yield* cbw.writeLine(`import { Api } from "@confect/server";`);
|
|
143
|
-
yield* cbw.blankLine();
|
|
144
|
-
yield* cbw.writeLine(`import schema from "${schemaImportPath}";`);
|
|
145
|
-
yield* cbw.writeLine(`import nodeSpec from "${nodeSpecImportPath}";`);
|
|
146
|
-
yield* cbw.blankLine();
|
|
147
|
-
yield* cbw.writeLine(`export default Api.make(schema, nodeSpec);`);
|
|
148
|
-
|
|
149
|
-
return yield* cbw.toString();
|
|
150
|
-
});
|
|
151
|
-
|
|
152
|
-
export const registeredFunctionsForGroup = ({
|
|
153
|
-
apiImportPath,
|
|
154
|
-
groupPathDot,
|
|
155
297
|
implImportPath,
|
|
156
298
|
layerExportName,
|
|
157
299
|
useNode = false,
|
|
158
300
|
}: {
|
|
159
|
-
|
|
160
|
-
|
|
301
|
+
schemaImportPath: string;
|
|
302
|
+
specImportPath: string;
|
|
161
303
|
implImportPath: string;
|
|
162
304
|
layerExportName: string;
|
|
163
305
|
useNode?: boolean;
|
|
@@ -178,14 +320,20 @@ export const registeredFunctionsForGroup = ({
|
|
|
178
320
|
);
|
|
179
321
|
}
|
|
180
322
|
|
|
181
|
-
yield* cbw.writeLine(`import
|
|
323
|
+
yield* cbw.writeLine(`import databaseSchema from "${schemaImportPath}";`);
|
|
182
324
|
yield* cbw.writeLine(`import ${layerExportName} from "${implImportPath}";`);
|
|
183
325
|
yield* cbw.blankLine();
|
|
184
|
-
|
|
326
|
+
// The group's own leaf spec is referenced type-only (`typeof import(...)`),
|
|
327
|
+
// so the spec module is erased at transpile time and never enters the
|
|
328
|
+
// per-function bundle; only `databaseSchema` and the impl are runtime
|
|
329
|
+
// imports. Typing from the leaf spec (not the project-wide assembled spec)
|
|
330
|
+
// keeps the registry's type dependent solely on its own group.
|
|
331
|
+
const specType = `typeof import("${specImportPath}")["default"]`;
|
|
332
|
+
const makeFn = useNode
|
|
333
|
+
? "RegisteredNodeFunction.make"
|
|
334
|
+
: "RegisteredConvexFunction.make";
|
|
185
335
|
yield* cbw.writeLine(
|
|
186
|
-
|
|
187
|
-
? `export default RegisteredFunctions.buildForGroup(api, ${quotedGroupPath}, ${layerExportName}, RegisteredNodeFunction.make);`
|
|
188
|
-
: `export default RegisteredFunctions.buildForGroup(api, ${quotedGroupPath}, ${layerExportName}, RegisteredConvexFunction.make);`,
|
|
336
|
+
`export default RegisteredFunctions.buildForGroup<${specType}>(databaseSchema, ${layerExportName}, ${makeFn});`,
|
|
189
337
|
);
|
|
190
338
|
|
|
191
339
|
return yield* cbw.toString();
|
|
@@ -473,13 +621,6 @@ export const assembledSpec = ({
|
|
|
473
621
|
runtime === "Convex" ? "GroupSpec.makeAt" : "GroupSpec.makeNodeAt";
|
|
474
622
|
|
|
475
623
|
yield* cbw.write(`export default ${specFactory}`);
|
|
476
|
-
yield* Effect.forEach(collectLeafPaths(nodes), (leaf) =>
|
|
477
|
-
Effect.gen(function* () {
|
|
478
|
-
yield* cbw.write(`.addPath(${leaf.binding.localName}, `);
|
|
479
|
-
yield* cbw.quote(leaf.dotPath);
|
|
480
|
-
yield* cbw.write(")");
|
|
481
|
-
}),
|
|
482
|
-
);
|
|
483
624
|
yield* Effect.forEach(nodes, (node) =>
|
|
484
625
|
writeRootAddAt(cbw, node, groupFactory),
|
|
485
626
|
);
|