@tbsten/mir-core 0.0.2-alpha02 → 0.0.2-alpha04

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 (120) hide show
  1. package/dist/__tests__/errors.test.d.ts +1 -0
  2. package/dist/__tests__/errors.test.js +51 -0
  3. package/dist/__tests__/errors.test.js.map +1 -0
  4. package/dist/__tests__/helpers/string-helpers.test.d.ts +1 -0
  5. package/dist/__tests__/helpers/string-helpers.test.js +61 -0
  6. package/dist/__tests__/helpers/string-helpers.test.js.map +1 -0
  7. package/dist/__tests__/hooks.test.d.ts +1 -0
  8. package/dist/__tests__/hooks.test.js +125 -0
  9. package/dist/__tests__/hooks.test.js.map +1 -0
  10. package/dist/__tests__/i18n.test.d.ts +1 -0
  11. package/dist/__tests__/i18n.test.js +51 -0
  12. package/dist/__tests__/i18n.test.js.map +1 -0
  13. package/dist/__tests__/remote-registry.test.d.ts +1 -0
  14. package/dist/__tests__/remote-registry.test.js +194 -0
  15. package/dist/__tests__/remote-registry.test.js.map +1 -0
  16. package/dist/__tests__/safe-yaml-parser.test.d.ts +1 -0
  17. package/dist/__tests__/safe-yaml-parser.test.js +159 -0
  18. package/dist/__tests__/safe-yaml-parser.test.js.map +1 -0
  19. package/dist/__tests__/snapshots/error-messages.snapshot.test.d.ts +1 -0
  20. package/dist/__tests__/snapshots/error-messages.snapshot.test.js +52 -0
  21. package/dist/__tests__/snapshots/error-messages.snapshot.test.js.map +1 -0
  22. package/dist/__tests__/snapshots/snippet-schema-output.snapshot.test.d.ts +1 -0
  23. package/dist/__tests__/snapshots/snippet-schema-output.snapshot.test.js +93 -0
  24. package/dist/__tests__/snapshots/snippet-schema-output.snapshot.test.js.map +1 -0
  25. package/dist/__tests__/snapshots/template-output.snapshot.test.d.ts +1 -0
  26. package/dist/__tests__/snapshots/template-output.snapshot.test.js +60 -0
  27. package/dist/__tests__/snapshots/template-output.snapshot.test.js.map +1 -0
  28. package/dist/__tests__/snippet-schema.test.d.ts +1 -0
  29. package/dist/__tests__/snippet-schema.test.js +137 -0
  30. package/dist/__tests__/snippet-schema.test.js.map +1 -0
  31. package/dist/__tests__/symlink-checker.test.d.ts +1 -0
  32. package/dist/__tests__/symlink-checker.test.js +81 -0
  33. package/dist/__tests__/symlink-checker.test.js.map +1 -0
  34. package/dist/__tests__/template-engine.test.d.ts +1 -0
  35. package/dist/__tests__/template-engine.test.js +175 -0
  36. package/dist/__tests__/template-engine.test.js.map +1 -0
  37. package/dist/__tests__/validate-name.test.d.ts +1 -0
  38. package/dist/__tests__/validate-name.test.js +49 -0
  39. package/dist/__tests__/validate-name.test.js.map +1 -0
  40. package/dist/errors.d.ts +31 -0
  41. package/dist/errors.js +68 -0
  42. package/dist/errors.js.map +1 -0
  43. package/dist/helpers/index.d.ts +5 -0
  44. package/dist/helpers/index.js +28 -0
  45. package/dist/helpers/index.js.map +1 -0
  46. package/dist/helpers/string-helpers.d.ts +15 -0
  47. package/dist/helpers/string-helpers.js +63 -0
  48. package/dist/helpers/string-helpers.js.map +1 -0
  49. package/dist/hooks.d.ts +9 -0
  50. package/dist/hooks.js +48 -0
  51. package/dist/hooks.js.map +1 -0
  52. package/dist/i18n/index.d.ts +6 -0
  53. package/dist/i18n/index.js +20 -0
  54. package/dist/i18n/index.js.map +1 -0
  55. package/dist/i18n/locales/en.d.ts +2 -0
  56. package/dist/i18n/locales/en.js +84 -0
  57. package/dist/i18n/locales/en.js.map +1 -0
  58. package/dist/i18n/locales/ja.d.ts +2 -0
  59. package/dist/i18n/locales/ja.js +84 -0
  60. package/dist/i18n/locales/ja.js.map +1 -0
  61. package/dist/i18n/types.d.ts +72 -0
  62. package/dist/i18n/types.js +2 -0
  63. package/dist/i18n/types.js.map +1 -0
  64. package/dist/index.d.ts +17 -0
  65. package/dist/index.js +23 -0
  66. package/dist/index.js.map +1 -0
  67. package/dist/lib/symlink-checker.d.ts +15 -0
  68. package/dist/lib/symlink-checker.js +54 -0
  69. package/dist/lib/symlink-checker.js.map +1 -0
  70. package/dist/registry.d.ts +8 -0
  71. package/dist/registry.js +86 -0
  72. package/dist/registry.js.map +1 -0
  73. package/dist/remote-registry.d.ts +51 -0
  74. package/dist/remote-registry.js +181 -0
  75. package/dist/remote-registry.js.map +1 -0
  76. package/dist/safe-yaml-parser.d.ts +26 -0
  77. package/{src/safe-yaml-parser.ts → dist/safe-yaml-parser.js} +28 -34
  78. package/dist/safe-yaml-parser.js.map +1 -0
  79. package/dist/snippet-schema.d.ts +38 -0
  80. package/dist/snippet-schema.js +63 -0
  81. package/dist/snippet-schema.js.map +1 -0
  82. package/dist/template-engine.d.ts +5 -0
  83. package/dist/template-engine.js +130 -0
  84. package/dist/template-engine.js.map +1 -0
  85. package/dist/validate-name.d.ts +1 -0
  86. package/dist/validate-name.js +9 -0
  87. package/dist/validate-name.js.map +1 -0
  88. package/package.json +6 -1
  89. package/src/__tests__/errors.test.ts +0 -71
  90. package/src/__tests__/helpers/string-helpers.test.ts +0 -100
  91. package/src/__tests__/hooks.test.ts +0 -145
  92. package/src/__tests__/i18n.test.ts +0 -60
  93. package/src/__tests__/remote-registry.test.ts +0 -265
  94. package/src/__tests__/safe-yaml-parser.test.ts +0 -187
  95. package/src/__tests__/snapshots/__snapshots__/error-messages.snapshot.test.ts.snap +0 -27
  96. package/src/__tests__/snapshots/__snapshots__/snippet-schema-output.snapshot.test.ts.snap +0 -78
  97. package/src/__tests__/snapshots/__snapshots__/template-output.snapshot.test.ts.snap +0 -45
  98. package/src/__tests__/snapshots/error-messages.snapshot.test.ts +0 -76
  99. package/src/__tests__/snapshots/snippet-schema-output.snapshot.test.ts +0 -98
  100. package/src/__tests__/snapshots/template-output.snapshot.test.ts +0 -73
  101. package/src/__tests__/snippet-schema.test.ts +0 -180
  102. package/src/__tests__/symlink-checker.test.ts +0 -95
  103. package/src/__tests__/template-engine.test.ts +0 -240
  104. package/src/__tests__/validate-name.test.ts +0 -61
  105. package/src/errors.ts +0 -82
  106. package/src/helpers/index.ts +0 -34
  107. package/src/helpers/string-helpers.ts +0 -76
  108. package/src/hooks.ts +0 -63
  109. package/src/i18n/index.ts +0 -32
  110. package/src/i18n/locales/en.ts +0 -96
  111. package/src/i18n/locales/ja.ts +0 -96
  112. package/src/i18n/types.ts +0 -96
  113. package/src/index.ts +0 -86
  114. package/src/lib/symlink-checker.ts +0 -62
  115. package/src/registry.ts +0 -120
  116. package/src/remote-registry.ts +0 -260
  117. package/src/snippet-schema.ts +0 -117
  118. package/src/template-engine.ts +0 -147
  119. package/src/validate-name.ts +0 -12
  120. package/tsconfig.json +0 -17
