From 3a09c1bb1a65ccabe5d4a86bd48958365b929afe Mon Sep 17 00:00:00 2001 From: Alex Newman Date: Thu, 26 Feb 2026 23:56:34 -0500 Subject: [PATCH] feat: add NPX CLI and OpenClaw build pipeline, optimize npm package size Adds esbuild steps for npx-cli (57KB, Node.js ESM) and openclaw plugin (12KB). Creates .npmignore to exclude node_modules and Bun binary from npm package, reducing pack size from 146MB to 2MB. Co-Authored-By: Claude Opus 4.6 --- .npmignore | 49 +++++++++++++++++++++++++++++ openclaw/.npmignore | 1 + package.json | 12 ++++++- scripts/build-hooks.js | 64 +++++++++++++++++++++++++++++++++++++- src/npx-cli/utils/paths.ts | 10 +++--- 5 files changed, 129 insertions(+), 7 deletions(-) create mode 100644 .npmignore create mode 100644 openclaw/.npmignore diff --git a/.npmignore b/.npmignore new file mode 100644 index 00000000..011858aa --- /dev/null +++ b/.npmignore @@ -0,0 +1,49 @@ +# Source code (dist/ and plugin/ are the shipped artifacts) +src/ +scripts/ +tests/ +docs/ +datasets/ +private/ +installer/ +antipattern-czar/ + +# Heavy binaries installed at runtime via smart-install.js +plugin/node_modules/ +plugin/scripts/claude-mem +plugin/bun.lock +plugin/data/ +plugin/data.backup/ + +# Development files +*.ts +!*.d.ts +tsconfig*.json +.eslintrc* +.prettierrc* +.editorconfig +jest.config* +vitest.config* + +# Git and CI +.git/ +.github/ +.gitignore +.claude/ +.cursor/ +.mcp.json +.plan/ + +# OS files +.DS_Store +*.log +*.tmp +*.temp +Thumbs.db + +# Misc +Auto Run Docs/ +~*/ +http*/ +https*/ +.idea/ diff --git a/openclaw/.npmignore b/openclaw/.npmignore new file mode 100644 index 00000000..c2658d7d --- /dev/null +++ b/openclaw/.npmignore @@ -0,0 +1 @@ +node_modules/ diff --git a/package.json b/package.json index 6933ab04..77046cad 100644 --- a/package.json +++ b/package.json @@ -42,7 +42,17 @@ }, "files": [ "dist", - "plugin" + "plugin/.claude-plugin", + "plugin/CLAUDE.md", + "plugin/package.json", + "plugin/hooks", + "plugin/modes", + "plugin/scripts/*.js", + "plugin/scripts/*.cjs", + "plugin/scripts/CLAUDE.md", + "plugin/skills", + "plugin/ui", + "openclaw" ], "engines": { "node": ">=18.0.0", diff --git a/scripts/build-hooks.js b/scripts/build-hooks.js index 519d196e..4fc704b7 100644 --- a/scripts/build-hooks.js +++ b/scripts/build-hooks.js @@ -182,6 +182,62 @@ async function buildHooks() { const contextGenStats = fs.statSync(`${hooksDir}/${CONTEXT_GENERATOR.name}.cjs`); console.log(`āœ“ context-generator built (${(contextGenStats.size / 1024).toFixed(2)} KB)`); + // Build NPX CLI (pure Node.js — no Bun dependency) + console.log(`\nšŸ”§ Building NPX CLI...`); + const npxCliOutDir = 'dist/npx-cli'; + if (!fs.existsSync(npxCliOutDir)) { + fs.mkdirSync(npxCliOutDir, { recursive: true }); + } + await build({ + entryPoints: ['src/npx-cli/index.ts'], + bundle: true, + platform: 'node', + target: 'node18', + format: 'esm', + outfile: `${npxCliOutDir}/index.js`, + minify: true, + logLevel: 'error', + external: [ + 'fs', 'fs/promises', 'path', 'os', 'child_process', 'url', + 'crypto', 'http', 'https', 'net', 'stream', 'util', 'events', + 'buffer', 'querystring', 'readline', 'tty', 'assert', + ], + define: { + '__DEFAULT_PACKAGE_VERSION__': `"${version}"` + }, + }); + + // Make NPX CLI executable + fs.chmodSync(`${npxCliOutDir}/index.js`, 0o755); + const npxCliStats = fs.statSync(`${npxCliOutDir}/index.js`); + console.log(`āœ“ npx-cli built (${(npxCliStats.size / 1024).toFixed(2)} KB)`); + + // Build OpenClaw plugin (self-contained, only Node builtins external) + if (fs.existsSync('openclaw/src/index.ts')) { + console.log(`\nšŸ”§ Building OpenClaw plugin...`); + const openclawOutDir = 'openclaw/dist'; + if (!fs.existsSync(openclawOutDir)) { + fs.mkdirSync(openclawOutDir, { recursive: true }); + } + await build({ + entryPoints: ['openclaw/src/index.ts'], + bundle: true, + platform: 'node', + target: 'node18', + format: 'esm', + outfile: `${openclawOutDir}/index.js`, + minify: true, + logLevel: 'error', + external: [ + 'fs', 'fs/promises', 'path', 'os', 'child_process', 'url', + 'crypto', 'http', 'https', 'net', 'stream', 'util', 'events', + ], + }); + + const openclawStats = fs.statSync(`${openclawOutDir}/index.js`); + console.log(`āœ“ openclaw plugin built (${(openclawStats.size / 1024).toFixed(2)} KB)`); + } + // Verify critical distribution files exist (skills are source files, not build outputs) console.log('\nšŸ“‹ Verifying distribution files...'); const requiredDistributionFiles = [ @@ -197,11 +253,17 @@ async function buildHooks() { } console.log('āœ“ All required distribution files present'); - console.log('\nāœ… Worker service, MCP server, and context generator built successfully!'); + console.log('\nāœ… All build targets compiled successfully!'); console.log(` Output: ${hooksDir}/`); console.log(` - Worker: worker-service.cjs`); console.log(` - MCP Server: mcp-server.cjs`); console.log(` - Context Generator: context-generator.cjs`); + console.log(` Output: ${npxCliOutDir}/`); + console.log(` - NPX CLI: index.js`); + if (fs.existsSync('openclaw/dist/index.js')) { + console.log(` Output: openclaw/dist/`); + console.log(` - OpenClaw Plugin: index.js`); + } } catch (error) { console.error('\nāŒ Build failed:', error.message); diff --git a/src/npx-cli/utils/paths.ts b/src/npx-cli/utils/paths.ts index 32cc1a97..dbefd2c1 100644 --- a/src/npx-cli/utils/paths.ts +++ b/src/npx-cli/utils/paths.ts @@ -66,14 +66,14 @@ export function claudeMemDataDirectory(): string { /** * Resolve the root of the installed npm package. * - * The CLI entry point lives at `/dist/cli/index.js`. Walking up - * from `import.meta.url` (or `__dirname` equivalent) we reach the - * package root where `plugin/` can be found. + * After bundling, the CLI entry point lives at `/dist/npx-cli/index.js`. + * Walking up 2 levels from `import.meta.url` reaches the package root + * where `plugin/` and `package.json` can be found. */ export function npmPackageRootDirectory(): string { const currentFilePath = fileURLToPath(import.meta.url); - // /dist/npx-cli/utils/paths.js -> up 3 levels -> - return join(dirname(currentFilePath), '..', '..', '..'); + // /dist/npx-cli/index.js -> up 2 levels -> + return join(dirname(currentFilePath), '..', '..'); } /**