thebird 1.2.79 → 1.2.81
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.github/workflows/publish.yml +9 -1
- package/CHANGELOG.md +217 -0
- package/CLAUDE.md +16 -0
- package/docs/agent-chat.js +7 -4
- package/docs/app.js +14 -11
- package/docs/defaults.json +1 -1
- package/docs/index.html +23 -6
- package/docs/kilo-fs-mirror.js +15 -0
- package/docs/kilo-http-stream.js +47 -0
- package/docs/node-builtins.js +24 -0
- package/docs/preview/index.html +32 -0
- package/docs/preview-sw-client.js +37 -6
- package/docs/preview-sw.js +55 -51
- package/docs/shell-awk.js +113 -0
- package/docs/shell-builtins-extra.js +121 -0
- package/docs/shell-builtins-text.js +109 -0
- package/docs/shell-builtins-util.js +112 -0
- package/docs/shell-builtins.js +183 -0
- package/docs/shell-bun.js +45 -0
- package/docs/shell-control.js +132 -0
- package/docs/shell-deno.js +54 -0
- package/docs/shell-exec.js +85 -0
- package/docs/shell-expand.js +164 -0
- package/docs/shell-fd.js +86 -0
- package/docs/shell-jobs.js +86 -0
- package/docs/shell-node-advanced.js +86 -0
- package/docs/shell-node-brotli.js +22 -0
- package/docs/shell-node-busnet.js +90 -0
- package/docs/shell-node-cipher.js +61 -0
- package/docs/shell-node-cluster.js +33 -0
- package/docs/shell-node-coreutils.js +36 -0
- package/docs/shell-node-crypto.js +137 -0
- package/docs/shell-node-dns.js +41 -0
- package/docs/shell-node-extras.js +148 -0
- package/docs/shell-node-firefox.js +95 -0
- package/docs/shell-node-git.js +60 -0
- package/docs/shell-node-inspector.js +39 -0
- package/docs/shell-node-io.js +131 -0
- package/docs/shell-node-ipc.js +15 -0
- package/docs/shell-node-keyobject.js +60 -0
- package/docs/shell-node-modules.js +157 -0
- package/docs/shell-node-native.js +31 -0
- package/docs/shell-node-net.js +71 -0
- package/docs/shell-node-observe.js +80 -0
- package/docs/shell-node-opfs.js +54 -0
- package/docs/shell-node-procfs.js +42 -0
- package/docs/shell-node-profiler.js +50 -0
- package/docs/shell-node-registry.js +24 -0
- package/docs/shell-node-resolve.js +147 -0
- package/docs/shell-node-runtime.js +83 -0
- package/docs/shell-node-srcmap.js +52 -0
- package/docs/shell-node-stdlib.js +103 -0
- package/docs/shell-node-streams.js +66 -0
- package/docs/shell-node-tar.js +47 -0
- package/docs/shell-node-testrunner.js +35 -0
- package/docs/shell-node-util-extras.js +66 -0
- package/docs/shell-node.js +175 -169
- package/docs/shell-npm.js +173 -0
- package/docs/shell-parser.js +122 -0
- package/docs/shell-pm-layout.js +62 -0
- package/docs/shell-pm.js +39 -0
- package/docs/shell-posix.js +70 -0
- package/docs/shell-procsub.js +65 -0
- package/docs/shell-readline.js +59 -4
- package/docs/shell-runtime.js +37 -0
- package/docs/shell-sed.js +83 -0
- package/docs/shell-signals.js +54 -0
- package/docs/shell-sw-jobs.js +76 -0
- package/docs/shell-ts.js +30 -0
- package/docs/shell.js +161 -152
- package/docs/terminal.js +9 -11
- package/docs/todo.html +211 -0
- package/package.json +1 -1
- package/server.js +43 -4
- package/start-kilo.js +45 -0
- package/test.js +199 -0
- package/.codeinsight +0 -73
- package/docs/acp-stream.js +0 -102
- package/docs/coi-serviceworker.js +0 -2
package/docs/shell-node.js
CHANGED
|
@@ -1,193 +1,199 @@
|
|
|
1
1
|
import { createPath, createFs, createEvents, createUrl, createQuerystring, createBuffer } from './node-builtins.js';
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
}
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
}
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
if (typeof args[0] === 'function') middlewares.push(args[0]);
|
|
32
|
-
else routes.USE.push({ path: args[0], fn: args[1] });
|
|
33
|
-
};
|
|
34
|
-
app.listen = (port, cb) => {
|
|
35
|
-
window.__debug.shell.httpHandlers[port] = { routes, middlewares };
|
|
36
|
-
navigator.serviceWorker?.controller?.postMessage({ type: 'REGISTER_ROUTES', port, routes: serializeRoutes(routes) });
|
|
37
|
-
term.write('Express listening on :' + port + '\r\n');
|
|
38
|
-
cb?.();
|
|
39
|
-
};
|
|
40
|
-
app.json = () => (req, res, next) => {
|
|
41
|
-
if (typeof req.body === 'string') try { req.body = JSON.parse(req.body); } catch {}
|
|
42
|
-
next?.();
|
|
43
|
-
};
|
|
44
|
-
app.static = dir => (req, res) => {
|
|
45
|
-
const fp = dir.replace(/\/$/, '') + req.path;
|
|
46
|
-
try { res.send(fsmod.readFileSync(fp)); } catch { res.status(404).send('Not Found'); }
|
|
47
|
-
};
|
|
48
|
-
return app;
|
|
49
|
-
};
|
|
50
|
-
}
|
|
51
|
-
|
|
52
|
-
function createSqlite() {
|
|
53
|
-
return class Database {
|
|
54
|
-
constructor(name) {
|
|
55
|
-
this._name = name;
|
|
56
|
-
if (!window.__sqlJs) throw new Error('sql.js not loaded');
|
|
57
|
-
this._db = new window.__sqlJs.Database();
|
|
58
|
-
}
|
|
59
|
-
prepare(sql) {
|
|
60
|
-
const db = this._db;
|
|
61
|
-
return {
|
|
62
|
-
run: (...p) => { db.run(sql, p); return { changes: 1 }; },
|
|
63
|
-
get: (...p) => { const r = db.exec(sql, p); return r[0]?.values[0] ? Object.fromEntries(r[0].columns.map((c, i) => [c, r[0].values[0][i]])) : undefined; },
|
|
64
|
-
all: (...p) => { const r = db.exec(sql, p); if (!r[0]) return []; return r[0].values.map(row => Object.fromEntries(r[0].columns.map((c, i) => [c, row[i]]))); },
|
|
65
|
-
};
|
|
66
|
-
}
|
|
67
|
-
close() {}
|
|
68
|
-
};
|
|
69
|
-
}
|
|
70
|
-
|
|
71
|
-
function createConsole(term) {
|
|
72
|
-
const w = s => term.write(s + '\r\n');
|
|
73
|
-
const timers = {};
|
|
74
|
-
return {
|
|
75
|
-
log: (...a) => w(a.map(v => typeof v === 'object' ? JSON.stringify(v, null, 2) : String(v)).join(' ')),
|
|
76
|
-
error: (...a) => term.write('\x1b[31m' + a.map(String).join(' ') + '\x1b[0m\r\n'),
|
|
77
|
-
warn: (...a) => term.write('\x1b[33m' + a.map(String).join(' ') + '\x1b[0m\r\n'),
|
|
78
|
-
info: (...a) => w(a.map(String).join(' ')),
|
|
79
|
-
dir: (o, opts) => w(JSON.stringify(o, null, 2)),
|
|
80
|
-
table: data => {
|
|
81
|
-
if (!Array.isArray(data)) { w(JSON.stringify(data, null, 2)); return; }
|
|
82
|
-
if (!data.length) { w('(empty)'); return; }
|
|
83
|
-
const cols = Object.keys(data[0]);
|
|
84
|
-
w(cols.join('\t'));
|
|
85
|
-
for (const row of data) w(cols.map(c => String(row[c] ?? '')).join('\t'));
|
|
86
|
-
},
|
|
87
|
-
time: label => { timers[label || 'default'] = performance.now(); },
|
|
88
|
-
timeEnd: label => {
|
|
89
|
-
const k = label || 'default';
|
|
90
|
-
const ms = timers[k] ? (performance.now() - timers[k]).toFixed(3) : 0;
|
|
91
|
-
delete timers[k];
|
|
92
|
-
w(k + ': ' + ms + 'ms');
|
|
93
|
-
},
|
|
94
|
-
assert: (cond, ...a) => { if (!cond) term.write('\x1b[31mAssertion failed: ' + a.join(' ') + '\x1b[0m\r\n'); },
|
|
95
|
-
count: (() => { const c = {}; return label => { const k = label || 'default'; c[k] = (c[k] || 0) + 1; w(k + ': ' + c[k]); }; })(),
|
|
96
|
-
clear: () => term.clear(),
|
|
97
|
-
trace: (...a) => w('Trace: ' + a.map(String).join(' ')),
|
|
98
|
-
group: () => {},
|
|
99
|
-
groupEnd: () => {},
|
|
100
|
-
};
|
|
101
|
-
}
|
|
102
|
-
|
|
103
|
-
function createProcess(term, ctx) {
|
|
104
|
-
return {
|
|
105
|
-
argv: ['node'],
|
|
106
|
-
env: ctx.env,
|
|
107
|
-
cwd: () => ctx.cwd,
|
|
108
|
-
chdir: d => { ctx.cwd = d; },
|
|
109
|
-
exit: code => term.write('[exit ' + (code || 0) + ']\r\n'),
|
|
110
|
-
platform: 'browser',
|
|
111
|
-
version: 'v20.0.0',
|
|
112
|
-
versions: { node: '20.0.0' },
|
|
113
|
-
pid: 1,
|
|
114
|
-
nextTick: fn => Promise.resolve().then(fn),
|
|
115
|
-
stdout: { write: s => term.write(String(s)) },
|
|
116
|
-
stderr: { write: s => term.write('\x1b[31m' + String(s) + '\x1b[0m') },
|
|
117
|
-
stdin: { on: () => {} },
|
|
118
|
-
on: () => {},
|
|
119
|
-
off: () => {},
|
|
120
|
-
hrtime: { bigint: () => BigInt(Math.round(performance.now() * 1e6)) },
|
|
121
|
-
};
|
|
122
|
-
}
|
|
2
|
+
import { createExpress, createHttp, createSqlite, createConsole, createProcess, NODE_VERSION, NODE_VERSIONS, NodeExit } from './shell-node-modules.js';
|
|
3
|
+
import { inspect, format, createZlib, preloadFflate } from './shell-node-stdlib.js';
|
|
4
|
+
import { createHash, createHmac, pbkdf2Sync, randomBytes } from './shell-node-crypto.js';
|
|
5
|
+
import { createChildProcess, createHttpClient, extendProcess, rewriteStack, isEsmCode, runEsm, parseDotEnv } from './shell-node-io.js';
|
|
6
|
+
import { resolveExports, resolveImports, walkUpNodeModules, resolvePackageEntry, makeModuleModule, makeModuleNotFoundError, makeFsPromises, makeFsWatch, makeNetStub, makeDgramStub, makeWorkerThreadsStub } from './shell-node-resolve.js';
|
|
7
|
+
import { extendBuffer, extendPath, createUrlExt, makeStringDecoder, makeReadline, makeTimersMod, makePerfHooks, makeV8Mod, makeAsyncHooks, makeStubs, makeErrorCodes, extendProcessExtras, makeStreamConsumers } from './shell-node-extras.js';
|
|
8
|
+
import { makeStream, extendFsStreams } from './shell-node-streams.js';
|
|
9
|
+
import { extendCrypto } from './shell-node-cipher.js';
|
|
10
|
+
import { extendKeys } from './shell-node-keyobject.js';
|
|
11
|
+
import { makeStreamingZlib, makeVmModule, makeModuleRegister, makeHttp2, makeWasi } from './shell-node-advanced.js';
|
|
12
|
+
import { makeDebugRegistry, makeDiagnosticsChannel, makeTraceEvents, makeBufferPool, makeProcessBindings, makePerfMemory, makeFetchPool, makeFsWatchReal, installPrepareStackTraceHook, installCaptureStackTrace } from './shell-node-observe.js';
|
|
13
|
+
import { makeWorkerThreads, makeChildProcessReal, makeRepl } from './shell-node-runtime.js';
|
|
14
|
+
import { detectBrowser, registerPolyfill, makeCompressionStreamZlib, makeWebCodecs, makeWebPush, makeStorageHelpers } from './shell-node-firefox.js';
|
|
15
|
+
import { makeOpfsBackend, wireOpfsIntoFs } from './shell-node-opfs.js';
|
|
16
|
+
import { preloadBrotli, makeBrotli } from './shell-node-brotli.js';
|
|
17
|
+
import { preloadSourceMap, installSourceMapStacks } from './shell-node-srcmap.js';
|
|
18
|
+
import { makeNet, makeTls, makeDgram } from './shell-node-net.js';
|
|
19
|
+
import { makeInspector } from './shell-node-inspector.js';
|
|
20
|
+
import { makeV8Profiler, makeHeapSnapshot } from './shell-node-profiler.js';
|
|
21
|
+
import { makeCluster } from './shell-node-cluster.js';
|
|
22
|
+
import { preloadX509 } from './shell-node-keyobject.js';
|
|
23
|
+
import { detectRuntime, registerRuntime, switchRuntime, logRuntimeSwitch } from './shell-runtime.js';
|
|
24
|
+
import { makeDenoGlobal } from './shell-deno.js'; import { makeBunGlobal } from './shell-bun.js';
|
|
25
|
+
import { makePmDispatcher, detectPm, makeCorepackStub } from './shell-pm.js';
|
|
26
|
+
import { isTsFile, preprocessSource } from './shell-ts.js'; import { installPosixFs, installFds, installTmpAndMisc } from './shell-posix.js';
|
|
27
|
+
import { makeTestRunner, makeTapReporter } from './shell-node-testrunner.js'; import { makeForkIpc } from './shell-node-ipc.js';
|
|
28
|
+
import { styleText, stripVTControlCharacters, getCallSites, MIMEType, MIMEParams, makeConsoleExtras } from './shell-node-util-extras.js';
|
|
29
|
+
import { makeProcFs, wireProcFs } from './shell-node-procfs.js'; import { makeGit } from './shell-node-git.js'; import { makeTar } from './shell-node-tar.js'; import { makeDns } from './shell-node-dns.js'; import { makeNativeLoader } from './shell-node-native.js'; import { makeRegistry } from './shell-node-registry.js';
|
|
30
|
+
import { makeBusnet, makeBusHttp } from './shell-node-busnet.js';
|
|
123
31
|
|
|
124
32
|
export function createNodeEnv({ ctx, term }) {
|
|
125
|
-
const pathmod = createPath();
|
|
126
|
-
const
|
|
127
|
-
const
|
|
33
|
+
const pathmod = extendPath(createPath()); const Buf = makeBufferPool(extendBuffer(createBuffer())); const debugReg = makeDebugRegistry();
|
|
34
|
+
const browserInfo = detectBrowser(); debugReg.browser = browserInfo; const snapFn = () => window.__debug?.idbSnapshot || {};
|
|
35
|
+
const fsmod = installTmpAndMisc(installFds(installPosixFs(extendFsStreams(createFs(), Buf), Buf, ctx), Buf), Buf, ctx);
|
|
36
|
+
const opfs = makeOpfsBackend(Buf); if (opfs) wireOpfsIntoFs(fsmod, opfs, debugReg);
|
|
37
|
+
const runtime = detectRuntime(); registerRuntime(debugReg, runtime); fsmod.promises = makeFsPromises(fsmod); fsmod.watch = makeFsWatchReal(snapFn);
|
|
38
|
+
fsmod.glob = (pat, opts, cb) => { if (typeof opts === 'function') { cb = opts; opts = {}; } const matches = Object.keys(window.__debug?.idbSnapshot || {}).filter(k => new RegExp('^' + pat.replace(/\*\*/g, '.+').replace(/\*/g, '[^/]*') + '$').test(k)); queueMicrotask(() => cb?.(null, matches)); };
|
|
39
|
+
fsmod.globSync = pat => Object.keys(window.__debug?.idbSnapshot || {}).filter(k => new RegExp('^' + pat.replace(/\*\*/g, '.+').replace(/\*/g, '[^/]*') + '$').test(k));
|
|
40
|
+
const zlibMod = createZlib(Buf); const httpClient = createHttpClient(Buf); const cpMod = createChildProcess(ctx); const streamMod = makeStream();
|
|
41
|
+
const cpReal = makeChildProcessReal(Buf, streamMod); Object.assign(cpMod, { exec: cpReal.exec.bind(cpReal), spawn: cpReal.spawn.bind(cpReal), execFile: cpReal.execFile.bind(cpReal), execSync: cpReal.execSync, spawnSync: cpReal.spawnSync, fork: cpReal.fork });
|
|
42
|
+
let cryptoMod = { createHash, createHmac, pbkdf2Sync, pbkdf2: (pw, salt, iter, len, dig, cb) => queueMicrotask(() => { try { cb(null, Buf.from(pbkdf2Sync(pw, salt, iter, len, dig))); } catch (e) { cb(e); } }), randomBytes: n => Buf.from(randomBytes(n)), randomUUID: () => crypto.randomUUID(), randomInt: (a, b) => Math.floor(Math.random() * (b - a) + a), webcrypto: globalThis.crypto, constants: {} };
|
|
43
|
+
cryptoMod = extendKeys(extendCrypto(cryptoMod, Buf)); cryptoMod._ops = () => ++debugReg.cryptoOps; cryptoMod.secureHeapUsed = () => ({ total: 0, min: 0, used: 0, utilization: 0 });
|
|
44
|
+
const errorCodes = makeErrorCodes(); const stubs = makeStubs(ctx); const diagCh = makeDiagnosticsChannel(); const traceEv = makeTraceEvents(debugReg);
|
|
45
|
+
const vmMod = makeVmModule(); const http2Mod = makeHttp2(); const wasiMod = makeWasi(); const moduleRegister = makeModuleRegister(); const workerThreads = makeWorkerThreads(snapFn, Buf);
|
|
46
|
+
const getMem = makePerfMemory(performance); const FetchAgent = makeFetchPool(); const netMod = makeNet(Buf); const tlsMod = makeTls(netMod, Buf); const dgramMod = makeDgram(Buf);
|
|
47
|
+
const v8Real = makeV8Profiler(debugReg); const heapSnap = makeHeapSnapshot(); const clusterReal = makeCluster(); const inspector = makeInspector(debugReg);
|
|
48
|
+
const nativeCS = makeCompressionStreamZlib(streamMod, Buf); const webCodecs = makeWebCodecs(); const webPush = makeWebPush(); const storage = makeStorageHelpers();
|
|
49
|
+
const gitMod = makeGit(fsmod); const tarMod = makeTar(fsmod, null, Buf); const dnsMod = makeDns(); const nativeLoader = makeNativeLoader(); const registryMod = makeRegistry(); const busnet = makeBusnet(); globalThis.__busnet = busnet; const busHttp = makeBusHttp(busnet); debugReg.busnet = busnet;
|
|
50
|
+
if (nativeCS) registerPolyfill(debugReg, 'compressionStream', 'native', 'CompressionStream available'); if (browserInfo.capabilities.webCodecs) registerPolyfill(debugReg, 'webCodecs', 'native', 'WebCodecs available');
|
|
51
|
+
const proc = extendProcessExtras(extendProcess(createProcess(term, ctx), ctx), ctx);
|
|
52
|
+
proc.stdin.setRawMode = () => proc.stdin; proc.stdin.isRaw = false; proc.binding = makeProcessBindings(); proc.memoryUsage = getMem; proc.storage = storage; proc.storageBuckets = storage.buckets; proc.cwd = () => ctx.cwd; proc.chdir = p => { ctx.cwd = p.startsWith('/') ? p : pathmod.resolve(ctx.cwd, p); }; proc.umask = m => { const prev = ctx.umask || 0o022; if (m != null) ctx.umask = m; return prev; }; makeForkIpc(proc); proc.dlopen = (t, p) => nativeLoader.dlopen(t, p); proc.resourceUsage = () => { const m = performance.memory || {}; return { userCPUTime: performance.now() * 1000 | 0, systemCPUTime: 0, maxRSS: (m.totalJSHeapSize || 0) / 1024 | 0, sharedMemorySize: 0, unsharedDataSize: 0, unsharedStackSize: 0, minorPageFault: 0, majorPageFault: 0, swappedOut: 0, fsRead: 0, fsWrite: 0, ipcSent: 0, ipcReceived: 0, signalsCount: 0, voluntaryContextSwitches: 0, involuntaryContextSwitches: 0 }; };
|
|
53
|
+
wireProcFs(fsmod, makeProcFs(proc)); const denoGlobal = makeDenoGlobal(fsmod, proc, cpMod, ctx.httpHandlers || {}, Buf); const bunGlobal = makeBunGlobal(fsmod, proc, cpMod, ctx.httpHandlers || {}, Buf, streamMod, cryptoMod);
|
|
128
54
|
const MODULES = {
|
|
129
|
-
path: () => pathmod,
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
55
|
+
path: () => pathmod, fs: () => fsmod, events: () => createEvents(), url: () => createUrlExt(), querystring: () => createQuerystring(),
|
|
56
|
+
os: () => { const n=navigator?.hardwareConcurrency||1; const mem=performance.memory||{}; return { platform: () => 'linux', arch: () => 'x64', homedir: () => ctx.env.HOME || '/root', tmpdir: () => '/tmp', cpus: () => Array.from({length:n},(_,i)=>({model:'Browser CPU',speed:3000,times:{user:0,nice:0,sys:0,idle:0,irq:0}})), totalmem: () => mem.jsHeapSizeLimit || 1073741824, freemem: () => (mem.jsHeapSizeLimit || 1073741824) - (mem.usedJSHeapSize || 0), hostname: () => ctx.env.HOSTNAME || 'thebird', EOL: '\n', release: () => '6.0.0-browser', type: () => 'Linux', uptime: () => performance.now() / 1000, networkInterfaces: () => ({ lo: [{ address: '127.0.0.1', netmask: '255.0.0.0', family: 'IPv4', mac: '00:00:00:00:00:00', internal: true, cidr: '127.0.0.1/8' }] }), loadavg: () => [0, 0, 0], userInfo: () => ({ username: ctx.env.USER || 'root', uid: 0, gid: 0, shell: ctx.env.SHELL || '/bin/sh', homedir: ctx.env.HOME || '/root' }), endianness: () => 'LE', version: () => '#1 SMP', machine: () => 'x86_64', devNull: '/dev/null', availableParallelism: () => n, constants: { signals: { SIGINT: 2, SIGTERM: 15, SIGKILL: 9, SIGHUP: 1 }, errno: { EACCES: 13, EEXIST: 17, ENOENT: 2, EISDIR: 21, ENOTDIR: 20 } } }; },
|
|
57
|
+
util: () => ({ inspect, format, promisify: fn => (...a) => new Promise((r, j) => fn(...a, (e, v) => e ? j(e) : r(v))), callbackify: fn => (...a) => { const cb = a.pop(); fn(...a).then(v => cb(null, v), e => cb(e)); }, types: { isPromise: p => p instanceof Promise, isDate: v => v instanceof Date, isRegExp: v => v instanceof RegExp, isBuffer: v => v instanceof Uint8Array, isTypedArray: v => ArrayBuffer.isView(v) && !(v instanceof DataView), isAsyncFunction: f => f?.constructor?.name === 'AsyncFunction', isNativeError: e => e instanceof Error }, deprecate: fn => fn, inherits: (a, b) => { Object.setPrototypeOf(a.prototype, b.prototype); }, debuglog: () => () => {}, isDeepStrictEqual: (a, b) => JSON.stringify(a) === JSON.stringify(b), styleText, stripVTControlCharacters, getCallSites, MIMEType, MIMEParams, parseArgs: ({ args = [], options = {} }) => { const values = {}, positionals = []; for (let i = 0; i < args.length; i++) { const a = args[i]; if (a.startsWith('--')) { const [k, v] = a.slice(2).split('='); if (v !== undefined) values[k] = v; else if (options[k]?.type === 'string') values[k] = args[++i]; else values[k] = true; } else positionals.push(a); } return { values, positionals }; } }),
|
|
58
|
+
crypto: () => cryptoMod,
|
|
59
|
+
stream: () => streamMod, 'stream/promises': () => streamMod.promises, 'stream/consumers': () => makeStreamConsumers(), 'stream/web': () => ({ ReadableStream, WritableStream, TransformStream }),
|
|
60
|
+
http: () => ({ ...httpClient, Agent: FetchAgent, globalAgent: new FetchAgent() }), https: () => ({ ...httpClient, Agent: FetchAgent, globalAgent: new FetchAgent() }),
|
|
61
|
+
http2: () => http2Mod, 'node:http2': () => http2Mod,
|
|
62
|
+
vm: () => vmMod, 'node:vm': () => vmMod,
|
|
63
|
+
buffer: () => ({ Buffer: Buf, constants: { MAX_LENGTH: 4294967295, MAX_STRING_LENGTH: 536870888 }, kMaxLength: 4294967295, Blob, File }),
|
|
64
|
+
child_process: () => cpMod,
|
|
65
|
+
net: () => netMod, dgram: () => dgramMod, tls: () => tlsMod, worker_threads: () => workerThreads,
|
|
66
|
+
zlib: () => ({ ...zlibMod, ...makeStreamingZlib(streamMod, Buf, globalThis.__fflate || {}), ...(nativeCS || {}), ...makeBrotli(streamMod, Buf) }),
|
|
67
|
+
assert: () => { const a = (v, m) => { if (!v) throw new Error(m || 'assertion failed'); }; a.ok = a; a.equal = (x, y, m) => a(x === y, m); a.deepEqual = (x, y, m) => a(JSON.stringify(x) === JSON.stringify(y), m); a.deepStrictEqual = a.deepEqual; a.strictEqual = a.equal; a.notEqual = (x, y, m) => a(x !== y, m); a.notDeepEqual = (x, y, m) => a(JSON.stringify(x) !== JSON.stringify(y), m); a.notStrictEqual = a.notEqual; a.throws = (fn, m) => { try { fn(); throw new Error('did not throw'); } catch (e) {} }; a.doesNotThrow = fn => fn(); a.rejects = async fn => { try { await (typeof fn === 'function' ? fn() : fn); throw new Error('did not reject'); } catch {} }; a.fail = m => { throw new Error(m || 'failed'); }; a.match = (s, re) => a(re.test(s)); return a; },
|
|
68
|
+
string_decoder: () => stubs.string_decoder, readline: () => makeReadline(term, proc), 'readline/promises': () => stubs.readline_promises,
|
|
69
|
+
timers: () => makeTimersMod(), 'timers/promises': () => makeTimersMod().promises, perf_hooks: () => makePerfHooks(),
|
|
70
|
+
v8: () => ({ ...makeV8Mod(), ...v8Real, ...heapSnap }), async_hooks: () => makeAsyncHooks(),
|
|
71
|
+
inspector: () => inspector, cluster: () => clusterReal || stubs.cluster,
|
|
72
|
+
codecs: () => { if (!webCodecs) throw makeModuleNotFoundError('codecs', []); return webCodecs; }, 'web-push': () => webPush,
|
|
73
|
+
sea: () => stubs.sea, 'node:sea': () => stubs.sea, test: () => makeTestRunner(term), 'node:test': () => makeTestRunner(term),
|
|
74
|
+
'node:test/reporters': () => ({ tap: makeTapReporter(term), spec: class {}, dot: class {} }), tty: () => stubs.tty, domain: () => stubs.domain,
|
|
75
|
+
diagnostics_channel: () => diagCh, punycode: () => stubs.punycode, errors: () => errorCodes, trace_events: () => traceEv,
|
|
76
|
+
wasi: () => wasiMod, module: () => ({ ...makeModuleModule(() => {}, MODULES), register: moduleRegister.register, _registerHooks: moduleRegister._hooks }), express: () => createExpress(term, fsmod),
|
|
77
|
+
'better-sqlite3': createSqlite, sqlite: () => ({ DatabaseSync: createSqlite, StatementSync: class {} }), 'node:sqlite': () => ({ DatabaseSync: createSqlite, StatementSync: class {} }),
|
|
78
|
+
dns: () => dnsMod, 'dns/promises': () => dnsMod.promises, 'node:dns': () => dnsMod, 'isomorphic-git': () => gitMod, git: () => gitMod, tar: () => tarMod, 'npm-registry-fetch': () => registryMod,
|
|
79
|
+
busnet: () => busnet, 'bus-http': () => busHttp,
|
|
140
80
|
};
|
|
141
|
-
|
|
81
|
+
for (const k of Object.keys(MODULES)) if (!k.startsWith('node:')) MODULES['node:' + k] = MODULES[k];
|
|
142
82
|
const cons = createConsole(term);
|
|
143
|
-
|
|
83
|
+
cons.log = (...a) => term.write(format(...a) + '\r\n'); cons.info = cons.log; cons.error = (...a) => term.write('\x1b[31m' + format(...a) + '\x1b[0m\r\n'); cons.warn = (...a) => term.write('\x1b[33m' + format(...a) + '\x1b[0m\r\n'); cons.debug = cons.log; Object.assign(cons, makeConsoleExtras(cons, term));
|
|
84
|
+
const pkgCache = {}; const reqCache = {}; let requireStack = [];
|
|
85
|
+
function loadDotEnv() { const envFile = snapFn()[ctx.cwd.replace(/^\//, '').replace(/\/$/, '') + '/.env'] || snapFn()['.env']; if (!envFile) return; for (const [k, v] of Object.entries(parseDotEnv(envFile))) if (!(k in ctx.env)) ctx.env[k] = v; }
|
|
86
|
+
|
|
87
|
+
const resolveCandidates = (dir, id) => [pathmod.resolve(dir, id) + '.js', pathmod.resolve(dir, id), pathmod.resolve(dir, id) + '/index.js', pathmod.resolve(dir, id, 'index.js')];
|
|
88
|
+
function findPkgJsonDir(s, dir) { let d = dir.replace(/^\//, '').replace(/\/$/, ''); while (true) { const k = (d ? d + '/' : '') + 'package.json'; if (k in s) return d; if (!d) return null; const up = d.slice(0, d.lastIndexOf('/')); if (up === d) return null; d = up; } }
|
|
89
|
+
function isEsmPkg(s, filePath) { const pjDir = findPkgJsonDir(s, pathmod.dirname(filePath)); try { const pj = JSON.parse(s[(pjDir || '') + (pjDir ? '/' : '') + 'package.json']); return pj.type === 'module'; } catch { return false; } }
|
|
144
90
|
|
|
145
91
|
function makeRequire(dir) {
|
|
146
|
-
|
|
92
|
+
const req = function require(id) {
|
|
93
|
+
if (id === 'module') return makeModuleModule(req, MODULES);
|
|
147
94
|
if (MODULES[id]) return MODULES[id]();
|
|
148
|
-
const
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
95
|
+
const s = snapFn();
|
|
96
|
+
if (id.startsWith('#')) {
|
|
97
|
+
const pjRoot = findPkgJsonDir(s, dir);
|
|
98
|
+
if (pjRoot) { const pj = JSON.parse(s[pjRoot + '/package.json']); const target = resolveImports(pj, id); if (target) { const resolved = pathmod.resolve('/' + pjRoot, target); return loadFile(resolved.replace(/^\//, ''), s); } }
|
|
99
|
+
throw makeModuleNotFoundError(id, requireStack);
|
|
100
|
+
}
|
|
101
|
+
if (!id.startsWith('.')) {
|
|
102
|
+
if (pkgCache[id]) return pkgCache[id];
|
|
103
|
+
const pkgDir = walkUpNodeModules(s, dir, id);
|
|
104
|
+
if (pkgDir) { const entry = resolvePackageEntry(s, pkgDir); if (entry) { const m = loadFile(entry.replace(/^\//, ''), s); if (m) return m; } }
|
|
105
|
+
throw makeModuleNotFoundError(id, requireStack);
|
|
106
|
+
}
|
|
107
|
+
for (const c of resolveCandidates(dir, id)) {
|
|
156
108
|
const key = c.replace(/^\//, '');
|
|
157
|
-
if (key in s) {
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
109
|
+
if (key in s) { const loaded = loadFile(key, s); if (loaded !== undefined) return loaded; }
|
|
110
|
+
}
|
|
111
|
+
throw makeModuleNotFoundError(id, requireStack);
|
|
112
|
+
};
|
|
113
|
+
function loadFile(key, s) {
|
|
114
|
+
if (key.endsWith('.json')) return JSON.parse(s[key]);
|
|
115
|
+
if (reqCache[key]) return reqCache[key].exports;
|
|
116
|
+
const mod = { exports: {} };
|
|
117
|
+
reqCache[key] = mod;
|
|
118
|
+
const modDir = pathmod.dirname('/' + key);
|
|
119
|
+
requireStack.push('/' + key);
|
|
120
|
+
try {
|
|
121
|
+
const src = s[key]; const esm = key.endsWith('.mjs') || (key.endsWith('.js') && isEsmPkg(s, '/' + key)) || isEsmCode(src);
|
|
122
|
+
if (esm) throw new Error("ESM module '" + key + "' requested via require() — use dynamic import() or run entry as ESM");
|
|
123
|
+
new Function('module', 'exports', 'require', '__filename', '__dirname', 'process', 'console', 'Buffer', 'setTimeout', 'setInterval', 'clearTimeout', 'clearInterval', 'fetch', src)(mod, mod.exports, makeRequire(modDir), '/' + key, modDir, proc, cons, Buf, setTimeout, setInterval, clearTimeout, clearInterval, fetch);
|
|
164
124
|
}
|
|
165
|
-
|
|
125
|
+
finally { requireStack.pop(); }
|
|
126
|
+
mod.loaded = true;
|
|
127
|
+
return mod.exports;
|
|
128
|
+
}
|
|
129
|
+
req.resolve = id => {
|
|
130
|
+
if (MODULES[id] || id === 'module') return id;
|
|
131
|
+
const s = snapFn();
|
|
132
|
+
if (!id.startsWith('.')) { const pkgDir = walkUpNodeModules(s, dir, id); if (pkgDir) return resolvePackageEntry(s, pkgDir) || pkgDir; throw makeModuleNotFoundError(id, requireStack); }
|
|
133
|
+
for (const c of resolveCandidates(dir, id)) { const key = c.replace(/^\//, ''); if (key in s) return '/' + key; }
|
|
134
|
+
throw makeModuleNotFoundError(id, requireStack);
|
|
166
135
|
};
|
|
136
|
+
req.cache = reqCache;
|
|
137
|
+
return req;
|
|
167
138
|
}
|
|
168
139
|
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
140
|
+
async function preloadAsyncPkgs(entryCode, entryDir) {
|
|
141
|
+
const s = snapFn();
|
|
142
|
+
const visited = new Set(); const queue = [{ code: entryCode, dir: entryDir }]; const pkgIds = new Set();
|
|
143
|
+
const re = /require\(\s*['"]([^'"]+)['"]\s*\)/g;
|
|
144
|
+
while (queue.length) {
|
|
145
|
+
const { code, dir } = queue.shift(); let m; re.lastIndex = 0;
|
|
146
|
+
while ((m = re.exec(code))) { const id = m[1]; if (MODULES[id]) continue; if (!id.startsWith('.')) { pkgIds.add(id); continue; } for (const c of resolveCandidates(dir, id)) { const key = c.replace(/^\//, ''); if (visited.has(key) || !(key in s)) continue; visited.add(key); queue.push({ code: s[key], dir: pathmod.dirname('/' + key) }); break; } }
|
|
147
|
+
}
|
|
148
|
+
for (const id of pkgIds) {
|
|
149
|
+
if (pkgCache[id]) continue;
|
|
150
|
+
const key = 'node_modules/' + id + '/index.js'; if (!(key in s)) continue;
|
|
151
|
+
const urlMatch = s[key].match(/import\((".+?")\)/); if (!urlMatch) continue;
|
|
152
|
+
const url = JSON.parse(urlMatch[1]);
|
|
153
|
+
try { const mod = await import(url); const exp = { ...mod }; if (mod.default && typeof mod.default === 'object') Object.assign(exp, mod.default); pkgCache[id] = mod.default && Object.keys(mod).length === 1 ? mod.default : exp; }
|
|
154
|
+
catch (e) { term.write('\x1b[31mfailed to load ' + id + ': ' + e.message + '\x1b[0m\r\n'); }
|
|
155
|
+
}
|
|
176
156
|
}
|
|
177
157
|
|
|
178
|
-
return async function nodeEval(code, filename, argv) {
|
|
158
|
+
return async function nodeEval(code, filename, argv, stdinBuf) {
|
|
179
159
|
const dir = filename ? pathmod.dirname(filename) : ctx.cwd;
|
|
180
|
-
const fpath = filename ||
|
|
181
|
-
proc.argv = ['node', fpath, ...(argv || [])];
|
|
182
|
-
|
|
160
|
+
const fpath = filename || '[eval]';
|
|
161
|
+
proc.argv = filename ? ['node', fpath, ...(argv || [])] : ['node'];
|
|
162
|
+
proc.exitCode = 0;
|
|
163
|
+
loadDotEnv();
|
|
164
|
+
globalThis.__fflate = await preloadFflate().catch(() => ({}));
|
|
165
|
+
if (proc.sourceMapsEnabled) { await preloadSourceMap().catch(() => {}); installSourceMapStacks(snapFn); }
|
|
166
|
+
const rtName = switchRuntime(code.startsWith('#!') ? code.slice(0, code.indexOf('\n')) : ''); logRuntimeSwitch(debugReg, debugReg.runtime.active, rtName, fpath);
|
|
167
|
+
if (isTsFile(fpath)) code = await preprocessSource(fpath, code);
|
|
168
|
+
await preloadAsyncPkgs(code, dir);
|
|
169
|
+
const reqFn = makeRequire(dir);
|
|
170
|
+
const scope = { process: proc, console: cons, require: reqFn, Buffer: Buf, __filename: fpath, __dirname: dir, setTimeout, setInterval, clearTimeout, clearInterval, fetch, module: { exports: {} }, exports: {}, global: globalThis, URL, URLSearchParams, TextEncoder, TextDecoder };
|
|
171
|
+
const prevGlobals = { process: globalThis.process, Buffer: globalThis.Buffer, Deno: globalThis.Deno, Bun: globalThis.Bun };
|
|
172
|
+
globalThis.process = proc; globalThis.Buffer = Buf;
|
|
173
|
+
if (rtName === 'deno') globalThis.Deno = denoGlobal; else delete globalThis.Deno;
|
|
174
|
+
if (rtName === 'bun') globalThis.Bun = bunGlobal; else delete globalThis.Bun;
|
|
175
|
+
installCaptureStackTrace(); installPrepareStackTraceHook();
|
|
176
|
+
const unhandledH = e => { e.preventDefault?.(); const err = e.reason || e; term.write('\x1b[31m' + rewriteStack(err, fpath) + '\x1b[0m\r\n'); ctx.lastExitCode = 1; };
|
|
177
|
+
window.addEventListener('unhandledrejection', unhandledH);
|
|
183
178
|
try {
|
|
184
|
-
const
|
|
185
|
-
const vals = Object.values(scope);
|
|
179
|
+
if (isEsmCode(code)) { const preamble = '\nconst __filename = ' + JSON.stringify(fpath) + ';\nconst __dirname = ' + JSON.stringify(dir) + ';\n'; const mod = await runEsm(preamble + code, scope); if (mod && !filename) { for (const [k, v] of Object.entries(mod)) if (k !== 'default') cons.log(k + ':', v); } ctx.lastExitCode = proc.exitCode | 0; return; }
|
|
180
|
+
const keys = Object.keys(scope), vals = Object.values(scope);
|
|
186
181
|
const fn = new Function(...keys, 'return (async () => {\n' + code + '\n})()');
|
|
187
|
-
const
|
|
182
|
+
const pending = fn(...vals);
|
|
183
|
+
if (stdinBuf) queueMicrotask(() => proc.stdin._feed(stdinBuf));
|
|
184
|
+
const result = await pending;
|
|
188
185
|
if (result !== undefined && !filename) cons.log(result);
|
|
186
|
+
ctx.lastExitCode = proc.exitCode | 0;
|
|
189
187
|
} catch (e) {
|
|
190
|
-
|
|
188
|
+
if (e && e.__nodeExit) { ctx.lastExitCode = e.code | 0; return; }
|
|
189
|
+
term.write('\x1b[31m' + rewriteStack(e, fpath) + '\x1b[0m\r\n');
|
|
190
|
+
ctx.lastExitCode = 1;
|
|
191
|
+
} finally {
|
|
192
|
+
window.removeEventListener('unhandledrejection', unhandledH);
|
|
193
|
+
if (prevGlobals.process !== undefined) globalThis.process = prevGlobals.process; else delete globalThis.process;
|
|
194
|
+
if (prevGlobals.Buffer !== undefined) globalThis.Buffer = prevGlobals.Buffer; else delete globalThis.Buffer;
|
|
195
|
+
if (prevGlobals.Deno !== undefined) globalThis.Deno = prevGlobals.Deno; else delete globalThis.Deno;
|
|
196
|
+
if (prevGlobals.Bun !== undefined) globalThis.Bun = prevGlobals.Bun; else delete globalThis.Bun;
|
|
191
197
|
}
|
|
192
198
|
};
|
|
193
199
|
}
|
|
@@ -0,0 +1,173 @@
|
|
|
1
|
+
import { NPM_VERSION } from './shell-node-modules.js';
|
|
2
|
+
|
|
3
|
+
const toKey = p => p.replace(/^\//, '');
|
|
4
|
+
const snap = () => window.__debug.idbSnapshot || {};
|
|
5
|
+
const persist = () => window.__debug.idbPersist?.();
|
|
6
|
+
|
|
7
|
+
function resolvePkgJson(cwd, ctx) {
|
|
8
|
+
const path = cwd.replace(/\/$/, '') + '/package.json';
|
|
9
|
+
const raw = snap()[toKey(path)];
|
|
10
|
+
if (!raw) throw new Error('npm: no package.json in ' + cwd);
|
|
11
|
+
try { return { path, data: JSON.parse(raw) }; } catch (e) { throw new Error('npm: invalid package.json: ' + e.message); }
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
async function installOne(pkg, version, term) {
|
|
15
|
+
const spec = version && version !== 'latest' ? pkg + '@' + version.replace(/^[\^~]/, '') : pkg;
|
|
16
|
+
const url = 'https://esm.sh/' + spec + '?bundle&target=es2022';
|
|
17
|
+
term.write(' → ' + spec + '\r\n');
|
|
18
|
+
await import(url);
|
|
19
|
+
const stubPath = 'node_modules/' + pkg + '/index.js';
|
|
20
|
+
snap()[stubPath] = '// esm.sh async stub\nawait import(' + JSON.stringify(url) + ');';
|
|
21
|
+
const meta = { name: pkg, version: version || 'latest', _resolved: url, _from: 'esm.sh' };
|
|
22
|
+
snap()['node_modules/' + pkg + '/package.json'] = JSON.stringify(meta, null, 2);
|
|
23
|
+
persist();
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
function writePkgJson(pkgPath, data) {
|
|
27
|
+
snap()[toKey(pkgPath)] = JSON.stringify(data, null, 2);
|
|
28
|
+
persist();
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
function injectNpmEnv(ctx, data, scriptName) {
|
|
32
|
+
const prev = {};
|
|
33
|
+
const set = (k, v) => { prev[k] = ctx.env[k]; ctx.env[k] = v; };
|
|
34
|
+
set('npm_lifecycle_event', scriptName);
|
|
35
|
+
set('npm_package_name', data.name || '');
|
|
36
|
+
set('npm_package_version', data.version || '');
|
|
37
|
+
set('npm_config_user_agent', 'npm/' + NPM_VERSION + ' node/v23.10.0');
|
|
38
|
+
set('NODE_ENV', ctx.env.NODE_ENV || 'development');
|
|
39
|
+
for (const [k, v] of Object.entries(data.scripts || {})) set('npm_package_scripts_' + k.replace(/[^a-z0-9_]/gi, '_'), v);
|
|
40
|
+
return () => { for (const k of Object.keys(prev)) { if (prev[k] === undefined) delete ctx.env[k]; else ctx.env[k] = prev[k]; } };
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
export function makeNpm(ctx) {
|
|
44
|
+
const w = s => ctx.term.write(s);
|
|
45
|
+
const wl = s => w(s + '\r\n');
|
|
46
|
+
|
|
47
|
+
async function cmdInstall(args) {
|
|
48
|
+
const saveDev = args.includes('--save-dev') || args.includes('-D');
|
|
49
|
+
const noSave = args.includes('--no-save');
|
|
50
|
+
const pkgs = args.filter(a => !a.startsWith('-'));
|
|
51
|
+
if (!pkgs.length) {
|
|
52
|
+
const { data } = resolvePkgJson(ctx.cwd, ctx);
|
|
53
|
+
const all = { ...(data.dependencies || {}), ...(data.devDependencies || {}), ...(data.peerDependencies || {}) };
|
|
54
|
+
const entries = Object.entries(all);
|
|
55
|
+
if (!entries.length) { wl('up to date, 0 packages'); return; }
|
|
56
|
+
wl('installing ' + entries.length + ' packages from package.json');
|
|
57
|
+
for (const [name, ver] of entries) await installOne(name, ver, ctx.term);
|
|
58
|
+
wl('added ' + entries.length + ' package' + (entries.length === 1 ? '' : 's'));
|
|
59
|
+
return;
|
|
60
|
+
}
|
|
61
|
+
for (const spec of pkgs) {
|
|
62
|
+
const m = spec.match(/^(@?[^@]+?)(?:@(.+))?$/);
|
|
63
|
+
const [, name, version] = m;
|
|
64
|
+
await installOne(name, version, ctx.term);
|
|
65
|
+
}
|
|
66
|
+
if (!noSave) {
|
|
67
|
+
const { path: pkgPath, data } = resolvePkgJson(ctx.cwd, ctx);
|
|
68
|
+
const target = saveDev ? 'devDependencies' : 'dependencies';
|
|
69
|
+
data[target] = data[target] || {};
|
|
70
|
+
for (const spec of pkgs) {
|
|
71
|
+
const m = spec.match(/^(@?[^@]+?)(?:@(.+))?$/);
|
|
72
|
+
data[target][m[1]] = m[2] || 'latest';
|
|
73
|
+
}
|
|
74
|
+
writePkgJson(pkgPath, data);
|
|
75
|
+
}
|
|
76
|
+
wl('added ' + pkgs.length + ' package' + (pkgs.length === 1 ? '' : 's'));
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
function cmdUninstall(args) {
|
|
80
|
+
const pkgs = args.filter(a => !a.startsWith('-'));
|
|
81
|
+
if (!pkgs.length) throw new Error('npm uninstall <pkg>');
|
|
82
|
+
for (const pkg of pkgs) {
|
|
83
|
+
const s = snap();
|
|
84
|
+
let n = 0;
|
|
85
|
+
for (const k of Object.keys(s)) {
|
|
86
|
+
if (k === 'node_modules/' + pkg + '/index.js' || k.startsWith('node_modules/' + pkg + '/')) { delete s[k]; n++; }
|
|
87
|
+
}
|
|
88
|
+
wl(n ? 'removed ' + pkg : pkg + ' not installed');
|
|
89
|
+
}
|
|
90
|
+
const { path: pkgPath, data } = resolvePkgJson(ctx.cwd, ctx);
|
|
91
|
+
for (const pkg of pkgs) { delete data.dependencies?.[pkg]; delete data.devDependencies?.[pkg]; }
|
|
92
|
+
writePkgJson(pkgPath, data);
|
|
93
|
+
persist();
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
function cmdList(args) {
|
|
97
|
+
const filter = args.find(a => !a.startsWith('-'));
|
|
98
|
+
const s = snap();
|
|
99
|
+
const installed = Object.keys(s).filter(k => k.match(/^node_modules\/[^/]+\/package\.json$/) || k.match(/^node_modules\/@[^/]+\/[^/]+\/package\.json$/));
|
|
100
|
+
try {
|
|
101
|
+
const { data } = resolvePkgJson(ctx.cwd, ctx);
|
|
102
|
+
wl(data.name + '@' + (data.version || '1.0.0') + ' ' + ctx.cwd);
|
|
103
|
+
} catch { wl('(no package.json)'); }
|
|
104
|
+
for (const k of installed) {
|
|
105
|
+
const name = k.replace(/^node_modules\//, '').replace(/\/package\.json$/, '');
|
|
106
|
+
if (filter && !name.includes(filter)) continue;
|
|
107
|
+
const pj = JSON.parse(s[k]);
|
|
108
|
+
wl('├── ' + name + '@' + (pj.version || 'latest'));
|
|
109
|
+
}
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
async function cmdRun(args) {
|
|
113
|
+
const [scriptName, ...rest] = args;
|
|
114
|
+
const { data } = resolvePkgJson(ctx.cwd, ctx);
|
|
115
|
+
if (!scriptName) {
|
|
116
|
+
wl('Lifecycle scripts included in ' + (data.name || 'package') + '@' + (data.version || '') + ':');
|
|
117
|
+
for (const [n, s] of Object.entries(data.scripts || {})) wl(' ' + n + '\r\n ' + s);
|
|
118
|
+
return null;
|
|
119
|
+
}
|
|
120
|
+
const cmd = data.scripts?.[scriptName];
|
|
121
|
+
if (!cmd) throw new Error('npm: Missing script: "' + scriptName + '"');
|
|
122
|
+
const pre = data.scripts?.['pre' + scriptName];
|
|
123
|
+
const post = data.scripts?.['post' + scriptName];
|
|
124
|
+
const restore = injectNpmEnv(ctx, data, scriptName);
|
|
125
|
+
try {
|
|
126
|
+
const chain = [];
|
|
127
|
+
if (pre) chain.push({ name: 'pre' + scriptName, cmd: pre });
|
|
128
|
+
chain.push({ name: scriptName, cmd: cmd + (rest.length ? ' ' + rest.join(' ') : '') });
|
|
129
|
+
if (post) chain.push({ name: 'post' + scriptName, cmd: post });
|
|
130
|
+
return { runInShell: null, npmChain: chain, pkgName: data.name || 'package', pkgVersion: data.version || '' };
|
|
131
|
+
} finally { queueMicrotask(restore); }
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
function cmdInit(args) {
|
|
135
|
+
const yes = args.includes('-y') || args.includes('--yes');
|
|
136
|
+
if (!yes) { wl('npm init -y — use -y for non-interactive'); return; }
|
|
137
|
+
const pj = { name: ctx.cwd.split('/').filter(Boolean).pop() || 'project', version: '1.0.0', main: 'index.js', scripts: { start: 'node index.js', test: 'echo "Error: no test specified" && exit 1' }, dependencies: {} };
|
|
138
|
+
writePkgJson(ctx.cwd.replace(/\/$/, '') + '/package.json', pj);
|
|
139
|
+
wl('Wrote to ' + ctx.cwd.replace(/\/$/, '') + '/package.json');
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
async function cmdExec(args) {
|
|
143
|
+
const pkg = args[0];
|
|
144
|
+
if (!pkg) throw new Error('npx: package required');
|
|
145
|
+
const s = snap();
|
|
146
|
+
if (!s['node_modules/' + pkg + '/index.js']) await installOne(pkg, null, ctx.term);
|
|
147
|
+
const binPath = 'node_modules/.bin/' + pkg;
|
|
148
|
+
if (s[binPath]) return { runInShell: 'node /' + binPath + (args.length > 1 ? ' ' + args.slice(1).join(' ') : '') };
|
|
149
|
+
return { runInShell: 'node -e "require(\'' + pkg + '\')"' };
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
return async function npm(args) {
|
|
153
|
+
const sub = args[0];
|
|
154
|
+
const rest = args.slice(1);
|
|
155
|
+
if (sub === 'install' || sub === 'i' || sub === 'add') return cmdInstall(rest);
|
|
156
|
+
if (sub === 'uninstall' || sub === 'remove' || sub === 'rm') return cmdUninstall(rest);
|
|
157
|
+
if (sub === 'ls' || sub === 'list') return cmdList(rest);
|
|
158
|
+
if (sub === 'run' || sub === 'run-script') return cmdRun(rest);
|
|
159
|
+
if (sub === 'start') return cmdRun(['start', ...rest]);
|
|
160
|
+
if (sub === 'test' || sub === 't') return cmdRun(['test', ...rest]);
|
|
161
|
+
if (sub === 'init' || sub === 'create') return cmdInit(rest);
|
|
162
|
+
if (sub === 'exec' || sub === 'x') return cmdExec(rest);
|
|
163
|
+
if (sub === '--version' || sub === '-v') { wl(NPM_VERSION); return; }
|
|
164
|
+
if (sub === 'prefix') { wl(ctx.cwd); return; }
|
|
165
|
+
if (sub === 'root') { wl(ctx.cwd.replace(/\/$/, '') + '/node_modules'); return; }
|
|
166
|
+
if (sub === 'view' || sub === 'info' || sub === 'show') { const p = rest[0]; wl(p + ' — use esm.sh to inspect'); return; }
|
|
167
|
+
throw new Error('npm: unknown command "' + sub + '"');
|
|
168
|
+
};
|
|
169
|
+
}
|
|
170
|
+
|
|
171
|
+
export function makeNpx(npmCmd) {
|
|
172
|
+
return args => npmCmd(['exec', ...args]);
|
|
173
|
+
}
|