@seanhogg/builderforce-webcontainers-core 2026.10.2 → 2026.10.3

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.
Files changed (426) hide show
  1. package/dist/build.d.ts +42 -0
  2. package/dist/build.d.ts.map +1 -0
  3. package/dist/build.js +65 -0
  4. package/dist/build.js.map +1 -0
  5. package/dist/buildHost.d.ts +31 -0
  6. package/dist/buildHost.d.ts.map +1 -0
  7. package/dist/buildHost.js +125 -0
  8. package/dist/buildHost.js.map +1 -0
  9. package/dist/buildHtml.d.ts +28 -0
  10. package/dist/buildHtml.d.ts.map +1 -0
  11. package/dist/buildHtml.js +74 -0
  12. package/dist/buildHtml.js.map +1 -0
  13. package/dist/bundler.d.ts +129 -0
  14. package/dist/bundler.d.ts.map +1 -0
  15. package/dist/bundler.js +79 -0
  16. package/dist/bundler.js.map +1 -0
  17. package/dist/check/acquire.d.ts +46 -0
  18. package/dist/check/acquire.d.ts.map +1 -0
  19. package/dist/check/acquire.js +179 -0
  20. package/dist/check/acquire.js.map +1 -0
  21. package/dist/check/compilerHost.d.ts +22 -0
  22. package/dist/check/compilerHost.d.ts.map +1 -0
  23. package/dist/check/compilerHost.js +128 -0
  24. package/dist/check/compilerHost.js.map +1 -0
  25. package/dist/check/diagnostics.d.ts +23 -0
  26. package/dist/check/diagnostics.d.ts.map +1 -0
  27. package/dist/check/diagnostics.js +28 -0
  28. package/dist/check/diagnostics.js.map +1 -0
  29. package/dist/check/index.d.ts +19 -0
  30. package/dist/check/index.d.ts.map +1 -0
  31. package/dist/check/index.js +13 -0
  32. package/dist/check/index.js.map +1 -0
  33. package/dist/check/projects.d.ts +26 -0
  34. package/dist/check/projects.d.ts.map +1 -0
  35. package/dist/check/projects.js +124 -0
  36. package/dist/check/projects.js.map +1 -0
  37. package/dist/check/shims.d.ts +29 -0
  38. package/dist/check/shims.d.ts.map +1 -0
  39. package/dist/check/shims.js +76 -0
  40. package/dist/check/shims.js.map +1 -0
  41. package/dist/check/typeStore.d.ts +57 -0
  42. package/dist/check/typeStore.d.ts.map +1 -0
  43. package/dist/check/typeStore.js +89 -0
  44. package/dist/check/typeStore.js.map +1 -0
  45. package/dist/check/typecheck.d.ts +29 -0
  46. package/dist/check/typecheck.d.ts.map +1 -0
  47. package/dist/check/typecheck.js +91 -0
  48. package/dist/check/typecheck.js.map +1 -0
  49. package/dist/compileComponent.d.ts +8 -0
  50. package/dist/compileComponent.d.ts.map +1 -0
  51. package/dist/compileComponent.js +22 -0
  52. package/dist/compileComponent.js.map +1 -0
  53. package/dist/compileScript.d.ts +19 -4
  54. package/dist/compileScript.d.ts.map +1 -1
  55. package/dist/compileScript.js +37 -12
  56. package/dist/compileScript.js.map +1 -1
  57. package/dist/components.d.ts +41 -0
  58. package/dist/components.d.ts.map +1 -0
  59. package/dist/components.js +14 -0
  60. package/dist/components.js.map +1 -0
  61. package/dist/detectProject.d.ts +3 -0
  62. package/dist/detectProject.d.ts.map +1 -1
  63. package/dist/detectProject.js +16 -6
  64. package/dist/detectProject.js.map +1 -1
  65. package/dist/devServer.d.ts +5 -2
  66. package/dist/devServer.d.ts.map +1 -1
  67. package/dist/devServer.js +10 -6
  68. package/dist/devServer.js.map +1 -1
  69. package/dist/hash.d.ts +2 -0
  70. package/dist/hash.d.ts.map +1 -0
  71. package/dist/hash.js +22 -0
  72. package/dist/hash.js.map +1 -0
  73. package/dist/index.d.ts +24 -6
  74. package/dist/index.d.ts.map +1 -1
  75. package/dist/index.js +14 -3
  76. package/dist/index.js.map +1 -1
  77. package/dist/installer/bins.d.ts +24 -0
  78. package/dist/installer/bins.d.ts.map +1 -0
  79. package/dist/installer/bins.js +56 -0
  80. package/dist/installer/bins.js.map +1 -0
  81. package/dist/installer/index.d.ts +14 -0
  82. package/dist/installer/index.d.ts.map +1 -0
  83. package/dist/installer/index.js +9 -0
  84. package/dist/installer/index.js.map +1 -0
  85. package/dist/installer/install.d.ts +46 -0
  86. package/dist/installer/install.d.ts.map +1 -0
  87. package/dist/installer/install.js +245 -0
  88. package/dist/installer/install.js.map +1 -0
  89. package/dist/installer/lockfile.d.ts +49 -0
  90. package/dist/installer/lockfile.d.ts.map +1 -0
  91. package/dist/installer/lockfile.js +110 -0
  92. package/dist/installer/lockfile.js.map +1 -0
  93. package/dist/installer/registry.d.ts +84 -0
  94. package/dist/installer/registry.d.ts.map +1 -0
  95. package/dist/installer/registry.js +130 -0
  96. package/dist/installer/registry.js.map +1 -0
  97. package/dist/installer/semver.d.ts +38 -0
  98. package/dist/installer/semver.d.ts.map +1 -0
  99. package/dist/installer/semver.js +187 -0
  100. package/dist/installer/semver.js.map +1 -0
  101. package/dist/installer/spec.d.ts +30 -0
  102. package/dist/installer/spec.d.ts.map +1 -0
  103. package/dist/installer/spec.js +30 -0
  104. package/dist/installer/spec.js.map +1 -0
  105. package/dist/installer/tar.d.ts +17 -0
  106. package/dist/installer/tar.d.ts.map +1 -0
  107. package/dist/installer/tar.js +150 -0
  108. package/dist/installer/tar.js.map +1 -0
  109. package/dist/installer/tree.d.ts +48 -0
  110. package/dist/installer/tree.d.ts.map +1 -0
  111. package/dist/installer/tree.js +179 -0
  112. package/dist/installer/tree.js.map +1 -0
  113. package/dist/node/assert.d.ts +15 -0
  114. package/dist/node/assert.d.ts.map +1 -0
  115. package/dist/node/assert.js +137 -0
  116. package/dist/node/assert.js.map +1 -0
  117. package/dist/node/buffer.d.ts +84 -0
  118. package/dist/node/buffer.d.ts.map +1 -0
  119. package/dist/node/buffer.js +304 -0
  120. package/dist/node/buffer.js.map +1 -0
  121. package/dist/node/builtins.d.ts +25 -0
  122. package/dist/node/builtins.d.ts.map +1 -0
  123. package/dist/node/builtins.js +101 -0
  124. package/dist/node/builtins.js.map +1 -0
  125. package/dist/node/callable.d.ts +13 -0
  126. package/dist/node/callable.d.ts.map +1 -0
  127. package/dist/node/callable.js +26 -0
  128. package/dist/node/callable.js.map +1 -0
  129. package/dist/node/childProcess.d.ts +13 -0
  130. package/dist/node/childProcess.d.ts.map +1 -0
  131. package/dist/node/childProcess.js +143 -0
  132. package/dist/node/childProcess.js.map +1 -0
  133. package/dist/node/crypto.d.ts +3 -0
  134. package/dist/node/crypto.d.ts.map +1 -0
  135. package/dist/node/crypto.js +180 -0
  136. package/dist/node/crypto.js.map +1 -0
  137. package/dist/node/esm.d.ts +9 -0
  138. package/dist/node/esm.d.ts.map +1 -0
  139. package/dist/node/esm.js +202 -0
  140. package/dist/node/esm.js.map +1 -0
  141. package/dist/node/events.d.ts +40 -0
  142. package/dist/node/events.d.ts.map +1 -0
  143. package/dist/node/events.js +160 -0
  144. package/dist/node/events.js.map +1 -0
  145. package/dist/node/fs/index.d.ts +7 -0
  146. package/dist/node/fs/index.d.ts.map +1 -0
  147. package/dist/node/fs/index.js +270 -0
  148. package/dist/node/fs/index.js.map +1 -0
  149. package/dist/node/fs/stats.d.ts +93 -0
  150. package/dist/node/fs/stats.d.ts.map +1 -0
  151. package/dist/node/fs/stats.js +104 -0
  152. package/dist/node/fs/stats.js.map +1 -0
  153. package/dist/node/fs/sync.d.ts +98 -0
  154. package/dist/node/fs/sync.d.ts.map +1 -0
  155. package/dist/node/fs/sync.js +348 -0
  156. package/dist/node/fs/sync.js.map +1 -0
  157. package/dist/node/hashes.d.ts +15 -0
  158. package/dist/node/hashes.d.ts.map +1 -0
  159. package/dist/node/hashes.js +195 -0
  160. package/dist/node/hashes.js.map +1 -0
  161. package/dist/node/http/client.d.ts +35 -0
  162. package/dist/node/http/client.d.ts.map +1 -0
  163. package/dist/node/http/client.js +161 -0
  164. package/dist/node/http/client.js.map +1 -0
  165. package/dist/node/http/index.d.ts +5 -0
  166. package/dist/node/http/index.d.ts.map +1 -0
  167. package/dist/node/http/index.js +125 -0
  168. package/dist/node/http/index.js.map +1 -0
  169. package/dist/node/http/server.d.ts +135 -0
  170. package/dist/node/http/server.d.ts.map +1 -0
  171. package/dist/node/http/server.js +308 -0
  172. package/dist/node/http/server.js.map +1 -0
  173. package/dist/node/http/status.d.ts +3 -0
  174. package/dist/node/http/status.d.ts.map +1 -0
  175. package/dist/node/http/status.js +13 -0
  176. package/dist/node/http/status.js.map +1 -0
  177. package/dist/node/inspect.d.ts +22 -0
  178. package/dist/node/inspect.d.ts.map +1 -0
  179. package/dist/node/inspect.js +172 -0
  180. package/dist/node/inspect.js.map +1 -0
  181. package/dist/node/loader.d.ts +91 -0
  182. package/dist/node/loader.d.ts.map +1 -0
  183. package/dist/node/loader.js +265 -0
  184. package/dist/node/loader.js.map +1 -0
  185. package/dist/node/loop.d.ts +71 -0
  186. package/dist/node/loop.d.ts.map +1 -0
  187. package/dist/node/loop.js +189 -0
  188. package/dist/node/loop.js.map +1 -0
  189. package/dist/node/misc.d.ts +10 -0
  190. package/dist/node/misc.d.ts.map +1 -0
  191. package/dist/node/misc.js +256 -0
  192. package/dist/node/misc.js.map +1 -0
  193. package/dist/node/path.d.ts +14 -0
  194. package/dist/node/path.d.ts.map +1 -0
  195. package/dist/node/path.js +128 -0
  196. package/dist/node/path.js.map +1 -0
  197. package/dist/node/process.d.ts +31 -0
  198. package/dist/node/process.d.ts.map +1 -0
  199. package/dist/node/process.js +309 -0
  200. package/dist/node/process.js.map +1 -0
  201. package/dist/node/program.d.ts +19 -0
  202. package/dist/node/program.d.ts.map +1 -0
  203. package/dist/node/program.js +165 -0
  204. package/dist/node/program.js.map +1 -0
  205. package/dist/node/readline.d.ts +2 -0
  206. package/dist/node/readline.d.ts.map +1 -0
  207. package/dist/node/readline.js +155 -0
  208. package/dist/node/readline.js.map +1 -0
  209. package/dist/node/resolve.d.ts +45 -0
  210. package/dist/node/resolve.d.ts.map +1 -0
  211. package/dist/node/resolve.js +206 -0
  212. package/dist/node/resolve.js.map +1 -0
  213. package/dist/node/runtime.d.ts +40 -0
  214. package/dist/node/runtime.d.ts.map +1 -0
  215. package/dist/node/runtime.js +149 -0
  216. package/dist/node/runtime.js.map +1 -0
  217. package/dist/node/stream/index.d.ts +52 -0
  218. package/dist/node/stream/index.d.ts.map +1 -0
  219. package/dist/node/stream/index.js +184 -0
  220. package/dist/node/stream/index.js.map +1 -0
  221. package/dist/node/stream/readable.d.ts +83 -0
  222. package/dist/node/stream/readable.d.ts.map +1 -0
  223. package/dist/node/stream/readable.js +325 -0
  224. package/dist/node/stream/readable.js.map +1 -0
  225. package/dist/node/stream/writable.d.ts +80 -0
  226. package/dist/node/stream/writable.d.ts.map +1 -0
  227. package/dist/node/stream/writable.js +224 -0
  228. package/dist/node/stream/writable.js.map +1 -0
  229. package/dist/node/urlqs.d.ts +22 -0
  230. package/dist/node/urlqs.d.ts.map +1 -0
  231. package/dist/node/urlqs.js +165 -0
  232. package/dist/node/urlqs.js.map +1 -0
  233. package/dist/node/util.d.ts +37 -0
  234. package/dist/node/util.d.ts.map +1 -0
  235. package/dist/node/util.js +160 -0
  236. package/dist/node/util.js.map +1 -0
  237. package/dist/node/zlib.d.ts +2 -0
  238. package/dist/node/zlib.d.ts.map +1 -0
  239. package/dist/node/zlib.js +133 -0
  240. package/dist/node/zlib.js.map +1 -0
  241. package/dist/packageCdn.d.ts +12 -0
  242. package/dist/packageCdn.d.ts.map +1 -1
  243. package/dist/packageCdn.js +3 -1
  244. package/dist/packageCdn.js.map +1 -1
  245. package/dist/projectConfig.d.ts +6 -2
  246. package/dist/projectConfig.d.ts.map +1 -1
  247. package/dist/projectConfig.js +32 -8
  248. package/dist/projectConfig.js.map +1 -1
  249. package/dist/shell/builtins.d.ts +23 -0
  250. package/dist/shell/builtins.d.ts.map +1 -0
  251. package/dist/shell/builtins.js +131 -0
  252. package/dist/shell/builtins.js.map +1 -0
  253. package/dist/shell/context.d.ts +41 -0
  254. package/dist/shell/context.d.ts.map +1 -0
  255. package/dist/shell/context.js +65 -0
  256. package/dist/shell/context.js.map +1 -0
  257. package/dist/shell/devHandoff.d.ts +18 -0
  258. package/dist/shell/devHandoff.d.ts.map +1 -0
  259. package/dist/shell/devHandoff.js +36 -0
  260. package/dist/shell/devHandoff.js.map +1 -0
  261. package/dist/shell/exec.d.ts +67 -0
  262. package/dist/shell/exec.d.ts.map +1 -0
  263. package/dist/shell/exec.js +247 -0
  264. package/dist/shell/exec.js.map +1 -0
  265. package/dist/shell/expand.d.ts +28 -0
  266. package/dist/shell/expand.d.ts.map +1 -0
  267. package/dist/shell/expand.js +149 -0
  268. package/dist/shell/expand.js.map +1 -0
  269. package/dist/shell/fileCommands.d.ts +10 -0
  270. package/dist/shell/fileCommands.d.ts.map +1 -0
  271. package/dist/shell/fileCommands.js +208 -0
  272. package/dist/shell/fileCommands.js.map +1 -0
  273. package/dist/shell/lineEditor.d.ts +43 -0
  274. package/dist/shell/lineEditor.d.ts.map +1 -0
  275. package/dist/shell/lineEditor.js +234 -0
  276. package/dist/shell/lineEditor.js.map +1 -0
  277. package/dist/shell/npm.d.ts +6 -0
  278. package/dist/shell/npm.d.ts.map +1 -0
  279. package/dist/shell/npm.js +234 -0
  280. package/dist/shell/npm.js.map +1 -0
  281. package/dist/shell/parse.d.ts +73 -0
  282. package/dist/shell/parse.d.ts.map +1 -0
  283. package/dist/shell/parse.js +300 -0
  284. package/dist/shell/parse.js.map +1 -0
  285. package/dist/shell/program.d.ts +12 -0
  286. package/dist/shell/program.d.ts.map +1 -0
  287. package/dist/shell/program.js +219 -0
  288. package/dist/shell/program.js.map +1 -0
  289. package/dist/shell/textCommands.d.ts +15 -0
  290. package/dist/shell/textCommands.d.ts.map +1 -0
  291. package/dist/shell/textCommands.js +185 -0
  292. package/dist/shell/textCommands.js.map +1 -0
  293. package/dist/styleModules.d.ts +6 -0
  294. package/dist/styleModules.d.ts.map +1 -1
  295. package/dist/styleModules.js +14 -4
  296. package/dist/styleModules.js.map +1 -1
  297. package/dist/svelteCompiler.d.ts +28 -0
  298. package/dist/svelteCompiler.d.ts.map +1 -0
  299. package/dist/svelteCompiler.js +13 -0
  300. package/dist/svelteCompiler.js.map +1 -0
  301. package/dist/system/commands.d.ts +26 -0
  302. package/dist/system/commands.d.ts.map +1 -0
  303. package/dist/system/commands.js +53 -0
  304. package/dist/system/commands.js.map +1 -0
  305. package/dist/system/index.d.ts +23 -0
  306. package/dist/system/index.d.ts.map +1 -0
  307. package/dist/system/index.js +16 -0
  308. package/dist/system/index.js.map +1 -0
  309. package/dist/system/kernel.d.ts +54 -0
  310. package/dist/system/kernel.d.ts.map +1 -0
  311. package/dist/system/kernel.js +81 -0
  312. package/dist/system/kernel.js.map +1 -0
  313. package/dist/system/ports.d.ts +32 -0
  314. package/dist/system/ports.d.ts.map +1 -0
  315. package/dist/system/ports.js +57 -0
  316. package/dist/system/ports.js.map +1 -0
  317. package/dist/system/process.d.ts +32 -0
  318. package/dist/system/process.d.ts.map +1 -0
  319. package/dist/system/process.js +130 -0
  320. package/dist/system/process.js.map +1 -0
  321. package/dist/system/programs.d.ts +10 -0
  322. package/dist/system/programs.d.ts.map +1 -0
  323. package/dist/system/programs.js +22 -0
  324. package/dist/system/programs.js.map +1 -0
  325. package/dist/system/terminal.d.ts +45 -0
  326. package/dist/system/terminal.d.ts.map +1 -0
  327. package/dist/system/terminal.js +127 -0
  328. package/dist/system/terminal.js.map +1 -0
  329. package/dist/system/types.d.ts +100 -0
  330. package/dist/system/types.d.ts.map +1 -0
  331. package/dist/system/types.js +2 -0
  332. package/dist/system/types.js.map +1 -0
  333. package/dist/vfs.d.ts +28 -4
  334. package/dist/vfs.d.ts.map +1 -1
  335. package/dist/vfs.js +95 -32
  336. package/dist/vfs.js.map +1 -1
  337. package/dist/vueCompiler.d.ts +75 -0
  338. package/dist/vueCompiler.d.ts.map +1 -0
  339. package/dist/vueCompiler.js +67 -0
  340. package/dist/vueCompiler.js.map +1 -0
  341. package/package.json +18 -2
  342. package/src/build.ts +105 -0
  343. package/src/buildHost.ts +154 -0
  344. package/src/buildHtml.ts +101 -0
  345. package/src/bundler.ts +190 -0
  346. package/src/check/acquire.ts +212 -0
  347. package/src/check/compilerHost.ts +138 -0
  348. package/src/check/diagnostics.ts +48 -0
  349. package/src/check/index.ts +28 -0
  350. package/src/check/projects.ts +149 -0
  351. package/src/check/shims.ts +88 -0
  352. package/src/check/typeStore.ts +103 -0
  353. package/src/check/typecheck.ts +134 -0
  354. package/src/compileComponent.ts +21 -0
  355. package/src/compileScript.ts +42 -13
  356. package/src/components.ts +59 -0
  357. package/src/detectProject.ts +18 -6
  358. package/src/devServer.ts +13 -8
  359. package/src/hash.ts +22 -0
  360. package/src/index.ts +35 -6
  361. package/src/installer/bins.ts +64 -0
  362. package/src/installer/index.ts +13 -0
  363. package/src/installer/install.ts +275 -0
  364. package/src/installer/lockfile.ts +146 -0
  365. package/src/installer/registry.ts +175 -0
  366. package/src/installer/semver.ts +190 -0
  367. package/src/installer/spec.ts +53 -0
  368. package/src/installer/tar.ts +155 -0
  369. package/src/installer/tree.ts +236 -0
  370. package/src/node/assert.ts +127 -0
  371. package/src/node/buffer.ts +304 -0
  372. package/src/node/builtins.ts +123 -0
  373. package/src/node/callable.ts +25 -0
  374. package/src/node/childProcess.ts +164 -0
  375. package/src/node/crypto.ts +187 -0
  376. package/src/node/esm.ts +196 -0
  377. package/src/node/events.ts +176 -0
  378. package/src/node/fs/index.ts +277 -0
  379. package/src/node/fs/stats.ts +110 -0
  380. package/src/node/fs/sync.ts +335 -0
  381. package/src/node/hashes.ts +171 -0
  382. package/src/node/http/client.ts +173 -0
  383. package/src/node/http/index.ts +122 -0
  384. package/src/node/http/server.ts +308 -0
  385. package/src/node/http/status.ts +13 -0
  386. package/src/node/inspect.ts +166 -0
  387. package/src/node/loader.ts +293 -0
  388. package/src/node/loop.ts +193 -0
  389. package/src/node/misc.ts +250 -0
  390. package/src/node/path.ts +135 -0
  391. package/src/node/process.ts +324 -0
  392. package/src/node/program.ts +170 -0
  393. package/src/node/readline.ts +157 -0
  394. package/src/node/resolve.ts +216 -0
  395. package/src/node/runtime.ts +184 -0
  396. package/src/node/stream/index.ts +196 -0
  397. package/src/node/stream/readable.ts +345 -0
  398. package/src/node/stream/writable.ts +244 -0
  399. package/src/node/urlqs.ts +162 -0
  400. package/src/node/util.ts +145 -0
  401. package/src/node/zlib.ts +145 -0
  402. package/src/packageCdn.ts +11 -1
  403. package/src/projectConfig.ts +34 -7
  404. package/src/shell/builtins.ts +141 -0
  405. package/src/shell/context.ts +89 -0
  406. package/src/shell/devHandoff.ts +49 -0
  407. package/src/shell/exec.ts +268 -0
  408. package/src/shell/expand.ts +145 -0
  409. package/src/shell/fileCommands.ts +206 -0
  410. package/src/shell/lineEditor.ts +233 -0
  411. package/src/shell/npm.ts +229 -0
  412. package/src/shell/parse.ts +295 -0
  413. package/src/shell/program.ts +223 -0
  414. package/src/shell/textCommands.ts +177 -0
  415. package/src/styleModules.ts +15 -4
  416. package/src/svelteCompiler.ts +31 -0
  417. package/src/system/commands.ts +72 -0
  418. package/src/system/index.ts +22 -0
  419. package/src/system/kernel.ts +106 -0
  420. package/src/system/ports.ts +73 -0
  421. package/src/system/process.ts +158 -0
  422. package/src/system/programs.ts +31 -0
  423. package/src/system/terminal.ts +135 -0
  424. package/src/system/types.ts +103 -0
  425. package/src/vfs.ts +92 -28
  426. package/src/vueCompiler.ts +121 -0
