@automateinc/fleet-types 1.0.110 → 1.0.111

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 CHANGED
@@ -26,8 +26,10 @@ FLEET_API_PATH=../fleet-api
26
26
  FLEET_API_TYPES_PATH=api/types.d.ts
27
27
  ```
28
28
 
29
- Both paths are resolved relative to the directory where the command is run. Generate the declaration from Fleet
30
- API's `AppRouter` with:
29
+ Both paths are resolved relative to the directory where the command is run. The generated declaration includes Fleet
30
+ API's global `PrismaJson` namespace from `prisma/types.d.ts`; backend-only dependencies referenced by that namespace
31
+ are emitted as `unknown` so the declaration remains portable. Generate the declaration from Fleet API's `AppRouter`
32
+ with:
31
33
 
32
34
  ```bash
33
35
  npx @automateinc/fleet-types generate
@@ -81,6 +81,7 @@ async function generateTypes() {
81
81
  const apiRoot = path.resolve(callerRoot, fleetApiPath);
82
82
  const outputPath = path.resolve(callerRoot, fleetApiTypesPath);
83
83
  const routerSourcePath = path.join(apiRoot, "src/routers/trpc/index.ts");
84
+ const prismaJsonSourcePath = path.join(apiRoot, "prisma/types.d.ts");
84
85
  const apiTsconfigPath = path.join(apiRoot, "tsconfig.json");
85
86
  const executableExtension = process.platform === "win32" ? ".cmd" : "";
86
87
  const callerBiomePath = path.join(callerRoot, `node_modules/.bin/biome${executableExtension}`);
@@ -118,10 +119,14 @@ async function generateTypes() {
118
119
  rootNames: parsedConfig.fileNames,
119
120
  });
120
121
  const routerSource = program.getSourceFile(routerSourcePath);
122
+ const prismaJsonSource = program.getSourceFile(prismaJsonSourcePath);
121
123
 
122
124
  if (!routerSource) {
123
125
  throw new Error(`Unable to load AppRouter source at ${routerSourcePath}.`);
124
126
  }
127
+ if (!prismaJsonSource) {
128
+ throw new Error(`Unable to load PrismaJson declarations at ${prismaJsonSourcePath}.`);
129
+ }
125
130
 
