源码快照 @ 2026-09-01 (source-only, 剔除非源码素材)
Dependency audit / audit (push) Canceled after 0s
CI / plan (push) Canceled after 0s
Gitoxide helper admission / macos-latest (push) Canceled after 0s
Gitoxide helper admission / ubuntu-latest (push) Canceled after 0s
Gitoxide helper admission / windows-latest (push) Canceled after 0s
Runtime Host peer admission / quality (push) Canceled after 0s
Windows recovery / windows_recovery (push) Canceled after 0s
CI / heavy (push) Canceled after 0s
CI / test (push) Canceled after 0s
Dependency audit / audit (push) Canceled after 0s
CI / plan (push) Canceled after 0s
Gitoxide helper admission / macos-latest (push) Canceled after 0s
Gitoxide helper admission / ubuntu-latest (push) Canceled after 0s
Gitoxide helper admission / windows-latest (push) Canceled after 0s
Runtime Host peer admission / quality (push) Canceled after 0s
Windows recovery / windows_recovery (push) Canceled after 0s
CI / heavy (push) Canceled after 0s
CI / test (push) Canceled after 0s
This commit is contained in:
@@ -0,0 +1,203 @@
|
||||
#!/usr/bin/env node
|
||||
/*
|
||||
* Licensed to the Apache Software Foundation (ASF) under one
|
||||
* or more contributor license agreements. See the NOTICE file
|
||||
* distributed with this work for additional information
|
||||
* regarding copyright ownership. The ASF licenses this file
|
||||
* to you under the Apache License, Version 2.0 (the
|
||||
* "License"); you may not use this file except in compliance
|
||||
* with the License. You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing,
|
||||
* software distributed under the License is distributed on an
|
||||
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
* KIND, either express or implied. See the License for the
|
||||
* specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*/
|
||||
|
||||
/**
|
||||
* Dev launcher with PARALLEL + INCREMENTAL builds.
|
||||
*
|
||||
* Uses `tsc --build` for library packages so the compiler skips
|
||||
* unchanged sub-projects via .tsbuildinfo (incremental).
|
||||
*
|
||||
* Dependency graph (→ compiles after):
|
||||
* core ─┬→ storage
|
||||
* ├→ runtime
|
||||
* └→ ui
|
||||
*
|
||||
* libs (tsc --build tsconfig.lib.json) ─── covers core+storage+runtime+ui
|
||||
* ├─→ preload (esbuild)
|
||||
* └─→ filesystem worker (esbuild)
|
||||
* cursor overlay (esbuild) ─── independent
|
||||
* main (esbuild) ─── fast app bundle for Electron
|
||||
* Vite dev server + Electron ─── fork
|
||||
*/
|
||||
import { spawn } from 'node:child_process';
|
||||
import { join, resolve } from 'node:path';
|
||||
import { fileURLToPath } from 'node:url';
|
||||
import { createServer } from 'vite';
|
||||
import { build as esbuildBuild } from 'esbuild';
|
||||
import { buildCursorOverlay } from '../../../scripts/build-cursor-overlay.mjs';
|
||||
import {
|
||||
createDevelopmentLaunchSession,
|
||||
handleDevelopmentLaunchOutcome,
|
||||
waitForDevelopmentLaunchVerdict,
|
||||
} from './dev-app-runtime.mjs';
|
||||
|
||||
const DESKTOP_DIR = resolve(fileURLToPath(new URL('..', import.meta.url)));
|
||||
const REPO_ROOT = resolve(DESKTOP_DIR, '..', '..');
|
||||
const TSC_CLI = join(REPO_ROOT, 'node_modules', 'typescript', 'bin', 'tsc');
|
||||
const MODEL_METADATA_SYNC = join(REPO_ROOT, 'scripts', 'sync-model-metadata.mjs');
|
||||
const RUNTIME_WORKER_BUILD = join(REPO_ROOT, 'packages', 'runtime', 'scripts', 'build-filesystem-worker.mjs');
|
||||
|
||||
// ── helpers ──────────────────────────────────────────────────────────────────
|
||||
|
||||
function log(label, msg) {
|
||||
const ts = new Date().toLocaleTimeString('zh-CN', { hour12: false });
|
||||
console.log(`[${ts}][${label}] ${msg}`);
|
||||
}
|
||||
|
||||
function runNodeTool(dir, script, args) {
|
||||
return new Promise((resolve_, reject_) => {
|
||||
const child = spawn(process.execPath, [script, ...args], {
|
||||
cwd: dir,
|
||||
stdio: ['ignore', 'inherit', 'inherit'],
|
||||
});
|
||||
child.on('exit', (code) => {
|
||||
if (code === 0) resolve_();
|
||||
else reject_(new Error(`"${script} ${args.join(' ')}" exited with code ${code}`));
|
||||
});
|
||||
child.on('error', reject_);
|
||||
});
|
||||
}
|
||||
|
||||
// ── build phases ─────────────────────────────────────────────────────────────
|
||||
|
||||
const TIMER_START = Date.now();
|
||||
|
||||
// A clean or ignore-scripts install has no generated model modules yet, and
|
||||
// `tsc --build` bypasses workspace prebuild hooks. Generate from the committed
|
||||
// snapshot before starting the incremental library graph.
|
||||
log('build', 'model metadata — generating from committed snapshot');
|
||||
await runNodeTool(REPO_ROOT, MODEL_METADATA_SYNC, []);
|
||||
|
||||
// Phase 1: all library packages via `tsc --build` (single process, shared
|
||||
// .tsbuildinfo, sub-project incremental detection). The preload bundle imports
|
||||
// workspace package dist files, so it starts only after that build is ready.
|
||||
log('build', 'libraries — starting (tsc --build)');
|
||||
const librariesBuild = runNodeTool(REPO_ROOT, TSC_CLI, ['--build', 'tsconfig.lib.json']).then(
|
||||
() => log('build', 'libraries (all) — done'),
|
||||
(e) => {
|
||||
log('build', `libraries — FAILED: ${e.message}`);
|
||||
throw e;
|
||||
},
|
||||
);
|
||||
await Promise.all([
|
||||
librariesBuild,
|
||||
librariesBuild.then(() => runNodeTool(REPO_ROOT, RUNTIME_WORKER_BUILD, [])).then(
|
||||
() => log('build', 'filesystem worker bundle — done'),
|
||||
(e) => { log('build', `filesystem worker bundle — FAILED: ${e.message}`); throw e; },
|
||||
),
|
||||
// esbuild via its JS API — NOT `node node_modules/esbuild/bin/esbuild`:
|
||||
// esbuild's postinstall swaps that file for a platform-native binary,
|
||||
// and executing a Mach-O file with node throws SyntaxError (broke
|
||||
// `npm run dev` on any machine where postinstall ran).
|
||||
librariesBuild.then(() => esbuildBuild({
|
||||
absWorkingDir: DESKTOP_DIR,
|
||||
entryPoints: ['src/preload/preload.ts'],
|
||||
bundle: true,
|
||||
platform: 'node',
|
||||
format: 'cjs',
|
||||
outfile: 'dist/preload/preload.cjs',
|
||||
external: ['electron'],
|
||||
logLevel: 'warning',
|
||||
})).then(
|
||||
() => log('build', 'preload — done'),
|
||||
(e) => { log('build', `preload — FAILED: ${e.message}`); throw e; },
|
||||
),
|
||||
buildCursorOverlay({ logLevel: 'warning' }).then(
|
||||
() => log('build', 'cursor overlay — done'),
|
||||
(e) => { log('build', `cursor overlay — FAILED: ${e.message}`); throw e; },
|
||||
),
|
||||
]);
|
||||
|
||||
// Phase 2: main — esbuild bundle for dev startup. The full
|
||||
// tsconfig.main.json still compiles tests for `npm test` and typechecks
|
||||
// main-process code in verification commands.
|
||||
log('build', 'main — starting');
|
||||
await esbuildBuild({
|
||||
absWorkingDir: DESKTOP_DIR,
|
||||
entryPoints: ['src/main/main.ts'],
|
||||
bundle: true,
|
||||
platform: 'node',
|
||||
format: 'esm',
|
||||
packages: 'external',
|
||||
outfile: 'dist/main/main.js',
|
||||
external: ['electron'],
|
||||
logLevel: 'warning',
|
||||
});
|
||||
log('build', 'main — done');
|
||||
|
||||
const BUILD_MS = Date.now() - TIMER_START;
|
||||
log('build', `all builds finished in ${(BUILD_MS / 1000).toFixed(1)}s`);
|
||||
|
||||
// ── Vite dev server + Electron ───────────────────────────────────────────────
|
||||
|
||||
process.chdir(DESKTOP_DIR);
|
||||
log('vite', 'starting dev server...');
|
||||
const server = await createServer();
|
||||
await server.listen();
|
||||
server.printUrls();
|
||||
|
||||
const devUrl = server.resolvedUrls?.local?.[0]?.replace(/\/$/, '');
|
||||
if (!devUrl) {
|
||||
console.error('[dev] vite did not report a local URL; aborting.');
|
||||
await server.close();
|
||||
process.exit(1);
|
||||
}
|
||||
|
||||
log('electron', `launching against ${devUrl} (renderer HMR live)`);
|
||||
|
||||
// Created before launch so signals during codesign/preparation are durable.
|
||||
// Closing the terminal still stops only launcher resources, not the
|
||||
// independently owned TCC app.
|
||||
const launchSession = createDevelopmentLaunchSession({
|
||||
close: () => server.close(),
|
||||
});
|
||||
let app = null;
|
||||
try {
|
||||
app = await launchSession.start({ argv: process.argv.slice(2), viteUrl: devUrl });
|
||||
} catch (error) {
|
||||
console.error(`[dev] failed to start Electron: ${String(error)}`);
|
||||
await launchSession.stop(1);
|
||||
}
|
||||
|
||||
if (app) {
|
||||
if (app.isMacosBundle) log('electron', 'launched Maka Dev.app through LaunchServices');
|
||||
|
||||
app.child.on('error', (err) => {
|
||||
console.error(`[dev] failed to start Electron: ${err.message}`);
|
||||
launchSession.stop(1);
|
||||
});
|
||||
if (app.isMacosBundle) {
|
||||
// `open` exits 0 at the handoff, so only a failure to hand off is news here.
|
||||
// The app's later lifetime is deliberately independent from this dev server.
|
||||
app.child.on('exit', (code) => {
|
||||
if (code) launchSession.stop(code);
|
||||
});
|
||||
// All branching lives in handleDevelopmentLaunchOutcome; this line is the only
|
||||
// un-automated surface here (launcher scripts are non-exported, darwin-only).
|
||||
waitForDevelopmentLaunchVerdict({ stopped: launchSession.isStopping, resultFile: app.resultFile }).then((outcome) =>
|
||||
handleDevelopmentLaunchOutcome(outcome, {
|
||||
log: (m) => console.error('[dev]', m),
|
||||
exit: (code) => launchSession.stop(code),
|
||||
}),
|
||||
);
|
||||
} else {
|
||||
app.child.on('exit', (code) => launchSession.stop(code ?? 0));
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user