项目文件夹

文件
wehub-resource-sync 2771cff92b
CI / lint (push) Failing after 1s
CI / typecheck (push) Failing after 2s
CI / test (push) Failing after 1s
CI / build (push) Has been skipped
chore: import upstream snapshot with attribution
2026-07-13 12:22:56 +08:00

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();
}