@confect/cli 8.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.
- package/CHANGELOG.md +43 -1
- package/dist/BuildError.mjs +101 -0
- package/dist/BuildError.mjs.map +1 -0
- package/dist/Bundler.mjs +91 -0
- package/dist/Bundler.mjs.map +1 -0
- package/dist/CodeBlockWriter.mjs +55 -0
- package/dist/CodeBlockWriter.mjs.map +1 -0
- package/dist/CodegenError.mjs +94 -0
- package/dist/CodegenError.mjs.map +1 -0
- package/dist/FunctionPaths.mjs +1 -1
- package/dist/LeafModule.mjs +167 -0
- package/dist/LeafModule.mjs.map +1 -0
- package/dist/SpecAssemblyNode.mjs +33 -0
- package/dist/SpecAssemblyNode.mjs.map +1 -0
- package/dist/confect/codegen.mjs +222 -72
- package/dist/confect/codegen.mjs.map +1 -1
- package/dist/confect/dev.mjs +234 -180
- package/dist/confect/dev.mjs.map +1 -1
- package/dist/log.mjs +13 -1
- package/dist/log.mjs.map +1 -1
- package/dist/package.mjs +1 -1
- package/dist/templates.mjs +65 -72
- package/dist/templates.mjs.map +1 -1
- package/dist/utils.mjs +77 -73
- package/dist/utils.mjs.map +1 -1
- package/package.json +4 -3
- package/src/BuildError.ts +210 -0
- package/src/Bundler.ts +144 -0
- package/src/CodeBlockWriter.ts +65 -0
- package/src/CodegenError.ts +344 -0
- package/src/LeafModule.ts +313 -0
- package/src/SpecAssemblyNode.ts +82 -0
- package/src/confect/codegen.ts +390 -141
- package/src/confect/dev.ts +556 -435
- package/src/log.ts +21 -0
- package/src/templates.ts +141 -109
- package/src/utils.ts +118 -93
package/src/confect/codegen.ts
CHANGED
|
@@ -1,7 +1,19 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { Spec } from "@confect/core";
|
|
2
|
+
import * as DatabaseSchema from "@confect/server/DatabaseSchema";
|
|
2
3
|
import { Command } from "@effect/cli";
|
|
3
4
|
import { FileSystem, Path } from "@effect/platform";
|
|
4
|
-
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";
|
|
5
17
|
import {
|
|
6
18
|
logFileAdded,
|
|
7
19
|
logFileModified,
|
|
@@ -9,66 +21,94 @@ import {
|
|
|
9
21
|
logPending,
|
|
10
22
|
logSuccess,
|
|
11
23
|
} from "../log";
|
|
12
|
-
import {
|
|
13
|
-
|
|
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";
|
|
14
40
|
import * as templates from "../templates";
|
|
41
|
+
import * as Bundler from "../Bundler";
|
|
15
42
|
import {
|
|
16
|
-
bundleAndImport,
|
|
17
43
|
generateAuthConfig,
|
|
18
44
|
generateCrons,
|
|
19
45
|
generateFunctions,
|
|
20
46
|
generateHttp,
|
|
21
47
|
removePathExtension,
|
|
48
|
+
removePathIfExists,
|
|
49
|
+
toModuleImportPath,
|
|
50
|
+
touchConvexSchema,
|
|
22
51
|
writeFileStringAndLog,
|
|
52
|
+
WriteTracker,
|
|
23
53
|
} from "../utils";
|
|
24
54
|
|
|
25
|
-
const
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
const nodeSpec = nodeSpecModule.default;
|
|
41
|
-
|
|
42
|
-
if (!Spec.isNodeSpec(nodeSpec)) {
|
|
43
|
-
return yield* Effect.die("nodeSpec.ts does not export a valid Node Spec");
|
|
44
|
-
}
|
|
45
|
-
|
|
46
|
-
return Option.some(nodeSpec);
|
|
47
|
-
});
|
|
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
|
+
];
|
|
48
70
|
|
|
49
71
|
export const codegen = Command.make("codegen", {}, () =>
|
|
50
72
|
Effect.gen(function* () {
|
|
51
73
|
yield* logPending("Performing initial sync…");
|
|
52
|
-
yield* codegenHandler
|
|
53
|
-
|
|
74
|
+
yield* codegenHandler.pipe(
|
|
75
|
+
Effect.asVoid,
|
|
76
|
+
Effect.tap(() => logSuccess("Generated files are up-to-date")),
|
|
77
|
+
CodegenError.tapAndLog,
|
|
78
|
+
);
|
|
54
79
|
}),
|
|
55
80
|
).pipe(
|
|
56
81
|
Command.withDescription(
|
|
57
|
-
"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`)",
|
|
58
83
|
),
|
|
59
84
|
);
|
|
60
85
|
|
|
61
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* () {
|
|
62
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);
|
|
63
110
|
yield* Effect.all(
|
|
64
|
-
[
|
|
65
|
-
generateApi,
|
|
66
|
-
generateRefs,
|
|
67
|
-
generateRegisteredFunctions,
|
|
68
|
-
generateNodeApi,
|
|
69
|
-
generateNodeRegisteredFunctions,
|
|
70
|
-
generateServices,
|
|
71
|
-
],
|
|
111
|
+
[generateApi, generateRefs, generateNodeApi, generateServices],
|
|
72
112
|
{ concurrency: "unbounded" },
|
|
73
113
|
);
|
|
74
114
|
const [functionPaths] = yield* Effect.all(
|
|
@@ -81,6 +121,7 @@ export const codegenHandler = Effect.gen(function* () {
|
|
|
81
121
|
],
|
|
82
122
|
{ concurrency: "unbounded" },
|
|
83
123
|
);
|
|
124
|
+
yield* touchConvexSchema;
|
|
84
125
|
return functionPaths;
|
|
85
126
|
});
|
|
86
127
|
|
|
@@ -97,6 +138,273 @@ const generateConfectGeneratedDirectory = Effect.gen(function* () {
|
|
|
97
138
|
}
|
|
98
139
|
});
|
|
99
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
|
+
|
|
100
408
|
const generateApi = Effect.gen(function* () {
|
|
101
409
|
const path = yield* Path.Path;
|
|
102
410
|
const confectDirectory = yield* ConfectDirectory.get;
|
|
@@ -104,12 +412,12 @@ const generateApi = Effect.gen(function* () {
|
|
|
104
412
|
const apiPath = path.join(confectDirectory, "_generated", "api.ts");
|
|
105
413
|
const apiDir = path.dirname(apiPath);
|
|
106
414
|
|
|
107
|
-
const schemaImportPath = yield*
|
|
415
|
+
const schemaImportPath = yield* toModuleImportPath(
|
|
108
416
|
path.relative(apiDir, path.join(confectDirectory, "schema.ts")),
|
|
109
417
|
);
|
|
110
418
|
|
|
111
|
-
const specImportPath = yield*
|
|
112
|
-
path.relative(apiDir, path.join(confectDirectory,
|
|
419
|
+
const specImportPath = yield* toModuleImportPath(
|
|
420
|
+
path.relative(apiDir, path.join(confectDirectory, GENERATED_SPEC_PATH)),
|
|
113
421
|
);
|
|
114
422
|
|
|
115
423
|
const apiContents = yield* templates.api({
|
|
@@ -125,12 +433,12 @@ export const generateNodeApi = Effect.gen(function* () {
|
|
|
125
433
|
const path = yield* Path.Path;
|
|
126
434
|
const confectDirectory = yield* ConfectDirectory.get;
|
|
127
435
|
|
|
128
|
-
const nodeSpecPath = yield*
|
|
436
|
+
const nodeSpecPath = yield* getGeneratedNodeSpecPath;
|
|
129
437
|
const nodeApiPath = path.join(confectDirectory, "_generated", "nodeApi.ts");
|
|
130
438
|
|
|
131
439
|
if (!(yield* fs.exists(nodeSpecPath))) {
|
|
132
440
|
if (yield* fs.exists(nodeApiPath)) {
|
|
133
|
-
yield*
|
|
441
|
+
yield* removePathIfExists(nodeApiPath);
|
|
134
442
|
yield* logFileRemoved(nodeApiPath);
|
|
135
443
|
}
|
|
136
444
|
return;
|
|
@@ -138,11 +446,11 @@ export const generateNodeApi = Effect.gen(function* () {
|
|
|
138
446
|
|
|
139
447
|
const nodeApiDir = path.dirname(nodeApiPath);
|
|
140
448
|
|
|
141
|
-
const schemaImportPath = yield*
|
|
449
|
+
const schemaImportPath = yield* toModuleImportPath(
|
|
142
450
|
path.relative(nodeApiDir, path.join(confectDirectory, "schema.ts")),
|
|
143
451
|
);
|
|
144
452
|
|
|
145
|
-
const nodeSpecImportPath = yield*
|
|
453
|
+
const nodeSpecImportPath = yield* toModuleImportPath(
|
|
146
454
|
path.relative(nodeApiDir, nodeSpecPath),
|
|
147
455
|
);
|
|
148
456
|
|
|
@@ -155,47 +463,52 @@ export const generateNodeApi = Effect.gen(function* () {
|
|
|
155
463
|
});
|
|
156
464
|
|
|
157
465
|
const generateFunctionModules = Effect.gen(function* () {
|
|
158
|
-
const
|
|
159
|
-
const
|
|
160
|
-
const confectDirectory = yield* ConfectDirectory.get;
|
|
161
|
-
|
|
162
|
-
const specPath = path.join(confectDirectory, "spec.ts");
|
|
163
|
-
|
|
164
|
-
const specModule = yield* bundleAndImport(specPath);
|
|
165
|
-
const spec = specModule.default;
|
|
166
|
-
|
|
167
|
-
if (!Spec.isConvexSpec(spec)) {
|
|
168
|
-
return yield* Effect.die("spec.ts does not export a valid Convex Spec");
|
|
169
|
-
}
|
|
170
|
-
|
|
171
|
-
const nodeImplPath = path.join(confectDirectory, "nodeImpl.ts");
|
|
172
|
-
const nodeImplExists = yield* fs.exists(nodeImplPath);
|
|
173
|
-
const nodeSpecOption = yield* loadNodeSpec;
|
|
466
|
+
const spec = yield* loadGeneratedSpec;
|
|
467
|
+
const nodeSpecOption = yield* loadGeneratedNodeSpec;
|
|
174
468
|
|
|
175
469
|
const mergedSpec = Option.match(nodeSpecOption, {
|
|
176
470
|
onNone: () => spec,
|
|
177
|
-
onSome: (nodeSpec) =>
|
|
471
|
+
onSome: (nodeSpec) => Spec.merge(spec, nodeSpec),
|
|
178
472
|
});
|
|
179
473
|
|
|
180
474
|
return yield* generateFunctions(mergedSpec);
|
|
181
475
|
});
|
|
182
476
|
|
|
183
|
-
const
|
|
477
|
+
export const validateSchema = Effect.gen(function* () {
|
|
478
|
+
const fs = yield* FileSystem.FileSystem;
|
|
184
479
|
const path = yield* Path.Path;
|
|
185
480
|
const confectDirectory = yield* ConfectDirectory.get;
|
|
186
|
-
const convexDirectory = yield* ConvexDirectory.get;
|
|
187
|
-
|
|
188
481
|
const confectSchemaPath = path.join(confectDirectory, "schema.ts");
|
|
189
482
|
|
|
190
|
-
yield*
|
|
191
|
-
|
|
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 }) => {
|
|
192
490
|
const defaultExport = schemaModule.default;
|
|
193
491
|
|
|
194
492
|
return DatabaseSchema.isDatabaseSchema(defaultExport)
|
|
195
493
|
? Effect.succeed(defaultExport)
|
|
196
|
-
: Effect.
|
|
494
|
+
: Effect.fail(
|
|
495
|
+
new SchemaInvalidDefaultExportError({
|
|
496
|
+
schemaPath: "schema.ts",
|
|
497
|
+
}),
|
|
498
|
+
);
|
|
197
499
|
}),
|
|
198
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.
|
|
199
512
|
|
|
200
513
|
const convexSchemaPath = path.join(convexDirectory, "schema.ts");
|
|
201
514
|
|
|
@@ -230,72 +543,6 @@ const generateServices = Effect.gen(function* () {
|
|
|
230
543
|
yield* writeFileStringAndLog(servicesPath, servicesContentsString);
|
|
231
544
|
});
|
|
232
545
|
|
|
233
|
-
const generateRegisteredFunctions = Effect.gen(function* () {
|
|
234
|
-
const path = yield* Path.Path;
|
|
235
|
-
const confectDirectory = yield* ConfectDirectory.get;
|
|
236
|
-
|
|
237
|
-
const confectGeneratedDirectory = path.join(confectDirectory, "_generated");
|
|
238
|
-
|
|
239
|
-
const registeredFunctionsPath = path.join(
|
|
240
|
-
confectGeneratedDirectory,
|
|
241
|
-
"registeredFunctions.ts",
|
|
242
|
-
);
|
|
243
|
-
const implImportPath = yield* removePathExtension(
|
|
244
|
-
path.relative(
|
|
245
|
-
path.dirname(registeredFunctionsPath),
|
|
246
|
-
path.join(confectDirectory, "impl.ts"),
|
|
247
|
-
),
|
|
248
|
-
);
|
|
249
|
-
|
|
250
|
-
const registeredFunctionsContents = yield* templates.registeredFunctions({
|
|
251
|
-
implImportPath,
|
|
252
|
-
});
|
|
253
|
-
|
|
254
|
-
yield* writeFileStringAndLog(
|
|
255
|
-
registeredFunctionsPath,
|
|
256
|
-
registeredFunctionsContents,
|
|
257
|
-
);
|
|
258
|
-
});
|
|
259
|
-
|
|
260
|
-
export const generateNodeRegisteredFunctions = Effect.gen(function* () {
|
|
261
|
-
const fs = yield* FileSystem.FileSystem;
|
|
262
|
-
const path = yield* Path.Path;
|
|
263
|
-
const confectDirectory = yield* ConfectDirectory.get;
|
|
264
|
-
|
|
265
|
-
const nodeImplPath = path.join(confectDirectory, "nodeImpl.ts");
|
|
266
|
-
const nodeSpecPath = yield* getNodeSpecPath;
|
|
267
|
-
const nodeRegisteredFunctionsPath = path.join(
|
|
268
|
-
confectDirectory,
|
|
269
|
-
"_generated",
|
|
270
|
-
"nodeRegisteredFunctions.ts",
|
|
271
|
-
);
|
|
272
|
-
|
|
273
|
-
const nodeImplExists = yield* fs.exists(nodeImplPath);
|
|
274
|
-
const nodeSpecExists = yield* fs.exists(nodeSpecPath);
|
|
275
|
-
|
|
276
|
-
if (!nodeImplExists || !nodeSpecExists) {
|
|
277
|
-
if (yield* fs.exists(nodeRegisteredFunctionsPath)) {
|
|
278
|
-
yield* fs.remove(nodeRegisteredFunctionsPath);
|
|
279
|
-
yield* logFileRemoved(nodeRegisteredFunctionsPath);
|
|
280
|
-
}
|
|
281
|
-
return;
|
|
282
|
-
}
|
|
283
|
-
|
|
284
|
-
const nodeImplImportPath = yield* removePathExtension(
|
|
285
|
-
path.relative(path.dirname(nodeRegisteredFunctionsPath), nodeImplPath),
|
|
286
|
-
);
|
|
287
|
-
|
|
288
|
-
const nodeRegisteredFunctionsContents =
|
|
289
|
-
yield* templates.nodeRegisteredFunctions({
|
|
290
|
-
nodeImplImportPath,
|
|
291
|
-
});
|
|
292
|
-
|
|
293
|
-
yield* writeFileStringAndLog(
|
|
294
|
-
nodeRegisteredFunctionsPath,
|
|
295
|
-
nodeRegisteredFunctionsContents,
|
|
296
|
-
);
|
|
297
|
-
});
|
|
298
|
-
|
|
299
546
|
const generateRefs = Effect.gen(function* () {
|
|
300
547
|
const fs = yield* FileSystem.FileSystem;
|
|
301
548
|
const path = yield* Path.Path;
|
|
@@ -305,19 +552,21 @@ const generateRefs = Effect.gen(function* () {
|
|
|
305
552
|
const refsPath = path.join(confectGeneratedDirectory, "refs.ts");
|
|
306
553
|
const refsDir = path.dirname(refsPath);
|
|
307
554
|
|
|
308
|
-
const specImportPath = yield*
|
|
309
|
-
path.relative(refsDir, path.join(confectDirectory,
|
|
555
|
+
const specImportPath = yield* toModuleImportPath(
|
|
556
|
+
path.relative(refsDir, path.join(confectDirectory, GENERATED_SPEC_PATH)),
|
|
310
557
|
);
|
|
311
558
|
|
|
312
|
-
const nodeSpecPath = yield*
|
|
559
|
+
const nodeSpecPath = yield* getGeneratedNodeSpecPath;
|
|
313
560
|
const nodeSpecExists = yield* fs.exists(nodeSpecPath);
|
|
314
561
|
const nodeSpecImportPath = nodeSpecExists
|
|
315
|
-
?
|
|
316
|
-
|
|
562
|
+
? Option.some(
|
|
563
|
+
yield* toModuleImportPath(path.relative(refsDir, nodeSpecPath)),
|
|
564
|
+
)
|
|
565
|
+
: Option.none<string>();
|
|
317
566
|
|
|
318
567
|
const refsContents = yield* templates.refs({
|
|
319
568
|
specImportPath,
|
|
320
|
-
|
|
569
|
+
nodeSpecImportPath,
|
|
321
570
|
});
|
|
322
571
|
|
|
323
572
|
yield* writeFileStringAndLog(refsPath, refsContents);
|