@@ -0,0 +1,206 @@
1
+ /**
2
+ * File coreutils over the VFS: ls, cat, mkdir, rm, cp, mv, touch, pwd. Errors
3
+ * read like GNU coreutils (`rm: cannot remove 'x': No such file or directory`)
4
+ * because people (and agents) pattern-match on them.
5
+ */
6
+ import type { VirtualFileSystem } from '../vfs.js';
7
+ import { basename, dirname, join } from '../paths.js';
8
+ import { parseFlags, resolvePath, type Command } from './context.js';
9
+
10
+ const encoder = new TextEncoder();
11
+ const sizeOf = (fs: VirtualFileSystem, path: string) => {
12
+ const c = fs.readFile(path);
13
+ return c === undefined ? 4096 : typeof c === 'string' ? encoder.encode(c).length : c.length;
14
+ };
15
+
16
+ const MONTHS = ['Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun', 'Jul', 'Aug', 'Sep', 'Oct', 'Nov', 'Dec'];
17
+ function lsDate(ms: number): string {
18
+ const d = new Date(ms);
19
+ return `${MONTHS[d.getMonth()]} ${String(d.getDate()).padStart(2)} ${String(d.getHours()).padStart(2, '0')}:${String(d.getMinutes()).padStart(2, '0')}`;
20
+ }
21
+
22
+ export const ls: Command = async (ctx) => {
23
+ const { flags, operands } = parseFlags(ctx.args);
24
+ const all = flags.has('a') || flags.has('A');
25
+ const long = flags.has('l');
26
+ const one = flags.has('1') || !ctx.io.isTerminal;
27
+ const targets = operands.length ? operands : ['.'];
28
+ let status = 0;
29
+ const colour = (name: string, dir: boolean) => (ctx.io.isTerminal && dir ? `\x1b[1;34m${name}\x1b[0m` : name);
30
+ const render = (dir: string, names: string[]) => {
31
+ if (long) {
32
+ const lines = names.map((name) => {
33
+ const path = name === '.' ? dir : name === '..' ? dirname(dir) : join(dir, name);
34
+ const isDir = ctx.fs.isDirectory(path);
35
+ const size = isDir ? 4096 : sizeOf(ctx.fs, path);
36
+ return `${isDir ? 'drwxr-xr-x' : '-rw-r--r--'} 1 user user ${String(size).padStart(8)} ${lsDate(ctx.fs.mtime(path) ?? Date.now())} ${colour(name, isDir)}`;
37
+ });
38
+ return lines.length ? `total ${names.length}\n${lines.join('\n')}\n` : 'total 0\n';
39
+ }
40
+ const shown = names.map((n) => colour(n, ctx.fs.isDirectory(join(dir, n))));
41
+ return shown.length ? shown.join(one ? '\n' : ' ') + '\n' : '';
42
+ };
43
+ for (const [index, target] of targets.entries()) {
44
+ const path = resolvePath(ctx, target);
45
+ if (ctx.fs.isFile(path)) {
46
+ ctx.io.stdout(render(dirname(path), [basename(path)]).replace(basename(path), target));
47
+ continue;
48
+ }
49
+ if (!ctx.fs.isDirectory(path)) {
50
+ ctx.io.stderr(`ls: cannot access '${target}': No such file or directory\n`);
51
+ status = 2;
52
+ continue;
53
+ }
54
+ let names = ctx.fs.readdir(path).filter((n) => all || !n.startsWith('.'));
55
+ if (flags.has('a')) names = ['.', '..', ...names];
56
+ if (targets.length > 1) ctx.io.stdout(`${index ? '\n' : ''}${target}:\n`);
57
+ ctx.io.stdout(render(path, names));
58
+ }
59
+ return status;
60
+ };
61
+
62
+ export const cat: Command = async (ctx) => {
63
+ const { operands } = parseFlags(ctx.args);
64
+ if (!operands.length || (operands.length === 1 && operands[0] === '-')) {
65
+ ctx.io.stdout(await ctx.io.stdin.readAll());
66
+ return 0;
67
+ }
68
+ let status = 0;
69
+ for (const file of operands) {
70
+ const path = resolvePath(ctx, file);
71
+ if (ctx.fs.isDirectory(path)) {
72
+ ctx.io.stderr(`cat: ${file}: Is a directory\n`);
73
+ status = 1;
74
+ } else if (!ctx.fs.isFile(path)) {
75
+ ctx.io.stderr(`cat: ${file}: No such file or directory\n`);
76
+ status = 1;
77
+ } else ctx.io.stdout(ctx.fs.readText(path) ?? '');
78
+ }
79
+ return status;
80
+ };
81
+
82
+ export const mkdir: Command = async (ctx) => {
83
+ const { flags, operands } = parseFlags(ctx.args, ['m']);
84
+ if (!operands.length) {
85
+ ctx.io.stderr('mkdir: missing operand\n');
86
+ return 1;
87
+ }
88
+ let status = 0;
89
+ for (const dir of operands) {
90
+ const path = resolvePath(ctx, dir);
91
+ if (ctx.fs.exists(path)) {
92
+ if (flags.has('p') && ctx.fs.isDirectory(path)) continue;
93
+ ctx.io.stderr(`mkdir: cannot create directory '${dir}': File exists\n`);
94
+ status = 1;
95
+ } else if (!flags.has('p') && !ctx.fs.isDirectory(dirname(path))) {
96
+ ctx.io.stderr(`mkdir: cannot create directory '${dir}': No such file or directory\n`);
97
+ status = 1;
98
+ } else ctx.fs.mkdir(path);
99
+ }
100
+ return status;
101
+ };
102
+
103
+ export const rm: Command = async (ctx) => {
104
+ const { flags, operands } = parseFlags(ctx.args);
105
+ const recursive = flags.has('r') || flags.has('R') || flags.has('recursive');
106
+ const force = flags.has('f') || flags.has('force');
107
+ if (!operands.length && !force) {
108
+ ctx.io.stderr('rm: missing operand\n');
109
+ return 1;
110
+ }
111
+ let status = 0;
112
+ for (const target of operands) {
113
+ const path = resolvePath(ctx, target);
114
+ if (path === '/') {
115
+ ctx.io.stderr(`rm: it is dangerous to operate recursively on '/'\n`);
116
+ status = 1;
117
+ } else if (!ctx.fs.exists(path)) {
118
+ if (!force) {
119
+ ctx.io.stderr(`rm: cannot remove '${target}': No such file or directory\n`);
120
+ status = 1;
121
+ }
122
+ } else if (ctx.fs.isDirectory(path) && !recursive) {
123
+ ctx.io.stderr(`rm: cannot remove '${target}': Is a directory\n`);
124
+ status = 1;
125
+ } else ctx.fs.rm(path);
126
+ }
127
+ return status;
128
+ };
129
+
130
+ function copyTree(fs: VirtualFileSystem, from: string, to: string): void {
131
+ if (fs.isFile(from)) {
132
+ const c = fs.readFile(from)!;
133
+ fs.writeFile(to, typeof c === 'string' ? c : c.slice());
134
+ return;
135
+ }
136
+ fs.mkdir(to);
137
+ for (const name of fs.readdir(from)) copyTree(fs, join(from, name), join(to, name));
138
+ }
139
+
140
+ /** cp and mv share argument handling: `SRC… DEST` where DEST may be a directory. */
141
+ function transfer(verb: 'cp' | 'mv'): Command {
142
+ return async (ctx) => {
143
+ const { flags, operands } = parseFlags(ctx.args);
144
+ if (operands.length < 2) {
145
+ ctx.io.stderr(`${verb}: missing destination file operand\n`);
146
+ return 1;
147
+ }
148
+ const dest = resolvePath(ctx, operands[operands.length - 1]!);
149
+ const sources = operands.slice(0, -1);
150
+ const destIsDir = ctx.fs.isDirectory(dest);
151
+ if (sources.length > 1 && !destIsDir) {
152
+ ctx.io.stderr(`${verb}: target '${operands[operands.length - 1]}' is not a directory\n`);
153
+ return 1;
154
+ }
155
+ let status = 0;
156
+ for (const source of sources) {
157
+ const from = resolvePath(ctx, source);
158
+ const to = destIsDir ? join(dest, basename(from)) : dest;
159
+ if (!ctx.fs.exists(from)) {
160
+ ctx.io.stderr(`${verb}: cannot stat '${source}': No such file or directory\n`);
161
+ status = 1;
162
+ continue;
163
+ }
164
+ if (verb === 'cp' && ctx.fs.isDirectory(from) && !(flags.has('r') || flags.has('R') || flags.has('a'))) {
165
+ ctx.io.stderr(`cp: -r not specified; omitting directory '${source}'\n`);
166
+ status = 1;
167
+ continue;
168
+ }
169
+ if (to === from || to.startsWith(from + '/')) {
170
+ ctx.io.stderr(`${verb}: cannot ${verb === 'cp' ? 'copy' : 'move'} '${source}' into itself\n`);
171
+ status = 1;
172
+ continue;
173
+ }
174
+ if (ctx.fs.isFile(to) && ctx.fs.isDirectory(from)) {
175
+ ctx.io.stderr(`${verb}: cannot overwrite non-directory '${to}' with directory '${source}'\n`);
176
+ status = 1;
177
+ continue;
178
+ }
179
+ if (ctx.fs.isFile(to)) ctx.fs.rm(to);
180
+ copyTree(ctx.fs, from, to);
181
+ if (verb === 'mv') ctx.fs.rm(from);
182
+ }
183
+ return status;
184
+ };
185
+ }
186
+
187
+ export const cp = transfer('cp');
188
+ export const mv = transfer('mv');
189
+
190
+ export const touch: Command = async (ctx) => {
191
+ const { operands } = parseFlags(ctx.args);
192
+ let status = 0;
193
+ for (const file of operands) {
194
+ const path = resolvePath(ctx, file);
195
+ if (!ctx.fs.isDirectory(dirname(path))) {
196
+ ctx.io.stderr(`touch: cannot touch '${file}': No such file or directory\n`);
197
+ status = 1;
198
+ } else if (!ctx.fs.isDirectory(path)) ctx.fs.writeFile(path, ctx.fs.readFile(path) ?? '');
199
+ }
200
+ return status;
201
+ };
202
+
203
+ export const pwd: Command = async (ctx) => {
204
+ ctx.io.stdout(ctx.cwd + '\n');
205
+ return 0;
206
+ };
@@ -0,0 +1,233 @@
1
+ /**
2
+ * Readline-style line editing for an xterm.js terminal in raw mode: insert and
3
+ * delete at the cursor, ←/→, Home/End (and Ctrl-A/E), ↑/↓ history, Ctrl-U/K/W,
4
+ * Ctrl-L, Tab completion, Ctrl-C (cancel the line) and Ctrl-D (end of input).
5
+ *
6
+ * `feed` consumes input up to the first completed line and hands back the rest,
7
+ * so a pasted block of several commands runs them one at a time.
8
+ */
9
+
10
+ export type EditorEvent = { type: 'line'; line: string } | { type: 'interrupt' } | { type: 'eof' };
11
+
12
+ export interface LineEditorOptions {
13
+ write(text: string): void;
14
+ history: string[];
15
+ /** Candidates for the word ending at the cursor (`word` is that word). */
16
+ complete?(line: string, word: string): string[];
17
+ }
18
+
19
+ const KEYS: Array<[RegExp, string]> = [
20
+ [/^\x1b\[(?:1;\d)?A/, 'up'],
21
+ [/^\x1b\[(?:1;\d)?B/, 'down'],
22
+ [/^\x1b\[1;[35]C|^\x1bf/, 'wordRight'],
23
+ [/^\x1b\[1;[35]D|^\x1bb/, 'wordLeft'],
24
+ [/^\x1b\[C|^\x1bOC/, 'right'],
25
+ [/^\x1b\[D|^\x1bOD/, 'left'],
26
+ [/^\x1b\[(?:H|1~|7~)|^\x1bOH/, 'home'],
27
+ [/^\x1b\[(?:F|4~|8~)|^\x1bOF/, 'end'],
28
+ [/^\x1b\[3~/, 'delete'],
29
+ [/^\x1b\[[0-9;]*[~A-Za-z]|^\x1bO[A-Za-z]|^\x1b./, 'ignore'],
30
+ ];
31
+
32
+ function commonPrefix(words: string[]): string {
33
+ if (!words.length) return '';
34
+ let prefix = words[0]!;
35
+ for (const w of words) while (!w.startsWith(prefix)) prefix = prefix.slice(0, -1);
36
+ return prefix;
37
+ }
38
+
39
+ export class LineEditor {
40
+ line = '';
41
+ cursor = 0;
42
+ prompt = '';
43
+ private historyIndex = -1;
44
+ private draft = '';
45
+
46
+ constructor(private readonly options: LineEditorOptions) {}
47
+
48
+ /** Show `prompt` and start a fresh line. */
49
+ begin(prompt: string): void {
50
+ this.prompt = prompt;
51
+ this.line = '';
52
+ this.cursor = 0;
53
+ this.historyIndex = -1;
54
+ this.options.write(prompt);
55
+ }
56
+
57
+ feed(data: string): { event?: EditorEvent; rest: string } {
58
+ let i = 0;
59
+ while (i < data.length) {
60
+ const ch = data[i]!;
61
+ if (ch === '\x1b') {
62
+ const slice = data.slice(i);
63
+ const key = KEYS.find(([re]) => re.test(slice));
64
+ const match = key ? key[0].exec(slice)![0] : '\x1b';
65
+ i += match.length;
66
+ this.key(key?.[1] ?? 'ignore');
67
+ continue;
68
+ }
69
+ i++;
70
+ switch (ch) {
71
+ case '\r':
72
+ case '\n': {
73
+ if (ch === '\r' && data[i] === '\n') i++;
74
+ const line = this.line;
75
+ this.options.write('\r\n');
76
+ if (line.trim() && this.options.history[this.options.history.length - 1] !== line) this.options.history.push(line);
77
+ this.line = '';
78
+ this.cursor = 0;
79
+ return { event: { type: 'line', line }, rest: data.slice(i) };
80
+ }
81
+ case '\x03':
82
+ this.options.write('^C\r\n');
83
+ this.line = '';
84
+ this.cursor = 0;
85
+ return { event: { type: 'interrupt' }, rest: data.slice(i) };
86
+ case '\x04':
87
+ if (!this.line) return { event: { type: 'eof' }, rest: data.slice(i) };
88
+ this.key('delete');
89
+ break;
90
+ case '\x7f':
91
+ case '\b':
92
+ if (this.cursor > 0) {
93
+ this.line = this.line.slice(0, this.cursor - 1) + this.line.slice(this.cursor);
94
+ this.cursor--;
95
+ this.redraw();
96
+ }
97
+ break;
98
+ case '\x01':
99
+ this.key('home');
100
+ break;
101
+ case '\x05':
102
+ this.key('end');
103
+ break;
104
+ case '\x02':
105
+ this.key('left');
106
+ break;
107
+ case '\x06':
108
+ this.key('right');
109
+ break;
110
+ case '\x10':
111
+ this.key('up');
112
+ break;
113
+ case '\x0e':
114
+ this.key('down');
115
+ break;
116
+ case '\x15':
117
+ this.line = this.line.slice(this.cursor);
118
+ this.cursor = 0;
119
+ this.redraw();
120
+ break;
121
+ case '\x0b':
122
+ this.line = this.line.slice(0, this.cursor);
123
+ this.redraw();
124
+ break;
125
+ case '\x17': {
126
+ const before = this.line.slice(0, this.cursor).replace(/\S+\s*$/, '');
127
+ this.line = before + this.line.slice(this.cursor);
128
+ this.cursor = before.length;
129
+ this.redraw();
130
+ break;
131
+ }
132
+ case '\x0c':
133
+ this.options.write('\x1b[2J\x1b[H');
134
+ this.redraw();
135
+ break;
136
+ case '\t':
137
+ this.complete();
138
+ break;
139
+ default:
140
+ if (ch >= ' ') this.insert(ch);
141
+ }
142
+ }
143
+ return { rest: '' };
144
+ }
145
+
146
+ private insert(text: string): void {
147
+ this.line = this.line.slice(0, this.cursor) + text + this.line.slice(this.cursor);
148
+ this.cursor += text.length;
149
+ if (this.cursor === this.line.length) this.options.write(text);
150
+ else this.redraw();
151
+ }
152
+
153
+ private key(name: string): void {
154
+ const history = this.options.history;
155
+ switch (name) {
156
+ case 'left':
157
+ if (this.cursor > 0) {
158
+ this.cursor--;
159
+ this.options.write('\x1b[D');
160
+ }
161
+ return;
162
+ case 'right':
163
+ if (this.cursor < this.line.length) {
164
+ this.cursor++;
165
+ this.options.write('\x1b[C');
166
+ }
167
+ return;
168
+ case 'wordLeft':
169
+ this.cursor = this.line.slice(0, this.cursor).replace(/\S+\s*$/, '').length;
170
+ return this.redraw();
171
+ case 'wordRight': {
172
+ const m = /^\s*\S+/.exec(this.line.slice(this.cursor));
173
+ this.cursor += m ? m[0].length : 0;
174
+ return this.redraw();
175
+ }
176
+ case 'home':
177
+ this.cursor = 0;
178
+ return this.redraw();
179
+ case 'end':
180
+ this.cursor = this.line.length;
181
+ return this.redraw();
182
+ case 'delete':
183
+ if (this.cursor < this.line.length) {
184
+ this.line = this.line.slice(0, this.cursor) + this.line.slice(this.cursor + 1);
185
+ this.redraw();
186
+ }
187
+ return;
188
+ case 'up':
189
+ if (!history.length) return;
190
+ if (this.historyIndex === -1) {
191
+ this.draft = this.line;
192
+ this.historyIndex = history.length - 1;
193
+ } else if (this.historyIndex > 0) this.historyIndex--;
194
+ this.line = history[this.historyIndex]!;
195
+ this.cursor = this.line.length;
196
+ return this.redraw();
197
+ case 'down':
198
+ if (this.historyIndex === -1) return;
199
+ this.historyIndex++;
200
+ if (this.historyIndex >= history.length) {
201
+ this.historyIndex = -1;
202
+ this.line = this.draft;
203
+ } else this.line = history[this.historyIndex]!;
204
+ this.cursor = this.line.length;
205
+ return this.redraw();
206
+ }
207
+ }
208
+
209
+ private complete(): void {
210
+ const before = this.line.slice(0, this.cursor);
211
+ const word = /\S*$/.exec(before)![0];
212
+ const candidates = this.options.complete?.(before, word) ?? [];
213
+ if (!candidates.length) return;
214
+ if (candidates.length === 1) {
215
+ const only = candidates[0]!;
216
+ this.insert(only.slice(word.length) + (only.endsWith('/') ? '' : ' '));
217
+ return;
218
+ }
219
+ const prefix = commonPrefix(candidates);
220
+ if (prefix.length > word.length) {
221
+ this.insert(prefix.slice(word.length));
222
+ return;
223
+ }
224
+ this.options.write('\r\n' + candidates.map((c) => c.slice(c.lastIndexOf('/', c.length - 2) + 1)).join(' ') + '\r\n');
225
+ this.redraw();
226
+ }
227
+
228
+ /** Repaint the prompt and line, then put the terminal cursor back where ours is. */
229
+ redraw(): void {
230
+ const back = this.line.length - this.cursor;
231
+ this.options.write(`\r\x1b[K${this.prompt}${this.line}${back ? `\x1b[${back}D` : ''}`);
232
+ }
233
+ }
@@ -0,0 +1,229 @@
1
+ /**
2
+ * `npm` and `npx`, in-process in the shell. Installs go through the core
3
+ * installer (installer/); scripts run through a child shell with npm's
4
+ * environment (`node_modules/.bin` on PATH, `npm_lifecycle_event`, …) and
5
+ * pre/post hooks. `npx` runs an installed bin, or installs the package into a
6
+ * scratch directory first, as npx does.
7
+ */
8
+ import { join } from '../paths.js';
9
+ import { installPackages, type InstallOptions } from '../installer/install.js';
10
+ import type { PackageJson } from '../installer/spec.js';
11
+ import { binDirectories, findExecutable } from '../system/commands.js';
12
+ import { findPackageScope } from '../node/resolve.js';
13
+ import { parseFlags, type CommandContext } from './context.js';
14
+ import type { JobInput, Shell } from './exec.js';
15
+ import { matchDevServer } from './devHandoff.js';
16
+
17
+ export const NPM_VERSION = '10.8.2';
18
+
19
+ const quote = (arg: string) => (/^[\w@%+=:,./-]+$/.test(arg) ? arg : `'${arg.replace(/'/g, `'\\''`)}'`);
20
+
21
+ function projectDir(ctx: CommandContext): string {
22
+ return findPackageScope(ctx.fs, ctx.cwd)?.dir ?? ctx.cwd;
23
+ }
24
+
25
+ function readPackage(ctx: CommandContext, dir: string): PackageJson | undefined {
26
+ try {
27
+ return JSON.parse(ctx.fs.readText(join(dir, 'package.json')) ?? 'null') ?? undefined;
28
+ } catch {
29
+ return undefined;
30
+ }
31
+ }
32
+
33
+ async function install(ctx: CommandContext, options: Partial<InstallOptions>, dir = projectDir(ctx)): Promise<number> {
34
+ const started = performance.now();
35
+ const progress = ctx.io.isTerminal;
36
+ try {
37
+ const result = await installPackages({
38
+ fs: ctx.fs,
39
+ fetch: (url, init) => ctx.system.fetch(url, init as RequestInit),
40
+ registry: ctx.env.npm_config_registry ?? ctx.system.registry,
41
+ cache: ctx.system.packageCache,
42
+ cwd: dir,
43
+ signal: ctx.signal,
44
+ onProgress: progress ? (p) => p.phase === 'fetch' && ctx.io.stdout(`\r\x1b[Kfetching ${p.done}/${p.total} ${p.package ?? ''}`) : undefined,
45
+ ...options,
46
+ });
47
+ if (progress) ctx.io.stdout('\r\x1b[K');
48
+ for (const warning of result.warnings) ctx.io.stderr(`npm warn ${warning}\n`);
49
+ const seconds = ((performance.now() - started) / 1000).toFixed(1);
50
+ const parts = [result.added && `added ${result.added} package${result.added === 1 ? '' : 's'}`, result.removed && `removed ${result.removed} package${result.removed === 1 ? '' : 's'}`].filter(Boolean);
51
+ ctx.io.stdout(`\n${parts.length ? parts.join(', ') : 'up to date'}, audited ${result.total + 1} packages in ${seconds}s\n`);
52
+ return 0;
53
+ } catch (error) {
54
+ if (progress) ctx.io.stdout('\r\x1b[K');
55
+ if (ctx.signal.aborted) return 130;
56
+ ctx.io.stderr(`npm error ${error instanceof Error ? error.message : String(error)}\n`);
57
+ return 1;
58
+ }
59
+ }
60
+
61
+ async function runScript(ctx: CommandContext, shell: Shell, input: JobInput, name: string, extra: string[]): Promise<number> {
62
+ const dir = projectDir(ctx);
63
+ const pkg = readPackage(ctx, dir);
64
+ if (!pkg) {
65
+ ctx.io.stderr(`npm error Could not read package.json in ${dir}\n`);
66
+ return 1;
67
+ }
68
+ const scripts = { ...pkg.scripts };
69
+ if (name === 'start' && !scripts.start && ctx.fs.isFile(join(dir, 'server.js'))) scripts.start = 'node server.js';
70
+ if (!scripts[name]) {
71
+ if (name === 'test') {
72
+ ctx.io.stderr('npm error Missing script: "test"\n');
73
+ return 1;
74
+ }
75
+ ctx.io.stderr(`npm error Missing script: "${name}"\n\nTo see a list of scripts, run:\n npm run\n`);
76
+ return 1;
77
+ }
78
+ const PATH = [...binDirectories(dir), ctx.env.PATH ?? ''].filter(Boolean).join(':');
79
+ for (const stage of [`pre${name}`, name, `post${name}`]) {
80
+ const script = scripts[stage];
81
+ if (!script) continue;
82
+ const command = stage === name && extra.length ? `${script} ${extra.map(quote).join(' ')}` : script;
83
+ ctx.io.stdout(`\n> ${pkg.name ?? ''}${pkg.version ? '@' + pkg.version : ''} ${stage}\n> ${command}\n\n`);
84
+ const child = shell.fork({
85
+ cwd: dir,
86
+ env: {
87
+ PATH,
88
+ npm_lifecycle_event: stage,
89
+ npm_lifecycle_script: script,
90
+ npm_command: 'run-script',
91
+ npm_package_name: pkg.name ?? '',
92
+ npm_package_version: pkg.version ?? '',
93
+ npm_package_json: join(dir, 'package.json'),
94
+ INIT_CWD: ctx.cwd,
95
+ NODE: '/usr/local/bin/node',
96
+ },
97
+ });
98
+ const status = await child.exec(command, { stdout: ctx.io.stdout, stderr: ctx.io.stderr, isTerminal: ctx.io.isTerminal }, input, ctx.signal);
99
+ if (status !== 0) {
100
+ if (!ctx.signal.aborted) ctx.io.stderr(`npm error Lifecycle script \`${stage}\` failed with error:\nnpm error code ${status}\n`);
101
+ return status;
102
+ }
103
+ }
104
+ return 0;
105
+ }
106
+
107
+ function listScripts(ctx: CommandContext): number {
108
+ const pkg = readPackage(ctx, projectDir(ctx));
109
+ const scripts = Object.entries(pkg?.scripts ?? {});
110
+ if (!scripts.length) return 0;
111
+ ctx.io.stdout(`Scripts available in ${pkg?.name ?? 'this package'} via \`npm run-script\`:\n${scripts.map(([k, v]) => ` ${k}\n ${v}`).join('\n')}\n`);
112
+ return 0;
113
+ }
114
+
115
+ export async function npmCommand(ctx: CommandContext, shell: Shell, input: JobInput): Promise<number> {
116
+ const [sub = 'help', ...rest] = ctx.args;
117
+ const { flags, operands } = parseFlags(rest, ['registry']);
118
+ const dev = flags.has('D') || flags.has('save-dev');
119
+ const production = flags.has('production') || rest.includes('--omit=dev');
120
+ switch (sub) {
121
+ case '-v':
122
+ case '--version':
123
+ case 'version':
124
+ ctx.io.stdout(`${NPM_VERSION}\n`);
125
+ return 0;
126
+ case 'install':
127
+ case 'i':
128
+ case 'add':
129
+ case 'isntall':
130
+ return install(ctx, { add: operands, saveDev: dev, production });
131
+ case 'ci':
132
+ case 'clean-install':
133
+ return install(ctx, { ci: true, production });
134
+ case 'uninstall':
135
+ case 'remove':
136
+ case 'rm':
137
+ case 'un': {
138
+ const dir = projectDir(ctx);
139
+ const pkg = readPackage(ctx, dir);
140
+ if (!pkg) {
141
+ ctx.io.stderr('npm error No package.json found\n');
142
+ return 1;
143
+ }
144
+ for (const field of ['dependencies', 'devDependencies', 'optionalDependencies'] as const) {
145
+ for (const name of operands) if (pkg[field]) delete pkg[field]![name];
146
+ }
147
+ ctx.fs.writeFile(join(dir, 'package.json'), JSON.stringify(pkg, null, 2) + '\n');
148
+ return install(ctx, {}, dir);
149
+ }
150
+ case 'run':
151
+ case 'run-script': {
152
+ const dash = rest.indexOf('--');
153
+ const [name, ...args] = dash >= 0 ? [...rest.slice(0, dash), ...rest.slice(dash + 1)] : rest;
154
+ return name ? runScript(ctx, shell, input, name, args) : listScripts(ctx);
155
+ }
156
+ case 'start':
157
+ case 'test':
158
+ case 't':
159
+ case 'stop':
160
+ case 'restart':
161
+ return runScript(ctx, shell, input, sub === 't' ? 'test' : sub, rest.filter((a) => a !== '--'));
162
+ case 'init': {
163
+ const path = join(ctx.cwd, 'package.json');
164
+ if (ctx.fs.isFile(path)) {
165
+ ctx.io.stderr('npm error package.json already exists\n');
166
+ return 1;
167
+ }
168
+ const name = ctx.cwd.split('/').filter(Boolean).pop() ?? 'project';
169
+ const pkg = { name, version: '1.0.0', description: '', main: 'index.js', scripts: { test: 'echo "Error: no test specified" && exit 1' }, keywords: [], author: '', license: 'ISC' };
170
+ ctx.fs.writeFile(path, JSON.stringify(pkg, null, 2) + '\n');
171
+ ctx.io.stdout(`Wrote to ${path}:\n\n${JSON.stringify(pkg, null, 2)}\n`);
172
+ return 0;
173
+ }
174
+ case 'ls':
175
+ case 'list': {
176
+ const dir = projectDir(ctx);
177
+ const pkg = readPackage(ctx, dir);
178
+ const deps = { ...pkg?.dependencies, ...pkg?.devDependencies };
179
+ ctx.io.stdout(`${pkg?.name ?? ''}@${pkg?.version ?? ''} ${dir}\n`);
180
+ const names = Object.keys(deps).sort();
181
+ names.forEach((name, i) => {
182
+ const installed = readPackage(ctx, join(dir, 'node_modules', name))?.version;
183
+ ctx.io.stdout(`${i === names.length - 1 ? '└──' : '├──'} ${name}@${installed ?? `(missing ${deps[name]})`}\n`);
184
+ });
185
+ return 0;
186
+ }
187
+ case 'exec':
188
+ case 'x':
189
+ return npxCommand({ ...ctx, args: rest.filter((a) => a !== '--') }, shell, input);
190
+ case 'help':
191
+ case '-h':
192
+ case '--help':
193
+ ctx.io.stdout(`npm ${NPM_VERSION} (BuilderForce WebContainers)\n\nUsage: npm <install|ci|uninstall|run|start|test|init|ls|exec> …\n`);
194
+ return 0;
195
+ default:
196
+ ctx.io.stderr(`npm error Unknown command: "${sub}"\n`);
197
+ return 1;
198
+ }
199
+ }
200
+
201
+ export async function npxCommand(ctx: CommandContext, shell: Shell, input: JobInput): Promise<number> {
202
+ const args = ctx.args.filter((a) => a !== '-y' && a !== '--yes');
203
+ const packageFlag = args.findIndex((a) => a === '-p' || a === '--package');
204
+ const explicitPackage = packageFlag >= 0 ? args.splice(packageFlag, 2)[1] : undefined;
205
+ const [bin, ...rest] = args;
206
+ if (!bin) {
207
+ ctx.io.stderr('npx: missing command\n');
208
+ return 1;
209
+ }
210
+ const binName = bin.replace(/^@[^/]+\//, '').replace(/@[^@]*$/, '');
211
+ const io = { stdout: ctx.io.stdout, stderr: ctx.io.stderr, isTerminal: ctx.io.isTerminal };
212
+ if (matchDevServer([binName, ...rest])) return shell.fork({}).exec([binName, ...rest].map(quote).join(' '), io, input, ctx.signal);
213
+ let found = findExecutable(ctx.fs, binName, ctx.cwd, { PATH: '' });
214
+ if (!found) {
215
+ // Not installed locally: install into a scratch project, as npx does.
216
+ const spec = explicitPackage ?? bin;
217
+ const scratch = `/tmp/.npx/${spec.replace(/[^\w.-]+/g, '_')}`;
218
+ if (!ctx.fs.isFile(join(scratch, 'package.json'))) ctx.fs.writeFile(join(scratch, 'package.json'), '{"private": true}\n');
219
+ ctx.io.stderr(`npx: installing ${spec}\n`);
220
+ const status = await install({ ...ctx, io: { ...ctx.io, stdout: () => undefined } }, { add: [spec] }, scratch);
221
+ if (status !== 0) return status;
222
+ found = findExecutable(ctx.fs, binName, scratch, { PATH: '' }) ?? findExecutable(ctx.fs, ctx.fs.readdir(join(scratch, 'node_modules/.bin'))[0] ?? binName, scratch, { PATH: '' });
223
+ if (!found) {
224
+ ctx.io.stderr(`npx: ${spec} has no executable named ${binName}\n`);
225
+ return 1;
226
+ }
227
+ }
228
+ return shell.fork({}).exec([found, ...rest].map(quote).join(' '), io, input, ctx.signal);
229
+ }