yishentu--claudian
196 行
5.4 KiB
JavaScript
196 行
5.4 KiB
JavaScript
import esbuild from 'esbuild';
|
|
import { builtinModules } from 'node:module';
|
|
import path from 'path';
|
|
import process from 'process';
|
|
import {
|
|
copyFileSync,
|
|
existsSync,
|
|
mkdirSync,
|
|
promises as fsPromises,
|
|
readFileSync,
|
|
rmSync,
|
|
} from 'fs';
|
|
import rendererSafeUnrefHelpers from './scripts/rendererSafeUnref.js';
|
|
|
|
const {
|
|
findUnsafeTimerUnrefSites,
|
|
patchRendererUnsafeUnrefSites,
|
|
} = rendererSafeUnrefHelpers;
|
|
|
|
// Load .env.local if it exists
|
|
if (existsSync('.env.local')) {
|
|
const envContent = readFileSync('.env.local', 'utf-8');
|
|
for (const line of envContent.split('\n')) {
|
|
const match = line.match(/^([^=]+)=["']?(.+?)["']?$/);
|
|
if (match && !process.env[match[1]]) {
|
|
process.env[match[1]] = match[2];
|
|
}
|
|
}
|
|
}
|
|
|
|
const prod = process.argv[2] === 'production';
|
|
|
|
function escapeRegExp(value) {
|
|
return value.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
|
|
}
|
|
|
|
function getNamedImportAliases(contents, exportName, moduleNames) {
|
|
const aliases = new Set([exportName]);
|
|
const importPattern = /import\s*\{([^}]+)\}\s*from\s*["']([^"']+)["']/g;
|
|
let match;
|
|
|
|
while ((match = importPattern.exec(contents)) !== null) {
|
|
const [, specifiers, moduleName] = match;
|
|
if (!moduleNames.includes(moduleName)) continue;
|
|
|
|
for (const specifier of specifiers.split(',')) {
|
|
const parts = specifier.trim().split(/\s+as\s+/);
|
|
if (parts[0] === exportName) {
|
|
aliases.add(parts[1] ?? exportName);
|
|
}
|
|
}
|
|
}
|
|
|
|
return [...aliases];
|
|
}
|
|
|
|
function patchSdkImportMetaUrl(contents) {
|
|
let patched = contents.replace(
|
|
'createRequire(import.meta.url)',
|
|
'createRequire(__filename)',
|
|
);
|
|
|
|
for (const alias of getNamedImportAliases(patched, 'createRequire', ['module', 'node:module'])) {
|
|
patched = patched.replace(
|
|
new RegExp(`\\b${escapeRegExp(alias)}\\(import\\.meta\\.url\\)`, 'g'),
|
|
`${alias}(__filename)`,
|
|
);
|
|
}
|
|
|
|
for (const alias of getNamedImportAliases(patched, 'fileURLToPath', ['url', 'node:url'])) {
|
|
patched = patched.replace(
|
|
new RegExp(`\\b${escapeRegExp(alias)}\\(import\\.meta\\.url\\)`, 'g'),
|
|
'__filename',
|
|
);
|
|
}
|
|
|
|
return patched;
|
|
}
|
|
|
|
const patchSdkImportMeta = {
|
|
name: 'patch-sdk-import-meta',
|
|
setup(build) {
|
|
build.onLoad(
|
|
{
|
|
filter: /[\\/]node_modules[\\/](?:@openai[\\/]codex-sdk[\\/]dist[\\/]index\.js|@anthropic-ai[\\/]claude-agent-sdk[\\/]sdk\.mjs)$/,
|
|
},
|
|
async (args) => {
|
|
const contents = await fsPromises.readFile(args.path, 'utf8');
|
|
return {
|
|
contents: patchSdkImportMetaUrl(contents),
|
|
loader: 'js',
|
|
};
|
|
},
|
|
);
|
|
},
|
|
};
|
|
|
|
const patchRendererUnsafeUnref = {
|
|
name: 'patch-renderer-unsafe-unref',
|
|
setup(build) {
|
|
build.onEnd(async (result) => {
|
|
if (result.errors.length > 0 || !existsSync('main.js')) return;
|
|
|
|
const bundlePath = path.join(process.cwd(), 'main.js');
|
|
const originalContents = await fsPromises.readFile(bundlePath, 'utf8');
|
|
const patchedBundle = patchRendererUnsafeUnrefSites(originalContents);
|
|
|
|
if (patchedBundle.contents !== originalContents) {
|
|
await fsPromises.writeFile(bundlePath, patchedBundle.contents, 'utf8');
|
|
}
|
|
|
|
const unsafeMatches = findUnsafeTimerUnrefSites(patchedBundle.contents);
|
|
if (unsafeMatches.length > 0) {
|
|
const details = unsafeMatches
|
|
.slice(0, 5)
|
|
.map((match) => `line ${match.line}: ${match.snippet}`)
|
|
.join('\n');
|
|
|
|
throw new Error(
|
|
`Renderer-unsafe timer .unref() calls remain in main.js:\n${details}`,
|
|
);
|
|
}
|
|
});
|
|
},
|
|
};
|
|
|
|
// Obsidian plugin folder path (set via OBSIDIAN_VAULT env var or .env.local)
|
|
const OBSIDIAN_VAULT = process.env.OBSIDIAN_VAULT;
|
|
const OBSIDIAN_PLUGIN_PATH = OBSIDIAN_VAULT && existsSync(OBSIDIAN_VAULT)
|
|
? path.join(OBSIDIAN_VAULT, '.obsidian', 'plugins', 'claudian')
|
|
: null;
|
|
|
|
// Plugin to copy built files to Obsidian plugin folder
|
|
const copyToObsidian = {
|
|
name: 'copy-to-obsidian',
|
|
setup(build) {
|
|
build.onEnd((result) => {
|
|
if (result.errors.length > 0) return;
|
|
rmSync(path.join(process.cwd(), '.codex-vendor'), { recursive: true, force: true });
|
|
|
|
if (!OBSIDIAN_PLUGIN_PATH) return;
|
|
|
|
if (!existsSync(OBSIDIAN_PLUGIN_PATH)) {
|
|
mkdirSync(OBSIDIAN_PLUGIN_PATH, { recursive: true });
|
|
}
|
|
|
|
const files = ['main.js', 'manifest.json', 'styles.css'];
|
|
for (const file of files) {
|
|
if (existsSync(file)) {
|
|
copyFileSync(file, path.join(OBSIDIAN_PLUGIN_PATH, file));
|
|
console.log(`Copied ${file} to Obsidian plugin folder`);
|
|
}
|
|
}
|
|
|
|
const pluginVendorRoot = path.join(OBSIDIAN_PLUGIN_PATH, '.codex-vendor');
|
|
rmSync(pluginVendorRoot, { recursive: true, force: true });
|
|
});
|
|
}
|
|
};
|
|
|
|
const context = await esbuild.context({
|
|
entryPoints: ['src/main.ts'],
|
|
bundle: true,
|
|
plugins: [patchSdkImportMeta, patchRendererUnsafeUnref, copyToObsidian],
|
|
external: [
|
|
'obsidian',
|
|
'electron',
|
|
'@codemirror/autocomplete',
|
|
'@codemirror/collab',
|
|
'@codemirror/commands',
|
|
'@codemirror/language',
|
|
'@codemirror/lint',
|
|
'@codemirror/search',
|
|
'@codemirror/state',
|
|
'@codemirror/view',
|
|
'@lezer/common',
|
|
'@lezer/highlight',
|
|
'@lezer/lr',
|
|
...builtinModules,
|
|
...builtinModules.map(m => `node:${m}`),
|
|
],
|
|
format: 'cjs',
|
|
target: 'es2018',
|
|
logLevel: 'info',
|
|
sourcemap: prod ? false : 'inline',
|
|
treeShaking: true,
|
|
outfile: 'main.js',
|
|
});
|
|
|
|
if (prod) {
|
|
await context.rebuild();
|
|
process.exit(0);
|
|
} else {
|
|
await context.watch();
|
|
}
|