@mandujs/core 0.9.16 → 0.9.17

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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@mandujs/core",
3
- "version": "0.9.16",
3
+ "version": "0.9.17",
4
4
  "description": "Mandu Framework Core - Spec, Generator, Guard, Runtime",
5
5
  "type": "module",
6
6
  "main": "./src/index.ts",
@@ -291,6 +291,47 @@ async function scanTsFiles(dir: string): Promise<string[]> {
291
291
  return files;
292
292
  }
293
293
 
294
+ // Rule: spec/ directory naming convention scan
295
+ export async function checkSpecDirNaming(
296
+ rootDir: string
297
+ ): Promise<GuardViolation[]> {
298
+ const violations: GuardViolation[] = [];
299
+
300
+ // Check spec/slots/ — only .slot.ts allowed
301
+ const slotsDir = path.join(rootDir, "spec/slots");
302
+ try {
303
+ const files = await fs.readdir(slotsDir);
304
+ for (const file of files) {
305
+ if (file.endsWith(".ts") && !file.endsWith(".slot.ts")) {
306
+ violations.push({
307
+ ruleId: GUARD_RULES.SLOT_DIR_INVALID_FILE.id,
308
+ file: `spec/slots/${file}`,
309
+ message: `spec/slots/에 .slot.ts가 아닌 파일: ${file}`,
310
+ suggestion: `.slot.ts로 이름을 바꾸거나, 이 파일이 client slot이면 apps/web/components/로 이동하세요`,
311
+ });
312
+ }
313
+ }
314
+ } catch {}
315
+
316
+ // Check spec/contracts/ — only .contract.ts allowed
317
+ const contractsDir = path.join(rootDir, "spec/contracts");
318
+ try {
319
+ const files = await fs.readdir(contractsDir);
320
+ for (const file of files) {
321
+ if (file.endsWith(".ts") && !file.endsWith(".contract.ts")) {
322
+ violations.push({
323
+ ruleId: GUARD_RULES.CONTRACT_DIR_INVALID_FILE.id,
324
+ file: `spec/contracts/${file}`,
325
+ message: `spec/contracts/에 .contract.ts가 아닌 파일: ${file}`,
326
+ suggestion: `.contract.ts로 이름을 바꾸세요`,
327
+ });
328
+ }
329
+ }
330
+ } catch {}
331
+
332
+ return violations;
333
+ }
334
+
294
335
  export async function runGuardCheck(
295
336
  manifest: RoutesManifest,
296
337
  rootDir: string
@@ -347,6 +388,10 @@ export async function runGuardCheck(
347
388
  const islandViolations = await checkIslandFirstIntegrity(manifest, rootDir);
348
389
  violations.push(...islandViolations);
349
390
 
391
+ // Rule: spec/ directory naming convention
392
+ const specDirViolations = await checkSpecDirNaming(rootDir);
393
+ violations.push(...specDirViolations);
394
+
350
395
  return {
351
396
  passed: violations.length === 0,
352
397
  violations,
@@ -108,6 +108,18 @@ export const GUARD_RULES: Record<string, GuardRule> = {
108
108
  description: "spec에 명시된 clientModule 파일을 찾을 수 없습니다",
109
109
  severity: "error",
110
110
  },
111
+ SLOT_DIR_INVALID_FILE: {
112
+ id: "SLOT_DIR_INVALID_FILE",
113
+ name: "Invalid File in Slots Directory",
114
+ description: "spec/slots/ 디렉토리에 .slot.ts가 아닌 파일이 있습니다",
115
+ severity: "error",
116
+ },
117
+ CONTRACT_DIR_INVALID_FILE: {
118
+ id: "CONTRACT_DIR_INVALID_FILE",
119
+ name: "Invalid File in Contracts Directory",
120
+ description: "spec/contracts/ 디렉토리에 .contract.ts가 아닌 파일이 있습니다",
121
+ severity: "error",
122
+ },
111
123
  };
112
124
 
113
125
  export const FORBIDDEN_IMPORTS = ["fs", "child_process", "cluster", "worker_threads"];