@supacloud/compiler 0.4.0 → 0.5.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.
@@ -0,0 +1,21 @@
1
+ import type { ApplicationGraph, Diagnostic } from "./types";
2
+ export interface DoctorResult {
3
+ checks: Array<{
4
+ name: string;
5
+ ok: boolean;
6
+ detail: string;
7
+ }>;
8
+ diagnostics: ApplicationGraph["diagnostics"];
9
+ errors: number;
10
+ }
11
+ export declare function formatGraph(graph: ApplicationGraph): string;
12
+ export declare function explainGraph(graph: ApplicationGraph, subject: string): string;
13
+ export declare function doctorProject(rootDir: string, outDir: string, graph: ApplicationGraph, upToDate: boolean, diagnostics?: Diagnostic[]): DoctorResult;
14
+ /**
15
+ * Exports the application module architecture as a Mermaid graph diagram.
16
+ */
17
+ export declare function exportGraphMermaid(graph: ApplicationGraph): string;
18
+ /**
19
+ * Exports the application module architecture as a Graphviz DOT script.
20
+ */
21
+ export declare function exportGraphDot(graph: ApplicationGraph): string;
package/dist/types.d.ts CHANGED
@@ -1,6 +1,6 @@
1
1
  /**
2
- * ApplicationGraph:编译器从源码 AST 构建出的静态应用图。
3
- * 运行期不反射、无容器,所有信息都在该结构与生成代码中显式给出。
2
+ * ApplicationGraph: Static application graph built by the compiler from source AST.
3
+ * No runtime reflection or container; all metadata is explicitly represented here and in generated code.
4
4
  */
5
5
  export type Scope = "application" | "request" | "job";
6
6
  export type ProviderKind = "class" | "value" | "factory" | "existing";
@@ -11,9 +11,15 @@ export interface Diagnostic {
11
11
  message: string;
12
12
  file?: string;
13
13
  line?: number;
14
+ /** Actionable Angular Ivy-style remediation hint. */
15
+ suggestion?: string;
16
+ /** Standardized compiler diagnostic code (e.g. SC1001) modeled after Angular ngtsc error codes. */
17
+ errorCode?: string;
18
+ /** Documentation URL for this diagnostic. */
19
+ docsUrl?: string;
14
20
  }
