typespun-codegen 0.0.4 → 0.0.8

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.
Files changed (53) hide show
  1. package/README.md +170 -47
  2. package/package.json +2 -2
  3. package/dist/analyzer/analyze.d.ts +0 -4
  4. package/dist/analyzer/analyze.d.ts.map +0 -1
  5. package/dist/analyzer/analyze.js +0 -414
  6. package/dist/analyzer/annotations.d.ts +0 -14
  7. package/dist/analyzer/annotations.d.ts.map +0 -1
  8. package/dist/analyzer/annotations.js +0 -102
  9. package/dist/analyzer/default-expression.d.ts +0 -10
  10. package/dist/analyzer/default-expression.d.ts.map +0 -1
  11. package/dist/analyzer/default-expression.js +0 -67
  12. package/dist/analyzer/diagnostic.d.ts +0 -5
  13. package/dist/analyzer/diagnostic.d.ts.map +0 -1
  14. package/dist/analyzer/diagnostic.js +0 -16
  15. package/dist/analyzer/ir.d.ts +0 -2
  16. package/dist/analyzer/ir.d.ts.map +0 -1
  17. package/dist/analyzer/ir.js +0 -1
  18. package/dist/bin.d.ts +0 -3
  19. package/dist/bin.d.ts.map +0 -1
  20. package/dist/bin.js +0 -3
  21. package/dist/cli/diagnostics.d.ts +0 -7
  22. package/dist/cli/diagnostics.d.ts.map +0 -1
  23. package/dist/cli/diagnostics.js +0 -26
  24. package/dist/cli/init.d.ts +0 -18
  25. package/dist/cli/init.d.ts.map +0 -1
  26. package/dist/cli/init.js +0 -417
  27. package/dist/cli/main.d.ts +0 -13
  28. package/dist/cli/main.d.ts.map +0 -1
  29. package/dist/cli/main.js +0 -157
  30. package/dist/contracts.d.ts +0 -33
  31. package/dist/contracts.d.ts.map +0 -1
  32. package/dist/contracts.js +0 -1
  33. package/dist/emitter/emit.d.ts +0 -13
  34. package/dist/emitter/emit.d.ts.map +0 -1
  35. package/dist/emitter/emit.js +0 -65
  36. package/dist/emitter/fingerprint.d.ts +0 -10
  37. package/dist/emitter/fingerprint.d.ts.map +0 -1
  38. package/dist/emitter/fingerprint.js +0 -28
  39. package/dist/generate.d.ts +0 -29
  40. package/dist/generate.d.ts.map +0 -1
  41. package/dist/generate.js +0 -267
  42. package/dist/index.d.ts +0 -3
  43. package/dist/index.d.ts.map +0 -1
  44. package/dist/index.js +0 -2
  45. package/dist/project/config.d.ts +0 -20
  46. package/dist/project/config.d.ts.map +0 -1
  47. package/dist/project/config.js +0 -140
  48. package/dist/project/defaults.d.ts +0 -22
  49. package/dist/project/defaults.d.ts.map +0 -1
  50. package/dist/project/defaults.js +0 -216
  51. package/dist/project/discovery.d.ts +0 -9
  52. package/dist/project/discovery.d.ts.map +0 -1
  53. package/dist/project/discovery.js +0 -66
