@vertz/compiler 0.2.0 → 0.2.3

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
@@ -1,711 +1,19 @@
1
1
  # @vertz/compiler
2
2
 
3
- > ⚠️ **Internal package** — This package is an implementation detail of the Vertz framework. It is published for use by other `@vertz/*` packages. No API stability is guaranteed between versions.
3
+ > **Internal package** — You don't use this directly. It powers `vertz build`, `vertz dev`, and `vertz codegen` behind the scenes.
4
4
 
5
- > Static analysis and code generation for vertz applications
5
+ Static analysis and code generation for Vertz applications. Analyzes TypeScript source code to extract application structure (routes, schemas, modules, middleware), validates conventions, and generates runtime artifacts like boot files, route tables, and OpenAPI specs.
6
6
 
7
- The vertz compiler analyzes TypeScript source code to extract application structure, validate conventions, and generate runtime artifacts like route tables, dependency graphs, and OpenAPI specs.
7
+ ## Who uses this
8
8
 
9
- ## Prerequisites
10
-
11
- - **Node.js** 18+ or **Bun** 1.0+
12
- - **TypeScript** 5.0+
13
-
14
- ## Installation
15
-
16
- ```bash
17
- # npm
18
- npm install @vertz/compiler
19
-
20
- # bun
21
- bun add @vertz/compiler
22
- ```
23
-
24
- ## Overview
25
-
26
- The compiler consists of three main stages:
27
-
28
- 1. **Analysis** — Parse TypeScript AST and extract IR (Intermediate Representation)
29
- 2. **Validation** — Check conventions, completeness, and correctness
30
- 3. **Generation** — Emit runtime artifacts and documentation
31
-
32
- ```
33
- Source Code (*.ts)
34
-
35
- [Analyzers] → AppIR (Intermediate Representation)
36
-
37
- [Validators] → Diagnostics (errors/warnings)
38
-
39
- [Generators] → Artifacts (boot.ts, manifest.json, openapi.json)
40
- ```
41
-
42
- ## Quick Start
43
-
44
- ```typescript
45
- import { createCompiler } from '@vertz/compiler';
46
-
47
- // Create compiler with default config
48
- const compiler = createCompiler({
49
- rootDir: './src',
50
- appFile: './src/app.ts',
51
- outDir: './.vertz',
52
- });
53
-
54
- // Analyze the application
55
- const ir = await compiler.analyze();
56
-
57
- // Validate the IR
58
- const diagnostics = await compiler.validate(ir);
59
-
60
- if (diagnostics.some((d) => d.severity === 'error')) {
61
- console.error('Validation errors:', diagnostics);
62
- process.exit(1);
63
- }
64
-
65
- // Generate artifacts
66
- await compiler.generate(ir);
67
-
68
- console.log('✅ Compilation successful');
69
- ```
70
-
71
- ## Core Concepts
72
-
73
- ### Intermediate Representation (IR)
74
-
75
- The IR is a structured representation of your vertz application extracted from source code:
76
-
77
- ```typescript
78
- interface AppIR {
79
- app: AppDefinition; // App configuration (basePath, CORS, etc.)
80
- modules: ModuleIR[]; // All modules
81
- middleware: MiddlewareIR[]; // Global middleware
82
- schemas: SchemaIR[]; // Schema definitions
83
- env: EnvIR; // Environment variables
84
- dependencyGraph: DependencyGraphIR; // Service dependency graph
85
- diagnostics: Diagnostic[]; // Analysis issues
86
- }
87
- ```
88
-
89
- ### Analyzers
90
-
91
- Analyzers extract specific aspects of your application:
92
-
93
- - **AppAnalyzer** — Extracts `createApp()` configuration
94
- - **ModuleAnalyzer** — Extracts modules, services, and routers
95
- - **SchemaAnalyzer** — Extracts schema definitions
96
- - **MiddlewareAnalyzer** — Extracts middleware definitions
97
- - **EnvAnalyzer** — Extracts environment variable declarations
98
- - **DependencyGraphAnalyzer** — Builds service dependency graph
99
-
100
- Each analyzer extends `BaseAnalyzer` and implements:
101
-
102
- ```typescript
103
- interface Analyzer<T> {
104
- analyze(): Promise<T>;
105
- }
106
- ```
107
-
108
- ### Validators
109
-
110
- Validators check the IR for correctness:
111
-
112
- - **NamingValidator** — Checks naming conventions (PascalCase modules, camelCase services)
113
- - **PlacementValidator** — Ensures code is in the correct directories
114
- - **CompletenessValidator** — Checks for missing exports, dangling references
115
- - **ModuleValidator** — Validates module structure (services, routers, exports)
116
-
117
- Each validator implements:
118
-
119
- ```typescript
120
- interface Validator {
121
- validate(ir: AppIR): Promise<Diagnostic[]>;
122
- }
123
- ```
124
-
125
- ### Generators
126
-
127
- Generators emit artifacts from the IR:
128
-
129
- - **BootGenerator** — Generates `boot.ts` with module registration code
130
- - **RouteTableGenerator** — Generates runtime route table with validation schemas
131
- - **SchemaRegistryGenerator** — Generates schema registry for runtime validation
132
- - **ManifestGenerator** — Generates JSON manifest for introspection
133
- - **OpenAPIGenerator** — Generates OpenAPI 3.0 specification
134
-
135
- Each generator implements:
136
-
137
- ```typescript
138
- interface Generator {
139
- generate(ir: AppIR, outputDir: string): Promise<void>;
140
- }
141
- ```
142
-
143
- ## Configuration
144
-
145
- ### Basic Config
146
-
147
- ```typescript
148
- import { defineConfig } from '@vertz/compiler';
149
-
150
- export default defineConfig({
151
- rootDir: './src',
152
- appFile: './src/app.ts',
153
- outDir: './.vertz',
154
- });
155
- ```
156
-
157
- ### Full Config Options
158
-
159
- ```typescript
160
- interface VertzConfig {
161
- rootDir: string; // Project root directory
162
- appFile: string; // Path to app entry file
163
- outDir: string; // Output directory for artifacts
164
-
165
- compiler?: {
166
- outputDir?: string; // Override output directory
167
- incremental?: boolean; // Enable incremental compilation
168
- watch?: boolean; // Enable watch mode
169
- };
170
-
171
- validation?: {
172
- strict?: boolean; // Fail on warnings
173
- rules?: {
174
- naming?: boolean; // Check naming conventions
175
- placement?: boolean; // Check file placement
176
- completeness?: boolean; // Check for missing references
177
- };
178
- };
179
-
180
- schema?: {
181
- generateJSONSchema?: boolean; // Generate JSON Schema for validation
182
- includeExamples?: boolean; // Include examples in schemas
183
- };
184
-
185
- openapi?: {
186
- enabled?: boolean; // Generate OpenAPI spec
187
- info?: {
188
- title?: string;
189
- version?: string;
190
- description?: string;
191
- };
192
- servers?: Array<{
193
- url: string;
194
- description?: string;
195
- }>;
196
- };
197
- }
198
- ```
199
-
200
- ## Analyzers
201
-
202
- ### AppAnalyzer
203
-
204
- Extracts `createApp()` configuration:
205
-
206
- ```typescript
207
- import { AppAnalyzer } from '@vertz/compiler';
208
- import { Project } from 'ts-morph';
209
-
210
- const project = new Project({ tsConfigFilePath: 'tsconfig.json' });
211
- const analyzer = new AppAnalyzer(project, './src/app.ts');
212
- const result = await analyzer.analyze();
213
-
214
- console.log(result.app);
215
- /*
216
- {
217
- basePath: '/api',
218
- globalMiddleware: ['auth', 'logging'],
219
- sourceFile: './src/app.ts',
220
- sourceLine: 10,
221
- sourceColumn: 15
222
- }
223
- */
224
- ```
225
-
226
- ### ModuleAnalyzer
227
-
228
- Extracts modules, services, and routers:
229
-
230
- ```typescript
231
- import { ModuleAnalyzer } from '@vertz/compiler';
232
-
233
- const analyzer = new ModuleAnalyzer(project, { rootDir: './src' });
234
- const result = await analyzer.analyze();
235
-
236
- console.log(result.modules);
237
- /*
238
- [
239
- {
240
- name: 'users',
241
- sourceFile: './src/users/users.module.ts',
242
- services: [...],
243
- routers: [...],
244
- exports: [...]
245
- }
246
- ]
247
- */
248
- ```
249
-
250
- ### SchemaAnalyzer
251
-
252
- Extracts schema definitions:
253
-
254
- ```typescript
255
- import { SchemaAnalyzer } from '@vertz/compiler';
256
-
257
- const analyzer = new SchemaAnalyzer(project, { rootDir: './src' });
258
- const result = await analyzer.analyze();
259
-
260
- console.log(result.schemas);
261
- /*
262
- [
263
- {
264
- id: 'users:CreateUserDto',
265
- moduleName: 'users',
266
- name: 'CreateUserDto',
267
- sourceFile: './src/users/schemas.ts',
268
- definition: { ... }
269
- }
270
- ]
271
- */
272
- ```
273
-
274
- ## Validators
275
-
276
- ### Custom Validator
277
-
278
- Create custom validators by implementing the `Validator` interface:
279
-
280
- ```typescript
281
- import type { Validator, AppIR, Diagnostic } from '@vertz/compiler';
282
- import { createDiagnostic } from '@vertz/compiler';
283
-
284
- class CustomValidator implements Validator {
285
- async validate(ir: AppIR): Promise<Diagnostic[]> {
286
- const diagnostics: Diagnostic[] = [];
287
-
288
- for (const module of ir.modules) {
289
- if (module.services.length === 0) {
290
- diagnostics.push(
291
- createDiagnostic({
292
- code: 'CUSTOM_001',
293
- severity: 'warning',
294
- message: `Module "${module.name}" has no services`,
295
- file: module.sourceFile,
296
- line: module.sourceLine,
297
- }),
298
- );
299
- }
300
- }
301
-
302
- return diagnostics;
303
- }
304
- }
305
- ```
306
-
307
- Use custom validators:
308
-
309
- ```typescript
310
- const compiler = createCompiler(config, {
311
- validators: [
312
- new NamingValidator(),
313
- new PlacementValidator(config),
314
- new CustomValidator(), // ← Your validator
315
- ],
316
- });
317
- ```
318
-
319
- ## Generators
320
-
321
- ### Custom Generator
322
-
323
- Create custom generators by extending `BaseGenerator`:
324
-
325
- ```typescript
326
- import { BaseGenerator, type AppIR } from '@vertz/compiler';
327
- import { writeFile } from 'node:fs/promises';
328
- import { join } from 'node:path';
329
-
330
- class CustomGenerator extends BaseGenerator {
331
- async generate(ir: AppIR, outputDir: string): Promise<void> {
332
- const output = this.generateOutput(ir);
333
- const outputPath = join(outputDir, 'custom-output.json');
334
- await writeFile(outputPath, JSON.stringify(output, null, 2));
335
- }
336
-
337
- private generateOutput(ir: AppIR): object {
338
- return {
339
- moduleCount: ir.modules.length,
340
- routeCount: ir.modules.flatMap((m) => m.routers.flatMap((r) => r.routes)).length,
341
- serviceCount: ir.modules.flatMap((m) => m.services).length,
342
- };
343
- }
344
- }
345
- ```
346
-
347
- Use custom generators:
348
-
349
- ```typescript
350
- const compiler = createCompiler(config, {
351
- generators: [
352
- new BootGenerator(config),
353
- new ManifestGenerator(),
354
- new CustomGenerator(), // ← Your generator
355
- ],
356
- });
357
- ```
358
-
359
- ## Incremental Compilation
360
-
361
- For faster rebuilds, use incremental compilation:
362
-
363
- ```typescript
364
- import { IncrementalCompiler } from '@vertz/compiler';
365
-
366
- const compiler = new IncrementalCompiler(config);
367
-
368
- // Initial compilation
369
- await compiler.compile();
370
-
371
- // On file change
372
- const changedFiles = ['./src/users/users.service.ts'];
373
- await compiler.recompile(changedFiles);
374
- ```
375
-
376
- The incremental compiler:
377
- - Caches previous IR
378
- - Categorizes file changes (app, module, schema, etc.)
379
- - Reanalyzes only affected modules
380
- - Merges changes into existing IR
381
-
382
- ## Typecheck Integration
383
-
384
- Run TypeScript type checking alongside compilation:
385
-
386
- ```typescript
387
- import { typecheck } from '@vertz/compiler';
388
-
389
- const result = await typecheck({
390
- project: './tsconfig.json',
391
- noEmit: true,
392
- });
393
-
394
- if (!result.success) {
395
- console.error('Type errors:', result.diagnostics);
396
- }
397
- ```
398
-
399
- Watch mode:
400
-
401
- ```typescript
402
- import { typecheckWatch } from '@vertz/compiler';
403
-
404
- for await (const result of typecheckWatch({ project: './tsconfig.json' })) {
405
- if (result.success) {
406
- console.log('✅ Types OK');
407
- } else {
408
- console.error('Type errors:', result.diagnostics);
409
- }
410
- }
411
- ```
412
-
413
- ## Diagnostics
414
-
415
- All errors and warnings are represented as `Diagnostic` objects:
416
-
417
- ```typescript
418
- interface Diagnostic {
419
- code: string; // Unique error code (e.g., 'MODULE_001')
420
- severity: 'error' | 'warning' | 'info';
421
- message: string; // Human-readable message
422
- file?: string; // Source file path
423
- line?: number; // Line number
424
- column?: number; // Column number
425
- context?: SourceContext; // Additional context
426
- }
427
- ```
428
-
429
- Create diagnostics:
430
-
431
- ```typescript
432
- import { createDiagnostic, createDiagnosticFromLocation } from '@vertz/compiler';
433
-
434
- const diagnostic = createDiagnostic({
435
- code: 'CUSTOM_001',
436
- severity: 'error',
437
- message: 'Invalid configuration',
438
- file: './src/app.ts',
439
- line: 10,
440
- column: 5,
441
- });
442
-
443
- // Or from a ts-morph Node
444
- const nodeWithError = sourceFile.getFunction('myFunction');
445
- const diagnostic2 = createDiagnosticFromLocation(nodeWithError, {
446
- code: 'CUSTOM_002',
447
- message: 'Function is deprecated',
448
- severity: 'warning',
449
- });
450
- ```
451
-
452
- Filter diagnostics:
453
-
454
- ```typescript
455
- import { filterBySeverity, hasErrors } from '@vertz/compiler';
456
-
457
- const errors = filterBySeverity(diagnostics, 'error');
458
- const warnings = filterBySeverity(diagnostics, 'warning');
459
-
460
- if (hasErrors(diagnostics)) {
461
- console.error('Compilation failed');
462
- process.exit(1);
463
- }
464
- ```
465
-
466
- ## Architecture
467
-
468
- ### Compiler Pipeline
469
-
470
- ```
471
- ┌─────────────────────────────────────────────────────────┐
472
- │ 1. Configuration │
473
- │ - Load vertz.config.ts │
474
- │ - Resolve paths, options │
475
- └─────────────────────────────────────────────────────────┘
476
-
477
- ┌─────────────────────────────────────────────────────────┐
478
- │ 2. Analysis (ts-morph AST traversal) │
479
- │ - AppAnalyzer → app config │
480
- │ - ModuleAnalyzer → modules, services, routers │
481
- │ - SchemaAnalyzer → schemas │
482
- │ - MiddlewareAnalyzer → middleware │
483
- │ - EnvAnalyzer → env vars │
484
- │ - DependencyGraphAnalyzer → service graph │
485
- └─────────────────────────────────────────────────────────┘
486
-
487
- ┌─────────────────────────────────────────────────────────┐
488
- │ 3. IR Construction │
489
- │ - Combine analyzer results │
490
- │ - Build unified AppIR │
491
- │ - Add cross-references │
492
- └─────────────────────────────────────────────────────────┘
493
-
494
- ┌─────────────────────────────────────────────────────────┐
495
- │ 4. Validation │
496
- │ - NamingValidator │
497
- │ - PlacementValidator │
498
- │ - CompletenessValidator │
499
- │ - ModuleValidator │
500
- └─────────────────────────────────────────────────────────┘
501
-
502
- ┌─────────────────────────────────────────────────────────┐
503
- │ 5. Generation │
504
- │ - BootGenerator → boot.ts │
505
- │ - RouteTableGenerator → routes.ts │
506
- │ - SchemaRegistryGenerator → schemas.ts │
507
- │ - ManifestGenerator → manifest.json │
508
- │ - OpenAPIGenerator → openapi.json │
509
- └─────────────────────────────────────────────────────────┘
510
- ```
511
-
512
- ### Extensibility
513
-
514
- The compiler is designed for extensibility:
515
-
516
- - **Custom Analyzers** — Extract additional information from source code
517
- - **Custom Validators** — Enforce custom rules and conventions
518
- - **Custom Generators** — Emit custom artifacts (GraphQL schemas, docs, etc.)
519
-
520
- All extension points use dependency injection — pass custom implementations to `createCompiler()`.
521
-
522
- ## CLI Integration
523
-
524
- The compiler is typically used through `@vertz/cli`:
525
-
526
- ```bash
527
- # Compile the app
528
- vertz build
529
-
530
- # Check for errors without building
531
- vertz check
532
-
533
- # Watch mode
534
- vertz dev
535
- ```
536
-
537
- See `@vertz/cli` for CLI documentation.
538
-
539
- ## Programmatic Usage
540
-
541
- ### Example: Extract Route Information
542
-
543
- ```typescript
544
- import { createCompiler } from '@vertz/compiler';
545
-
546
- const compiler = createCompiler({
547
- rootDir: './src',
548
- appFile: './src/app.ts',
549
- outDir: './.vertz',
550
- });
551
-
552
- const ir = await compiler.analyze();
553
-
554
- // Extract all routes
555
- const routes = ir.modules.flatMap((module) =>
556
- module.routers.flatMap((router) =>
557
- router.routes.map((route) => ({
558
- method: route.method,
559
- path: `${module.prefix ?? ''}${router.prefix}${route.path}`,
560
- module: module.name,
561
- })),
562
- ),
563
- );
564
-
565
- console.log('Routes:', routes);
566
- ```
567
-
568
- ### Example: Generate Custom Documentation
569
-
570
- ```typescript
571
- import { createCompiler, type AppIR } from '@vertz/compiler';
572
- import { writeFile } from 'node:fs/promises';
573
-
574
- const compiler = createCompiler(config);
575
- const ir = await compiler.analyze();
576
-
577
- // Generate markdown docs
578
- const markdown = generateDocs(ir);
579
- await writeFile('./docs/api.md', markdown);
580
-
581
- function generateDocs(ir: AppIR): string {
582
- let md = '# API Documentation\n\n';
583
-
584
- for (const module of ir.modules) {
585
- md += `## Module: ${module.name}\n\n`;
586
-
587
- for (const router of module.routers) {
588
- for (const route of router.routes) {
589
- md += `### ${route.method.toUpperCase()} ${router.prefix}${route.path}\n\n`;
590
- if (route.schema?.body) {
591
- md += `**Request Body:**\n\`\`\`json\n${JSON.stringify(route.schema.body, null, 2)}\n\`\`\`\n\n`;
592
- }
593
- }
594
- }
595
- }
596
-
597
- return md;
598
- }
599
- ```
600
-
601
- ## API Reference
602
-
603
- ### Core Functions
604
-
605
- - `createCompiler(config, deps?)` — Create compiler instance
606
- - `defineConfig(config)` — Define configuration (for `vertz.config.ts`)
607
- - `resolveConfig(config)` — Resolve configuration with defaults
608
-
609
- ### Analyzers
610
-
611
- - `AppAnalyzer` — Extract app configuration
612
- - `ModuleAnalyzer` — Extract modules
613
- - `SchemaAnalyzer` — Extract schemas
614
- - `MiddlewareAnalyzer` — Extract middleware
615
- - `EnvAnalyzer` — Extract env variables
616
- - `DependencyGraphAnalyzer` — Build dependency graph
617
-
618
- ### Validators
619
-
620
- - `NamingValidator` — Check naming conventions
621
- - `PlacementValidator` — Check file placement
622
- - `CompletenessValidator` — Check completeness
623
- - `ModuleValidator` — Validate module structure
624
-
625
- ### Generators
626
-
627
- - `BootGenerator` — Generate boot file
628
- - `RouteTableGenerator` — Generate route table
629
- - `SchemaRegistryGenerator` — Generate schema registry
630
- - `ManifestGenerator` — Generate JSON manifest
631
- - `OpenAPIGenerator` — Generate OpenAPI spec
632
-
633
- ### Utilities
634
-
635
- - `typecheck(options)` — Run TypeScript type checking
636
- - `typecheckWatch(options)` — Run type checking in watch mode
637
- - `createDiagnostic(options)` — Create diagnostic
638
- - `hasErrors(diagnostics)` — Check if diagnostics contain errors
639
- - `filterBySeverity(diagnostics, severity)` — Filter diagnostics
9
+ - **`@vertz/cli`** — All CLI commands (`vertz build`, `vertz dev`, `vertz check`) invoke the compiler.
10
+ - **`@vertz/codegen`** — Consumes the compiler's intermediate representation (IR) to generate SDKs and CLIs.
11
+ - **Framework contributors** See [INTERNALS.md](./INTERNALS.md) for architecture, pipeline stages, and extension points.
640
12
 
641
13
  ## Related Packages
642
14
 
643
- - **[@vertz/cli](../cli)**Command-line interface (uses the compiler)
644
- - **[@vertz/codegen](../codegen)** — Code generation utilities
645
- - **[@vertz/core](../core)** — Runtime framework (analyzed by compiler)
646
-
647
- ## Advanced Topics
648
-
649
- ### AST Traversal
650
-
651
- The compiler uses `ts-morph` for AST traversal. Utilities are available:
652
-
653
- ```typescript
654
- import { findCallExpressions, getStringValue, extractObjectLiteral } from '@vertz/compiler';
655
- import { Project } from 'ts-morph';
656
-
657
- const project = new Project({ tsConfigFilePath: 'tsconfig.json' });
658
- const sourceFile = project.getSourceFile('app.ts');
659
-
660
- // Find all createApp() calls
661
- const appCalls = findCallExpressions(sourceFile, 'createApp');
662
-
663
- // Extract string literal value
664
- const basePath = getStringValue(configObject, 'basePath'); // '/api'
665
-
666
- // Extract object literal
667
- const corsConfig = extractObjectLiteral(configObject, 'cors');
668
- ```
669
-
670
- ### Schema Execution
671
-
672
- Execute schemas at compile-time to extract metadata:
673
-
674
- ```typescript
675
- import { createSchemaExecutor } from '@vertz/compiler';
676
-
677
- const executor = createSchemaExecutor(project);
678
- const result = await executor.execute('./src/schemas.ts', 'userSchema');
679
-
680
- console.log(result.jsonSchema); // JSON Schema representation
681
- ```
682
-
683
- ### Import Resolution
684
-
685
- Resolve imports and exports across files:
686
-
687
- ```typescript
688
- import { resolveIdentifier, resolveExport } from '@vertz/compiler';
689
-
690
- const resolved = resolveIdentifier(sourceFile, 'userService');
691
- console.log(resolved.sourceFile, resolved.name);
692
-
693
- const exported = resolveExport(sourceFile, 'userSchema');
694
- console.log(exported);
695
- ```
696
-
697
- ## Performance
698
-
699
- The compiler is optimized for large codebases:
700
-
701
- - **Incremental compilation** — Only reanalyze changed modules
702
- - **Parallel analysis** — Analyzers run concurrently
703
- - **Caching** — IR is cached between runs
704
- - **Lazy evaluation** — Generators only run when needed
705
-
706
- Typical performance (1000 files, 50 modules):
707
- - Initial compilation: ~2-5 seconds
708
- - Incremental recompilation: ~100-500ms
15
+ - [`@vertz/cli`](../cli) — The CLI that invokes the compiler
16
+ - [`@vertz/codegen`](../codegen) — Code generation from compiler IR
709
17
 
710
18
  ## License
711
19