-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathinstall.js
More file actions
188 lines (163 loc) · 7.16 KB
/
Copy pathinstall.js
File metadata and controls
188 lines (163 loc) · 7.16 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
/**
* PlayCanvas Editor Console Script
* GitHub リポジトリから CLAUDE.md / .claude/ 配下のルールファイルを
* 現在のプロジェクトにコピーする。
*
* 使い方: PlayCanvas Editor の開発者コンソール (F12) にコピペして実行
*/
(async () => {
const REPO = 'FrameSynthesis/PlayCanvasEditor_ClaudeCodeRules';
const BRANCH = 'main';
const ROOT = 'AssetRoot';
const projectId = config.project.id;
const branchId = config.self.branch.id;
const token = config.accessToken;
const apiBase = 'https://playcanvas.com/api';
console.log(`[install] Project: ${projectId}, Branch: ${branchId}`);
// --- ユーティリティ ---
const delay = ms => new Promise(r => setTimeout(r, ms));
async function apiPost(path, formData) {
const resp = await fetch(`${apiBase}${path}`, {
method: 'POST',
headers: { Authorization: `Bearer ${token}` },
body: formData,
});
if (!resp.ok) throw new Error(`POST ${path}: ${resp.status} ${await resp.text()}`);
return resp.json();
}
function rel(fullPath) {
return fullPath.slice(ROOT.length + 1);
}
// pathプロパティ(祖先フォルダIDの配列)の末尾要素でparentを判定する
// path=[] → ルート, path=[A] → Aの子, path=[A,B] → Bの子
function getParentFromPath(asset) {
const p = asset.get('path') || [];
return p.length > 0 ? p[p.length - 1] : null;
}
// --- GitHub Tree 取得 ---
console.log('[install] Fetching GitHub tree...');
const treeResp = await fetch(
`https://api.github.com/repos/${REPO}/git/trees/${BRANCH}?recursive=1`
);
if (!treeResp.ok) {
console.error('[install] GitHub API error:', await treeResp.text());
return;
}
const treeData = await treeResp.json();
const items = treeData.tree.filter(t => t.path.startsWith(ROOT + '/'));
const folders = items
.filter(t => t.type === 'tree')
.sort((a, b) => a.path.split('/').length - b.path.split('/').length);
const files = items.filter(t => t.type === 'blob');
console.log(`[install] GitHub: ${folders.length} folders, ${files.length} files`);
// --- フォルダ作成(階層ごとにまとめて作成 → 待機) ---
const folderIds = {}; // relPath -> assetId
// 階層(depth)ごとにグループ化
const foldersByDepth = {};
for (const f of folders) {
const r = rel(f.path);
const depth = r.split('/').length;
(foldersByDepth[depth] = foldersByDepth[depth] || []).push(f);
}
for (const depth of Object.keys(foldersByDepth).sort((a, b) => a - b)) {
const group = foldersByDepth[depth];
const toCreate = [];
for (const f of group) {
const r = rel(f.path);
const parts = r.split('/');
const name = parts[parts.length - 1];
const parentRel = parts.slice(0, -1).join('/');
const parentId = parentRel ? folderIds[parentRel] : null;
// 既存チェック (path ベース)
const existing = editor.assets.list().find(a =>
a.get('name') === name &&
a.get('type') === 'folder' &&
getParentFromPath(a) === parentId
);
if (existing) {
folderIds[r] = existing.get('id');
console.log(`[install] = folder: ${r} (id=${existing.get('id')})`);
continue;
}
// Editor API でフォルダ作成
const parentAsset = parentId ? editor.assets.get(parentId) : null;
const opts = { name };
if (parentAsset) opts.folder = parentAsset;
editor.assets.createFolder(opts);
toCreate.push({ r, name, parentId });
console.log(`[install] creating folder: ${r}...`);
}
if (toCreate.length === 0) continue;
// この階層の全フォルダが同期されるのを待つ
await delay(3000);
for (const { r, name, parentId } of toCreate) {
const created = await new Promise((resolve, reject) => {
const start = Date.now();
const check = () => {
const found = editor.assets.list().find(a =>
a.get('name') === name &&
a.get('type') === 'folder' &&
getParentFromPath(a) === parentId
);
if (found) return resolve(found);
if (Date.now() - start > 30000) return reject(new Error(`Timeout: folder ${r}`));
setTimeout(check, 300);
};
check();
});
folderIds[r] = created.get('id');
console.log(`[install] + folder: ${r} (id=${created.get('id')})`);
}
}
// --- ファイル作成 (REST API POST) ---
console.log('[install] Creating files...');
for (const f of files) {
const r = rel(f.path);
const parts = r.split('/');
const fileName = parts[parts.length - 1];
const parentRel = parts.slice(0, -1).join('/');
const parentId = parentRel ? folderIds[parentRel] : null;
// アセット名とblob ファイル名を決定
// .md.txt → name: 'CLAUDE.md', blob: 'CLAUDE.md.txt' (text型 + 正しいローカル名)
// .json → name: 'settings.json', blob: 'settings.json' (json型)
let assetName = fileName;
let blobName = fileName;
if (/\.\w+\.txt$/.test(fileName)) {
assetName = fileName.slice(0, -4); // .md.txt → .md
blobName = fileName; // .md.txt のまま (.txt末尾 → text型)
}
// 既存チェック (path ベース)
const existing = editor.assets.list().find(a =>
(a.get('name') === assetName || a.get('name') === fileName) &&
getParentFromPath(a) === parentId
);
if (existing) {
console.log(`[install] = file: ${r} (id=${existing.get('id')})`);
continue;
}
// GitHub raw content
const rawUrl = `https://raw.githubusercontent.com/${REPO}/${BRANCH}/${f.path}`;
const resp = await fetch(rawUrl);
if (!resp.ok) {
console.error(`[install] x fetch failed: ${r}`);
continue;
}
const content = await resp.text();
// REST API POST でアセット作成
const mime = fileName.endsWith('.json') ? 'application/json' : 'text/plain';
const formData = new FormData();
formData.append('name', assetName);
formData.append('projectId', projectId);
formData.append('branchId', branchId);
if (parentId != null) formData.append('parent', parentId);
formData.append('preload', 'true');
formData.append('file', new Blob([content], { type: mime }), blobName);
try {
const created = await apiPost('/assets', formData);
console.log(`[install] + file: ${r} (id=${created.id})`);
} catch (e) {
console.error(`[install] x failed: ${r}`, e.message);
}
}
console.log('[install] Done!');
})();