@@ -0,0 +1,159 @@
1
+ /**
2
+ * mir-core: safe-yaml-parser のセキュリティテスト
3
+ *
4
+ * YAML Injection 攻撃シナリオ(チケット 008-yaml-injection)の検証
5
+ */
6
+ import { describe, it, expect } from "vitest";
7
+ import { safeParseYaml, checkNoRefInSchema, YAML_MAX_SIZE_BYTES, } from "../safe-yaml-parser.js";
8
+ import { ValidationError } from "../index.js";
9
+ // ---------------------------------------------------------------------------
10
+ // 正常系: 通常の YAML は引き続きパースできること
11
+ // ---------------------------------------------------------------------------
12
+ describe("safeParseYaml - 正常系", () => {
13
+ it("プレーンな文字列 YAML をパースできる", () => {
14
+ const result = safeParseYaml("name: my-snippet\n");
15
+ expect(result).toEqual({ name: "my-snippet" });
16
+ });
17
+ it("ネストしたオブジェクトをパースできる", () => {
18
+ const yaml = `
19
+ name: react-hook
20
+ variables:
21
+ foo:
22
+ description: フック名
23
+ schema:
24
+ type: string
25
+ `;
26
+ const result = safeParseYaml(yaml);
27
+ expect(result.name).toBe("react-hook");
28
+ });
29
+ it("配列をパースできる", () => {
30
+ const yaml = "tags:\n - react\n - typescript\n";
31
+ const result = safeParseYaml(yaml);
32
+ expect(result.tags).toEqual(["react", "typescript"]);
33
+ });
34
+ it("真偽値をパースできる", () => {
35
+ const yaml = "enabled: true\ndisabled: false\n";
36
+ const result = safeParseYaml(yaml);
37
+ expect(result.enabled).toBe(true);
38
+ expect(result.disabled).toBe(false);
39
+ });
40
+ it("数値をパースできる", () => {
41
+ const yaml = "count: 42\nfloat: 3.14\n";
42
+ const result = safeParseYaml(yaml);
43
+ expect(result.count).toBe(42);
44
+ expect(result.float).toBe(3.14);
45
+ });
46
+ it("null をパースできる", () => {
47
+ const result = safeParseYaml("value: null\n");
48
+ expect(result.value).toBeNull();
49
+ });
50
+ });
51
+ // ---------------------------------------------------------------------------
52
+ // YAML Bomb (Billion Laughs) 対策
53
+ // ---------------------------------------------------------------------------
54
+ describe("safeParseYaml - YAML Bomb 対策", () => {
55
+ it("最大サイズを超える YAML は ValidationError を投げる", () => {
56
+ const oversized = "x: " + "a".repeat(YAML_MAX_SIZE_BYTES);
57
+ expect(() => safeParseYaml(oversized)).toThrow(ValidationError);
58
+ });
59
+ it("最大サイズちょうどの YAML は許可される (境界値)", () => {
60
+ // 境界値ちょうどのサイズのシンプルな YAML は OK
61
+ const small = "name: ok\n";
62
+ expect(() => safeParseYaml(small)).not.toThrow();
63
+ });
64
+ it("エイリアスを大量に展開する YAML Bomb はサイズ制限で防がれる", () => {
65
+ // 実際の YAML bomb を小さめに再現
66
+ const bomb = [
67
+ "a: &a [lol,lol,lol,lol,lol,lol,lol,lol,lol]",
68
+ "b: &b [*a,*a,*a,*a,*a,*a,*a,*a,*a]",
69
+ "c: &c [*b,*b,*b,*b,*b,*b,*b,*b,*b]",
70
+ "d: &d [*c,*c,*c,*c,*c,*c,*c,*c,*c]",
71
+ ].join("\n");
72
+ // サイズは小さいのでパースは通るが、爆発的な展開はしない
73
+ // (js-yaml は alias を参照のまま扱い、展開しない)
74
+ // ここではエラーを投げないことを確認(サイズ制限内)
75
+ expect(() => safeParseYaml(bomb)).not.toThrow();
76
+ });
77
+ });
78
+ // ---------------------------------------------------------------------------
79
+ // YAML タグ攻撃対策
80
+ // ---------------------------------------------------------------------------
81
+ describe("safeParseYaml - カスタムタグ攻撃対策", () => {
82
+ it("!!js/undefined タグは無視されて通常の文字列になる", () => {
83
+ // CORE_SCHEMA では未知のタグはエラーまたは文字列として扱われる
84
+ // js-yaml の CORE_SCHEMA は !!js タグを認識しないのでエラーになる
85
+ expect(() => safeParseYaml("name: !!js/undefined foo")).toThrow();
86
+ });
87
+ it("!!js/regexp タグはエラーになる", () => {
88
+ expect(() => safeParseYaml("re: !!js/regexp /foo/")).toThrow();
89
+ });
90
+ it("!!js/function タグはエラーになる", () => {
91
+ expect(() => safeParseYaml("fn: !!js/function 'function(){}'")).toThrow();
92
+ });
93
+ });
94
+ // ---------------------------------------------------------------------------
95
+ // checkNoRefInSchema - JSON Schema $ref 禁止チェック
96
+ // ---------------------------------------------------------------------------
97
+ describe("checkNoRefInSchema", () => {
98
+ it("$ref がなければエラーを投げない", () => {
99
+ const schema = { type: "string", default: "hello" };
100
+ expect(() => checkNoRefInSchema(schema)).not.toThrow();
101
+ });
102
+ it("$ref があれば ValidationError を投げる", () => {
103
+ const schema = { $ref: "https://evil.com/schema.json" };
104
+ expect(() => checkNoRefInSchema(schema)).toThrow(ValidationError);
105
+ });
106
+ it("ネストされた $ref も検出する", () => {
107
+ const schema = {
108
+ type: "object",
109
+ properties: {
110
+ name: { $ref: "#/definitions/Name" },
111
+ },
112
+ };
113
+ expect(() => checkNoRefInSchema(schema)).toThrow(ValidationError);
114
+ });
115
+ it("$ref を含む文字列値は検出する(キーではなく値でも禁止)", () => {
116
+ // $ref というキーそのものを禁止
117
+ const schema = { anyOf: [{ $ref: "https://example.com/schema" }] };
118
+ expect(() => checkNoRefInSchema(schema)).toThrow(ValidationError);
119
+ });
120
+ it("null や undefined を渡してもエラーにならない", () => {
121
+ expect(() => checkNoRefInSchema(null)).not.toThrow();
122
+ expect(() => checkNoRefInSchema(undefined)).not.toThrow();
123
+ });
124
+ it("空オブジェクトはエラーにならない", () => {
125
+ expect(() => checkNoRefInSchema({})).not.toThrow();
126
+ });
127
+ });
128
+ // ---------------------------------------------------------------------------
129
+ // parseSnippetYaml との統合確認
130
+ // ---------------------------------------------------------------------------
131
+ describe("parseSnippetYaml - $ref を含む snippet は拒否される", () => {
132
+ it("variables.schema に $ref があれば ValidationError を投げる", async () => {
133
+ const { parseSnippetYaml } = await import("../index.js");
134
+ const yaml = `
135
+ name: evil-snippet
136
+ variables:
137
+ name:
138
+ schema:
139
+ $ref: "https://evil.com/schema.json"
140
+ `;
141
+ expect(() => parseSnippetYaml(yaml)).toThrow(ValidationError);
142
+ });
143
+ it("通常の snippet YAML は引き続きパースできる", async () => {
144
+ const { parseSnippetYaml } = await import("../index.js");
145
+ const yaml = `
146
+ name: normal-snippet
147
+ variables:
148
+ name:
149
+ description: 名前
150
+ schema:
151
+ type: string
152
+ default: world
153
+ `;
154
+ const def = parseSnippetYaml(yaml);
155
+ expect(def.name).toBe("normal-snippet");
156
+ expect(def.variables?.name?.schema?.type).toBe("string");
157
+ });
158
+ });
159
+ //# sourceMappingURL=safe-yaml-parser.test.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"safe-yaml-parser.test.js","sourceRoot":"","sources":["../../src/__tests__/safe-yaml-parser.test.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,52 @@
1
+ /**
2
+ * mir-core: エラーメッセージの snapshot テスト (日英)
3
+ */
4
+ import { describe, it, expect, beforeEach } from "vitest";
5
+ import { SnippetNotFoundError, SnippetAlreadyExistsError, RegistryNotFoundError, RegistryRemoteError, PathTraversalError, FileConflictError, setLocale, } from "../../index.js";
6
+ // TODO: 現時点では理想の挙動をテストケースとして記述。後で有効化する。
7
+ describe("エラーメッセージ snapshot (日本語)", () => {
8
+ beforeEach(() => setLocale("ja"));
9
+ it("SnippetNotFoundError", () => {
10
+ expect(new SnippetNotFoundError("react-hook").message).toMatchSnapshot();
11
+ });
12
+ it("SnippetAlreadyExistsError", () => {
13
+ expect(new SnippetAlreadyExistsError("react-hook").message).toMatchSnapshot();
14
+ });
15
+ it("RegistryNotFoundError", () => {
16
+ expect(new RegistryNotFoundError("official").message).toMatchSnapshot();
17
+ });
18
+ it("RegistryRemoteError (名前あり)", () => {
19
+ expect(new RegistryRemoteError("official").message).toMatchSnapshot();
20
+ });
21
+ it("RegistryRemoteError (名前なし)", () => {
22
+ expect(new RegistryRemoteError().message).toMatchSnapshot();
23
+ });
24
+ it("PathTraversalError", () => {
25
+ expect(new PathTraversalError("../etc/passwd").message).toMatchSnapshot();
26
+ });
27
+ it("FileConflictError", () => {
28
+ expect(new FileConflictError("index.ts").message).toMatchSnapshot();
29
+ });
30
+ });
31
+ describe("エラーメッセージ snapshot (英語)", () => {
32
+ beforeEach(() => setLocale("en"));
33
+ it("SnippetNotFoundError", () => {
34
+ expect(new SnippetNotFoundError("react-hook").message).toMatchSnapshot();
35
+ });
36
+ it("SnippetAlreadyExistsError", () => {
37
+ expect(new SnippetAlreadyExistsError("react-hook").message).toMatchSnapshot();
38
+ });
39
+ it("RegistryNotFoundError", () => {
40
+ expect(new RegistryNotFoundError("official").message).toMatchSnapshot();
41
+ });
42
+ it("RegistryRemoteError (名前あり)", () => {
43
+ expect(new RegistryRemoteError("official").message).toMatchSnapshot();
44
+ });
45
+ it("PathTraversalError", () => {
46
+ expect(new PathTraversalError("../etc/passwd").message).toMatchSnapshot();
47
+ });
48
+ it("FileConflictError", () => {
49
+ expect(new FileConflictError("index.ts").message).toMatchSnapshot();
50
+ });
51
+ });
52
+ //# sourceMappingURL=error-messages.snapshot.test.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"error-messages.snapshot.test.js","sourceRoot":"","sources":["../../../src/__tests__/snapshots/error-messages.snapshot.test.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,93 @@
1
+ /**
2
+ * mir-core: snippet スキーマ処理結果の snapshot テスト
3
+ */
4
+ import { describe, it, expect } from "vitest";
5
+ import { parseSnippetYaml, serializeSnippetYaml } from "../../index.js";
6
+ // TODO: 現時点では理想の挙動をテストケースとして記述。後で有効化する。
7
+ describe("snippet スキーマ snapshot", () => {
8
+ it("最小限の snippet 定義の YAML 出力", () => {
9
+ const yaml = serializeSnippetYaml({ name: "minimal" });
10
+ expect(yaml).toMatchSnapshot();
11
+ });
12
+ it("変数付き snippet 定義の YAML 出力", () => {
13
+ const yaml = serializeSnippetYaml({
14
+ name: "react-hook",
15
+ description: "React カスタムフック雛形",
16
+ variables: {
17
+ name: {
18
+ description: "フック名",
19
+ schema: { type: "string" },
20
+ },
21
+ description: {
22
+ description: "説明文",
23
+ schema: { type: "string", default: "" },
24
+ },
25
+ },
26
+ });
27
+ expect(yaml).toMatchSnapshot();
28
+ });
29
+ it("hooks 付き snippet 定義の YAML 出力", () => {
30
+ const yaml = serializeSnippetYaml({
31
+ name: "with-hooks",
32
+ hooks: {
33
+ "before-install": [
34
+ { echo: "インストールを開始します" },
35
+ {
36
+ input: {
37
+ confirm: {
38
+ description: "続行しますか?",
39
+ "answer-to": "confirmed",
40
+ schema: { type: "boolean", default: true },
41
+ },
42
+ },
43
+ },
44
+ ],
45
+ "after-install": [
46
+ { echo: "✅ インストール完了" },
47
+ ],
48
+ },
49
+ });
50
+ expect(yaml).toMatchSnapshot();
51
+ });
52
+ it("suggests 付き変数の YAML 出力", () => {
53
+ const yaml = serializeSnippetYaml({
54
+ name: "css-setup",
55
+ variables: {
56
+ framework: {
57
+ description: "CSSフレームワーク",
58
+ suggests: ["tailwind", "vanilla-extract", "css-modules"],
59
+ schema: { type: "string", default: "tailwind" },
60
+ },
61
+ },
62
+ });
63
+ expect(yaml).toMatchSnapshot();
64
+ });
65
+ it("フル構成 YAML のパース → シリアライズ往復", () => {
66
+ const input = `
67
+ name: full-example
68
+ description: フル構成のサンプル
69
+ variables:
70
+ name:
71
+ description: コンポーネント名
72
+ schema:
73
+ type: string
74
+ framework:
75
+ description: フレームワーク
76
+ suggests:
77
+ - react
78
+ - vue
79
+ schema:
80
+ type: string
81
+ default: react
82
+ hooks:
83
+ before-install:
84
+ - echo: "セットアップ中..."
85
+ after-install:
86
+ - echo: "完了: {{ name }}"
87
+ `;
88
+ const parsed = parseSnippetYaml(input);
89
+ const serialized = serializeSnippetYaml(parsed);
90
+ expect(serialized).toMatchSnapshot();
91
+ });
92
+ });
93
+ //# sourceMappingURL=snippet-schema-output.snapshot.test.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"snippet-schema-output.snapshot.test.js","sourceRoot":"","sources":["../../../src/__tests__/snapshots/snippet-schema-output.snapshot.test.ts"],"names":[],"mappings":"AAAA;;GAEG;AACH,OAAO,EAAE,QAAQ,EAAE,EAAE,EAAE,MAAM,EAAE,MAAM,QAAQ,CAAC;AAC9C,OAAO,EAAE,gBAAgB,EAAE,oBAAoB,EAAE,MAAM,gBAAgB,CAAC;AAExE,wCAAwC;AAExC,QAAQ,CAAC,uBAAuB,EAAE,GAAG,EAAE;IACrC,EAAE,CAAC,0BAA0B,EAAE,GAAG,EAAE;QAClC,MAAM,IAAI,GAAG,oBAAoB,CAAC,EAAE,IAAI,EAAE,SAAS,EAAE,CAAC,CAAC;QACvD,MAAM,CAAC,IAAI,CAAC,CAAC,eAAe,EAAE,CAAC;IACjC,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,0BAA0B,EAAE,GAAG,EAAE;QAClC,MAAM,IAAI,GAAG,oBAAoB,CAAC;YAChC,IAAI,EAAE,YAAY;YAClB,WAAW,EAAE,iBAAiB;YAC9B,SAAS,EAAE;gBACT,IAAI,EAAE;oBACJ,WAAW,EAAE,MAAM;oBACnB,MAAM,EAAE,EAAE,IAAI,EAAE,QAAQ,EAAE;iBAC3B;gBACD,WAAW,EAAE;oBACX,WAAW,EAAE,KAAK;oBAClB,MAAM,EAAE,EAAE,IAAI,EAAE,QAAQ,EAAE,OAAO,EAAE,EAAE,EAAE;iBACxC;aACF;SACF,CAAC,CAAC;QACH,MAAM,CAAC,IAAI,CAAC,CAAC,eAAe,EAAE,CAAC;IACjC,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,8BAA8B,EAAE,GAAG,EAAE;QACtC,MAAM,IAAI,GAAG,oBAAoB,CAAC;YAChC,IAAI,EAAE,YAAY;YAClB,KAAK,EAAE;gBACL,gBAAgB,EAAE;oBAChB,EAAE,IAAI,EAAE,cAAc,EAAE;oBACxB;wBACE,KAAK,EAAE;4BACL,OAAO,EAAE;gCACP,WAAW,EAAE,SAAS;gCACtB,WAAW,EAAE,WAAW;gCACxB,MAAM,EAAE,EAAE,IAAI,EAAE,SAAS,EAAE,OAAO,EAAE,IAAI,EAAE;6BAC3C;yBACF;qBACF;iBACF;gBACD,eAAe,EAAE;oBACf,EAAE,IAAI,EAAE,YAAY,EAAE;iBACvB;aACF;SACF,CAAC,CAAC;QACH,MAAM,CAAC,IAAI,CAAC,CAAC,eAAe,EAAE,CAAC;IACjC,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,wBAAwB,EAAE,GAAG,EAAE;QAChC,MAAM,IAAI,GAAG,oBAAoB,CAAC;YAChC,IAAI,EAAE,WAAW;YACjB,SAAS,EAAE;gBACT,SAAS,EAAE;oBACT,WAAW,EAAE,YAAY;oBACzB,QAAQ,EAAE,CAAC,UAAU,EAAE,iBAAiB,EAAE,aAAa,CAAC;oBACxD,MAAM,EAAE,EAAE,IAAI,EAAE,QAAQ,EAAE,OAAO,EAAE,UAAU,EAAE;iBAChD;aACF;SACF,CAAC,CAAC;QACH,MAAM,CAAC,IAAI,CAAC,CAAC,eAAe,EAAE,CAAC;IACjC,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,2BAA2B,EAAE,GAAG,EAAE;QACnC,MAAM,KAAK,GAAG;;;;;;;;;;;;;;;;;;;;;CAqBjB,CAAC;QACE,MAAM,MAAM,GAAG,gBAAgB,CAAC,KAAK,CAAC,CAAC;QACvC,MAAM,UAAU,GAAG,oBAAoB,CAAC,MAAM,CAAC,CAAC;QAChD,MAAM,CAAC,UAAU,CAAC,CAAC,eAAe,EAAE,CAAC;IACvC,CAAC,CAAC,CAAC;AACL,CAAC,CAAC,CAAC"}
@@ -0,0 +1,60 @@
1
+ /**
2
+ * mir-core: テンプレート展開結果の snapshot テスト
3
+ */
4
+ import { describe, it, expect } from "vitest";
5
+ import { expandTemplate, expandPath } from "../../index.js";
6
+ // TODO: 現時点では理想の挙動をテストケースとして記述。後で有効化する。
7
+ describe("テンプレート展開結果 snapshot", () => {
8
+ it("React コンポーネントテンプレート", () => {
9
+ const template = `import React from "react";
10
+
11
+ export interface {{ name }}Props {
12
+ children?: React.ReactNode;
13
+ }
14
+
15
+ export function {{ name }}({ children }: {{ name }}Props) {
16
+ return <div className="{{ name }}">{children}</div>;
17
+ }`;
18
+ const result = expandTemplate(template, { name: "Button" });
19
+ expect(result).toMatchSnapshot();
20
+ });
21
+ it("React フックテンプレート", () => {
22
+ const template = `import { useState, useCallback } from "react";
23
+
24
+ /**
25
+ * {{ description }}
26
+ */
27
+ export function {{ name }}() {
28
+ const [state, setState] = useState(null);
29
+ return { state, setState };
30
+ }`;
31
+ const result = expandTemplate(template, {
32
+ name: "useAuth",
33
+ description: "認証フック",
34
+ });
35
+ expect(result).toMatchSnapshot();
36
+ });
37
+ it("条件分岐付きテンプレート", () => {
38
+ const template = `{{#if useTypescript}}
39
+ import type { FC } from "react";
40
+ {{else}}
41
+ import React from "react";
42
+ {{/if}}
43
+
44
+ export const {{ name }} = () => {
45
+ return <div>{{ name }}</div>;
46
+ };`;
47
+ expect(expandTemplate(template, { name: "App", useTypescript: true })).toMatchSnapshot();
48
+ expect(expandTemplate(template, { name: "App", useTypescript: false })).toMatchSnapshot();
49
+ });
50
+ it("パス展開", () => {
51
+ expect(expandPath("{{ dir }}/{{ name }}.ts", { dir: "hooks", name: "useAuth" })).toMatchSnapshot();
52
+ });
53
+ it("ネストしたディレクトリパス展開", () => {
54
+ expect(expandPath("src/{{ module }}/{{ name }}/index.ts", {
55
+ module: "features",
56
+ name: "auth",
57
+ })).toMatchSnapshot();
58
+ });
59
+ });
60
+ //# sourceMappingURL=template-output.snapshot.test.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"template-output.snapshot.test.js","sourceRoot":"","sources":["../../../src/__tests__/snapshots/template-output.snapshot.test.ts"],"names":[],"mappings":"AAAA;;GAEG;AACH,OAAO,EAAE,QAAQ,EAAE,EAAE,EAAE,MAAM,EAAE,MAAM,QAAQ,CAAC;AAC9C,OAAO,EAAE,cAAc,EAAE,UAAU,EAAE,MAAM,gBAAgB,CAAC;AAE5D,wCAAwC;AAExC,QAAQ,CAAC,qBAAqB,EAAE,GAAG,EAAE;IACnC,EAAE,CAAC,qBAAqB,EAAE,GAAG,EAAE;QAC7B,MAAM,QAAQ,GAAG;;;;;;;;EAQnB,CAAC;QACC,MAAM,MAAM,GAAG,cAAc,CAAC,QAAQ,EAAE,EAAE,IAAI,EAAE,QAAQ,EAAE,CAAC,CAAC;QAC5D,MAAM,CAAC,MAAM,CAAC,CAAC,eAAe,EAAE,CAAC;IACnC,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,iBAAiB,EAAE,GAAG,EAAE;QACzB,MAAM,QAAQ,GAAG;;;;;;;;EAQnB,CAAC;QACC,MAAM,MAAM,GAAG,cAAc,CAAC,QAAQ,EAAE;YACtC,IAAI,EAAE,SAAS;YACf,WAAW,EAAE,OAAO;SACrB,CAAC,CAAC;QACH,MAAM,CAAC,MAAM,CAAC,CAAC,eAAe,EAAE,CAAC;IACnC,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,cAAc,EAAE,GAAG,EAAE;QACtB,MAAM,QAAQ,GAAG;;;;;;;;GAQlB,CAAC;QACA,MAAM,CACJ,cAAc,CAAC,QAAQ,EAAE,EAAE,IAAI,EAAE,KAAK,EAAE,aAAa,EAAE,IAAI,EAAE,CAAC,CAC/D,CAAC,eAAe,EAAE,CAAC;QACpB,MAAM,CACJ,cAAc,CAAC,QAAQ,EAAE,EAAE,IAAI,EAAE,KAAK,EAAE,aAAa,EAAE,KAAK,EAAE,CAAC,CAChE,CAAC,eAAe,EAAE,CAAC;IACtB,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,MAAM,EAAE,GAAG,EAAE;QACd,MAAM,CACJ,UAAU,CAAC,yBAAyB,EAAE,EAAE,GAAG,EAAE,OAAO,EAAE,IAAI,EAAE,SAAS,EAAE,CAAC,CACzE,CAAC,eAAe,EAAE,CAAC;IACtB,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,iBAAiB,EAAE,GAAG,EAAE;QACzB,MAAM,CACJ,UAAU,CAAC,sCAAsC,EAAE;YACjD,MAAM,EAAE,UAAU;YAClB,IAAI,EAAE,MAAM;SACb,CAAC,CACH,CAAC,eAAe,EAAE,CAAC;IACtB,CAAC,CAAC,CAAC;AACL,CAAC,CAAC,CAAC"}
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1,137 @@
1
+ /**
2
+ * mir-core: snippet-schema の unit テスト
3
+ */
4
+ import { describe, it, expect } from "vitest";
5
+ import { parseSnippetYaml, serializeSnippetYaml, validateSnippetDefinition, ValidationError, } from "../index.js";
6
+ // TODO: 現時点では理想の挙動をテストケースとして記述。後で有効化する。
7
+ describe("parseSnippetYaml", () => {
8
+ it("最小限の YAML をパースできる", () => {
9
+ const def = parseSnippetYaml("name: my-snippet\n");
10
+ expect(def.name).toBe("my-snippet");
11
+ });
12
+ it("変数付き YAML をパースできる", () => {
13
+ const yaml = `
14
+ name: react-hook
15
+ description: React カスタムフック
16
+ variables:
17
+ name:
18
+ description: フック名
19
+ schema:
20
+ type: string
21
+ `;
22
+ const def = parseSnippetYaml(yaml);
23
+ expect(def.name).toBe("react-hook");
24
+ expect(def.variables?.name?.description).toBe("フック名");
25
+ expect(def.variables?.name?.schema?.type).toBe("string");
26
+ });
27
+ it("hooks 付き YAML をパースできる", () => {
28
+ const yaml = `
29
+ name: test-snippet
30
+ hooks:
31
+ before-install:
32
+ - echo: "Installing..."
33
+ after-install:
34
+ - echo: "Done!"
35
+ `;
36
+ const def = parseSnippetYaml(yaml);
37
+ expect(def.hooks?.["before-install"]).toHaveLength(1);
38
+ expect(def.hooks?.["after-install"]).toHaveLength(1);
39
+ });
40
+ it("dependencies 付き YAML をパースできる", () => {
41
+ const yaml = `
42
+ name: react-component
43
+ description: React component
44
+ dependencies:
45
+ - react-hook
46
+ - typescript-setup
47
+ `;
48
+ const def = parseSnippetYaml(yaml);
49
+ expect(def.dependencies).toEqual(["react-hook", "typescript-setup"]);
50
+ });
51
+ it("不正な YAML でエラー", () => {
52
+ expect(() => parseSnippetYaml("not a yaml object: [")).toThrow();
53
+ });
54
+ it("name がない YAML でエラー", () => {
55
+ expect(() => parseSnippetYaml("description: no name\n")).toThrow(ValidationError);
56
+ });
57
+ });
58
+ describe("serializeSnippetYaml", () => {
59
+ it("SnippetDefinition を YAML 文字列に変換する", () => {
60
+ const yaml = serializeSnippetYaml({ name: "test" });
61
+ expect(yaml).toContain("name: test");
62
+ });
63
+ it("パースと逆変換で元に戻る", () => {
64
+ const original = { name: "test", description: "desc" };
65
+ const yaml = serializeSnippetYaml(original);
66
+ const parsed = parseSnippetYaml(yaml);
67
+ expect(parsed.name).toBe(original.name);
68
+ expect(parsed.description).toBe(original.description);
69
+ });
70
+ });
71
+ describe("validateSnippetDefinition", () => {
72
+ it("最小限の定義はバリデーション通過", () => {
73
+ expect(() => validateSnippetDefinition({ name: "valid" })).not.toThrow();
74
+ });
75
+ it("name が空だとエラー", () => {
76
+ expect(() => validateSnippetDefinition({ name: "" })).toThrow(ValidationError);
77
+ });
78
+ it("suggests が配列でないとエラー", () => {
79
+ expect(() => validateSnippetDefinition({
80
+ name: "test",
81
+ variables: {
82
+ key: { suggests: "not-array" },
83
+ },
84
+ })).toThrow(ValidationError);
85
+ });
86
+ it("schema.type が不正だとエラー", () => {
87
+ expect(() => validateSnippetDefinition({
88
+ name: "test",
89
+ variables: {
90
+ key: {
91
+ schema: { type: "invalid" },
92
+ },
93
+ },
94
+ })).toThrow(ValidationError);
95
+ });
96
+ it("正しい schema.type は通過 (string, number, boolean)", () => {
97
+ expect(() => validateSnippetDefinition({
98
+ name: "test",
99
+ variables: {
100
+ a: { schema: { type: "string" } },
101
+ b: { schema: { type: "number" } },
102
+ c: { schema: { type: "boolean" } },
103
+ },
104
+ })).not.toThrow();
105
+ });
106
+ it("dependencies が配列の場合は通過", () => {
107
+ expect(() => validateSnippetDefinition({
108
+ name: "react-component",
109
+ dependencies: ["react-hook"],
110
+ })).not.toThrow();
111
+ });
112
+ it("dependencies に複数要素を持つ場合は通過", () => {
113
+ expect(() => validateSnippetDefinition({
114
+ name: "test",
115
+ dependencies: ["dep1", "dep2", "dep3"],
116
+ })).not.toThrow();
117
+ });
118
+ it("dependencies が配列でないとエラー", () => {
119
+ expect(() => validateSnippetDefinition({
120
+ name: "test",
121
+ dependencies: "not-array",
122
+ })).toThrow(ValidationError);
123
+ });
124
+ it("dependencies の要素が文字列でないとエラー", () => {
125
+ expect(() => validateSnippetDefinition({
126
+ name: "test",
127
+ dependencies: ["valid-dep", 123],
128
+ })).toThrow(ValidationError);
129
+ });
130
+ it("dependencies の要素に不正な名前があるとエラー", () => {
131
+ expect(() => validateSnippetDefinition({
132
+ name: "test",
133
+ dependencies: ["-invalid-dep-name"],
134
+ })).toThrow(ValidationError);
135
+ });
136
+ });
137
+ //# sourceMappingURL=snippet-schema.test.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"snippet-schema.test.js","sourceRoot":"","sources":["../../src/__tests__/snippet-schema.test.ts"],"names":[],"mappings":"AAAA;;GAEG;AACH,OAAO,EAAE,QAAQ,EAAE,EAAE,EAAE,MAAM,EAAE,MAAM,QAAQ,CAAC;AAC9C,OAAO,EACL,gBAAgB,EAChB,oBAAoB,EACpB,yBAAyB,EACzB,eAAe,GAChB,MAAM,aAAa,CAAC;AAErB,wCAAwC;AAExC,QAAQ,CAAC,kBAAkB,EAAE,GAAG,EAAE;IAChC,EAAE,CAAC,mBAAmB,EAAE,GAAG,EAAE;QAC3B,MAAM,GAAG,GAAG,gBAAgB,CAAC,oBAAoB,CAAC,CAAC;QACnD,MAAM,CAAC,GAAG,CAAC,IAAI,CAAC,CAAC,IAAI,CAAC,YAAY,CAAC,CAAC;IACtC,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,mBAAmB,EAAE,GAAG,EAAE;QAC3B,MAAM,IAAI,GAAG;;;;;;;;CAQhB,CAAC;QACE,MAAM,GAAG,GAAG,gBAAgB,CAAC,IAAI,CAAC,CAAC;QACnC,MAAM,CAAC,GAAG,CAAC,IAAI,CAAC,CAAC,IAAI,CAAC,YAAY,CAAC,CAAC;QACpC,MAAM,CAAC,GAAG,CAAC,SAAS,EAAE,IAAI,EAAE,WAAW,CAAC,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC;QACtD,MAAM,CAAC,GAAG,CAAC,SAAS,EAAE,IAAI,EAAE,MAAM,EAAE,IAAI,CAAC,CAAC,IAAI,CAAC,QAAQ,CAAC,CAAC;IAC3D,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,uBAAuB,EAAE,GAAG,EAAE;QAC/B,MAAM,IAAI,GAAG;;;;;;;CAOhB,CAAC;QACE,MAAM,GAAG,GAAG,gBAAgB,CAAC,IAAI,CAAC,CAAC;QACnC,MAAM,CAAC,GAAG,CAAC,KAAK,EAAE,CAAC,gBAAgB,CAAC,CAAC,CAAC,YAAY,CAAC,CAAC,CAAC,CAAC;QACtD,MAAM,CAAC,GAAG,CAAC,KAAK,EAAE,CAAC,eAAe,CAAC,CAAC,CAAC,YAAY,CAAC,CAAC,CAAC,CAAC;IACvD,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,8BAA8B,EAAE,GAAG,EAAE;QACtC,MAAM,IAAI,GAAG;;;;;;CAMhB,CAAC;QACE,MAAM,GAAG,GAAG,gBAAgB,CAAC,IAAI,CAAC,CAAC;QACnC,MAAM,CAAC,GAAG,CAAC,YAAY,CAAC,CAAC,OAAO,CAAC,CAAC,YAAY,EAAE,kBAAkB,CAAC,CAAC,CAAC;IACvE,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,eAAe,EAAE,GAAG,EAAE;QACvB,MAAM,CAAC,GAAG,EAAE,CAAC,gBAAgB,CAAC,sBAAsB,CAAC,CAAC,CAAC,OAAO,EAAE,CAAC;IACnE,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,oBAAoB,EAAE,GAAG,EAAE;QAC5B,MAAM,CAAC,GAAG,EAAE,CAAC,gBAAgB,CAAC,wBAAwB,CAAC,CAAC,CAAC,OAAO,CAC9D,eAAe,CAChB,CAAC;IACJ,CAAC,CAAC,CAAC;AACL,CAAC,CAAC,CAAC;AAEH,QAAQ,CAAC,sBAAsB,EAAE,GAAG,EAAE;IACpC,EAAE,CAAC,mCAAmC,EAAE,GAAG,EAAE;QAC3C,MAAM,IAAI,GAAG,oBAAoB,CAAC,EAAE,IAAI,EAAE,MAAM,EAAE,CAAC,CAAC;QACpD,MAAM,CAAC,IAAI,CAAC,CAAC,SAAS,CAAC,YAAY,CAAC,CAAC;IACvC,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,cAAc,EAAE,GAAG,EAAE;QACtB,MAAM,QAAQ,GAAG,EAAE,IAAI,EAAE,MAAM,EAAE,WAAW,EAAE,MAAM,EAAE,CAAC;QACvD,MAAM,IAAI,GAAG,oBAAoB,CAAC,QAAQ,CAAC,CAAC;QAC5C,MAAM,MAAM,GAAG,gBAAgB,CAAC,IAAI,CAAC,CAAC;QACtC,MAAM,CAAC,MAAM,CAAC,IAAI,CAAC,CAAC,IAAI,CAAC,QAAQ,CAAC,IAAI,CAAC,CAAC;QACxC,MAAM,CAAC,MAAM,CAAC,WAAW,CAAC,CAAC,IAAI,CAAC,QAAQ,CAAC,WAAW,CAAC,CAAC;IACxD,CAAC,CAAC,CAAC;AACL,CAAC,CAAC,CAAC;AAEH,QAAQ,CAAC,2BAA2B,EAAE,GAAG,EAAE;IACzC,EAAE,CAAC,kBAAkB,EAAE,GAAG,EAAE;QAC1B,MAAM,CAAC,GAAG,EAAE,CAAC,yBAAyB,CAAC,EAAE,IAAI,EAAE,OAAO,EAAE,CAAC,CAAC,CAAC,GAAG,CAAC,OAAO,EAAE,CAAC;IAC3E,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,cAAc,EAAE,GAAG,EAAE;QACtB,MAAM,CAAC,GAAG,EAAE,CAAC,yBAAyB,CAAC,EAAE,IAAI,EAAE,EAAE,EAAE,CAAC,CAAC,CAAC,OAAO,CAC3D,eAAe,CAChB,CAAC;IACJ,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,qBAAqB,EAAE,GAAG,EAAE;QAC7B,MAAM,CAAC,GAAG,EAAE,CACV,yBAAyB,CAAC;YACxB,IAAI,EAAE,MAAM;YACZ,SAAS,EAAE;gBACT,GAAG,EAAE,EAAE,QAAQ,EAAE,WAAkC,EAAE;aACtD;SACF,CAAC,CACH,CAAC,OAAO,CAAC,eAAe,CAAC,CAAC;IAC7B,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,sBAAsB,EAAE,GAAG,EAAE;QAC9B,MAAM,CAAC,GAAG,EAAE,CACV,yBAAyB,CAAC;YACxB,IAAI,EAAE,MAAM;YACZ,SAAS,EAAE;gBACT,GAAG,EAAE;oBACH,MAAM,EAAE,EAAE,IAAI,EAAE,SAAqB,EAAE;iBACxC;aACF;SACF,CAAC,CACH,CAAC,OAAO,CAAC,eAAe,CAAC,CAAC;IAC7B,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,+CAA+C,EAAE,GAAG,EAAE;QACvD,MAAM,CAAC,GAAG,EAAE,CACV,yBAAyB,CAAC;YACxB,IAAI,EAAE,MAAM;YACZ,SAAS,EAAE;gBACT,CAAC,EAAE,EAAE,MAAM,EAAE,EAAE,IAAI,EAAE,QAAQ,EAAE,EAAE;gBACjC,CAAC,EAAE,EAAE,MAAM,EAAE,EAAE,IAAI,EAAE,QAAQ,EAAE,EAAE;gBACjC,CAAC,EAAE,EAAE,MAAM,EAAE,EAAE,IAAI,EAAE,SAAS,EAAE,EAAE;aACnC;SACF,CAAC,CACH,CAAC,GAAG,CAAC,OAAO,EAAE,CAAC;IAClB,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,wBAAwB,EAAE,GAAG,EAAE;QAChC,MAAM,CAAC,GAAG,EAAE,CACV,yBAAyB,CAAC;YACxB,IAAI,EAAE,iBAAiB;YACvB,YAAY,EAAE,CAAC,YAAY,CAAC;SAC7B,CAAC,CACH,CAAC,GAAG,CAAC,OAAO,EAAE,CAAC;IAClB,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,4BAA4B,EAAE,GAAG,EAAE;QACpC,MAAM,CAAC,GAAG,EAAE,CACV,yBAAyB,CAAC;YACxB,IAAI,EAAE,MAAM;YACZ,YAAY,EAAE,CAAC,MAAM,EAAE,MAAM,EAAE,MAAM,CAAC;SACvC,CAAC,CACH,CAAC,GAAG,CAAC,OAAO,EAAE,CAAC;IAClB,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,yBAAyB,EAAE,GAAG,EAAE;QACjC,MAAM,CAAC,GAAG,EAAE,CACV,yBAAyB,CAAC;YACxB,IAAI,EAAE,MAAM;YACZ,YAAY,EAAE,WAAkC;SACjD,CAAC,CACH,CAAC,OAAO,CAAC,eAAe,CAAC,CAAC;IAC7B,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,6BAA6B,EAAE,GAAG,EAAE;QACrC,MAAM,CAAC,GAAG,EAAE,CACV,yBAAyB,CAAC;YACxB,IAAI,EAAE,MAAM;YACZ,YAAY,EAAE,CAAC,WAAW,EAAE,GAAwB,CAAC;SACtD,CAAC,CACH,CAAC,OAAO,CAAC,eAAe,CAAC,CAAC;IAC7B,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,+BAA+B,EAAE,GAAG,EAAE;QACvC,MAAM,CAAC,GAAG,EAAE,CACV,yBAAyB,CAAC;YACxB,IAAI,EAAE,MAAM;YACZ,YAAY,EAAE,CAAC,mBAAmB,CAAC;SACpC,CAAC,CACH,CAAC,OAAO,CAAC,eAAe,CAAC,CAAC;IAC7B,CAAC,CAAC,CAAC;AACL,CAAC,CAAC,CAAC"}
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1,81 @@
1
+ import { describe, it, expect, beforeEach, afterEach } from "vitest";
2
+ import fs from "node:fs";
3
+ import os from "node:os";
4
+ import path from "node:path";
5
+ import { isSymbolicLink, findSymlinksInDirectory } from "../lib/symlink-checker.js";
6
+ let tmpDir;
7
+ beforeEach(() => {
8
+ tmpDir = fs.mkdtempSync(path.join(os.tmpdir(), "mir-symlink-test-"));
9
+ });
10
+ afterEach(() => {
11
+ fs.rmSync(tmpDir, { recursive: true, force: true });
12
+ });
13
+ describe("isSymbolicLink", () => {
14
+ it("通常のファイルに対して false を返す", () => {
15
+ const filePath = path.join(tmpDir, "regular.txt");
16
+ fs.writeFileSync(filePath, "content");
17
+ expect(isSymbolicLink(filePath)).toBe(false);
18
+ });
19
+ it("ディレクトリに対して false を返す", () => {
20
+ const dirPath = path.join(tmpDir, "subdir");
21
+ fs.mkdirSync(dirPath);
22
+ expect(isSymbolicLink(dirPath)).toBe(false);
23
+ });
24
+ it("シンボリックリンクに対して true を返す", () => {
25
+ const targetPath = path.join(tmpDir, "target.txt");
26
+ const linkPath = path.join(tmpDir, "link.txt");
27
+ fs.writeFileSync(targetPath, "content");
28
+ fs.symlinkSync(targetPath, linkPath);
29
+ expect(isSymbolicLink(linkPath)).toBe(true);
30
+ });
31
+ it("存在しないパスに対して false を返す", () => {
32
+ expect(isSymbolicLink(path.join(tmpDir, "nonexistent"))).toBe(false);
33
+ });
34
+ });
35
+ describe("findSymlinksInDirectory", () => {
36
+ it("シンボリックリンクがない場合 hasSymlinks=false を返す", () => {
37
+ fs.writeFileSync(path.join(tmpDir, "index.ts"), "content");
38
+ const result = findSymlinksInDirectory(tmpDir);
39
+ expect(result.hasSymlinks).toBe(false);
40
+ expect(result.symlinkPaths).toHaveLength(0);
41
+ });
42
+ it("シンボリックリンクを検出する", () => {
43
+ const targetPath = path.join(tmpDir, "target.txt");
44
+ const linkPath = path.join(tmpDir, "link.txt");
45
+ fs.writeFileSync(targetPath, "content");
46
+ fs.symlinkSync(targetPath, linkPath);
47
+ const result = findSymlinksInDirectory(tmpDir);
48
+ expect(result.hasSymlinks).toBe(true);
49
+ expect(result.symlinkPaths).toContain("link.txt");
50
+ });
51
+ it("サブディレクトリ内のシンボリックリンクを検出する", () => {
52
+ const subDir = path.join(tmpDir, "src");
53
+ fs.mkdirSync(subDir);
54
+ const targetPath = path.join(tmpDir, "target.txt");
55
+ const linkPath = path.join(subDir, "link.txt");
56
+ fs.writeFileSync(targetPath, "content");
57
+ fs.symlinkSync(targetPath, linkPath);
58
+ const result = findSymlinksInDirectory(tmpDir);
59
+ expect(result.hasSymlinks).toBe(true);
60
+ expect(result.symlinkPaths.some((p) => p.includes("link.txt"))).toBe(true);
61
+ });
62
+ it("複数のシンボリックリンクをすべて検出する", () => {
63
+ const target1 = path.join(tmpDir, "target1.txt");
64
+ const target2 = path.join(tmpDir, "target2.txt");
65
+ const link1 = path.join(tmpDir, "link1.txt");
66
+ const link2 = path.join(tmpDir, "link2.txt");
67
+ fs.writeFileSync(target1, "content1");
68
+ fs.writeFileSync(target2, "content2");
69
+ fs.symlinkSync(target1, link1);
70
+ fs.symlinkSync(target2, link2);
71
+ const result = findSymlinksInDirectory(tmpDir);
72
+ expect(result.hasSymlinks).toBe(true);
73
+ expect(result.symlinkPaths).toHaveLength(2);
74
+ });
75
+ it("存在しないディレクトリに対して空の結果を返す", () => {
76
+ const result = findSymlinksInDirectory(path.join(tmpDir, "nonexistent"));
77
+ expect(result.hasSymlinks).toBe(false);
78
+ expect(result.symlinkPaths).toHaveLength(0);
79
+ });
80
+ });
81
+ //# sourceMappingURL=symlink-checker.test.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"symlink-checker.test.js","sourceRoot":"","sources":["../../src/__tests__/symlink-checker.test.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,QAAQ,EAAE,EAAE,EAAE,MAAM,EAAE,UAAU,EAAE,SAAS,EAAE,MAAM,QAAQ,CAAC;AACrE,OAAO,EAAE,MAAM,SAAS,CAAC;AACzB,OAAO,EAAE,MAAM,SAAS,CAAC;AACzB,OAAO,IAAI,MAAM,WAAW,CAAC;AAC7B,OAAO,EAAE,cAAc,EAAE,uBAAuB,EAAE,MAAM,2BAA2B,CAAC;AAEpF,IAAI,MAAc,CAAC;AAEnB,UAAU,CAAC,GAAG,EAAE;IACd,MAAM,GAAG,EAAE,CAAC,WAAW,CAAC,IAAI,CAAC,IAAI,CAAC,EAAE,CAAC,MAAM,EAAE,EAAE,mBAAmB,CAAC,CAAC,CAAC;AACvE,CAAC,CAAC,CAAC;AAEH,SAAS,CAAC,GAAG,EAAE;IACb,EAAE,CAAC,MAAM,CAAC,MAAM,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,KAAK,EAAE,IAAI,EAAE,CAAC,CAAC;AACtD,CAAC,CAAC,CAAC;AAEH,QAAQ,CAAC,gBAAgB,EAAE,GAAG,EAAE;IAC9B,EAAE,CAAC,uBAAuB,EAAE,GAAG,EAAE;QAC/B,MAAM,QAAQ,GAAG,IAAI,CAAC,IAAI,CAAC,MAAM,EAAE,aAAa,CAAC,CAAC;QAClD,EAAE,CAAC,aAAa,CAAC,QAAQ,EAAE,SAAS,CAAC,CAAC;QACtC,MAAM,CAAC,cAAc,CAAC,QAAQ,CAAC,CAAC,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC;IAC/C,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,sBAAsB,EAAE,GAAG,EAAE;QAC9B,MAAM,OAAO,GAAG,IAAI,CAAC,IAAI,CAAC,MAAM,EAAE,QAAQ,CAAC,CAAC;QAC5C,EAAE,CAAC,SAAS,CAAC,OAAO,CAAC,CAAC;QACtB,MAAM,CAAC,cAAc,CAAC,OAAO,CAAC,CAAC,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC;IAC9C,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,wBAAwB,EAAE,GAAG,EAAE;QAChC,MAAM,UAAU,GAAG,IAAI,CAAC,IAAI,CAAC,MAAM,EAAE,YAAY,CAAC,CAAC;QACnD,MAAM,QAAQ,GAAG,IAAI,CAAC,IAAI,CAAC,MAAM,EAAE,UAAU,CAAC,CAAC;QAC/C,EAAE,CAAC,aAAa,CAAC,UAAU,EAAE,SAAS,CAAC,CAAC;QACxC,EAAE,CAAC,WAAW,CAAC,UAAU,EAAE,QAAQ,CAAC,CAAC;QACrC,MAAM,CAAC,cAAc,CAAC,QAAQ,CAAC,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;IAC9C,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,uBAAuB,EAAE,GAAG,EAAE;QAC/B,MAAM,CAAC,cAAc,CAAC,IAAI,CAAC,IAAI,CAAC,MAAM,EAAE,aAAa,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC;IACvE,CAAC,CAAC,CAAC;AACL,CAAC,CAAC,CAAC;AAEH,QAAQ,CAAC,yBAAyB,EAAE,GAAG,EAAE;IACvC,EAAE,CAAC,sCAAsC,EAAE,GAAG,EAAE;QAC9C,EAAE,CAAC,aAAa,CAAC,IAAI,CAAC,IAAI,CAAC,MAAM,EAAE,UAAU,CAAC,EAAE,SAAS,CAAC,CAAC;QAC3D,MAAM,MAAM,GAAG,uBAAuB,CAAC,MAAM,CAAC,CAAC;QAC/C,MAAM,CAAC,MAAM,CAAC,WAAW,CAAC,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC;QACvC,MAAM,CAAC,MAAM,CAAC,YAAY,CAAC,CAAC,YAAY,CAAC,CAAC,CAAC,CAAC;IAC9C,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,gBAAgB,EAAE,GAAG,EAAE;QACxB,MAAM,UAAU,GAAG,IAAI,CAAC,IAAI,CAAC,MAAM,EAAE,YAAY,CAAC,CAAC;QACnD,MAAM,QAAQ,GAAG,IAAI,CAAC,IAAI,CAAC,MAAM,EAAE,UAAU,CAAC,CAAC;QAC/C,EAAE,CAAC,aAAa,CAAC,UAAU,EAAE,SAAS,CAAC,CAAC;QACxC,EAAE,CAAC,WAAW,CAAC,UAAU,EAAE,QAAQ,CAAC,CAAC;QAErC,MAAM,MAAM,GAAG,uBAAuB,CAAC,MAAM,CAAC,CAAC;QAC/C,MAAM,CAAC,MAAM,CAAC,WAAW,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;QACtC,MAAM,CAAC,MAAM,CAAC,YAAY,CAAC,CAAC,SAAS,CAAC,UAAU,CAAC,CAAC;IACpD,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,0BAA0B,EAAE,GAAG,EAAE;QAClC,MAAM,MAAM,GAAG,IAAI,CAAC,IAAI,CAAC,MAAM,EAAE,KAAK,CAAC,CAAC;QACxC,EAAE,CAAC,SAAS,CAAC,MAAM,CAAC,CAAC;QACrB,MAAM,UAAU,GAAG,IAAI,CAAC,IAAI,CAAC,MAAM,EAAE,YAAY,CAAC,CAAC;QACnD,MAAM,QAAQ,GAAG,IAAI,CAAC,IAAI,CAAC,MAAM,EAAE,UAAU,CAAC,CAAC;QAC/C,EAAE,CAAC,aAAa,CAAC,UAAU,EAAE,SAAS,CAAC,CAAC;QACxC,EAAE,CAAC,WAAW,CAAC,UAAU,EAAE,QAAQ,CAAC,CAAC;QAErC,MAAM,MAAM,GAAG,uBAAuB,CAAC,MAAM,CAAC,CAAC;QAC/C,MAAM,CAAC,MAAM,CAAC,WAAW,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;QACtC,MAAM,CAAC,MAAM,CAAC,YAAY,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,QAAQ,CAAC,UAAU,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;IAC7E,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,sBAAsB,EAAE,GAAG,EAAE;QAC9B,MAAM,OAAO,GAAG,IAAI,CAAC,IAAI,CAAC,MAAM,EAAE,aAAa,CAAC,CAAC;QACjD,MAAM,OAAO,GAAG,IAAI,CAAC,IAAI,CAAC,MAAM,EAAE,aAAa,CAAC,CAAC;QACjD,MAAM,KAAK,GAAG,IAAI,CAAC,IAAI,CAAC,MAAM,EAAE,WAAW,CAAC,CAAC;QAC7C,MAAM,KAAK,GAAG,IAAI,CAAC,IAAI,CAAC,MAAM,EAAE,WAAW,CAAC,CAAC;QAC7C,EAAE,CAAC,aAAa,CAAC,OAAO,EAAE,UAAU,CAAC,CAAC;QACtC,EAAE,CAAC,aAAa,CAAC,OAAO,EAAE,UAAU,CAAC,CAAC;QACtC,EAAE,CAAC,WAAW,CAAC,OAAO,EAAE,KAAK,CAAC,CAAC;QAC/B,EAAE,CAAC,WAAW,CAAC,OAAO,EAAE,KAAK,CAAC,CAAC;QAE/B,MAAM,MAAM,GAAG,uBAAuB,CAAC,MAAM,CAAC,CAAC;QAC/C,MAAM,CAAC,MAAM,CAAC,WAAW,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;QACtC,MAAM,CAAC,MAAM,CAAC,YAAY,CAAC,CAAC,YAAY,CAAC,CAAC,CAAC,CAAC;IAC9C,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,wBAAwB,EAAE,GAAG,EAAE;QAChC,MAAM,MAAM,GAAG,uBAAuB,CAAC,IAAI,CAAC,IAAI,CAAC,MAAM,EAAE,aAAa,CAAC,CAAC,CAAC;QACzE,MAAM,CAAC,MAAM,CAAC,WAAW,CAAC,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC;QACvC,MAAM,CAAC,MAAM,CAAC,YAAY,CAAC,CAAC,YAAY,CAAC,CAAC,CAAC,CAAC;IAC9C,CAAC,CAAC,CAAC;AACL,CAAC,CAAC,CAAC"}