@confect/cli 7.0.0 → 9.0.0-next.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.
@@ -1,8 +1,19 @@
1
1
  import { Spec } from "@confect/core";
2
- import { DatabaseSchema } from "@confect/server";
2
+ import * as DatabaseSchema from "@confect/server/DatabaseSchema";
3
3
  import { Command } from "@effect/cli";
4
4
  import { FileSystem, Path } from "@effect/platform";
5
- import { Effect, Match, Option } from "effect";
5
+ import { Array, Effect, Either, HashSet, Match, Option, Ref } from "effect";
6
+ import { fromBundlerError } from "../BuildError";
7
+ import * as CodegenError from "../CodegenError";
8
+ import {
9
+ MissingImplFileError,
10
+ MissingSchemaFileError,
11
+ MissingSpecFileError,
12
+ SchemaInvalidDefaultExportError,
13
+ } from "../CodegenError";
14
+ import { ConfectDirectory } from "../ConfectDirectory";
15
+ import { ConvexDirectory } from "../ConvexDirectory";
16
+ import * as FunctionPaths from "../FunctionPaths";
6
17
  import {
7
18
  logFileAdded,
8
19
  logFileModified,
@@ -10,66 +21,94 @@ import {
10
21
  logPending,
11
22
  logSuccess,
12
23
  } from "../log";
13
- import { ConfectDirectory } from "../ConfectDirectory";
14
- import { ConvexDirectory } from "../ConvexDirectory";
24
+ import {
25
+ discoverLeafImplFiles,
26
+ discoverLeafSpecFiles,
27
+ implPathForSpec,
28
+ registeredFunctionsRelativePath,
29
+ specPathForImpl,
30
+ toLeafModule,
31
+ toNodeRegistryLeaf,
32
+ validateImpl,
33
+ validateSpec,
34
+ type LeafModule,
35
+ } from "../LeafModule";
36
+ import {
37
+ assemblyNodesFromLeaves,
38
+ partitionByRuntime,
39
+ } from "../SpecAssemblyNode";
15
40
  import * as templates from "../templates";
41
+ import * as Bundler from "../Bundler";
16
42
  import {
17
- bundleAndImport,
18
43
  generateAuthConfig,
19
44
  generateCrons,
20
45
  generateFunctions,
21
46
  generateHttp,
22
47
  removePathExtension,
48
+ removePathIfExists,
49
+ toModuleImportPath,
50
+ touchConvexSchema,
23
51
  writeFileStringAndLog,
52
+ WriteTracker,
24
53
  } from "../utils";
25
54
 
26
- const getNodeSpecPath = Effect.gen(function* () {
27
- const path = yield* Path.Path;
28
- const confectDirectory = yield* ConfectDirectory.get;
29
- return path.join(confectDirectory, "nodeSpec.ts");
30
- });
31
-
32
- const loadNodeSpec = Effect.gen(function* () {
33
- const fs = yield* FileSystem.FileSystem;
34
- const nodeSpecPath = yield* getNodeSpecPath;
35
-
36
- if (!(yield* fs.exists(nodeSpecPath))) {
37
- return Option.none<Spec.AnyWithPropsWithRuntime<"Node">>();
38
- }
39
-
40
- const nodeSpecModule = yield* bundleAndImport(nodeSpecPath);
41
- const nodeSpec = nodeSpecModule.default;
42
-
43
- if (!Spec.isNodeSpec(nodeSpec)) {
44
- return yield* Effect.die("nodeSpec.ts does not export a valid Node Spec");
45
- }
46
-
47
- return Option.some(nodeSpec);
48
- });
55
+ const GENERATED_SPEC_PATH = "_generated/spec.ts";
56
+ const GENERATED_NODE_SPEC_PATH = "_generated/nodeSpec.ts";
57
+
58
+ const LEGACY_PATHS = [
59
+ "spec.ts",
60
+ "nodeSpec.ts",
61
+ "impl.ts",
62
+ "nodeImpl.ts",
63
+ "notesAndRandom.impl.ts",
64
+ "groups.impl.ts",
65
+ "_generated/registeredFunctions.ts",
66
+ "_generated/nodeRegisteredFunctions.ts",
67
+ "_generated/impl.ts",
68
+ "_generated/nodeImpl.ts",
69
+ ];
49
70
 
50
71
  export const codegen = Command.make("codegen", {}, () =>
51
72
  Effect.gen(function* () {
52
73
  yield* logPending("Performing initial sync…");
53
- yield* codegenHandler;
54
- yield* logSuccess("Generated files are up-to-date");
74
+ yield* codegenHandler.pipe(
75
+ Effect.asVoid,
76
+ Effect.tap(() => logSuccess("Generated files are up-to-date")),
77
+ CodegenError.tapAndLog,
78
+ );
55
79
  }),
56
80
  ).pipe(
57
81
  Command.withDescription(
58
- "Generate `confect/_generated` files and the contents of the `convex` directory (except `tsconfig.json`)",
82
+ "Generate `confect/_generated` files and the contents of the `convex` directory (except `convex.config.ts` and `tsconfig.json`)",
59
83
  ),
60
84
  );
61
85
 
62
86
  export const codegenHandler = Effect.gen(function* () {
87
+ const tracker = yield* Ref.make(false);
88
+
89
+ const functionPaths = yield* runCodegen.pipe(
90
+ Effect.provideService(WriteTracker, tracker),
91
+ );
92
+
93
+ const anyWritesHappened = yield* Ref.get(tracker);
94
+ return { functionPaths, anyWritesHappened };
95
+ });
96
+
97
+ const runCodegen = Effect.gen(function* () {
63
98
  yield* generateConfectGeneratedDirectory;
99
+ // Validate schema first so its missing-file / invalid-default-export
100
+ // diagnostics surface ahead of impl bundling, which transitively depends
101
+ // on schema via `_generated/api.ts` and would otherwise blow up with a
102
+ // less actionable bundler error.
103
+ yield* validateSchema;
104
+ const leaves = yield* loadAndValidateLeafModules;
105
+ yield* removeLegacyFiles;
106
+ yield* generateAssembledSpecs(leaves);
107
+ yield* validateImplModules(leaves);
108
+ yield* generateGroupRegisteredFunctions(leaves);
109
+ yield* removeObsoleteRegisteredFunctions(leaves);
64
110
  yield* Effect.all(
65
- [
66
- generateApi,
67
- generateRefs,
68
- generateRegisteredFunctions,
69
- generateNodeApi,
70
- generateNodeRegisteredFunctions,
71
- generateServices,
72
- ],
111
+ [generateApi, generateRefs, generateNodeApi, generateServices],
73
112
  { concurrency: "unbounded" },
74
113
  );
75
114
  const [functionPaths] = yield* Effect.all(
@@ -82,6 +121,7 @@ export const codegenHandler = Effect.gen(function* () {
82
121
  ],
83
122
  { concurrency: "unbounded" },
84
123
  );
124
+ yield* touchConvexSchema;
85
125
  return functionPaths;
86
126
  });
87
127
 
@@ -98,6 +138,273 @@ const generateConfectGeneratedDirectory = Effect.gen(function* () {
98
138
  }
99
139
  });
100
140
 
141
+ const loadAndValidateLeafModules = Effect.gen(function* () {
142
+ const fs = yield* FileSystem.FileSystem;
143
+ const path = yield* Path.Path;
144
+ const confectDirectory = yield* ConfectDirectory.get;
145
+ const specFiles = yield* discoverLeafSpecFiles;
146
+
147
+ const leaves = yield* Effect.forEach(specFiles, (specRelativePath) =>
148
+ Effect.gen(function* () {
149
+ const leaf = yield* toLeafModule(specRelativePath);
150
+ yield* validateSpec(leaf);
151
+
152
+ const implRelativePath = yield* implPathForSpec(specRelativePath);
153
+ const implAbsolutePath = path.join(confectDirectory, implRelativePath);
154
+ if (!(yield* fs.exists(implAbsolutePath))) {
155
+ return yield* new MissingImplFileError({
156
+ specPath: specRelativePath,
157
+ expectedImplPath: implRelativePath,
158
+ });
159
+ }
160
+
161
+ return leaf;
162
+ }),
163
+ );
164
+
165
+ yield* validateOrphanImpls(specFiles);
166
+
167
+ return leaves;
168
+ });
169
+
170
+ const validateOrphanImpls = (specFiles: ReadonlyArray<string>) =>
171
+ Effect.gen(function* () {
172
+ const fs = yield* FileSystem.FileSystem;
173
+ const path = yield* Path.Path;
174
+ const confectDirectory = yield* ConfectDirectory.get;
175
+ const implFiles = yield* discoverLeafImplFiles;
176
+ const specPaths = new Set(specFiles);
177
+
178
+ yield* Effect.forEach(implFiles, (implRelativePath) =>
179
+ Effect.gen(function* () {
180
+ const specRelativePath = yield* specPathForImpl(implRelativePath);
181
+ if (specPaths.has(specRelativePath)) {
182
+ return;
183
+ }
184
+
185
+ const specAbsolutePath = path.join(confectDirectory, specRelativePath);
186
+ if (!(yield* fs.exists(specAbsolutePath))) {
187
+ return yield* new MissingSpecFileError({
188
+ implPath: implRelativePath,
189
+ expectedSpecPath: specRelativePath,
190
+ });
191
+ }
192
+ }),
193
+ );
194
+ });
195
+
196
+ const removeLegacyFiles = Effect.gen(function* () {
197
+ const fs = yield* FileSystem.FileSystem;
198
+ const path = yield* Path.Path;
199
+ const confectDirectory = yield* ConfectDirectory.get;
200
+
201
+ yield* Effect.forEach(LEGACY_PATHS, (relativePath) =>
202
+ Effect.gen(function* () {
203
+ const absolutePath = path.join(confectDirectory, relativePath);
204
+ if (yield* fs.exists(absolutePath)) {
205
+ yield* removePathIfExists(absolutePath);
206
+ yield* logFileRemoved(absolutePath);
207
+ }
208
+ }),
209
+ );
210
+ });
211
+
212
+ const generateAssembledSpecs = (leaves: ReadonlyArray<LeafModule>) =>
213
+ Effect.gen(function* () {
214
+ const path = yield* Path.Path;
215
+ const confectDirectory = yield* ConfectDirectory.get;
216
+ const { convex, node } = partitionByRuntime(leaves);
217
+
218
+ if (convex.length > 0) {
219
+ const nodes = assemblyNodesFromLeaves(convex);
220
+ const specContents = yield* templates.assembledSpec({
221
+ nodes,
222
+ runtime: "Convex",
223
+ });
224
+ yield* writeFileStringAndLog(
225
+ path.join(confectDirectory, GENERATED_SPEC_PATH),
226
+ specContents,
227
+ );
228
+ }
229
+
230
+ if (node.length > 0) {
231
+ const nodes = assemblyNodesFromLeaves(
232
+ Array.map(node, toNodeRegistryLeaf),
233
+ );
234
+ const nodeSpecContents = yield* templates.assembledSpec({
235
+ nodes,
236
+ runtime: "Node",
237
+ });
238
+ yield* writeFileStringAndLog(
239
+ path.join(confectDirectory, GENERATED_NODE_SPEC_PATH),
240
+ nodeSpecContents,
241
+ );
242
+ }
243
+ });
244
+
245
+ const validateImplModules = (leaves: ReadonlyArray<LeafModule>) =>
246
+ Effect.forEach(leaves, validateImpl);
247
+
248
+ const generateGroupRegisteredFunctions = (leaves: ReadonlyArray<LeafModule>) =>
249
+ Effect.gen(function* () {
250
+ const path = yield* Path.Path;
251
+ const confectDirectory = yield* ConfectDirectory.get;
252
+
253
+ yield* Effect.forEach(leaves, (leaf) =>
254
+ Effect.gen(function* () {
255
+ const registryRelativePath =
256
+ yield* registeredFunctionsRelativePath(leaf);
257
+ const registryPath = path.join(
258
+ confectDirectory,
259
+ "_generated",
260
+ registryRelativePath,
261
+ );
262
+ const registryDir = path.dirname(registryPath);
263
+ const fs = yield* FileSystem.FileSystem;
264
+ if (!(yield* fs.exists(registryDir))) {
265
+ yield* fs.makeDirectory(registryDir, { recursive: true });
266
+ }
267
+
268
+ const implRelativePath = yield* implPathForSpec(leaf.relativePath);
269
+ const apiFileName = leaf.runtime === "Node" ? "nodeApi.ts" : "api.ts";
270
+ const apiImportPath = yield* toModuleImportPath(
271
+ path.relative(
272
+ path.dirname(registryPath),
273
+ path.join(confectDirectory, "_generated", apiFileName),
274
+ ),
275
+ );
276
+ const implImportPath = yield* toModuleImportPath(
277
+ path.relative(
278
+ path.dirname(registryPath),
279
+ path.join(confectDirectory, implRelativePath),
280
+ ),
281
+ );
282
+
283
+ const contents = yield* templates.registeredFunctionsForGroup({
284
+ apiImportPath,
285
+ groupPathDot: leaf.registryGroupPathDot,
286
+ implImportPath,
287
+ layerExportName: leaf.exportName,
288
+ useNode: leaf.runtime === "Node",
289
+ });
290
+
291
+ yield* writeFileStringAndLog(registryPath, contents);
292
+ }),
293
+ );
294
+ });
295
+
296
+ const removeObsoleteRegisteredFunctions = (leaves: ReadonlyArray<LeafModule>) =>
297
+ Effect.gen(function* () {
298
+ const fs = yield* FileSystem.FileSystem;
299
+ const path = yield* Path.Path;
300
+ const confectDirectory = yield* ConfectDirectory.get;
301
+ const registryRoot = path.join(
302
+ confectDirectory,
303
+ "_generated",
304
+ "registeredFunctions",
305
+ );
306
+
307
+ if (!(yield* fs.exists(registryRoot))) {
308
+ return;
309
+ }
310
+
311
+ const expected = new Set(
312
+ yield* Effect.forEach(leaves, (leaf) =>
313
+ registeredFunctionsRelativePath(leaf),
314
+ ),
315
+ );
316
+
317
+ const existing = yield* fs.readDirectory(registryRoot, { recursive: true });
318
+ yield* Effect.forEach(existing, (relativePath) => {
319
+ if (path.extname(relativePath) !== ".ts") {
320
+ return Effect.void;
321
+ }
322
+ const normalized = path.join("registeredFunctions", relativePath);
323
+ if (!expected.has(normalized)) {
324
+ return Effect.gen(function* () {
325
+ const absolutePath = path.join(registryRoot, relativePath);
326
+ if (yield* fs.exists(absolutePath)) {
327
+ yield* removePathIfExists(absolutePath);
328
+ yield* logFileRemoved(absolutePath);
329
+ }
330
+ });
331
+ }
332
+ return Effect.void;
333
+ });
334
+ });
335
+
336
+ const getGeneratedSpecPath = Effect.gen(function* () {
337
+ const path = yield* Path.Path;
338
+ const confectDirectory = yield* ConfectDirectory.get;
339
+ return path.join(confectDirectory, GENERATED_SPEC_PATH);
340
+ });
341
+
342
+ const getGeneratedNodeSpecPath = Effect.gen(function* () {
343
+ const path = yield* Path.Path;
344
+ const confectDirectory = yield* ConfectDirectory.get;
345
+ return path.join(confectDirectory, GENERATED_NODE_SPEC_PATH);
346
+ });
347
+
348
+ const loadGeneratedSpec = Effect.gen(function* () {
349
+ const specPath = yield* getGeneratedSpecPath;
350
+ const { module: specModule } = yield* Bundler.bundle(specPath);
351
+ const spec = specModule.default;
352
+
353
+ if (!Spec.isConvexSpec(spec)) {
354
+ return yield* Effect.dieMessage(
355
+ "_generated/spec.ts does not export a valid Convex Spec",
356
+ );
357
+ }
358
+
359
+ return spec;
360
+ });
361
+
362
+ const loadGeneratedNodeSpec = Effect.gen(function* () {
363
+ const fs = yield* FileSystem.FileSystem;
364
+ const nodeSpecPath = yield* getGeneratedNodeSpecPath;
365
+
366
+ if (!(yield* fs.exists(nodeSpecPath))) {
367
+ return Option.none<Spec.AnyWithPropsWithRuntime<"Node">>();
368
+ }
369
+
370
+ const { module: nodeSpecModule } = yield* Bundler.bundle(nodeSpecPath);
371
+ const nodeSpec = nodeSpecModule.default;
372
+
373
+ if (!Spec.isNodeSpec(nodeSpec)) {
374
+ return yield* Effect.dieMessage(
375
+ "_generated/nodeSpec.ts does not export a valid Node Spec",
376
+ );
377
+ }
378
+
379
+ return Option.some(nodeSpec);
380
+ });
381
+
382
+ const emptyFunctionPaths = FunctionPaths.FunctionPaths.make(HashSet.empty());
383
+
384
+ export const loadPreviousFunctionPaths = Effect.gen(function* () {
385
+ const fs = yield* FileSystem.FileSystem;
386
+ const specPath = yield* getGeneratedSpecPath;
387
+
388
+ if (!(yield* fs.exists(specPath))) {
389
+ return emptyFunctionPaths;
390
+ }
391
+
392
+ const specEither = yield* loadGeneratedSpec.pipe(Effect.either);
393
+
394
+ return yield* Either.match(specEither, {
395
+ onLeft: () => Effect.succeed(emptyFunctionPaths),
396
+ onRight: (spec) =>
397
+ Effect.gen(function* () {
398
+ const nodeSpecOption = yield* loadGeneratedNodeSpec;
399
+ const mergedSpec = Option.match(nodeSpecOption, {
400
+ onNone: () => spec,
401
+ onSome: (nodeSpec) => Spec.merge(spec, nodeSpec),
402
+ });
403
+ return FunctionPaths.make(mergedSpec);
404
+ }),
405
+ });
406
+ });
407
+
101
408
  const generateApi = Effect.gen(function* () {
102
409
  const path = yield* Path.Path;
103
410
  const confectDirectory = yield* ConfectDirectory.get;
@@ -105,12 +412,12 @@ const generateApi = Effect.gen(function* () {
105
412
  const apiPath = path.join(confectDirectory, "_generated", "api.ts");
106
413
  const apiDir = path.dirname(apiPath);
107
414
 
108
- const schemaImportPath = yield* removePathExtension(
415
+ const schemaImportPath = yield* toModuleImportPath(
109
416
  path.relative(apiDir, path.join(confectDirectory, "schema.ts")),
110
417
  );
111
418
 
112
- const specImportPath = yield* removePathExtension(
113
- path.relative(apiDir, path.join(confectDirectory, "spec.ts")),
419
+ const specImportPath = yield* toModuleImportPath(
420
+ path.relative(apiDir, path.join(confectDirectory, GENERATED_SPEC_PATH)),
114
421
  );
115
422
 
116
423
  const apiContents = yield* templates.api({
@@ -126,12 +433,12 @@ export const generateNodeApi = Effect.gen(function* () {
126
433
  const path = yield* Path.Path;
127
434
  const confectDirectory = yield* ConfectDirectory.get;
128
435
 
129
- const nodeSpecPath = yield* getNodeSpecPath;
436
+ const nodeSpecPath = yield* getGeneratedNodeSpecPath;
130
437
  const nodeApiPath = path.join(confectDirectory, "_generated", "nodeApi.ts");
131
438
 
132
439
  if (!(yield* fs.exists(nodeSpecPath))) {
133
440
  if (yield* fs.exists(nodeApiPath)) {
134
- yield* fs.remove(nodeApiPath);
441
+ yield* removePathIfExists(nodeApiPath);
135
442
  yield* logFileRemoved(nodeApiPath);
136
443
  }
137
444
  return;
@@ -139,11 +446,11 @@ export const generateNodeApi = Effect.gen(function* () {
139
446
 
140
447
  const nodeApiDir = path.dirname(nodeApiPath);
141
448
 
142
- const schemaImportPath = yield* removePathExtension(
449
+ const schemaImportPath = yield* toModuleImportPath(
143
450
  path.relative(nodeApiDir, path.join(confectDirectory, "schema.ts")),
144
451
  );
145
452
 
146
- const nodeSpecImportPath = yield* removePathExtension(
453
+ const nodeSpecImportPath = yield* toModuleImportPath(
147
454
  path.relative(nodeApiDir, nodeSpecPath),
148
455
  );
149
456
 
@@ -156,47 +463,52 @@ export const generateNodeApi = Effect.gen(function* () {
156
463
  });
157
464
 
158
465
  const generateFunctionModules = Effect.gen(function* () {
159
- const fs = yield* FileSystem.FileSystem;
160
- const path = yield* Path.Path;
161
- const confectDirectory = yield* ConfectDirectory.get;
162
-
163
- const specPath = path.join(confectDirectory, "spec.ts");
164
-
165
- const specModule = yield* bundleAndImport(specPath);
166
- const spec = specModule.default;
167
-
168
- if (!Spec.isConvexSpec(spec)) {
169
- return yield* Effect.die("spec.ts does not export a valid Convex Spec");
170
- }
171
-
172
- const nodeImplPath = path.join(confectDirectory, "nodeImpl.ts");
173
- const nodeImplExists = yield* fs.exists(nodeImplPath);
174
- const nodeSpecOption = yield* loadNodeSpec;
466
+ const spec = yield* loadGeneratedSpec;
467
+ const nodeSpecOption = yield* loadGeneratedNodeSpec;
175
468
 
176
469
  const mergedSpec = Option.match(nodeSpecOption, {
177
470
  onNone: () => spec,
178
- onSome: (nodeSpec) => (nodeImplExists ? Spec.merge(spec, nodeSpec) : spec),
471
+ onSome: (nodeSpec) => Spec.merge(spec, nodeSpec),
179
472
  });
180
473
 
181
474
  return yield* generateFunctions(mergedSpec);
182
475
  });
183
476
 
184
- const generateSchema = Effect.gen(function* () {
477
+ export const validateSchema = Effect.gen(function* () {
478
+ const fs = yield* FileSystem.FileSystem;
185
479
  const path = yield* Path.Path;
186
480
  const confectDirectory = yield* ConfectDirectory.get;
187
- const convexDirectory = yield* ConvexDirectory.get;
188
-
189
481
  const confectSchemaPath = path.join(confectDirectory, "schema.ts");
190
482
 
191
- yield* bundleAndImport(confectSchemaPath).pipe(
192
- Effect.andThen((schemaModule) => {
483
+ if (!(yield* fs.exists(confectSchemaPath))) {
484
+ return yield* new MissingSchemaFileError({ schemaPath: "schema.ts" });
485
+ }
486
+
487
+ yield* Bundler.bundle(confectSchemaPath).pipe(
488
+ Effect.mapError((error) => fromBundlerError("schema.ts", error)),
489
+ Effect.andThen(({ module: schemaModule }) => {
193
490
  const defaultExport = schemaModule.default;
194
491
 
195
- return DatabaseSchema.isSchema(defaultExport)
492
+ return DatabaseSchema.isDatabaseSchema(defaultExport)
196
493
  ? Effect.succeed(defaultExport)
197
- : Effect.die("Invalid schema module");
494
+ : Effect.fail(
495
+ new SchemaInvalidDefaultExportError({
496
+ schemaPath: "schema.ts",
497
+ }),
498
+ );
198
499
  }),
199
500
  );
501
+ });
502
+
503
+ const generateSchema = Effect.gen(function* () {
504
+ const path = yield* Path.Path;
505
+ const confectDirectory = yield* ConfectDirectory.get;
506
+ const convexDirectory = yield* ConvexDirectory.get;
507
+
508
+ const confectSchemaPath = path.join(confectDirectory, "schema.ts");
509
+
510
+ // `validateSchema` runs once at the top of `runCodegen`; no need to
511
+ // bundle the schema again here.
200
512
 
201
513
  const convexSchemaPath = path.join(convexDirectory, "schema.ts");
202
514
 
@@ -231,72 +543,6 @@ const generateServices = Effect.gen(function* () {
231
543
  yield* writeFileStringAndLog(servicesPath, servicesContentsString);
232
544
  });
233
545
 
234
- const generateRegisteredFunctions = Effect.gen(function* () {
235
- const path = yield* Path.Path;
236
- const confectDirectory = yield* ConfectDirectory.get;
237
-
238
- const confectGeneratedDirectory = path.join(confectDirectory, "_generated");
239
-
240
- const registeredFunctionsPath = path.join(
241
- confectGeneratedDirectory,
242
- "registeredFunctions.ts",
243
- );
244
- const implImportPath = yield* removePathExtension(
245
- path.relative(
246
- path.dirname(registeredFunctionsPath),
247
- path.join(confectDirectory, "impl.ts"),
248
- ),
249
- );
250
-
251
- const registeredFunctionsContents = yield* templates.registeredFunctions({
252
- implImportPath,
253
- });
254
-
255
- yield* writeFileStringAndLog(
256
- registeredFunctionsPath,
257
- registeredFunctionsContents,
258
- );
259
- });
260
-
261
- export const generateNodeRegisteredFunctions = Effect.gen(function* () {
262
- const fs = yield* FileSystem.FileSystem;
263
- const path = yield* Path.Path;
264
- const confectDirectory = yield* ConfectDirectory.get;
265
-
266
- const nodeImplPath = path.join(confectDirectory, "nodeImpl.ts");
267
- const nodeSpecPath = yield* getNodeSpecPath;
268
- const nodeRegisteredFunctionsPath = path.join(
269
- confectDirectory,
270
- "_generated",
271
- "nodeRegisteredFunctions.ts",
272
- );
273
-
274
- const nodeImplExists = yield* fs.exists(nodeImplPath);
275
- const nodeSpecExists = yield* fs.exists(nodeSpecPath);
276
-
277
- if (!nodeImplExists || !nodeSpecExists) {
278
- if (yield* fs.exists(nodeRegisteredFunctionsPath)) {
279
- yield* fs.remove(nodeRegisteredFunctionsPath);
280
- yield* logFileRemoved(nodeRegisteredFunctionsPath);
281
- }
282
- return;
283
- }
284
-
285
- const nodeImplImportPath = yield* removePathExtension(
286
- path.relative(path.dirname(nodeRegisteredFunctionsPath), nodeImplPath),
287
- );
288
-
289
- const nodeRegisteredFunctionsContents =
290
- yield* templates.nodeRegisteredFunctions({
291
- nodeImplImportPath,
292
- });
293
-
294
- yield* writeFileStringAndLog(
295
- nodeRegisteredFunctionsPath,
296
- nodeRegisteredFunctionsContents,
297
- );
298
- });
299
-
300
546
  const generateRefs = Effect.gen(function* () {
301
547
  const fs = yield* FileSystem.FileSystem;
302
548
  const path = yield* Path.Path;
@@ -306,19 +552,21 @@ const generateRefs = Effect.gen(function* () {
306
552
  const refsPath = path.join(confectGeneratedDirectory, "refs.ts");
307
553
  const refsDir = path.dirname(refsPath);
308
554
 
309
- const specImportPath = yield* removePathExtension(
310
- path.relative(refsDir, path.join(confectDirectory, "spec.ts")),
555
+ const specImportPath = yield* toModuleImportPath(
556
+ path.relative(refsDir, path.join(confectDirectory, GENERATED_SPEC_PATH)),
311
557
  );
312
558
 
313
- const nodeSpecPath = yield* getNodeSpecPath;
559
+ const nodeSpecPath = yield* getGeneratedNodeSpecPath;
314
560
  const nodeSpecExists = yield* fs.exists(nodeSpecPath);
315
561
  const nodeSpecImportPath = nodeSpecExists
316
- ? yield* removePathExtension(path.relative(refsDir, nodeSpecPath))
317
- : null;
562
+ ? Option.some(
563
+ yield* toModuleImportPath(path.relative(refsDir, nodeSpecPath)),
564
+ )
565
+ : Option.none<string>();
318
566
 
319
567
  const refsContents = yield* templates.refs({
320
568
  specImportPath,
321
- ...(nodeSpecImportPath === null ? {} : { nodeSpecImportPath }),
569
+ nodeSpecImportPath,
322
570
  });
323
571
 
324
572
  yield* writeFileStringAndLog(refsPath, refsContents);