childrenがduplicate key errorを吐く問題をいったん解決

This commit is contained in:
tai-cha 2025-03-02 02:12:53 +00:00
parent 6d4d2c5c20
commit f8282e8d1f
1 changed files with 127 additions and 32 deletions

View File

@ -201,6 +201,15 @@ async function processVueFile(
options: { targetFilePaths: string[], exportFilePath: string },
transformedCodeCache: Record<string, string>
) {
// すでにキャッシュに存在する場合は、そのまま返す
if (transformedCodeCache[id] && transformedCodeCache[id].includes('markerId=')) {
console.log(`[create-search-index] Using cached version for ${id}`);
return {
code: transformedCodeCache[id],
map: null
};
}
const s = new MagicString(code); // magic-string のインスタンスを作成
const parsed = vueSfcParse(code, { filename: id });
if (!parsed.descriptor.template) {
@ -238,9 +247,34 @@ async function processVueFile(
});
if (!hasMarkerIdProp) {
const startTagEnd = code.indexOf('>', node.loc.start.offset);
if (startTagEnd !== -1) {
s.appendRight(startTagEnd, ` markerId="${generatedMarkerId}"`);
const nodeStart = node.loc.start.offset;
let endOfStartTag;
if (node.children && node.children.length > 0) {
// 子要素がある場合、最初の子要素の開始位置を基準にする
endOfStartTag = code.lastIndexOf('>', node.children[0].loc.start.offset);
} else if (node.loc.end.offset > nodeStart) {
// 子要素がない場合、自身の終了位置から逆算
const nodeSource = code.substring(nodeStart, node.loc.end.offset);
// 自己終了タグか通常の終了タグかを判断
if (nodeSource.includes('/>')) {
endOfStartTag = code.indexOf('/>', nodeStart) - 1;
} else {
endOfStartTag = code.indexOf('>', nodeStart);
}
}
if (endOfStartTag !== undefined && endOfStartTag !== -1) {
// markerId が既に存在しないことを確認
const tagText = code.substring(nodeStart, endOfStartTag + 1);
const markerIdRegex = /\s+markerId\s*=\s*["'][^"']*["']/;
if (!markerIdRegex.test(tagText)) {
s.appendRight(endOfStartTag, ` markerId="${generatedMarkerId}"`);
console.log(`[create-search-index] Adding markerId="${generatedMarkerId}" to ${id}:${lineNumber}`);
} else {
console.log(`[create-search-index] markerId already exists in ${id}:${lineNumber}`);
}
}
}
}
@ -254,39 +288,100 @@ async function processVueFile(
traverse(ast); // AST を traverse (1段階目: ID 生成と親子関係記録)
// 2段階目: :children 属性の追加
markerRelations.forEach(relation => {
if (relation.parentId) { // 親 ID が存在する (子マーカーである) 場合
const parentRelation = markerRelations.find(r => r.markerId === relation.parentId); // 親 Relation を検索
if (parentRelation && parentRelation.node) {
const parentNode = parentRelation.node;
const childrenProp = parentNode.props?.find((prop: any) => prop.type === 7 && prop.name === 'bind' && prop.arg?.content === 'children');
const childMarkerId = relation.markerId;
// 最初に親マーカーごとに子マーカーIDを集約する処理を追加
const parentChildrenMap = new Map<string, string[]>();
if (childrenProp) {
// 既存の :children 属性を JavaScript 配列として解析・更新
try {
const childrenStart = code.indexOf('[', childrenProp.exp.loc.start.offset);
const childrenEnd = code.indexOf(']', childrenProp.exp.loc.start.offset);
if (childrenStart !== -1 && childrenEnd !== -1) {
const childrenArrayStr = code.slice(childrenStart, childrenEnd + 1);
const childrenArray = JSON5.parse(childrenArrayStr.replace(/'/g, '"')); // JSON5 で解析 (シングルクォート対応)
childrenArray.push(childMarkerId); // 子マーカーIDを追加
const updatedChildrenArrayStr = JSON.stringify(childrenArray).replace(/"/g, "'"); // シングルクォートの配列文字列に再変換
s.overwrite(childrenStart, childrenEnd + 1, updatedChildrenArrayStr); // 属性値を書き換え
}
} catch (e) {
console.error('[create-search-index] Error updating :children attribute:', e); // エラーログ
}
} else {
// :children 属性が存在しない場合は新規作成 (テンプレートリテラルを使用)
const startTagEnd = code.indexOf('>', parentNode.loc.start.offset); // 親の開始タグの閉じ > の位置
if (startTagEnd !== -1) {
s.appendRight(startTagEnd, ` :children="${JSON.stringify([childMarkerId]).replace(/"/g, "'")}"`); // :children 属性を追記
}
}
// 1. まず親ごとのすべての子マーカーIDを収集
markerRelations.forEach(relation => {
if (relation.parentId) {
if (!parentChildrenMap.has(relation.parentId)) {
parentChildrenMap.set(relation.parentId, []);
}
parentChildrenMap.get(relation.parentId)?.push(relation.markerId);
}
});
// 2. 親ごとにまとめて :children 属性を処理
for (const [parentId, childIds] of parentChildrenMap.entries()) {
const parentRelation = markerRelations.find(r => r.markerId === parentId);
if (!parentRelation || !parentRelation.node) continue;
const parentNode = parentRelation.node;
const childrenProp = parentNode.props?.find((prop: any) => prop.type === 7 && prop.name === 'bind' && prop.arg?.content === 'children');
// 親ノードの開始位置を特定
const parentNodeStart = parentNode.loc.start.offset;
const endOfParentStartTag = parentNode.children && parentNode.children.length > 0
? code.lastIndexOf('>', parentNode.children[0].loc.start.offset)
: code.indexOf('>', parentNodeStart);
if (endOfParentStartTag === -1) continue;
// 親タグのテキストを取得
const parentTagText = code.substring(parentNodeStart, endOfParentStartTag + 1);
if (childrenProp) {
// AST で :children 属性が検出された場合、それを更新
try {
const childrenStart = code.indexOf('[', childrenProp.exp.loc.start.offset);
const childrenEnd = code.indexOf(']', childrenProp.exp.loc.start.offset);
if (childrenStart !== -1 && childrenEnd !== -1) {
const childrenArrayStr = code.slice(childrenStart, childrenEnd + 1);
let childrenArray = JSON5.parse(childrenArrayStr.replace(/'/g, '"'));
// 新しいIDを追加重複は除外
const newIds = childIds.filter(id => !childrenArray.includes(id));
if (newIds.length > 0) {
childrenArray = [...childrenArray, ...newIds];
const updatedChildrenArrayStr = JSON.stringify(childrenArray).replace(/"/g, "'");
s.overwrite(childrenStart, childrenEnd + 1, updatedChildrenArrayStr);
console.log(`[create-search-index] Added ${newIds.length} child markerIds to existing :children in ${id}`);
}
}
} catch (e) {
console.error('[create-search-index] Error updating :children attribute:', e);
}
} else {
// AST では検出されなかった場合、タグテキストを調べる
const childrenRegex = /:children\s*=\s*["']\[(.*?)\]["']/;
const childrenMatch = parentTagText.match(childrenRegex);
if (childrenMatch) {
// テキストから :children 属性値を解析して更新
try {
const childrenContent = childrenMatch[1];
const childrenArrayStr = `[${childrenContent}]`;
const childrenArray = JSON5.parse(childrenArrayStr.replace(/'/g, '"'));
// 新しいIDを追加重複は除外
const newIds = childIds.filter(id => !childrenArray.includes(id));
if (newIds.length > 0) {
childrenArray.push(...newIds);
// :children="[...]" の位置を特定して上書き
const attrStart = parentTagText.indexOf(':children=');
if (attrStart > -1) {
const attrValueStart = parentTagText.indexOf('[', attrStart);
const attrValueEnd = parentTagText.indexOf(']', attrValueStart) + 1;
if (attrValueStart > -1 && attrValueEnd > -1) {
const absoluteStart = parentNodeStart + attrValueStart;
const absoluteEnd = parentNodeStart + attrValueEnd;
const updatedArrayStr = JSON.stringify(childrenArray).replace(/"/g, "'");
s.overwrite(absoluteStart, absoluteEnd, updatedArrayStr);
console.log(`[create-search-index] Updated existing :children in tag text for ${id}`);
}
}
}
} catch (e) {
console.error('[create-search-index] Error updating :children in tag text:', e);
}
} else {
// :children 属性がまだない場合、新規作成
s.appendRight(endOfParentStartTag, ` :children="${JSON.stringify(childIds).replace(/"/g, "'")}"`);
console.log(`[create-search-index] Created new :children attribute with ${childIds.length} markerIds in ${id}`);
}
}
}
}
const transformedCode = s.toString(); // 変換後のコードを取得