merge: integrate remote main with local H2 prototypes and xll driver training nav.
Keep remote xll-miniapp registry while preserving local hydrogen station weekly/analysis pages; sync driver-training into ONEOS xll navigation. Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
167
scripts/merge-registry-sidebar-conflicts.mjs
Normal file
167
scripts/merge-registry-sidebar-conflicts.mjs
Normal file
@@ -0,0 +1,167 @@
|
||||
#!/usr/bin/env node
|
||||
/**
|
||||
* 合并 merge 冲突中的 prototype-registry.json 与 sidebar-tree.json(保留两侧内容)。
|
||||
* 用法:node scripts/merge-registry-sidebar-conflicts.mjs
|
||||
*/
|
||||
import fs from 'node:fs';
|
||||
import path from 'node:path';
|
||||
import { execSync } from 'node:child_process';
|
||||
import { fileURLToPath } from 'node:url';
|
||||
|
||||
const __dirname = path.dirname(fileURLToPath(import.meta.url));
|
||||
const projectRoot = path.resolve(__dirname, '..');
|
||||
|
||||
function gitShow(stage, filePath) {
|
||||
return execSync(`git show :${stage}:${filePath}`, {
|
||||
cwd: projectRoot,
|
||||
encoding: 'utf8',
|
||||
});
|
||||
}
|
||||
|
||||
function readJsonFromGit(stage, relativePath) {
|
||||
return JSON.parse(gitShow(stage, relativePath));
|
||||
}
|
||||
|
||||
function writeJson(relativePath, value) {
|
||||
const abs = path.join(projectRoot, relativePath);
|
||||
fs.writeFileSync(abs, `${JSON.stringify(value, null, 2)}\n`, 'utf8');
|
||||
}
|
||||
|
||||
function collectItemKeys(items, acc = new Set()) {
|
||||
for (const item of items || []) {
|
||||
if (item?.itemKey) acc.add(item.itemKey);
|
||||
if (Array.isArray(item?.children)) collectItemKeys(item.children, acc);
|
||||
}
|
||||
return acc;
|
||||
}
|
||||
|
||||
function findItemByKey(items, itemKey) {
|
||||
for (const item of items || []) {
|
||||
if (item?.itemKey === itemKey) return item;
|
||||
if (Array.isArray(item?.children)) {
|
||||
const found = findItemByKey(item.children, itemKey);
|
||||
if (found) return found;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
function findFolder(items, title) {
|
||||
for (const item of items || []) {
|
||||
if (item?.kind === 'folder' && item?.title === title) return item;
|
||||
if (Array.isArray(item?.children)) {
|
||||
const found = findFolder(item.children, title);
|
||||
if (found) return found;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
function ensureChildFolder(parent, folderTitle, folderId) {
|
||||
parent.children = parent.children || [];
|
||||
let folder = parent.children.find((c) => c.kind === 'folder' && c.title === folderTitle);
|
||||
if (!folder) {
|
||||
folder = {
|
||||
id: folderId,
|
||||
kind: 'folder',
|
||||
title: folderTitle,
|
||||
children: [],
|
||||
};
|
||||
parent.children.push(folder);
|
||||
}
|
||||
folder.children = folder.children || [];
|
||||
return folder;
|
||||
}
|
||||
|
||||
function ensureItem(folder, item) {
|
||||
if (!item?.itemKey) return;
|
||||
const exists = folder.children.some((c) => c.itemKey === item.itemKey);
|
||||
if (!exists) folder.children.push(JSON.parse(JSON.stringify(item)));
|
||||
}
|
||||
|
||||
function mergeRegistry() {
|
||||
const relativePath = 'src/prototypes/oneos-prototype-nav/prototype-registry.json';
|
||||
const ours = readJsonFromGit(2, relativePath);
|
||||
const theirs = readJsonFromGit(3, relativePath);
|
||||
|
||||
const merged = {
|
||||
version: 1,
|
||||
updatedAt: new Date().toISOString(),
|
||||
prototypes: { ...theirs.prototypes },
|
||||
recentUpdates: [],
|
||||
};
|
||||
|
||||
for (const key of ['oneos-web-h2-station-weekly', 'oneos-web-h2-station-analysis']) {
|
||||
if (ours.prototypes[key]) merged.prototypes[key] = ours.prototypes[key];
|
||||
}
|
||||
|
||||
const seen = new Set();
|
||||
const combinedRecent = [...(ours.recentUpdates || []), ...(theirs.recentUpdates || [])];
|
||||
for (const entry of combinedRecent) {
|
||||
const sig = `${entry.prototypeId}|${entry.version}|${entry.date}|${entry.time}`;
|
||||
if (seen.has(sig)) continue;
|
||||
seen.add(sig);
|
||||
merged.recentUpdates.push(entry);
|
||||
}
|
||||
merged.recentUpdates = merged.recentUpdates.slice(0, 40);
|
||||
|
||||
writeJson(relativePath, merged);
|
||||
return {
|
||||
prototypeCount: Object.keys(merged.prototypes).length,
|
||||
recentCount: merged.recentUpdates.length,
|
||||
hasXll: Boolean(merged.prototypes['xll-miniapp']),
|
||||
h2AnalysisVersion: merged.prototypes['oneos-web-h2-station-analysis']?.version,
|
||||
};
|
||||
}
|
||||
|
||||
function mergeSidebar() {
|
||||
const relativePath = '.axhub/make/sidebar-tree.json';
|
||||
const ours = readJsonFromGit(2, relativePath);
|
||||
const theirs = readJsonFromGit(3, relativePath);
|
||||
|
||||
const merged = JSON.parse(JSON.stringify(theirs));
|
||||
merged.updatedAt = new Date().toISOString();
|
||||
|
||||
const oursKeys = collectItemKeys(ours.prototypes);
|
||||
const mergedKeys = collectItemKeys(merged.prototypes);
|
||||
const localOnlyKeys = [
|
||||
'prototypes/oneos-web-h2-station-weekly',
|
||||
'prototypes/oneos-web-h2-station-analysis',
|
||||
].filter((key) => oursKeys.has(key) && !mergedKeys.has(key));
|
||||
|
||||
const oneOsFolder = findFolder(merged.prototypes, 'OneOS');
|
||||
const h2Folder = oneOsFolder ? findFolder(oneOsFolder.children, '加氢站管理') : findFolder(merged.prototypes, '加氢站管理');
|
||||
|
||||
if (h2Folder) {
|
||||
for (const itemKey of localOnlyKeys) {
|
||||
const source =
|
||||
findItemByKey(ours.prototypes, itemKey) ||
|
||||
({
|
||||
'prototypes/oneos-web-h2-station-weekly': {
|
||||
id: 'item:prototypes:oneos-web-h2-station-weekly',
|
||||
kind: 'item',
|
||||
title: '站点周报统计',
|
||||
itemKey: 'prototypes/oneos-web-h2-station-weekly',
|
||||
},
|
||||
'prototypes/oneos-web-h2-station-analysis': {
|
||||
id: 'item-prototypes-oneos-web-h2-station-analysis',
|
||||
kind: 'item',
|
||||
title: '加氢站分析',
|
||||
itemKey: 'prototypes/oneos-web-h2-station-analysis',
|
||||
},
|
||||
}[itemKey]);
|
||||
ensureItem(h2Folder, source);
|
||||
}
|
||||
}
|
||||
|
||||
writeJson(relativePath, merged);
|
||||
return {
|
||||
addedKeys: localOnlyKeys,
|
||||
itemCount: collectItemKeys(merged.prototypes).size,
|
||||
};
|
||||
}
|
||||
|
||||
const registry = mergeRegistry();
|
||||
const sidebar = mergeSidebar();
|
||||
console.log('[merge] prototype-registry.json', registry);
|
||||
console.log('[merge] sidebar-tree.json', sidebar);
|
||||
@@ -7,6 +7,7 @@
|
||||
* node scripts/publish-all-to-s3.mjs
|
||||
* node scripts/publish-all-to-s3.mjs --dry-run
|
||||
* node scripts/publish-all-to-s3.mjs --group prototypes
|
||||
* node scripts/publish-all-to-s3.mjs --ids vehicle-management,oneos-prototype-nav
|
||||
*/
|
||||
import fs from 'node:fs';
|
||||
import os from 'node:os';
|
||||
@@ -26,6 +27,7 @@ function parseArgs(argv) {
|
||||
adminOrigin: '',
|
||||
dryRun: false,
|
||||
group: 'all',
|
||||
ids: [],
|
||||
includeSource: true,
|
||||
};
|
||||
for (let i = 0; i < argv.length; i += 1) {
|
||||
@@ -33,6 +35,9 @@ function parseArgs(argv) {
|
||||
if (arg === '--project-id' && argv[i + 1]) args.projectId = argv[++i].trim();
|
||||
else if (arg === '--admin-origin' && argv[i + 1]) args.adminOrigin = argv[++i].trim();
|
||||
else if (arg === '--group' && argv[i + 1]) args.group = argv[++i].trim();
|
||||
else if (arg === '--ids' && argv[i + 1]) {
|
||||
args.ids = argv[++i].split(',').map((id) => id.trim()).filter(Boolean);
|
||||
}
|
||||
else if (arg === '--dry-run') args.dryRun = true;
|
||||
else if (arg === '--no-source') args.includeSource = false;
|
||||
}
|
||||
@@ -69,8 +74,9 @@ function resolveObjectPrefix(entry, pathAliases = {}) {
|
||||
if (alias) return String(alias).replace(/^\/+|\/+$/gu, '');
|
||||
|
||||
const normalized = entry.path.replace(/^\/+|\/+$/gu, '');
|
||||
const prototypeMatch = normalized.match(/^(?:src\/)?prototypes\/(.+)$/u);
|
||||
if (prototypeMatch?.[1]) return `prototypes/${prototypeMatch[1]}`;
|
||||
const prototypeMatch = normalized.match(/^(?:src\/)?prototypes\/([^/]+)$/u);
|
||||
// 与 Make 客户端「发布到对象存储」一致:/{prototype-id}/index.html(不加 prototypes/ 前缀)
|
||||
if (prototypeMatch?.[1]) return prototypeMatch[1];
|
||||
const themeMatch = normalized.match(/^(?:src\/)?themes\/(.+)$/u);
|
||||
if (themeMatch?.[1]) return `themes/${themeMatch[1]}`;
|
||||
return entry.id;
|
||||
@@ -227,7 +233,15 @@ async function main() {
|
||||
const adminOrigin = resolveAdminOrigin(args.adminOrigin);
|
||||
const s3Config = readS3Config();
|
||||
const includeSource = args.includeSource && s3Config.includeSource;
|
||||
const entries = await collectPublishEntries(adminOrigin, args.projectId, args.group, s3Config.pathAliases);
|
||||
let entries = await collectPublishEntries(adminOrigin, args.projectId, args.group, s3Config.pathAliases);
|
||||
if (args.ids.length > 0) {
|
||||
const idSet = new Set(args.ids);
|
||||
entries = entries.filter((entry) => idSet.has(entry.id));
|
||||
const missing = args.ids.filter((id) => !entries.some((entry) => entry.id === id));
|
||||
if (missing.length > 0) {
|
||||
throw new Error(`未找到原型: ${missing.join(', ')}`);
|
||||
}
|
||||
}
|
||||
|
||||
console.log(`Admin: ${adminOrigin}`);
|
||||
console.log(`Bucket: ${s3Config.bucket}`);
|
||||
|
||||
@@ -23,9 +23,10 @@ const XLL_MODULES = [
|
||||
/** 从 ONE-OS Web 合包拆出、由项目内手写 index 维护的独立原型 */
|
||||
const STANDALONE_MODULES = [
|
||||
{ slug: 'business-dept-ledger', title: '业务部台账', skipCodegen: true },
|
||||
{ slug: 'customer-payment-collection', title: '客户回款情况', skipCodegen: true },
|
||||
{ slug: 'vehicle-maintenance-ledger', title: '车辆维修明细', skipCodegen: true },
|
||||
{ slug: 'oneos-web-h2-station-site', title: '站点信息', skipCodegen: true },
|
||||
{ slug: 'oneos-web-h2-station-weekly', title: '站点周报统计', skipCodegen: true },
|
||||
{ slug: 'oneos-web-h2-station-stats', title: '加氢站数量统计', skipCodegen: true },
|
||||
{ slug: 'vehicle-return-settlement', title: '还车应结款', skipCodegen: true },
|
||||
{ slug: 'vehicle-pickup-receivable', title: '提车应收款', skipCodegen: true },
|
||||
{ slug: 'oneos-web-approval', title: '审批中心', skipCodegen: true },
|
||||
@@ -61,7 +62,7 @@ const MODULES = [
|
||||
{ slug: 'oneos-web-procurement', title: '采购管理', dir: '采购管理' },
|
||||
{ slug: 'oneos-web-lease-contract', title: '车辆租赁合同', dir: '车辆租赁合同' },
|
||||
{ slug: 'oneos-web-h2-station', title: '加氢站管理', dir: '加氢站管理', excludeDirs: ['export-tools', 'AI-加氢站站点信息-complete'], excludeFiles: ['站点信息.jsx'] },
|
||||
{ slug: 'oneos-web-data-analysis', title: '数据分析', dir: '数据分析', excludeFiles: ['业务部台账.jsx'] },
|
||||
{ slug: 'oneos-web-data-analysis', title: '数据分析', dir: '数据分析', excludeFiles: ['业务部台账.jsx', '客户回款情况.jsx'] },
|
||||
{ slug: 'oneos-web-ledger-data', title: '台账数据', dir: '台账数据', excludeDirs: ['docs'], excludeFiles: ['车辆维修明细.jsx'] },
|
||||
{ slug: 'oneos-web-business', title: '业务管理', dir: '业务管理', excludeDirs: ['文档', 'export-tools', 'AI-保险采购-complete'] },
|
||||
{ slug: 'oneos-web-ops', title: '运维管理', dir: '运维管理' },
|
||||
|
||||
@@ -24,6 +24,8 @@ const ROUTE_TO_PAGE = {
|
||||
'third-return': 'third-return',
|
||||
replace: 'replace',
|
||||
'audit-return': 'audit-return',
|
||||
training: 'driver-training',
|
||||
'driver-training': 'driver-training',
|
||||
};
|
||||
|
||||
function parseArgs(argv) {
|
||||
|
||||
Reference in New Issue
Block a user