@supacloud/compiler 0.2.0 → 0.3.1

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/dist/index.d.ts CHANGED
@@ -4,4 +4,4 @@ export { generateApplication } from "./generate";
4
4
  export type { GenerateOptions } from "./generate";
5
5
  export { validateGraph } from "./validate";
6
6
  export { camelName } from "./util";
7
- export type { ApplicationGraph, CommandNode, CompileOptions, CompileResult, ControllerNode, Diagnostic, ModuleNode, ProviderKind, ProviderNode, QueryNode, RouteNode, Scope, TokenKind, } from "./types";
7
+ export type { ApplicationGraph, CommandNode, CompileOptions, CompileResult, ControllerNode, Diagnostic, ModuleBoundaryRule, ModuleNode, ProviderKind, ProviderNode, QueryNode, RouteNode, Scope, TokenKind, } from "./types";
package/dist/index.js CHANGED
@@ -137,6 +137,7 @@ function parseTokenVariable(decl, file) {
137
137
  function parseModule(candidate, nameByNode, ctx) {
138
138
  const { options, className, file, line } = candidate;
139
139
  const name = nameByNode.get(candidate.node) ?? className;
140
+ const tags = arrayProp(options, "tags").map((el) => Node.isStringLiteral(el) ? el.getLiteralText() : el.getText().replace(/['"]/g, "")).filter(Boolean);
140
141
  const imports = arrayProp(options, "imports").map((el) => {
141
142
  const decl = Node.isIdentifier(el) ? resolveDeclaration(el)[0] : undefined;
142
143
  if (decl) {
@@ -221,6 +222,7 @@ function parseModule(candidate, nameByNode, ctx) {
221
222
  return {
222
223
  name,
223
224
  className,
225
+ tags: tags.length > 0 ? tags : undefined,
224
226
  file: sourcePath(ctx.rootDir, file),
225
227
  line,
226
228
  imports,
@@ -1012,7 +1014,9 @@ var SCOPE_LIFETIME_RANK = {
1012
1014
  request: 1,
1013
1015
  job: 1
1014
1016
  };
1015
- function validateGraph(graph, strict = false) {
1017
+ function validateGraph(graph, options = false) {
1018
+ const strict = typeof options === "boolean" ? options : options.strict ?? false;
1019
+ const moduleBoundaries = typeof options === "object" ? options.moduleBoundaries : undefined;
1016
1020
  const diagnostics = [];
1017
1021
  const globalProviders = new Map;
1018
1022
  for (const module of graph.modules) {
@@ -1113,6 +1117,35 @@ function validateGraph(graph, strict = false) {
1113
1117
  }
1114
1118
  }
1115
1119
  }
1120
+ if (moduleBoundaries && moduleBoundaries.length > 0) {
1121
+ for (const module of graph.modules) {
1122
+ const sourceTags = module.tags ?? [];
1123
+ for (const importName of module.imports) {
1124
+ const targetModule = graph.modules.find((m) => m.name === importName);
1125
+ if (!targetModule)
1126
+ continue;
1127
+ const targetTags = targetModule.tags ?? [];
1128
+ for (const rule of moduleBoundaries) {
1129
+ const matchesSource = rule.sourceTag === "*" || sourceTags.includes(rule.sourceTag);
1130
+ if (!matchesSource)
1131
+ continue;
1132
+ if (rule.bannedDependenciesWithTags) {
1133
+ for (const bannedTag of rule.bannedDependenciesWithTags) {
1134
+ if (targetTags.includes(bannedTag)) {
1135
+ error("module-boundary-violation", `模块 ${module.name} (tags: [${sourceTags.join(", ")}]) 禁止依赖带有标签 '${bannedTag}' 的模块 ${targetModule.name} (tags: [${targetTags.join(", ")}])`, module.file, module.line);
1136
+ }
1137
+ }
1138
+ }
1139
+ if (rule.onlyDependOnLibsWithTags && rule.onlyDependOnLibsWithTags.length > 0) {
1140
+ const hasAllowed = targetTags.some((t) => rule.onlyDependOnLibsWithTags.includes(t));
1141
+ if (!hasAllowed && targetTags.length > 0) {
1142
+ error("module-boundary-violation", `模块 ${module.name} (tags: [${sourceTags.join(", ")}]) 仅允许依赖带有 [${rule.onlyDependOnLibsWithTags.join(", ")}] 标签的模块,但模块 ${targetModule.name} 的标签为 [${targetTags.join(", ")}]`, module.file, module.line);
1143
+ }
1144
+ }
1145
+ }
1146
+ }
1147
+ }
1148
+ }
1116
1149
  diagnostics.push(...detectCycles(graph, resolveDep));
1117
1150
  return diagnostics;
1118
1151
  }
@@ -1169,7 +1202,10 @@ async function compileProject(options) {
1169
1202
  const graph = await analyzeProject(options.rootDir, options.include);
1170
1203
  const diagnostics = [
1171
1204
  ...graph.diagnostics ?? [],
1172
- ...validateGraph(graph, options.strict)
1205
+ ...validateGraph(graph, {
1206
+ strict: options.strict,
1207
+ moduleBoundaries: options.moduleBoundaries
1208
+ })
1173
1209
  ];
1174
1210
  if (options.strict) {
1175
1211
  for (const diagnostic of diagnostics) {
package/dist/types.d.ts CHANGED
@@ -68,6 +68,8 @@ export interface ModuleNode {
68
68
  /** @Module({ name }) 或 defineModule 的 name。 */
69
69
  name: string;
70
70
  className: string;
71
+ /** 模块标签(如 ['scope:case', 'type:feature']),用于架构边界治理。 */
72
+ tags?: string[];
71
73
  file: string;
72
74
  line: number;
73
75
  /** 被 import 模块的 name。 */
@@ -104,6 +106,16 @@ export interface CompileOptions {
104
106
  outDir: string;
105
107
  /** warn 级诊断升级为 error。 */
106
108
  strict?: boolean;
109
+ /** 模块边界与架构治理规则(受 Nx enforce-module-boundaries 启发)。 */
110
+ moduleBoundaries?: ModuleBoundaryRule[];
111
+ }
112
+ export interface ModuleBoundaryRule {
113
+ /** 源模块标签模式或标签(如 'type:ui', 'scope:case', '*')。 */
114
+ sourceTag: string;
115
+ /** 源模块仅允许依赖具有这些标签的模块。 */
116
+ onlyDependOnLibsWithTags?: string[];
117
+ /** 源模块禁止依赖具有这些标签的模块。 */
118
+ bannedDependenciesWithTags?: string[];
107
119
  }
108
120
  export interface CompileResult {
109
121
  diagnostics: Diagnostic[];
@@ -1,6 +1,9 @@
1
- import type { ApplicationGraph, Diagnostic } from "./types";
1
+ import type { ApplicationGraph, Diagnostic, ModuleBoundaryRule } from "./types";
2
2
  /**
3
3
  * 校验 ApplicationGraph,产出诊断列表。
4
4
  * strict 时 warn 级诊断升级为 error。
5
5
  */
6
- export declare function validateGraph(graph: ApplicationGraph, strict?: boolean): Diagnostic[];
6
+ export declare function validateGraph(graph: ApplicationGraph, options?: boolean | {
7
+ strict?: boolean;
8
+ moduleBoundaries?: ModuleBoundaryRule[];
9
+ }): Diagnostic[];
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@supacloud/compiler",
3
- "version": "0.2.0",
3
+ "version": "0.3.1",
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",
@@ -41,7 +41,7 @@
41
41
  "directory": "packages/compiler"
42
42
  },
43
43
  "dependencies": {
44
- "ts-morph": "^26.0.0"
44
+ "ts-morph": "^28.0.0"
45
45
  },
46
46
  "devDependencies": {
47
47
  "@types/bun": "^1.4.0",