@automateinc/fleet-types 1.0.98 → 1.0.100
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/.env.example +2 -0
- package/README.md +19 -0
- package/bin/fleet-types.mjs +193 -0
- package/package.json +8 -3
package/.env.example
ADDED
package/README.md
CHANGED
|
@@ -15,3 +15,22 @@ npm i @automateinc/fleet-types
|
|
|
15
15
|
```js
|
|
16
16
|
import { IAttendance } from "@automateinc/fleet-types";
|
|
17
17
|
```
|
|
18
|
+
|
|
19
|
+
## Generating tRPC Types
|
|
20
|
+
|
|
21
|
+
When running the CLI from a consuming project such as `fleet-web`, configure the Fleet API source and generated
|
|
22
|
+
type destination in that project's `.env`:
|
|
23
|
+
|
|
24
|
+
```dotenv
|
|
25
|
+
FLEET_API_PATH=../fleet-api
|
|
26
|
+
FLEET_API_TYPES_PATH=api/types.d.ts
|
|
27
|
+
```
|
|
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:
|
|
31
|
+
|
|
32
|
+
```bash
|
|
33
|
+
npx @automateinc/fleet-types generate
|
|
34
|
+
```
|
|
35
|
+
|
|
36
|
+
Inside the `fleet-types` repository, `npm run generate:trpc-types` remains available as a convenience alias.
|
|
@@ -0,0 +1,193 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
|
|
3
|
+
import { spawnSync } from "node:child_process";
|
|
4
|
+
import { existsSync } from "node:fs";
|
|
5
|
+
import { mkdir, mkdtemp, readFile, rm, writeFile } from "node:fs/promises";
|
|
6
|
+
import { createRequire } from "node:module";
|
|
7
|
+
import { tmpdir } from "node:os";
|
|
8
|
+
import path from "node:path";
|
|
9
|
+
import { loadEnvFile } from "node:process";
|
|
10
|
+
import { fileURLToPath } from "node:url";
|
|
11
|
+
|
|
12
|
+
const command = process.argv[2];
|
|
13
|
+
|
|
14
|
+
if (!command || command === "--help" || command === "-h") {
|
|
15
|
+
printUsage();
|
|
16
|
+
} else if (command !== "generate") {
|
|
17
|
+
console.error(`Unknown command: ${command}\n`);
|
|
18
|
+
printUsage();
|
|
19
|
+
process.exitCode = 1;
|
|
20
|
+
} else {
|
|
21
|
+
try {
|
|
22
|
+
await generateTypes();
|
|
23
|
+
} catch (error) {
|
|
24
|
+
console.error(error instanceof Error ? error.message : error);
|
|
25
|
+
process.exitCode = 1;
|
|
26
|
+
}
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
async function generateTypes() {
|
|
30
|
+
const callerRoot = process.cwd();
|
|
31
|
+
const packageRoot = path.resolve(path.dirname(fileURLToPath(import.meta.url)), "..");
|
|
32
|
+
|
|
33
|
+
try {
|
|
34
|
+
loadEnvFile(path.join(callerRoot, ".env"));
|
|
35
|
+
} catch (error) {
|
|
36
|
+
if (error?.code !== "ENOENT") {
|
|
37
|
+
throw error;
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
const fleetApiPath = process.env.FLEET_API_PATH;
|
|
42
|
+
if (!fleetApiPath) {
|
|
43
|
+
throw new Error("FLEET_API_PATH is not set. Add it to .env or the process environment.");
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
const fleetApiTypesPath = process.env.FLEET_API_TYPES_PATH;
|
|
47
|
+
if (!fleetApiTypesPath) {
|
|
48
|
+
throw new Error("FLEET_API_TYPES_PATH is not set. Add it to .env or the process environment.");
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
const apiRoot = path.resolve(callerRoot, fleetApiPath);
|
|
52
|
+
const outputPath = path.resolve(callerRoot, fleetApiTypesPath);
|
|
53
|
+
const routerSourcePath = path.join(apiRoot, "src/services/trpc/index.ts");
|
|
54
|
+
const prismaTypesPath = path.join(apiRoot, "prisma/types.d.ts");
|
|
55
|
+
const executableExtension = process.platform === "win32" ? ".cmd" : "";
|
|
56
|
+
const apiTscPath = path.join(apiRoot, `node_modules/.bin/tsc${executableExtension}`);
|
|
57
|
+
const biomePath = path.join(packageRoot, `node_modules/.bin/biome${executableExtension}`);
|
|
58
|
+
const temporaryDirectory = await mkdtemp(path.join(tmpdir(), "fleet-trpc-types-"));
|
|
59
|
+
const declarationOutputDirectory = path.join(temporaryDirectory, "declarations");
|
|
60
|
+
const declarationTsconfigPath = path.join(temporaryDirectory, "tsconfig.json");
|
|
61
|
+
const prismaJsonShimPath = path.join(temporaryDirectory, "prisma-json.d.ts");
|
|
62
|
+
const serviceProviderShimPath = path.join(temporaryDirectory, "service-provider.d.ts");
|
|
63
|
+
const requireFromPackage = createRequire(path.join(packageRoot, "package.json"));
|
|
64
|
+
const ts = requireFromPackage("typescript");
|
|
65
|
+
|
|
66
|
+
try {
|
|
67
|
+
const prismaTypesContent = await readFile(prismaTypesPath, "utf8");
|
|
68
|
+
const prismaTypesSource = ts.createSourceFile(prismaTypesPath, prismaTypesContent, ts.ScriptTarget.Latest, true);
|
|
69
|
+
let coordsType;
|
|
70
|
+
const findCoordsType = node => {
|
|
71
|
+
if (ts.isModuleDeclaration(node) && ts.isIdentifier(node.name) && node.name.text === "PrismaJson") {
|
|
72
|
+
const body = node.body;
|
|
73
|
+
if (body && ts.isModuleBlock(body)) {
|
|
74
|
+
coordsType = body.statements.find(
|
|
75
|
+
statement => ts.isTypeAliasDeclaration(statement) && statement.name.text === "Coords",
|
|
76
|
+
);
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
ts.forEachChild(node, findCoordsType);
|
|
80
|
+
};
|
|
81
|
+
findCoordsType(prismaTypesSource);
|
|
82
|
+
if (!coordsType) {
|
|
83
|
+
throw new Error(`PrismaJson.Coords was not found in ${prismaTypesPath}.`);
|
|
84
|
+
}
|
|
85
|
+
const coordsTypeDeclaration = ts
|
|
86
|
+
.createPrinter({ newLine: ts.NewLineKind.LineFeed })
|
|
87
|
+
.printNode(ts.EmitHint.Unspecified, coordsType, prismaTypesSource);
|
|
88
|
+
|
|
89
|
+
await writeFile(
|
|
90
|
+
prismaJsonShimPath,
|
|
91
|
+
`export {};\ndeclare global { namespace PrismaJson { ${coordsTypeDeclaration} } }\n`,
|
|
92
|
+
);
|
|
93
|
+
await writeFile(
|
|
94
|
+
serviceProviderShimPath,
|
|
95
|
+
"export declare class ServiceProvider { static getEncryptionService(): { decodeId(encodedId: string, prefix: string): number }; }\n",
|
|
96
|
+
);
|
|
97
|
+
await writeFile(
|
|
98
|
+
declarationTsconfigPath,
|
|
99
|
+
`${JSON.stringify(
|
|
100
|
+
{
|
|
101
|
+
compilerOptions: {
|
|
102
|
+
declaration: true,
|
|
103
|
+
declarationMap: false,
|
|
104
|
+
emitDeclarationOnly: true,
|
|
105
|
+
incremental: true,
|
|
106
|
+
noEmit: false,
|
|
107
|
+
outDir: declarationOutputDirectory,
|
|
108
|
+
paths: {
|
|
109
|
+
"@/*": [path.join(apiRoot, "src/*")],
|
|
110
|
+
"@/providers/service.provider": [serviceProviderShimPath],
|
|
111
|
+
},
|
|
112
|
+
tsBuildInfoFile: path.join(temporaryDirectory, "tsconfig.tsbuildinfo"),
|
|
113
|
+
typeRoots: [path.join(apiRoot, "node_modules/@types")],
|
|
114
|
+
},
|
|
115
|
+
extends: path.join(apiRoot, "tsconfig.json"),
|
|
116
|
+
files: [routerSourcePath, prismaJsonShimPath],
|
|
117
|
+
},
|
|
118
|
+
null,
|
|
119
|
+
2,
|
|
120
|
+
)}\n`,
|
|
121
|
+
);
|
|
122
|
+
|
|
123
|
+
const typeScriptResult = spawnSync(apiTscPath, ["--project", declarationTsconfigPath, "--pretty"], {
|
|
124
|
+
cwd: apiRoot,
|
|
125
|
+
stdio: "inherit",
|
|
126
|
+
});
|
|
127
|
+
|
|
128
|
+
if (typeScriptResult.error) {
|
|
129
|
+
throw typeScriptResult.error;
|
|
130
|
+
}
|
|
131
|
+
if (typeScriptResult.status !== 0) {
|
|
132
|
+
throw new Error(`TypeScript declaration generation failed with exit code ${typeScriptResult.status}.`);
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
const emittedPath = path.join(declarationOutputDirectory, "services/trpc.service.d.ts");
|
|
136
|
+
const emittedDeclaration = await readFile(emittedPath, "utf8");
|
|
137
|
+
const sourceFile = ts.createSourceFile(emittedPath, emittedDeclaration, ts.ScriptTarget.Latest, true);
|
|
138
|
+
const statements = sourceFile.statements.filter(
|
|
139
|
+
statement =>
|
|
140
|
+
ts.isImportDeclaration(statement) ||
|
|
141
|
+
ts.isImportEqualsDeclaration(statement) ||
|
|
142
|
+
(ts.isVariableStatement(statement) &&
|
|
143
|
+
statement.declarationList.declarations.some(
|
|
144
|
+
declaration => ts.isIdentifier(declaration.name) && declaration.name.text === "appRouter",
|
|
145
|
+
)) ||
|
|
146
|
+
(ts.isTypeAliasDeclaration(statement) && statement.name.text === "AppRouter"),
|
|
147
|
+
);
|
|
148
|
+
|
|
149
|
+
if (!statements.some(statement => ts.isTypeAliasDeclaration(statement))) {
|
|
150
|
+
throw new Error("AppRouter was not found in the emitted declaration.");
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
const printer = ts.createPrinter({ newLine: ts.NewLineKind.LineFeed });
|
|
154
|
+
const appRouterDeclaration = statements
|
|
155
|
+
.map(statement => printer.printNode(ts.EmitHint.Unspecified, statement, sourceFile))
|
|
156
|
+
.join("\n")
|
|
157
|
+
.replace(/^(?: {4})+/gm, indentation => "\t".repeat(indentation.length / 4));
|
|
158
|
+
|
|
159
|
+
await mkdir(path.dirname(outputPath), { recursive: true });
|
|
160
|
+
await writeFile(
|
|
161
|
+
outputPath,
|
|
162
|
+
`// Generated by \`npx @automateinc/fleet-types generate\`. Do not edit manually.\n${appRouterDeclaration}\n`,
|
|
163
|
+
);
|
|
164
|
+
|
|
165
|
+
if (existsSync(biomePath)) {
|
|
166
|
+
const biomeResult = spawnSync(biomePath, ["format", "--write", outputPath], {
|
|
167
|
+
cwd: callerRoot,
|
|
168
|
+
stdio: "inherit",
|
|
169
|
+
});
|
|
170
|
+
if (biomeResult.error) {
|
|
171
|
+
throw biomeResult.error;
|
|
172
|
+
}
|
|
173
|
+
if (biomeResult.status !== 0) {
|
|
174
|
+
throw new Error(`Biome formatting failed with exit code ${biomeResult.status}.`);
|
|
175
|
+
}
|
|
176
|
+
}
|
|
177
|
+
|
|
178
|
+
console.log(`Generated ${path.relative(callerRoot, outputPath)} from ${routerSourcePath}`);
|
|
179
|
+
} finally {
|
|
180
|
+
await rm(temporaryDirectory, { force: true, recursive: true });
|
|
181
|
+
}
|
|
182
|
+
}
|
|
183
|
+
|
|
184
|
+
function printUsage() {
|
|
185
|
+
console.log(`Usage: npx @automateinc/fleet-types generate
|
|
186
|
+
|
|
187
|
+
Commands:
|
|
188
|
+
generate Generate AppRouter types from Fleet API
|
|
189
|
+
|
|
190
|
+
Environment variables:
|
|
191
|
+
FLEET_API_PATH Fleet API path relative to the current directory
|
|
192
|
+
FLEET_API_TYPES_PATH Generated declaration path relative to the current directory`);
|
|
193
|
+
}
|
package/package.json
CHANGED
|
@@ -1,9 +1,13 @@
|
|
|
1
1
|
{
|
|
2
2
|
"author": "Automate Inc. <hello@automate.bh> (https://automate.bh)",
|
|
3
|
+
"bin": {
|
|
4
|
+
"fleet-types": "bin/fleet-types.mjs"
|
|
5
|
+
},
|
|
3
6
|
"bugs": {
|
|
4
7
|
"url": "https://github.com/automateinc/fleet-types/issues"
|
|
5
8
|
},
|
|
6
9
|
"dependencies": {
|
|
10
|
+
"@trpc/server": "^11.18.0",
|
|
7
11
|
"asynckit": "^0.4.0",
|
|
8
12
|
"combined-stream": "^1.0.8",
|
|
9
13
|
"delayed-stream": "^1.0.0",
|
|
@@ -14,6 +18,7 @@
|
|
|
14
18
|
"mime-db": "^1.52.0",
|
|
15
19
|
"mime-types": "^2.1.35",
|
|
16
20
|
"proxy-from-env": "^1.1.0",
|
|
21
|
+
"typescript": "5.7.2",
|
|
17
22
|
"use-sync-external-store": "^1.4.0"
|
|
18
23
|
},
|
|
19
24
|
"description": "Reusable TypeScript types and interfaces for Fleet API.",
|
|
@@ -23,8 +28,7 @@
|
|
|
23
28
|
"@tanstack/react-query": "^4.36.1",
|
|
24
29
|
"axios": "^1.7.9",
|
|
25
30
|
"husky": "^9.1.7",
|
|
26
|
-
"react": "*"
|
|
27
|
-
"typescript": "5.1.6"
|
|
31
|
+
"react": "*"
|
|
28
32
|
},
|
|
29
33
|
"homepage": "https://github.com/automateinc/fleet-types#readme",
|
|
30
34
|
"keywords": [
|
|
@@ -48,9 +52,10 @@
|
|
|
48
52
|
},
|
|
49
53
|
"scripts": {
|
|
50
54
|
"build": "tsc && cp -R src/types dist/",
|
|
55
|
+
"generate:trpc-types": "node bin/fleet-types.mjs generate",
|
|
51
56
|
"prepare": "husky install",
|
|
52
57
|
"test": "echo \"Error: no test specified\" && exit 1"
|
|
53
58
|
},
|
|
54
59
|
"types": "dist/types/index.d.ts",
|
|
55
|
-
"version": "1.0.
|
|
60
|
+
"version": "1.0.100"
|
|
56
61
|
}
|