@@ -1,7 +0,0 @@
1
- import type { GenerateDiagnostic } from '../generate.js';
2
- export interface DiagnosticFormatOptions {
3
- readonly cwd: string;
4
- readonly color: boolean;
5
- }
6
- export declare function formatDiagnostic(diagnostic: GenerateDiagnostic, options: DiagnosticFormatOptions): string;
7
- //# sourceMappingURL=diagnostics.d.ts.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"diagnostics.d.ts","sourceRoot":"","sources":["../../src/cli/diagnostics.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,gBAAgB,CAAC;AAEzD,MAAM,WAAW,uBAAuB;IACtC,QAAQ,CAAC,GAAG,EAAE,MAAM,CAAC;IACrB,QAAQ,CAAC,KAAK,EAAE,OAAO,CAAC;CACzB;AAED,wBAAgB,gBAAgB,CAC9B,UAAU,EAAE,kBAAkB,EAC9B,OAAO,EAAE,uBAAuB,GAC/B,MAAM,CAoBR"}
@@ -1,26 +0,0 @@
1
- import { isAbsolute, relative } from 'node:path';
2
- export function formatDiagnostic(diagnostic, options) {
3
- const file = diagnosticFile(diagnostic);
4
- const location = 'location' in diagnostic ? diagnostic.location : undefined;
5
- const displayedFile = file === undefined
6
- ? '<project>'
7
- : isAbsolute(file)
8
- ? relative(options.cwd, file) || file
9
- : file;
10
- const prefix = `${displayedFile}:${location?.line ?? 1}:${location?.column ?? 1}`;
11
- const code = options.color
12
- ? `\u001b[36m[${diagnostic.code}]\u001b[0m`
13
- : `[${diagnostic.code}]`;
14
- const suggestion = 'suggestion' in diagnostic && diagnostic.suggestion !== undefined
15
- ? `\n suggestion: ${diagnostic.suggestion}`
16
- : '';
17
- const affectedPath = 'path' in diagnostic && diagnostic.path ? `path ${diagnostic.path}: ` : '';
18
- return `${prefix} ${code} ${affectedPath}${diagnostic.message}${suggestion}`;
19
- }
20
- function diagnosticFile(diagnostic) {
21
- if ('location' in diagnostic)
22
- return diagnostic.location.file;
23
- if ('file' in diagnostic)
24
- return diagnostic.file;
25
- return undefined;
26
- }
@@ -1,18 +0,0 @@
1
- import type { GenerateDiagnostic } from '../generate.js';
2
- export interface InitOptions {
3
- readonly style?: 'interface' | 'class';
4
- readonly input?: string;
5
- readonly output?: string;
6
- readonly envPrefix?: string;
7
- }
8
- export interface InitResult {
9
- readonly exitCode: 0 | 1 | 2;
10
- readonly messages: readonly string[];
11
- readonly warnings: readonly string[];
12
- readonly diagnostics: readonly GenerateDiagnostic[];
13
- }
14
- export declare class InitProjectError extends Error {
15
- constructor(message: string);
16
- }
17
- export declare function initializeProject(cwd: string, options: InitOptions): Promise<InitResult>;
18
- //# sourceMappingURL=init.d.ts.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"init.d.ts","sourceRoot":"","sources":["../../src/cli/init.ts"],"names":[],"mappings":"AAUA,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,gBAAgB,CAAC;AAQzD,MAAM,WAAW,WAAW;IAC1B,QAAQ,CAAC,KAAK,CAAC,EAAE,WAAW,GAAG,OAAO,CAAC;IACvC,QAAQ,CAAC,KAAK,CAAC,EAAE,MAAM,CAAC;IACxB,QAAQ,CAAC,MAAM,CAAC,EAAE,MAAM,CAAC;IACzB,QAAQ,CAAC,SAAS,CAAC,EAAE,MAAM,CAAC;CAC7B;AAED,MAAM,WAAW,UAAU;IACzB,QAAQ,CAAC,QAAQ,EAAE,CAAC,GAAG,CAAC,GAAG,CAAC,CAAC;IAC7B,QAAQ,CAAC,QAAQ,EAAE,SAAS,MAAM,EAAE,CAAC;IACrC,QAAQ,CAAC,QAAQ,EAAE,SAAS,MAAM,EAAE,CAAC;IACrC,QAAQ,CAAC,WAAW,EAAE,SAAS,kBAAkB,EAAE,CAAC;CACrD;AAkBD,qBAAa,gBAAiB,SAAQ,KAAK;gBAC7B,OAAO,EAAE,MAAM;CAI5B;AAED,wBAAsB,iBAAiB,CACrC,GAAG,EAAE,MAAM,EACX,OAAO,EAAE,WAAW,GACnB,OAAO,CAAC,UAAU,CAAC,CA6GrB"}
package/dist/cli/init.js DELETED
@@ -1,417 +0,0 @@
1
- import { existsSync, lstatSync, readFileSync, realpathSync, statSync, } from 'node:fs';
2
- import { mkdir, writeFile } from 'node:fs/promises';
3
- import { basename, dirname, extname, join, resolve } from 'node:path';
4
- import ts from 'typescript';
5
- import { generateProject } from '../generate.js';
6
- import { CONVENTIONAL_INPUTS, discoverDefaultsPath, findNearestTsconfig, } from '../project/discovery.js';
7
- const SCHEMA_EXTENSIONS = new Set(['.ts', '.mts', '.cts']);
8
- export class InitProjectError extends Error {
9
- constructor(message) {
10
- super(message);
11
- this.name = 'InitProjectError';
12
- }
13
- }
14
- export async function initializeProject(cwd, options) {
15
- const projectDirectory = resolve(cwd);
16
- const packagePath = join(projectDirectory, 'package.json');
17
- const packageDocument = readPackageDocument(packagePath);
18
- const configPath = join(projectDirectory, 'typespun.json');
19
- const existingConfig = existsSync(configPath)
20
- ? readInitConfig(configPath)
21
- : undefined;
22
- const input = selectInput(projectDirectory, options, existingConfig);
23
- const output = options.output ??
24
- existingConfig?.output ??
25
- `src/generated/typespun${extname(input)}`;
26
- validateSchemaPath(input, '--input');
27
- validateSchemaPath(output, '--output');
28
- rejectConflictingOptions(options, existingConfig, input, output);
29
- validateExistingSchemaStyle(projectDirectory, input, options.style);
30
- const inputPath = resolve(projectDirectory, input);
31
- const outputPath = resolve(projectDirectory, output);
32
- if (pathsReferToSameFile(inputPath, outputPath)) {
33
- throw new InitProjectError('Schema input and generated output must differ');
34
- }
35
- if (pathEntryExists(outputPath) && !isTypespunGeneratedOutput(outputPath)) {
36
- throw new InitProjectError(`Initialization refuses to overwrite the existing output at ${output}`);
37
- }
38
- const config = {
39
- ...existingConfig,
40
- input,
41
- output,
42
- ...(options.envPrefix === undefined
43
- ? existingConfig?.envPrefix === undefined
44
- ? {}
45
- : { envPrefix: existingConfig.envPrefix }
46
- : { envPrefix: options.envPrefix }),
47
- };
48
- preflightResolvedConfig(projectDirectory, config, inputPath);
49
- const configNeedsMissingKeys = existingConfig !== undefined &&
50
- (existingConfig.input === undefined ||
51
- existingConfig.output === undefined ||
52
- (options.envPrefix !== undefined &&
53
- existingConfig.envPrefix === undefined));
54
- const messages = [];
55
- if (!existsSync(inputPath)) {
56
- await writeNewFile(inputPath, schemaTemplate(options.style ?? 'interface'));
57
- messages.push(`Created ${input}.`);
58
- }
59
- const serializedConfig = `${JSON.stringify(config, null, 2)}\n`;
60
- if (existingConfig === undefined) {
61
- await writeNewFile(configPath, serializedConfig);
62
- messages.push('Created typespun.json.');
63
- }
64
- else if (configNeedsMissingKeys) {
65
- await writeFile(configPath, serializedConfig);
66
- messages.push('Added missing paths to typespun.json.');
67
- }
68
- if (addMissingScripts(packageDocument)) {
69
- await writeFile(packagePath, `${JSON.stringify(packageDocument, null, 2)}\n`);
70
- messages.push('Added config:generate and config:check scripts.');
71
- }
72
- const missing = missingDependencies(projectDirectory);
73
- if (missing.length > 0) {
74
- return {
75
- exitCode: 0,
76
- messages,
77
- warnings: installationGuidance(projectDirectory, packageDocument, missing),
78
- diagnostics: [],
79
- };
80
- }
81
- const generated = await generateProject({
82
- projectDirectory,
83
- configPath,
84
- mode: 'write',
85
- });
86
- if (generated.diagnostics.length > 0) {
87
- return {
88
- exitCode: generated.diagnostics.some((diagnostic) => diagnostic.code === 'typescript_config')
89
- ? 2
90
- : 1,
91
- messages,
92
- warnings: [],
93
- diagnostics: [...generated.warnings, ...generated.diagnostics],
94
- };
95
- }
96
- messages.push(`${generated.status === 'written' ? 'Generated' : 'Unchanged'} ${displayPath(projectDirectory, generated.outputPath)}.`);
97
- return {
98
- exitCode: 0,
99
- messages,
100
- warnings: [],
101
- diagnostics: generated.warnings,
102
- };
103
- }
104
- function readPackageDocument(path) {
105
- if (!isFile(path))
106
- throw new InitProjectError('package.json is required');
107
- let value;
108
- try {
109
- value = JSON.parse(readFileSync(path, 'utf8'));
110
- }
111
- catch {
112
- throw new InitProjectError('package.json must contain valid JSON');
113
- }
114
- if (!isRecord(value)) {
115
- throw new InitProjectError('package.json must contain an object');
116
- }
117
- if (value.scripts !== undefined && !isRecord(value.scripts)) {
118
- throw new InitProjectError('package.json scripts must contain an object');
119
- }
120
- return value;
121
- }
122
- function validateTsconfig(path) {
123
- if (!isFile(path))
124
- throw new InitProjectError('tsconfig.json is required');
125
- const loaded = ts.readConfigFile(path, ts.sys.readFile);
126
- if (loaded.error !== undefined) {
127
- throw new InitProjectError('tsconfig.json must contain usable TypeScript configuration');
128
- }
129
- const parsed = ts.parseJsonConfigFileContent(loaded.config, ts.sys, dirname(path), undefined, path);
130
- if (parsed.errors.some((diagnostic) => diagnostic.code !== 18003)) {
131
- throw new InitProjectError('tsconfig.json must contain usable TypeScript configuration');
132
- }
133
- }
134
- function preflightResolvedConfig(projectDirectory, config, inputPath) {
135
- const tsconfigPath = config.tsconfig === undefined
136
- ? findNearestTsconfig(inputPath)
137
- : resolve(projectDirectory, config.tsconfig);
138
- if (tsconfigPath === undefined) {
139
- throw new InitProjectError(`Could not find tsconfig.json for ${displayPath(projectDirectory, inputPath)}`);
140
- }
141
- validateTsconfig(tsconfigPath);
142
- const defaults = config.defaults;
143
- const hasExplicitDefaultsPath = typeof defaults === 'string' ||
144
- (isRecord(defaults) && typeof defaults.path === 'string');
145
- if (!hasExplicitDefaultsPath) {
146
- try {
147
- discoverDefaultsPath(projectDirectory);
148
- }
149
- catch (error) {
150
- throw new InitProjectError(error instanceof Error
151
- ? error.message
152
- : 'Defaults-file discovery failed');
153
- }
154
- }
155
- }
156
- function readInitConfig(path) {
157
- let value;
158
- try {
159
- value = JSON.parse(readFileSync(path, 'utf8'));
160
- }
161
- catch {
162
- throw new InitProjectError('typespun.json must contain valid JSON');
163
- }
164
- if (!isRecord(value)) {
165
- throw new InitProjectError('typespun.json must contain an object');
166
- }
167
- const allowed = new Set([
168
- 'input',
169
- 'output',
170
- 'tsconfig',
171
- 'envPrefix',
172
- 'defaults',
173
- 'secretDefaults',
174
- ]);
175
- for (const key of Object.keys(value)) {
176
- if (!allowed.has(key)) {
177
- throw new InitProjectError(`Unknown typespun.json key: ${key}`);
178
- }
179
- }
180
- for (const key of ['input', 'output', 'tsconfig', 'envPrefix']) {
181
- if (value[key] !== undefined && typeof value[key] !== 'string') {
182
- throw new InitProjectError(`typespun.json ${key} must be a string`);
183
- }
184
- }
185
- validateDefaultsConfig(value.defaults);
186
- if (value.secretDefaults !== undefined &&
187
- value.secretDefaults !== 'warn' &&
188
- value.secretDefaults !== 'allow' &&
189
- value.secretDefaults !== 'error') {
190
- throw new InitProjectError('typespun.json secretDefaults must be warn, allow, or error');
191
- }
192
- return value;
193
- }
194
- function validateDefaultsConfig(value) {
195
- if (value === undefined || typeof value === 'string')
196
- return;
197
- if (!isRecord(value)) {
198
- throw new InitProjectError('typespun.json defaults must be a string or object');
199
- }
200
- for (const key of Object.keys(value)) {
201
- if (key !== 'path' && key !== 'unknownKeys') {
202
- throw new InitProjectError(`Unknown typespun.json defaults key: ${key}`);
203
- }
204
- }
205
- if (value.path !== undefined && typeof value.path !== 'string') {
206
- throw new InitProjectError('typespun.json defaults.path must be a string');
207
- }
208
- if (value.unknownKeys !== undefined &&
209
- value.unknownKeys !== 'error' &&
210
- value.unknownKeys !== 'warn' &&
211
- value.unknownKeys !== 'ignore') {
212
- throw new InitProjectError('typespun.json defaults.unknownKeys must be error, warn, or ignore');
213
- }
214
- }
215
- function selectInput(projectDirectory, options, existingConfig) {
216
- if (options.input !== undefined)
217
- return options.input;
218
- if (existingConfig?.input !== undefined)
219
- return existingConfig.input;
220
- const candidates = CONVENTIONAL_INPUTS.filter((path) => isFile(resolve(projectDirectory, path)));
221
- if (candidates.length > 1) {
222
- throw new InitProjectError(`Multiple conventional schemas exist: ${candidates.join(', ')}; use --input.`);
223
- }
224
- return candidates[0] ?? 'src/config.ts';
225
- }
226
- function rejectConflictingOptions(options, existing, input, output) {
227
- if (existing === undefined)
228
- return;
229
- const comparisons = [
230
- ['--input', options.input, existing.input ?? input],
231
- ['--output', options.output, existing.output ?? output],
232
- [
233
- '--env-prefix',
234
- options.envPrefix,
235
- existing.envPrefix ?? options.envPrefix,
236
- ],
237
- ];
238
- for (const [flag, requested, configured] of comparisons) {
239
- if (requested !== undefined && requested !== configured) {
240
- throw new InitProjectError(`${flag} conflicts with the existing typespun.json`);
241
- }
242
- }
243
- }
244
- function validateExistingSchemaStyle(projectDirectory, input, requestedStyle) {
245
- const path = resolve(projectDirectory, input);
246
- if (requestedStyle === undefined || !isFile(path))
247
- return;
248
- const source = readFileSync(path, 'utf8');
249
- const matches = requestedStyle === 'class'
250
- ? /\b(?:export\s+)?class\s+[A-Za-z_$]/.test(source)
251
- : /\b(?:export\s+)?interface\s+[A-Za-z_$]/.test(source);
252
- if (!matches) {
253
- throw new InitProjectError(`--style ${requestedStyle} conflicts with the existing schema at ${input}`);
254
- }
255
- }
256
- function validateSchemaPath(path, flag) {
257
- if (!SCHEMA_EXTENSIONS.has(extname(path))) {
258
- throw new InitProjectError(`${flag} must use a .ts, .mts, or .cts extension`);
259
- }
260
- }
261
- function schemaTemplate(style) {
262
- return style === 'interface'
263
- ? `/** @typespun */\nexport interface AppConfig {\n port: number;\n}\n`
264
- : `import { Config } from 'typespun';\n\n@Config()\nexport class AppConfig {\n port!: number;\n}\n`;
265
- }
266
- function addMissingScripts(document) {
267
- const scripts = document.scripts ?? {};
268
- let changed = false;
269
- if (scripts['config:generate'] === undefined) {
270
- scripts['config:generate'] = 'typespun generate';
271
- changed = true;
272
- }
273
- if (scripts['config:check'] === undefined) {
274
- scripts['config:check'] = 'typespun check';
275
- changed = true;
276
- }
277
- if (document.scripts === undefined)
278
- document.scripts = scripts;
279
- return changed;
280
- }
281
- function missingDependencies(projectDirectory) {
282
- return ['typespun', 'typespun-codegen'].filter((name) => !findInstalledPackage(projectDirectory, name));
283
- }
284
- function findInstalledPackage(projectDirectory, packageName) {
285
- let directory = resolve(projectDirectory);
286
- while (true) {
287
- if (isFile(join(directory, 'node_modules', packageName, 'package.json'))) {
288
- return true;
289
- }
290
- const parent = dirname(directory);
291
- if (parent === directory)
292
- return false;
293
- directory = parent;
294
- }
295
- }
296
- function installationGuidance(projectDirectory, packageDocument, missing) {
297
- const manager = detectPackageManager(projectDirectory, packageDocument);
298
- const commands = [];
299
- if (missing.includes('typespun')) {
300
- commands.push(installCommand(manager, 'typespun', false));
301
- }
302
- if (missing.includes('typespun-codegen')) {
303
- commands.push(installCommand(manager, 'typespun-codegen', true));
304
- }
305
- return [
306
- 'Dependencies are missing; generated output was not created. Install them, then run typespun generate:',
307
- ...commands,
308
- ];
309
- }
310
- function detectPackageManager(projectDirectory, document) {
311
- const configured = document.packageManager?.split('@')[0];
312
- if (configured === 'bun' ||
313
- configured === 'npm' ||
314
- configured === 'pnpm' ||
315
- configured === 'yarn') {
316
- return configured;
317
- }
318
- if (isFile(join(projectDirectory, 'bun.lock')) ||
319
- isFile(join(projectDirectory, 'bun.lockb'))) {
320
- return 'bun';
321
- }
322
- if (isFile(join(projectDirectory, 'pnpm-lock.yaml')))
323
- return 'pnpm';
324
- if (isFile(join(projectDirectory, 'yarn.lock')))
325
- return 'yarn';
326
- return 'npm';
327
- }
328
- function installCommand(manager, packageName, development) {
329
- if (manager === 'npm') {
330
- return `npm install${development ? ' --save-dev' : ''} ${packageName}`;
331
- }
332
- if (manager === 'yarn') {
333
- return `yarn add${development ? ' --dev' : ''} ${packageName}`;
334
- }
335
- const developmentFlag = development
336
- ? manager === 'pnpm'
337
- ? ' --save-dev'
338
- : ' --dev'
339
- : '';
340
- return `${manager} add${developmentFlag} ${packageName}`;
341
- }
342
- async function writeNewFile(path, contents) {
343
- await mkdir(dirname(path), { recursive: true });
344
- await writeFile(path, contents, { encoding: 'utf8', flag: 'wx' });
345
- }
346
- function displayPath(projectDirectory, path) {
347
- return path.startsWith(`${projectDirectory}/`)
348
- ? path.slice(projectDirectory.length + 1)
349
- : path;
350
- }
351
- function pathsReferToSameFile(first, second) {
352
- const firstIdentity = fileIdentity(first);
353
- const secondIdentity = fileIdentity(second);
354
- if (firstIdentity.stat !== undefined &&
355
- secondIdentity.stat !== undefined &&
356
- firstIdentity.stat.dev === secondIdentity.stat.dev &&
357
- firstIdentity.stat.ino === secondIdentity.stat.ino) {
358
- return true;
359
- }
360
- return firstIdentity.canonicalPath === secondIdentity.canonicalPath;
361
- }
362
- function fileIdentity(path) {
363
- try {
364
- return { canonicalPath: realpathSync(path), stat: statSync(path) };
365
- }
366
- catch {
367
- let ancestor = dirname(path);
368
- const remainder = [basename(path)];
369
- while (true) {
370
- try {
371
- return {
372
- canonicalPath: resolve(realpathSync(ancestor), ...remainder),
373
- };
374
- }
375
- catch {
376
- const parent = dirname(ancestor);
377
- if (parent === ancestor)
378
- return { canonicalPath: resolve(path) };
379
- remainder.unshift(basename(ancestor));
380
- ancestor = parent;
381
- }
382
- }
383
- }
384
- }
385
- function isTypespunGeneratedOutput(path) {
386
- let contents;
387
- try {
388
- contents = readFileSync(path, 'utf8');
389
- }
390
- catch {
391
- return false;
392
- }
393
- return (/^\/\/ Generated by typespun-codegen\. Do not edit\.\n\/\/ Schema fingerprint: [a-f0-9]{64}\n\n/u.test(contents) &&
394
- contents.includes("from 'typespun/generated';") &&
395
- contents.includes('export type Config = TypespunConfig;') &&
396
- contents.includes('export const loadConfig = createLoader<Config>(schema);'));
397
- }
398
- function isFile(path) {
399
- try {
400
- return existsSync(path) && statSync(path).isFile();
401
- }
402
- catch {
403
- return false;
404
- }
405
- }
406
- function pathEntryExists(path) {
407
- try {
408
- lstatSync(path);
409
- return true;
410
- }
411
- catch {
412
- return false;
413
- }
414
- }
415
- function isRecord(value) {
416
- return typeof value === 'object' && value !== null && !Array.isArray(value);
417
- }
@@ -1,13 +0,0 @@
1
- interface CliStreams {
2
- readonly stdout: {
3
- write(value: string): unknown;
4
- readonly isTTY?: boolean;
5
- };
6
- readonly stderr: {
7
- write(value: string): unknown;
8
- readonly isTTY?: boolean;
9
- };
10
- }
11
- export declare function runCli(args: readonly string[], streams?: CliStreams, cwd?: string): Promise<number>;
12
- export {};
13
- //# sourceMappingURL=main.d.ts.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"main.d.ts","sourceRoot":"","sources":["../../src/cli/main.ts"],"names":[],"mappings":"AAwBA,UAAU,UAAU;IAClB,QAAQ,CAAC,MAAM,EAAE;QAAE,KAAK,CAAC,KAAK,EAAE,MAAM,GAAG,OAAO,CAAC;QAAC,QAAQ,CAAC,KAAK,CAAC,EAAE,OAAO,CAAA;KAAE,CAAC;IAC7E,QAAQ,CAAC,MAAM,EAAE;QAAE,KAAK,CAAC,KAAK,EAAE,MAAM,GAAG,OAAO,CAAC;QAAC,QAAQ,CAAC,KAAK,CAAC,EAAE,OAAO,CAAA;KAAE,CAAC;CAC9E;AAID,wBAAsB,MAAM,CAC1B,IAAI,EAAE,SAAS,MAAM,EAAE,EACvB,OAAO,GAAE,UAAoB,EAC7B,GAAG,SAAgB,GAClB,OAAO,CAAC,MAAM,CAAC,CA+EjB"}
package/dist/cli/main.js DELETED
@@ -1,157 +0,0 @@
1
- import { generateProject } from '../generate.js';
2
- import { formatDiagnostic } from './diagnostics.js';
3
- import { initializeProject } from './init.js';
4
- const HELP = `Usage: typespun <command> [options]
5
-
6
- Commands:
7
- init Initialize a Typespun project
8
- generate Generate the typed configuration loader
9
- check Check that generated output is current
10
-
11
- Options:
12
- -h, --help Show help
13
-
14
- Init options:
15
- --style interface|class
16
- --input <path>
17
- --output <path>
18
- --env-prefix <prefix>
19
-
20
- Generate/check options:
21
- --config <path>
22
- `;
23
- class UsageError extends Error {
24
- }
25
- export async function runCli(args, streams = process, cwd = process.cwd()) {
26
- if (args.length === 0 || args[0] === '--help' || args[0] === '-h') {
27
- streams.stdout.write(HELP);
28
- return 0;
29
- }
30
- try {
31
- const [command, ...commandArgs] = args;
32
- if (command === 'generate' || command === 'check') {
33
- if (isHelpOnly(commandArgs)) {
34
- streams.stdout.write(HELP);
35
- return 0;
36
- }
37
- const configPath = parseConfigArguments(commandArgs);
38
- const result = await generateProject({
39
- projectDirectory: cwd,
40
- mode: command === 'generate' ? 'write' : 'check',
41
- ...(configPath === undefined ? {} : { configPath }),
42
- });
43
- const color = streams.stderr.isTTY === true && process.env.NO_COLOR === undefined;
44
- for (const warning of result.warnings) {
45
- streams.stderr.write(`${formatDiagnostic(warning, { cwd, color })}\n`);
46
- }
47
- for (const diagnostic of result.diagnostics) {
48
- streams.stderr.write(`${formatDiagnostic(diagnostic, { cwd, color })}\n`);
49
- }
50
- if (result.diagnostics.length > 0) {
51
- return result.diagnostics.some((diagnostic) => diagnostic.code === 'typescript_config')
52
- ? 2
53
- : 1;
54
- }
55
- if (result.status === 'stale') {
56
- streams.stderr.write(`${relativeOutput(result.outputPath, cwd)} is stale; run typespun generate.\n`);
57
- return 1;
58
- }
59
- streams.stdout.write(command === 'generate'
60
- ? `${result.status === 'written' ? 'Generated' : 'Unchanged'} ${relativeOutput(result.outputPath, cwd)}.\n`
61
- : `${relativeOutput(result.outputPath, cwd)} is up to date.\n`);
62
- return 0;
63
- }
64
- if (command === 'init') {
65
- if (isHelpOnly(commandArgs)) {
66
- streams.stdout.write(HELP);
67
- return 0;
68
- }
69
- const result = await initializeProject(cwd, parseInitArguments(commandArgs));
70
- for (const line of result.messages)
71
- streams.stdout.write(`${line}\n`);
72
- for (const line of result.warnings)
73
- streams.stderr.write(`${line}\n`);
74
- const color = streams.stderr.isTTY === true && process.env.NO_COLOR === undefined;
75
- for (const diagnostic of result.diagnostics) {
76
- streams.stderr.write(`${formatDiagnostic(diagnostic, { cwd, color })}\n`);
77
- }
78
- return result.exitCode;
79
- }
80
- throw new UsageError(`Unknown command: ${command ?? ''}`);
81
- }
82
- catch (error) {
83
- const message = error instanceof Error ? error.message : String(error);
84
- streams.stderr.write(`typespun: ${message}\n`);
85
- if (error instanceof UsageError || isProjectConfigurationError(error)) {
86
- streams.stderr.write('Run typespun --help for usage.\n');
87
- return 2;
88
- }
89
- return 1;
90
- }
91
- }
92
- function parseConfigArguments(args) {
93
- let configPath;
94
- for (let index = 0; index < args.length; index++) {
95
- const argument = args[index];
96
- if (argument !== '--config')
97
- throw new UsageError(`Unknown option: ${argument}`);
98
- if (configPath !== undefined)
99
- throw new UsageError('--config may be specified only once');
100
- configPath = requiredValue(args, ++index, '--config');
101
- }
102
- return configPath;
103
- }
104
- function parseInitArguments(args) {
105
- const options = {};
106
- for (let index = 0; index < args.length; index++) {
107
- const argument = args[index];
108
- if (argument === '--style') {
109
- const style = requiredValue(args, ++index, argument);
110
- if (style !== 'interface' && style !== 'class') {
111
- throw new UsageError('--style must be interface or class');
112
- }
113
- if (options.style !== undefined)
114
- throw new UsageError('--style may be specified only once');
115
- options.style = style;
116
- }
117
- else if (argument === '--input') {
118
- options.input = uniqueValue(options.input, requiredValue(args, ++index, argument), argument);
119
- }
120
- else if (argument === '--output') {
121
- options.output = uniqueValue(options.output, requiredValue(args, ++index, argument), argument);
122
- }
123
- else if (argument === '--env-prefix') {
124
- options.envPrefix = uniqueValue(options.envPrefix, requiredValue(args, ++index, argument), argument);
125
- }
126
- else {
127
- throw new UsageError(`Unknown option: ${argument}`);
128
- }
129
- }
130
- return options;
131
- }
132
- function requiredValue(args, index, flag) {
133
- const value = args[index];
134
- if (value === undefined || value.length === 0 || value.startsWith('--')) {
135
- throw new UsageError(`${flag} requires a value`);
136
- }
137
- return value;
138
- }
139
- function uniqueValue(previous, value, flag) {
140
- if (previous !== undefined)
141
- throw new UsageError(`${flag} may be specified only once`);
142
- return value;
143
- }
144
- function isHelpOnly(args) {
145
- return args.length === 1 && (args[0] === '--help' || args[0] === '-h');
146
- }
147
- function isProjectConfigurationError(error) {
148
- if (!(error instanceof Error))
149
- return false;
150
- return (error.name === 'ProjectConfigError' ||
151
- error.name === 'ProjectDiscoveryError' ||
152
- error.name === 'InitProjectError' ||
153
- error.message.startsWith('Could not find tsconfig.json'));
154
- }
155
- function relativeOutput(path, cwd) {
156
- return (path.startsWith(cwd) ? path.slice(cwd.length + 1) : path) || path;
157
- }
@@ -1,33 +0,0 @@
1
- import type { FieldKind, FieldSchema } from 'typespun/generated';
2
- export interface SourceLocation {
3
- readonly file: string;
4
- readonly line: number;
5
- readonly column: number;
6
- }
7
- export interface Diagnostic {
8
- readonly code: string;
9
- readonly message: string;
10
- readonly location: SourceLocation;
11
- readonly suggestion?: string;
12
- }
13
- export interface FieldIR extends FieldSchema {
14
- readonly location: SourceLocation;
15
- readonly kind: FieldKind;
16
- }
17
- export type RootExport = {
18
- readonly kind: 'named';
19
- readonly name: string;
20
- } | {
21
- readonly kind: 'default';
22
- };
23
- export interface AnalyzeResult {
24
- readonly inputPath: string;
25
- readonly rootName?: string;
26
- /** The import binding for the selected root, present on successful analysis. */
27
- readonly rootExport?: RootExport;
28
- readonly fields: readonly FieldIR[];
29
- readonly diagnostics: readonly Diagnostic[];
30
- }
31
- export type UnknownKeysPolicy = 'error' | 'warn' | 'ignore';
32
- export type SecretDefaultsPolicy = 'warn' | 'allow' | 'error';
33
- //# sourceMappingURL=contracts.d.ts.map