@einja/dev-cli 0.1.45 → 0.1.49
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/cli.js +7 -1
- package/dist/cli.js.map +1 -1
- package/dist/commands/sync.d.ts.map +1 -1
- package/dist/commands/sync.js +50 -11
- package/dist/commands/sync.js.map +1 -1
- package/dist/commands/sync.test.js +1 -1
- package/dist/commands/sync.test.js.map +1 -1
- package/dist/lib/sync/file-filter.d.ts.map +1 -1
- package/dist/lib/sync/file-filter.js +49 -0
- package/dist/lib/sync/file-filter.js.map +1 -1
- package/dist/lib/sync/file-filter.test.js +40 -0
- package/dist/lib/sync/file-filter.test.js.map +1 -1
- package/dist/lib/sync/json-processor.d.ts +49 -27
- package/dist/lib/sync/json-processor.d.ts.map +1 -1
- package/dist/lib/sync/json-processor.js +182 -82
- package/dist/lib/sync/json-processor.js.map +1 -1
- package/dist/lib/sync/json-processor.test.d.ts +2 -0
- package/dist/lib/sync/json-processor.test.d.ts.map +1 -0
- package/dist/lib/sync/json-processor.test.js +334 -0
- package/dist/lib/sync/json-processor.test.js.map +1 -0
- package/dist/lib/sync/metadata-manager.d.ts +6 -1
- package/dist/lib/sync/metadata-manager.d.ts.map +1 -1
- package/dist/lib/sync/metadata-manager.js +26 -6
- package/dist/lib/sync/metadata-manager.js.map +1 -1
- package/dist/types/sync.d.ts +2 -0
- package/dist/types/sync.d.ts.map +1 -1
- package/dist/types/sync.js +1 -0
- package/dist/types/sync.js.map +1 -1
- package/package.json +1 -1
- package/presets/default/.claude/agents/einja/backend-architect.md +27 -17
- package/presets/default/.claude/commands/einja/einja-sync.md +6 -6
- package/presets/default/.claude/skills/einja-coding-standards/references/testing-strategy.md +3 -3
- package/presets/default/docs/einja/instructions/setup-flow.md +40 -0
- package/presets/default/docs/einja/steering/architecture.md +7 -1
- package/presets/default/docs/einja/steering/development/api-development.md +199 -67
- package/presets/default/docs/einja/steering/development/backend-architecture.md +12 -16
- package/presets/default/docs/einja/steering/development/frontend-development.md +61 -50
- package/presets/default/docs/einja/steering/development/review-guidelines.md +4 -1
- package/presets/default/docs/einja/steering/development/testing-strategy.md +3 -3
- package/presets/default/package.json +73 -0
- package/presets/default/preset.yaml +2 -2
- package/presets/default/scripts/ensure-serena.sh +26 -7
|
@@ -1,4 +1,20 @@
|
|
|
1
1
|
import type { JsonPathsConfig } from "../../types/sync.js";
|
|
2
|
+
/**
|
|
3
|
+
* JSONコンフリクト情報
|
|
4
|
+
*/
|
|
5
|
+
export interface JsonConflict {
|
|
6
|
+
keyPath: string;
|
|
7
|
+
baseValue: unknown;
|
|
8
|
+
localValue: unknown;
|
|
9
|
+
templateValue: unknown;
|
|
10
|
+
}
|
|
11
|
+
/**
|
|
12
|
+
* JSONマージ結果
|
|
13
|
+
*/
|
|
14
|
+
export interface JsonMergeResult {
|
|
15
|
+
result: Record<string, unknown>;
|
|
16
|
+
conflicts: JsonConflict[];
|
|
17
|
+
}
|
|
2
18
|
/**
|
|
3
19
|
* JSONファイルのマージ処理を行うクラス
|
|
4
20
|
* marker-processor.tsのパターンに従い、パス指定によるマージを実装します。
|
|
@@ -7,47 +23,38 @@ export declare class JsonProcessor {
|
|
|
7
23
|
/**
|
|
8
24
|
* JSONをマージする
|
|
9
25
|
* - managed パス: テンプレート値で上書き
|
|
10
|
-
* - project-private パス:
|
|
11
|
-
* -
|
|
26
|
+
* - project-private パス: 完全除外(テンプレートから追加しない)
|
|
27
|
+
* - その他(baseあり): 3方向マージ
|
|
28
|
+
* - その他(base なし = 初回sync): ローカル優先 + テンプレート新規キーのみ追加
|
|
12
29
|
*
|
|
13
30
|
* @param templateJson - テンプレート版のJSON
|
|
14
|
-
* @param localJson - ローカル版のJSON(null
|
|
31
|
+
* @param localJson - ローカル版のJSON(nullの場合はproject-private除外済みテンプレートを返す)
|
|
15
32
|
* @param jsonPaths - JSONパス設定
|
|
16
33
|
* @param filePath - 対象ファイルパス
|
|
17
|
-
* @
|
|
34
|
+
* @param baseJson - 前回sync時のテンプレートJSON(3方向マージ用。未指定は初回sync)
|
|
35
|
+
* @returns マージ結果(result + conflicts)
|
|
18
36
|
*/
|
|
19
|
-
mergeJson(templateJson: Record<string, unknown>, localJson: Record<string, unknown> | null, jsonPaths: JsonPathsConfig, filePath: string
|
|
37
|
+
mergeJson(templateJson: Record<string, unknown>, localJson: Record<string, unknown> | null, jsonPaths: JsonPathsConfig, filePath: string, baseJson?: Record<string, unknown>): JsonMergeResult;
|
|
20
38
|
/**
|
|
21
|
-
*
|
|
39
|
+
* project-privateキーをテンプレートから除外した新しいオブジェクトを返す
|
|
22
40
|
*
|
|
23
|
-
* @param
|
|
24
|
-
* @param
|
|
25
|
-
* @
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
/**
|
|
29
|
-
* 指定されたパスに値を設定
|
|
30
|
-
*
|
|
31
|
-
* @param obj - 対象オブジェクト
|
|
32
|
-
* @param path - ドット区切りのパス (例: "scripts.build")
|
|
33
|
-
* @param value - 設定する値
|
|
34
|
-
*/
|
|
35
|
-
private setValueAtPath;
|
|
36
|
-
/**
|
|
37
|
-
* オブジェクトのディープクローンを作成
|
|
38
|
-
*
|
|
39
|
-
* @param obj - クローン対象のオブジェクト
|
|
40
|
-
* @returns クローンされたオブジェクト
|
|
41
|
+
* @param template - テンプレートオブジェクト
|
|
42
|
+
* @param jsonPaths - JSONパス設定
|
|
43
|
+
* @param filePath - ファイルパス
|
|
44
|
+
* @param currentPath - 現在のパス(再帰用)
|
|
45
|
+
* @returns project-privateキーを除外したオブジェクト
|
|
41
46
|
*/
|
|
42
|
-
private
|
|
47
|
+
private removeProjectPrivateKeys;
|
|
43
48
|
/**
|
|
44
|
-
*
|
|
49
|
+
* パス設定を考慮したディープマージ(3方向マージ対応)
|
|
45
50
|
*
|
|
46
51
|
* @param template - テンプレートオブジェクト
|
|
47
|
-
* @param existing -
|
|
52
|
+
* @param existing - 既存(ローカル)オブジェクト
|
|
48
53
|
* @param jsonPaths - JSONパス設定
|
|
49
54
|
* @param filePath - ファイルパス
|
|
50
55
|
* @param currentPath - 現在のパス(再帰用)
|
|
56
|
+
* @param conflicts - コンフリクト収集配列
|
|
57
|
+
* @param base - 前回sync時のテンプレートオブジェクト(3方向マージ用)
|
|
51
58
|
* @returns マージ後のオブジェクト
|
|
52
59
|
*/
|
|
53
60
|
private deepMergeWithPaths;
|
|
@@ -76,5 +83,20 @@ export declare class JsonProcessor {
|
|
|
76
83
|
* @returns オブジェクトの場合true
|
|
77
84
|
*/
|
|
78
85
|
private isObject;
|
|
86
|
+
/**
|
|
87
|
+
* オブジェクトのディープクローンを作成
|
|
88
|
+
*
|
|
89
|
+
* @param obj - クローン対象のオブジェクト
|
|
90
|
+
* @returns クローンされたオブジェクト
|
|
91
|
+
*/
|
|
92
|
+
private deepClone;
|
|
93
|
+
/**
|
|
94
|
+
* キー順序非依存の深い等値比較
|
|
95
|
+
*
|
|
96
|
+
* @param a - 比較対象A
|
|
97
|
+
* @param b - 比較対象B
|
|
98
|
+
* @returns 等しい場合true
|
|
99
|
+
*/
|
|
100
|
+
private deepEqual;
|
|
79
101
|
}
|
|
80
102
|
//# sourceMappingURL=json-processor.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"json-processor.d.ts","sourceRoot":"","sources":["../../../src/lib/sync/json-processor.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,iBAAiB,CAAC;AAEvD;;;GAGG;AACH,qBAAa,aAAa;IACxB
|
|
1
|
+
{"version":3,"file":"json-processor.d.ts","sourceRoot":"","sources":["../../../src/lib/sync/json-processor.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,iBAAiB,CAAC;AAEvD;;GAEG;AACH,MAAM,WAAW,YAAY;IAC3B,OAAO,EAAE,MAAM,CAAC;IAChB,SAAS,EAAE,OAAO,CAAC;IACnB,UAAU,EAAE,OAAO,CAAC;IACpB,aAAa,EAAE,OAAO,CAAC;CACxB;AAED;;GAEG;AACH,MAAM,WAAW,eAAe;IAC9B,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;IAChC,SAAS,EAAE,YAAY,EAAE,CAAC;CAC3B;AAED;;;GAGG;AACH,qBAAa,aAAa;IACxB;;;;;;;;;;;;;OAaG;IACH,SAAS,CACP,YAAY,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EACrC,SAAS,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,IAAI,EACzC,SAAS,EAAE,eAAe,EAC1B,QAAQ,EAAE,MAAM,EAChB,QAAQ,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GACjC,eAAe;IAoBlB;;;;;;;;OAQG;IACH,OAAO,CAAC,wBAAwB;IA0BhC;;;;;;;;;;;OAWG;IACH,OAAO,CAAC,kBAAkB;IAqJ1B;;;;;;;OAOG;IACH,OAAO,CAAC,aAAa;IAOrB;;;;;;;OAOG;IACH,OAAO,CAAC,oBAAoB;IAU5B;;;;;OAKG;IACH,OAAO,CAAC,QAAQ;IAIhB;;;;;OAKG;IACH,OAAO,CAAC,SAAS;IAkBjB;;;;;;OAMG;IACH,OAAO,CAAC,SAAS;CAmBlB"}
|
|
@@ -6,124 +6,176 @@ export class JsonProcessor {
|
|
|
6
6
|
/**
|
|
7
7
|
* JSONをマージする
|
|
8
8
|
* - managed パス: テンプレート値で上書き
|
|
9
|
-
* - project-private パス:
|
|
10
|
-
* -
|
|
9
|
+
* - project-private パス: 完全除外(テンプレートから追加しない)
|
|
10
|
+
* - その他(baseあり): 3方向マージ
|
|
11
|
+
* - その他(base なし = 初回sync): ローカル優先 + テンプレート新規キーのみ追加
|
|
11
12
|
*
|
|
12
13
|
* @param templateJson - テンプレート版のJSON
|
|
13
|
-
* @param localJson - ローカル版のJSON(null
|
|
14
|
+
* @param localJson - ローカル版のJSON(nullの場合はproject-private除外済みテンプレートを返す)
|
|
14
15
|
* @param jsonPaths - JSONパス設定
|
|
15
16
|
* @param filePath - 対象ファイルパス
|
|
16
|
-
* @
|
|
17
|
+
* @param baseJson - 前回sync時のテンプレートJSON(3方向マージ用。未指定は初回sync)
|
|
18
|
+
* @returns マージ結果(result + conflicts)
|
|
17
19
|
*/
|
|
18
|
-
mergeJson(templateJson, localJson, jsonPaths, filePath) {
|
|
19
|
-
//
|
|
20
|
+
mergeJson(templateJson, localJson, jsonPaths, filePath, baseJson) {
|
|
21
|
+
// ローカルファイルが存在しない場合はproject-private除外済みテンプレートを返す
|
|
20
22
|
if (!localJson) {
|
|
21
|
-
|
|
23
|
+
const result = this.removeProjectPrivateKeys(templateJson, jsonPaths, filePath, "");
|
|
24
|
+
return { result, conflicts: [] };
|
|
22
25
|
}
|
|
23
|
-
|
|
24
|
-
|
|
26
|
+
const conflicts = [];
|
|
27
|
+
const result = this.deepMergeWithPaths(templateJson, localJson, jsonPaths, filePath, "", conflicts, baseJson);
|
|
28
|
+
return { result, conflicts };
|
|
25
29
|
}
|
|
26
30
|
/**
|
|
27
|
-
*
|
|
31
|
+
* project-privateキーをテンプレートから除外した新しいオブジェクトを返す
|
|
28
32
|
*
|
|
29
|
-
* @param
|
|
30
|
-
* @param
|
|
31
|
-
* @
|
|
33
|
+
* @param template - テンプレートオブジェクト
|
|
34
|
+
* @param jsonPaths - JSONパス設定
|
|
35
|
+
* @param filePath - ファイルパス
|
|
36
|
+
* @param currentPath - 現在のパス(再帰用)
|
|
37
|
+
* @returns project-privateキーを除外したオブジェクト
|
|
32
38
|
*/
|
|
33
|
-
|
|
34
|
-
const
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
if (
|
|
38
|
-
|
|
39
|
+
removeProjectPrivateKeys(template, jsonPaths, filePath, currentPath) {
|
|
40
|
+
const result = {};
|
|
41
|
+
for (const [key, value] of Object.entries(template)) {
|
|
42
|
+
const keyPath = currentPath ? `${currentPath}.${key}` : key;
|
|
43
|
+
if (this.isPathProjectPrivate(filePath, keyPath, jsonPaths)) {
|
|
44
|
+
continue; // project-private は除外
|
|
39
45
|
}
|
|
40
|
-
if (
|
|
41
|
-
|
|
46
|
+
if (this.isObject(value)) {
|
|
47
|
+
result[key] = this.removeProjectPrivateKeys(value, jsonPaths, filePath, keyPath);
|
|
42
48
|
}
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
return current;
|
|
46
|
-
}
|
|
47
|
-
/**
|
|
48
|
-
* 指定されたパスに値を設定
|
|
49
|
-
*
|
|
50
|
-
* @param obj - 対象オブジェクト
|
|
51
|
-
* @param path - ドット区切りのパス (例: "scripts.build")
|
|
52
|
-
* @param value - 設定する値
|
|
53
|
-
*/
|
|
54
|
-
setValueAtPath(obj, path, value) {
|
|
55
|
-
const keys = path.split(".");
|
|
56
|
-
let current = obj;
|
|
57
|
-
for (let i = 0; i < keys.length - 1; i++) {
|
|
58
|
-
const key = keys[i];
|
|
59
|
-
if (!(key in current) || typeof current[key] !== "object" || current[key] === null) {
|
|
60
|
-
current[key] = {};
|
|
49
|
+
else {
|
|
50
|
+
result[key] = this.deepClone(value);
|
|
61
51
|
}
|
|
62
|
-
current = current[key];
|
|
63
52
|
}
|
|
64
|
-
|
|
65
|
-
current[lastKey] = value;
|
|
66
|
-
}
|
|
67
|
-
/**
|
|
68
|
-
* オブジェクトのディープクローンを作成
|
|
69
|
-
*
|
|
70
|
-
* @param obj - クローン対象のオブジェクト
|
|
71
|
-
* @returns クローンされたオブジェクト
|
|
72
|
-
*/
|
|
73
|
-
deepClone(obj) {
|
|
74
|
-
if (obj === null || typeof obj !== "object") {
|
|
75
|
-
return obj;
|
|
76
|
-
}
|
|
77
|
-
if (Array.isArray(obj)) {
|
|
78
|
-
return obj.map((item) => this.deepClone(item));
|
|
79
|
-
}
|
|
80
|
-
const cloned = {};
|
|
81
|
-
for (const key in obj) {
|
|
82
|
-
if (Object.prototype.hasOwnProperty.call(obj, key)) {
|
|
83
|
-
cloned[key] = this.deepClone(obj[key]);
|
|
84
|
-
}
|
|
85
|
-
}
|
|
86
|
-
return cloned;
|
|
53
|
+
return result;
|
|
87
54
|
}
|
|
88
55
|
/**
|
|
89
|
-
*
|
|
56
|
+
* パス設定を考慮したディープマージ(3方向マージ対応)
|
|
90
57
|
*
|
|
91
58
|
* @param template - テンプレートオブジェクト
|
|
92
|
-
* @param existing -
|
|
59
|
+
* @param existing - 既存(ローカル)オブジェクト
|
|
93
60
|
* @param jsonPaths - JSONパス設定
|
|
94
61
|
* @param filePath - ファイルパス
|
|
95
62
|
* @param currentPath - 現在のパス(再帰用)
|
|
63
|
+
* @param conflicts - コンフリクト収集配列
|
|
64
|
+
* @param base - 前回sync時のテンプレートオブジェクト(3方向マージ用)
|
|
96
65
|
* @returns マージ後のオブジェクト
|
|
97
66
|
*/
|
|
98
|
-
deepMergeWithPaths(template, existing, jsonPaths, filePath, currentPath) {
|
|
67
|
+
deepMergeWithPaths(template, existing, jsonPaths, filePath, currentPath, conflicts, base) {
|
|
68
|
+
// ローカルをベースにスタート(ローカルのキーはすべて保持)
|
|
99
69
|
const result = this.deepClone(existing);
|
|
70
|
+
// テンプレートのキーを処理
|
|
100
71
|
for (const [key, templateValue] of Object.entries(template)) {
|
|
101
72
|
const keyPath = currentPath ? `${currentPath}.${key}` : key;
|
|
102
73
|
if (this.isPathManaged(filePath, keyPath, jsonPaths)) {
|
|
103
|
-
// managed:
|
|
74
|
+
// managed: テンプレートで強制上書き
|
|
104
75
|
result[key] = this.deepClone(templateValue);
|
|
76
|
+
continue;
|
|
77
|
+
}
|
|
78
|
+
if (this.isPathProjectPrivate(filePath, keyPath, jsonPaths)) {
|
|
79
|
+
// project-private: 完全除外(テンプレートから追加しない)
|
|
80
|
+
continue;
|
|
105
81
|
}
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
82
|
+
// デフォルト処理
|
|
83
|
+
const localValue = existing[key];
|
|
84
|
+
const baseValue = base ? base[key] : undefined;
|
|
85
|
+
const localExists = key in existing;
|
|
86
|
+
const baseExists = base != null && key in base;
|
|
87
|
+
if (base) {
|
|
88
|
+
// 3方向マージモード
|
|
89
|
+
if (!localExists) {
|
|
90
|
+
if (!baseExists) {
|
|
91
|
+
// base にも local にも存在しない → テンプレートで新規追加
|
|
92
|
+
result[key] = this.deepClone(templateValue);
|
|
93
|
+
}
|
|
94
|
+
else {
|
|
95
|
+
// base には存在したが local で削除された
|
|
96
|
+
const templateChanged = !this.deepEqual(baseValue, templateValue);
|
|
97
|
+
if (templateChanged) {
|
|
98
|
+
// テンプレートも変更 → コンフリクト(ローカルの削除を保持)
|
|
99
|
+
conflicts.push({ keyPath, baseValue, localValue: undefined, templateValue });
|
|
100
|
+
// result にキーなし(削除維持)
|
|
101
|
+
}
|
|
102
|
+
// テンプレート変更なし → 削除を維持(result にキーなし)
|
|
103
|
+
}
|
|
104
|
+
continue;
|
|
111
105
|
}
|
|
112
|
-
|
|
113
|
-
//
|
|
106
|
+
if (this.isObject(templateValue) && this.isObject(localValue)) {
|
|
107
|
+
// 両方オブジェクト: 再帰的に3方向マージ
|
|
108
|
+
result[key] = this.deepMergeWithPaths(templateValue, localValue, jsonPaths, filePath, keyPath, conflicts, baseExists ? baseValue : undefined);
|
|
109
|
+
continue;
|
|
110
|
+
}
|
|
111
|
+
const localChanged = !this.deepEqual(baseValue, localValue);
|
|
112
|
+
const templateChanged = !this.deepEqual(baseValue, templateValue);
|
|
113
|
+
if (!localChanged && templateChanged) {
|
|
114
|
+
// ローカル変更なし + テンプレート変更あり → テンプレート適用
|
|
114
115
|
result[key] = this.deepClone(templateValue);
|
|
115
116
|
}
|
|
116
|
-
|
|
117
|
+
else if (localChanged && !templateChanged) {
|
|
118
|
+
// ローカル変更あり + テンプレート変更なし → ローカル保持(何もしない)
|
|
119
|
+
}
|
|
120
|
+
else if (localChanged && templateChanged) {
|
|
121
|
+
if (this.deepEqual(localValue, templateValue)) {
|
|
122
|
+
// 両方変更 + 同じ値 → コンフリクトなし、どちらでも同じ
|
|
123
|
+
}
|
|
124
|
+
else {
|
|
125
|
+
// 両方変更 + 異なる値 → コンフリクト(ローカル保持)
|
|
126
|
+
conflicts.push({ keyPath, baseValue, localValue, templateValue });
|
|
127
|
+
}
|
|
128
|
+
}
|
|
129
|
+
// !localChanged && !templateChanged → 変更なし(ローカル保持)
|
|
117
130
|
}
|
|
118
|
-
else
|
|
119
|
-
//
|
|
120
|
-
|
|
131
|
+
else {
|
|
132
|
+
// 初回sync(base なし): ローカル優先 + テンプレート新規キーのみ追加
|
|
133
|
+
if (!localExists) {
|
|
134
|
+
// ローカルに存在しない → テンプレートのキーを追加
|
|
135
|
+
result[key] = this.deepClone(templateValue);
|
|
136
|
+
}
|
|
137
|
+
else if (this.isObject(templateValue) && this.isObject(localValue)) {
|
|
138
|
+
// 両方オブジェクト: 再帰的にマージ(baseなしモード)
|
|
139
|
+
result[key] = this.deepMergeWithPaths(templateValue, localValue, jsonPaths, filePath, keyPath, conflicts, undefined);
|
|
140
|
+
}
|
|
141
|
+
// ローカルに存在する場合は保持(何もしない)
|
|
121
142
|
}
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
143
|
+
}
|
|
144
|
+
// base にあってテンプレートから削除されたキーの処理(3方向マージモードのみ)
|
|
145
|
+
if (base) {
|
|
146
|
+
for (const key of Object.keys(base)) {
|
|
147
|
+
// テンプレートに存在しないキー(テンプレートから削除された)
|
|
148
|
+
if (!(key in template)) {
|
|
149
|
+
const keyPath = currentPath ? `${currentPath}.${key}` : key;
|
|
150
|
+
// managed / project-private はスキップ(上のループで処理済み)
|
|
151
|
+
if (this.isPathManaged(filePath, keyPath, jsonPaths) ||
|
|
152
|
+
this.isPathProjectPrivate(filePath, keyPath, jsonPaths)) {
|
|
153
|
+
continue;
|
|
154
|
+
}
|
|
155
|
+
const localValue = existing[key];
|
|
156
|
+
const baseValue = base[key];
|
|
157
|
+
const localExists = key in existing;
|
|
158
|
+
if (!localExists) {
|
|
159
|
+
// ローカルでも削除済み → 何もしない
|
|
160
|
+
continue;
|
|
161
|
+
}
|
|
162
|
+
const localChanged = !this.deepEqual(baseValue, localValue);
|
|
163
|
+
if (localChanged) {
|
|
164
|
+
// ローカルで変更あり → コンフリクト(ローカル保持)
|
|
165
|
+
conflicts.push({
|
|
166
|
+
keyPath,
|
|
167
|
+
baseValue,
|
|
168
|
+
localValue,
|
|
169
|
+
templateValue: undefined,
|
|
170
|
+
});
|
|
171
|
+
// result にはローカル値が保持されている(deepClone(existing)から)
|
|
172
|
+
}
|
|
173
|
+
else {
|
|
174
|
+
// ローカル未変更 → 削除
|
|
175
|
+
delete result[key];
|
|
176
|
+
}
|
|
177
|
+
}
|
|
125
178
|
}
|
|
126
|
-
// 既存にあるキー: 保持(何もしない)
|
|
127
179
|
}
|
|
128
180
|
return result;
|
|
129
181
|
}
|
|
@@ -163,5 +215,53 @@ export class JsonProcessor {
|
|
|
163
215
|
isObject(value) {
|
|
164
216
|
return value !== null && typeof value === "object" && !Array.isArray(value);
|
|
165
217
|
}
|
|
218
|
+
/**
|
|
219
|
+
* オブジェクトのディープクローンを作成
|
|
220
|
+
*
|
|
221
|
+
* @param obj - クローン対象のオブジェクト
|
|
222
|
+
* @returns クローンされたオブジェクト
|
|
223
|
+
*/
|
|
224
|
+
deepClone(obj) {
|
|
225
|
+
if (obj === null || typeof obj !== "object") {
|
|
226
|
+
return obj;
|
|
227
|
+
}
|
|
228
|
+
if (Array.isArray(obj)) {
|
|
229
|
+
return obj.map((item) => this.deepClone(item));
|
|
230
|
+
}
|
|
231
|
+
const cloned = {};
|
|
232
|
+
for (const key in obj) {
|
|
233
|
+
if (Object.prototype.hasOwnProperty.call(obj, key)) {
|
|
234
|
+
cloned[key] = this.deepClone(obj[key]);
|
|
235
|
+
}
|
|
236
|
+
}
|
|
237
|
+
return cloned;
|
|
238
|
+
}
|
|
239
|
+
/**
|
|
240
|
+
* キー順序非依存の深い等値比較
|
|
241
|
+
*
|
|
242
|
+
* @param a - 比較対象A
|
|
243
|
+
* @param b - 比較対象B
|
|
244
|
+
* @returns 等しい場合true
|
|
245
|
+
*/
|
|
246
|
+
deepEqual(a, b) {
|
|
247
|
+
if (a === b)
|
|
248
|
+
return true;
|
|
249
|
+
if (a === null || b === null || typeof a !== typeof b)
|
|
250
|
+
return false;
|
|
251
|
+
if (Array.isArray(a) && Array.isArray(b)) {
|
|
252
|
+
if (a.length !== b.length)
|
|
253
|
+
return false;
|
|
254
|
+
return a.every((item, i) => this.deepEqual(item, b[i]));
|
|
255
|
+
}
|
|
256
|
+
if (typeof a === "object" && typeof b === "object") {
|
|
257
|
+
const keysA = Object.keys(a).sort();
|
|
258
|
+
const keysB = Object.keys(b).sort();
|
|
259
|
+
if (keysA.length !== keysB.length)
|
|
260
|
+
return false;
|
|
261
|
+
return keysA.every((key, i) => key === keysB[i] &&
|
|
262
|
+
this.deepEqual(a[key], b[key]));
|
|
263
|
+
}
|
|
264
|
+
return false;
|
|
265
|
+
}
|
|
166
266
|
}
|
|
167
267
|
//# sourceMappingURL=json-processor.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"json-processor.js","sourceRoot":"","sources":["../../../src/lib/sync/json-processor.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"json-processor.js","sourceRoot":"","sources":["../../../src/lib/sync/json-processor.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"json-processor.test.d.ts","sourceRoot":"","sources":["../../../src/lib/sync/json-processor.test.ts"],"names":[],"mappings":""}
|