126
131
  const sourceDiagnostics = [
127
132
  ...program.getSyntacticDiagnostics(routerSource),
@@ -156,6 +161,7 @@ async function generateTypes() {
156
161
  const sourceFile = ts.createSourceFile(emittedPath, emittedDeclaration, ts.ScriptTarget.Latest, true);
157
162
  const sanitizedSourceFile = sanitizeBackendTypes(ts, sourceFile);
158
163
  const transformedSourceFile = transformProcedureOutputs(ts, sanitizedSourceFile);
164
+ const prismaJsonDeclaration = createPrismaJsonDeclaration(ts, prismaJsonSource);
159
165
  const statements = transformedSourceFile.statements.filter(
160
166
  statement =>
161
167
  ts.isImportDeclaration(statement) ||
@@ -180,7 +186,7 @@ async function generateTypes() {
180
186
  await mkdir(path.dirname(outputPath), { recursive: true });
181
187
  await writeFile(
182
188
  outputPath,
183
- `// Generated by \`npx @automateinc/fleet-types generate\`. Do not edit manually.\n${SERIALIZED_OUTPUT_TYPES}\n${appRouterDeclaration}\n`,
189
+ `// Generated by \`npx @automateinc/fleet-types generate\`. Do not edit manually.\n${prismaJsonDeclaration}\n${SERIALIZED_OUTPUT_TYPES}\n${appRouterDeclaration}\n`,
184
190
  );
185
191
 
186
192
  if (existsSync(biomePath)) {
@@ -199,6 +205,40 @@ async function generateTypes() {
199
205
  console.log(`Generated ${path.relative(callerRoot, outputPath)} from ${routerSourcePath}`);
200
206
  }
201
207
 
208
+ function createPrismaJsonDeclaration(ts, sourceFile) {
209
+ const backendTypeNames = new Set();
210
+
211
+ for (const statement of sourceFile.statements) {
212
+ if (!ts.isImportDeclaration(statement) || !statement.importClause) continue;
213
+ if (!ts.isStringLiteral(statement.moduleSpecifier) || !statement.moduleSpecifier.text.startsWith("@/")) continue;
214
+
215
+ if (statement.importClause.name) backendTypeNames.add(statement.importClause.name.text);
216
+
217
+ const bindings = statement.importClause.namedBindings;
218
+ if (bindings && ts.isNamespaceImport(bindings)) {
219
+ backendTypeNames.add(bindings.name.text);
220
+ } else if (bindings) {
221
+ for (const element of bindings.elements) backendTypeNames.add(element.name.text);
222
+ }
223
+ }
224
+
225
+ const sanitizedSourceFile = sanitizeBackendTypes(ts, sourceFile, backendTypeNames);
226
+ const globalDeclarations = sanitizedSourceFile.statements.filter(
227
+ statement => ts.isModuleDeclaration(statement) && statement.name.text === "global",
228
+ );
229
+
230
+ if (globalDeclarations.length === 0) {
231
+ throw new Error(`PrismaJson global declarations were not found in ${sourceFile.fileName}.`);
232
+ }
233
+
234
+ const printer = ts.createPrinter({ newLine: ts.NewLineKind.LineFeed });
235
+
236
+ return globalDeclarations
237
+ .map(statement => printer.printNode(ts.EmitHint.Unspecified, statement, sanitizedSourceFile))
238
+ .join("\n")
239
+ .replace(/^(?: {4})+/gm, indentation => "\t".repeat(indentation.length / 4));
240
+ }
241
+
202
242
  function transformProcedureOutputs(ts, sourceFile) {
203
243
  const procedureTypes = new Set(["TRPCMutationProcedure", "TRPCQueryProcedure", "TRPCSubscriptionProcedure"]);
204
244
  const transformation = ts.transform(sourceFile, [
@@ -256,18 +296,15 @@ function transformProcedureOutputs(ts, sourceFile) {
256
296
  return transformedSourceFile;
257
297
  }
258
298
 
259
- function sanitizeBackendTypes(ts, sourceFile) {
299
+ function sanitizeBackendTypes(ts, sourceFile, backendTypeNames = new Set()) {
260
300
  const transformation = ts.transform(sourceFile, [
261
301
  context => {
262
302
  const visit = node => {
263
- if (ts.isTypeReferenceNode(node)) {
264
- let typeName = node.typeName;
265
-
266
- while (ts.isQualifiedName(typeName)) typeName = typeName.left;
267
-
268
- if (ts.isIdentifier(typeName) && typeName.text === "PrismaJson") {
269
- return ts.factory.createKeywordTypeNode(ts.SyntaxKind.UnknownKeyword);
270
- }
303
+ if (
304
+ (ts.isTypeReferenceNode(node) || ts.isIndexedAccessTypeNode(node) || ts.isTypeQueryNode(node)) &&
305
+ referencesBackendType(ts, node, backendTypeNames)
306
+ ) {
307
+ return ts.factory.createKeywordTypeNode(ts.SyntaxKind.UnknownKeyword);
271
308
  }
272
309
 
273
310
  if (
@@ -292,6 +329,28 @@ function sanitizeBackendTypes(ts, sourceFile) {
292
329
  return transformedSourceFile;
293
330
  }
294
331
 
332
+ function referencesBackendType(ts, node, backendTypeNames) {
333
+ if (ts.isTypeReferenceNode(node)) {
334
+ let typeName = node.typeName;
335
+
336
+ while (ts.isQualifiedName(typeName)) typeName = typeName.left;
337
+
338
+ return ts.isIdentifier(typeName) && backendTypeNames.has(typeName.text);
339
+ }
340
+
341
+ if (ts.isIndexedAccessTypeNode(node)) return referencesBackendType(ts, node.objectType, backendTypeNames);
342
+
343
+ if (ts.isTypeQueryNode(node)) {
344
+ let expressionName = node.exprName;
345
+
346
+ while (ts.isQualifiedName(expressionName)) expressionName = expressionName.left;
347
+
348
+ return ts.isIdentifier(expressionName) && backendTypeNames.has(expressionName.text);
349
+ }
350
+
351
+ return false;
352
+ }
353
+
295
354
  function formatTypeScriptDiagnostics(ts, diagnostics, currentDirectory) {
296
355
  return ts.formatDiagnosticsWithColorAndContext(diagnostics, {
297
356
  getCanonicalFileName: fileName => fileName,
package/package.json CHANGED
@@ -57,5 +57,5 @@
57
57
  "test": "echo \"Error: no test specified\" && exit 1"
58
58
  },
59
59
  "types": "dist/types/index.d.ts",
60
- "version": "1.0.110"
60
+ "version": "1.0.111"
61
61
  }