15
21
  export interface ProviderNode {
16
- /** token 名(InjectionToken 变量名或类名)。 */
22
+ /** Token name (InjectionToken variable name or class name). */
17
23
  token: string;
18
24
  tokenKind: TokenKind;
19
25
  kind: ProviderKind;
@@ -22,14 +28,35 @@ export interface ProviderNode {
22
28
  useFactoryName?: string;
23
29
  useExisting?: string;
24
30
  scope: Scope;
25
- /** token 名,构造/工厂参数顺序。 */
31
+ /** Token names in constructor/factory parameter order. */
26
32
  deps: string[];
33
+ /** Parameter tokens that are marked @Optional() (receive undefined if unresolved). */
34
+ optionalDeps?: string[];
35
+ /** Parameter tokens marked @Self() (must be resolved from current module's own providers). */
36
+ selfDeps?: string[];
37
+ /** Parameter tokens marked @SkipSelf() (must NOT be resolved from current module's own providers). */
38
+ skipSelfDeps?: string[];
39
+ /** Parameter tokens marked @Host(). */
40
+ hostDeps?: string[];
41
+ /** When true, multiple providers can contribute to this token as an array of instances (Angular multi-providers). */
42
+ multi?: boolean;
43
+ /** Automatically provided in the root injector context without manual module declaration (Angular-style). */
44
+ providedIn?: "root";
45
+ /** Class implements OnDestroy interface or onDestroy method. */
46
+ hasOnDestroy?: boolean;
27
47
  exported: boolean;
28
48
  file: string;
29
49
  line: number;
30
- /** useClass/useFactory/useValue 符号的模块相对路径(供生成 import)。 */
50
+ /** Relative module path of useClass/useFactory/useValue symbol (for import generation). */
31
51
  importPath?: string;
32
52
  }
53
+ export interface HandlerParamNode {
54
+ name: string;
55
+ kind: "param" | "query" | "body" | "headers" | "context" | "unknown";
56
+ bindingName?: string;
57
+ transform?: "number" | "boolean" | "string";
58
+ default?: unknown;
59
+ }
33
60
  export interface RouteNode {
34
61
  method: "GET" | "POST" | "PUT" | "PATCH" | "DELETE" | "HEAD" | "OPTIONS";
35
62
  path: string;
@@ -40,16 +67,56 @@ export interface RouteNode {
40
67
  response?: string;
41
68
  /** @Command-decorated class explicitly bound by the route. */
42
69
  command?: string;
70
+ /** Route guards executed before handler (Angular CanActivateFn style). */
71
+ guards?: string[];
72
+ /** Route matching guards executed before route activation (Angular CanMatchFn style). */
73
+ canMatch?: string[];
74
+ /** Route deactivation guards executed before leaving route (Angular CanDeactivateFn style). */
75
+ canDeactivate?: string[];
76
+ /** Route resolvers executed before handler (Angular ResolveFn style). */
77
+ resolvers?: Record<string, string>;
78
+ /** Route redirect target (Angular Router style). */
79
+ redirectTo?: string;
80
+ /** Route redirect matching rule (Angular Router style). */
81
+ pathMatch?: "full" | "prefix";
82
+ /** Path parameters parsed from route path (e.g. :id -> 'id'). */
83
+ pathParams?: string[];
84
+ /** Handler method parameter bindings declared via @Param('name'). */
85
+ paramBindings?: string[];
86
+ /** Handler method query bindings declared via @Query('name'). */
87
+ queryBindings?: string[];
88
+ /** Handler method has @Body() binding. */
89
+ hasBodyBinding?: boolean;
90
+ /** Parameter transforms declared via @Param({ transform: ... }) */
91
+ paramTransforms?: Record<string, "number" | "boolean" | "string">;
92
+ /** Parameter defaults declared via @Param({ default: ... }) */
93
+ paramDefaults?: Record<string, unknown>;
94
+ /** Query transforms declared via @Query({ transform: ... }) */
95
+ queryTransforms?: Record<string, "number" | "boolean" | "string">;
96
+ /** Query defaults declared via @Query({ default: ... }) */
97
+ queryDefaults?: Record<string, unknown>;
98
+ /** Route title (Angular Route.title style). */
99
+ title?: string;
100
+ /** Route static metadata dictionary (Angular Route.data style). */
101
+ data?: Record<string, unknown>;
102
+ /** Detailed method parameter metadata for compile-time typed invoker generation. */
103
+ handlerParams?: HandlerParamNode[];
43
104
  }
44
105
  export interface ControllerNode {
45
106
  className: string;
46
107
  path: string;
47
108
  scope: Scope;
48
109
  deps: string[];
110
+ /** Parameter tokens marked @Optional(). */
111
+ optionalDeps?: string[];
112
+ selfDeps?: string[];
113
+ skipSelfDeps?: string[];
114
+ /** Automatically registered without manual module declaration (Angular standalone controller style). */
115
+ standalone?: boolean;
49
116
  routes: RouteNode[];
50
117
  file: string;
51
118
  importPath: string;
52
- /** 路由 schema 符号名 → 模块相对路径(供生成 import)。 */
119
+ /** Route schema symbol name -> relative module path (for import generation). */
53
120
  schemaImports?: Record<string, string>;
54
121
  }
55
122
  export interface CommandNode {
@@ -59,52 +126,59 @@ export interface CommandNode {
59
126
  transaction: "required" | "none";
60
127
  audit?: string;
61
128
  idempotency: "required" | "none";
129
+ /** Automatically registered without manual module declaration. */
130
+ standalone?: boolean;
62
131
  }
63
132
  export interface QueryNode {
64
133
  className: string;
65
134
  name: string;
66
135
  }
67
136
  export interface ModuleNode {
68
- /** @Module({ name }) 或 defineModule 的 name。 */
137
+ /** Name from @Module({ name }) or defineModule. */
69
138
  name: string;
70
139
  className: string;
71
- /** 模块标签(如 ['scope:case', 'type:feature']),用于架构边界治理。 */
140
+ /** Module tags (e.g. ['scope:case', 'type:feature']) for architecture boundary governance. */
72
141
  tags?: string[];
73
142
  file: string;
74
143
  line: number;
75
- /** 被 import 模块的 name。 */
144
+ /** Names of imported modules. */
76
145
  imports: string[];
77
146
  providers: ProviderNode[];
78
147
  controllers: ControllerNode[];
79
148
  commands: CommandNode[];
80
149
  queries: QueryNode[];
81
- /** 导出的 token 名。 */
150
+ /** Exported token names. */
82
151
  exports: string[];
83
152
  }
84
153
  export interface ApplicationGraph {
85
154
  modules: ModuleNode[];
86
- /** 被依赖但无任何模块提供的 token 名(平台注入)。 */
155
+ /** Depended token names provided by platform injection rather than any module. */
87
156
  externalTokens: string[];
88
157
  /**
89
- * 分析阶段产生的诊断(如 missing-deps),由 compileProject 合并进结果。
90
- * 不写入 app.manifest.json。
158
+ * Diagnostics produced during analysis (e.g. missing-deps), merged by compileProject.
159
+ * Omitted from app.manifest.json.
91
160
  */
92
161
  diagnostics?: Diagnostic[];
93
162
  /**
94
- * InjectionToken 变量名 → 字符串 name(如 REQUEST_CONTEXT →
95
- * "supacloud.request-context"),供代码生成识别内置上下文 token。
96
- * 不写入 app.manifest.json。
163
+ * InjectionToken variable name -> string name (e.g. REQUEST_CONTEXT ->
164
+ * "supacloud.request-context"), used during code generation to identify built-in context tokens.
165
+ * Omitted from app.manifest.json.
97
166
  */
98
167
  tokenNames?: Record<string, string>;
168
+ /** Incremental cache statistics (modules reused vs reanalyzed). */
169
+ cacheStats?: {
170
+ reusedModules: string[];
171
+ reanalyzedModules: string[];
172
+ };
99
173
  }
100
174
  export interface CompileOptions {
101
- /** 项目根(含 tsconfig)。 */
175
+ /** Project root directory (containing tsconfig). */
102
176
  rootDir: string;
103
- /** glob,默认 ['**\/*.module.ts', '**\/*.ts']。 */
177
+ /** Glob patterns, defaults to ['**\/*.module.ts', '**\/*.ts']. */
104
178
  include?: string[];
105
- /** 生成目录(如 <rootDir>/generated)。 */
179
+ /** Output directory (e.g. <rootDir>/generated). */
106
180
  outDir: string;
107
- /** warn 级诊断升级为 error。 */
181
+ /** Upgrade warn-level diagnostics to error. */
108
182
  strict?: boolean;
109
183
  /** Built-in architecture boundary preset (for example, 'modular-monolith'). */
110
184
  moduleBoundaryPreset?: ModuleBoundaryPresetName;
@@ -118,6 +192,16 @@ export interface CompileOptions {
118
192
  disallowControllerDirectDb?: boolean;
119
193
  /** Detect modules declared in the project that are unreachable from any root module. */
120
194
  detectOrphanModules?: boolean;
195
+ /** Write generated artifacts even when error-level diagnostics exist (default: true). */
196
+ writeOnError?: boolean;
197
+ /** Generate typed API client in client.ts (default: false). */
198
+ generateClient?: boolean;
199
+ /** Generate typed permissions registry in permissions.ts (default: false). */
200
+ generatePermissions?: boolean;
201
+ /** Prune unused root providers from compiled output (Angular Ivy AOT tree-shaking). */
202
+ treeShakeUnusedProviders?: boolean;
203
+ /** Incremental dependency graph cache. */
204
+ cache?: DependencyGraphCache;
121
205
  }
122
206
  export interface ModuleBoundaryRule {
123
207
  /** Source module tag pattern or tag (for example, 'type:ui', 'scope:case', or '*'). */
@@ -167,6 +251,14 @@ export interface CompileResult {
167
251
  diagnostics: Diagnostic[];
168
252
  graph: ApplicationGraph;
169
253
  written: string[];
254
+ stats?: CompileStats;
255
+ }
256
+ export interface CompileStats {
257
+ cacheHit: boolean;
258
+ changedFiles: string[];
259
+ affectedModules: string[];
260
+ reanalyzedModules?: string[];
261
+ reusedModules?: string[];
170
262
  }
171
263
  export interface CheckProjectResult {
172
264
  /** Whether generated artifacts exactly match the files on disk. */
@@ -176,3 +268,44 @@ export interface CheckProjectResult {
176
268
  diagnostics: Diagnostic[];
177
269
  graph: ApplicationGraph;
178
270
  }
271
+ export interface WatchEvent {
272
+ type: "compile-start" | "compiled" | "compile-error";
273
+ initial: boolean;
274
+ durationMs: number;
275
+ diagnostics: Diagnostic[];
276
+ written: string[];
277
+ stats?: CompileStats;
278
+ }
279
+ export interface WatchOptions extends CompileOptions {
280
+ /** Debounce source changes before starting a compile (default: 100ms). */
281
+ debounceMs?: number;
282
+ onEvent?: (event: WatchEvent) => void;
283
+ }
284
+ export interface WatchHandle {
285
+ /** Resolves after the initial compile has completed. */
286
+ ready: Promise<WatchEvent>;
287
+ close(): Promise<void>;
288
+ }
289
+ export interface CachedModuleEntry {
290
+ module: ModuleNode;
291
+ /** Files owned by this module (normalized relative paths). */
292
+ ownedFiles: string[];
293
+ /** File hash mapping for owned files. */
294
+ fileHashes: Record<string, string>;
295
+ /** Diagnostics captured during this module's analysis. */
296
+ diagnostics?: Diagnostic[];
297
+ }
298
+ export interface DependencyGraphCache {
299
+ /** Cached modules by module name. */
300
+ modules: Map<string, CachedModuleEntry>;
301
+ /** Global file hashes by relative file path. */
302
+ fileHashes: Map<string, string>;
303
+ /** Retained AST project for true incremental graph re-analysis. */
304
+ project?: any;
305
+ /** Retained module dependency graph tracking imports and reverse dependents. */
306
+ dependencyGraph?: any;
307
+ lastStats?: {
308
+ reusedModules: string[];
309
+ reanalyzedModules: string[];
310
+ };
311
+ }
package/dist/util.d.ts CHANGED
@@ -1,15 +1,19 @@
1
1
  /**
2
- * token 名 → services 对象的 key。
3
- * CASE_REPOSITORY → caseRepository、LOGGER → logger(常量命名转 camelCase),
4
- * CaseService → caseService(PascalCase 首字母小写)。
2
+ * Token name -> key in services object.
3
+ * CASE_REPOSITORY -> caseRepository, LOGGER -> logger (convert CONSTANT_CASE to camelCase),
4
+ * CaseService -> caseService (lowercase first letter of PascalCase).
5
5
  */
6
6
  export declare function camelName(token: string): string;
7
- /** 计算 from 目录到 to 文件的相对 import 路径(去扩展名,保证 ./ 或 ../ 前缀)。 */
7
+ /** Computes relative import path from fromDir to toFile (stripping extension, ensuring ./ or ../ prefix). */
8
8
  export declare function relativeImportPath(fromDir: string, toFile: string): string;
9
- /** 内置上下文 token 的字符串 name(与 @supacloud/app 的 REQUEST_CONTEXT/JOB_CONTEXT 对齐)。 */
9
+ /** String names of built-in context tokens (aligned with REQUEST_CONTEXT/JOB_CONTEXT in @supacloud/app). */
10
10
  export declare const REQUEST_CONTEXT_TOKEN_NAME = "supacloud.request-context";
11
11
  export declare const JOB_CONTEXT_TOKEN_NAME = "supacloud.job-context";
12
- /** 判断 token 是否为内置 request 上下文(变量名 REQUEST_CONTEXT 或 token name 匹配)。 */
12
+ /** Determines whether token is built-in request context (variable name REQUEST_CONTEXT or matching token name). */
13
13
  export declare function isRequestContextToken(token: string, tokenNames?: Record<string, string>): boolean;
14
- /** 判断 token 是否为内置 job 上下文。 */
14
+ /** Determines whether token is built-in job context. */
15
15
  export declare function isJobContextToken(token: string, tokenNames?: Record<string, string>): boolean;
16
+ /** Normalizes and combines controller path and route path. */
17
+ export declare function joinRoutePaths(prefix: string, path: string): string;
18
+ /** Finds the closest match from a list of candidate strings (case/delimiter-insensitive). */
19
+ export declare function findClosestMatch(target: string, candidates: string[]): string | undefined;
@@ -1,6 +1,10 @@
1
1
  import type { ApplicationGraph, Diagnostic, ValidateOptions } from "./types";
2
+ export declare const COMPILER_DIAGNOSTIC_CODES: Record<string, {
3
+ code: string;
4
+ docsUrl: string;
5
+ }>;
2
6
  /**
3
- * 校验 ApplicationGraph,产出诊断列表。
4
- * strict 时 warn 级诊断升级为 error。
7
+ * Validates ApplicationGraph and produces diagnostics list.
8
+ * In strict mode, warn-level diagnostics are promoted to error.
5
9
  */
6
10
  export declare function validateGraph(graph: ApplicationGraph, options?: boolean | ValidateOptions): Diagnostic[];
@@ -0,0 +1,3 @@
1
+ import type { WatchHandle, WatchOptions } from "./types";
2
+ /** Watch a project and keep the last successful generated artifacts active on errors. */
3
+ export declare function watchProject(options: WatchOptions): WatchHandle;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@supacloud/compiler",
3
- "version": "0.4.0",
3
+ "version": "0.5.0",
4
4
  "description": "Static compiler for @supacloud/app metadata: builds the application graph from AST, validates it, and generates reflection-free factory code",
5
5
  "type": "module",
6
6
  "main": "./dist/index.js",