11export * as Npm from "."
22
33import path from "path"
4+ import { fileURLToPath } from "url"
45import npa from "npm-package-arg"
56import semver from "semver"
7+ import Config from "@npmcli/config"
8+ import { definitions , flatten , nerfDarts , shorthands } from "@npmcli/config/lib/definitions/index.js"
69import { Effect , Schema , Context , Layer , Option , FileSystem } from "effect"
710import { NodeFileSystem } from "@effect/platform-node"
811import { AppFileSystem } from "@opencode-ai/shared/filesystem"
@@ -40,12 +43,39 @@ export interface Interface {
4043export class Service extends Context . Service < Service , Interface > ( ) ( "@opencode/Npm" ) { }
4144
4245const illegal = process . platform === "win32" ? new Set ( [ "<" , ">" , ":" , '"' , "|" , "?" , "*" ] ) : undefined
46+ const npmPath = fileURLToPath ( new URL ( "../.." , import . meta. url ) )
4347
4448export function sanitize ( pkg : string ) {
4549 if ( ! illegal ) return pkg
4650 return Array . from ( pkg , ( char ) => ( illegal . has ( char ) || char . charCodeAt ( 0 ) < 32 ? "_" : char ) ) . join ( "" )
4751}
4852
53+ const loadOptions = ( dir : string ) =>
54+ Effect . tryPromise ( {
55+ try : async ( ) => {
56+ const config = new Config ( {
57+ npmPath,
58+ cwd : dir ,
59+ env : { ...process . env } ,
60+ argv : [ process . execPath , process . execPath ] ,
61+ execPath : process . execPath ,
62+ platform : process . platform ,
63+ definitions,
64+ flatten,
65+ nerfDarts,
66+ shorthands,
67+ warn : false ,
68+ } )
69+ await config . load ( )
70+ return config . flat
71+ } ,
72+ catch : ( cause ) =>
73+ new InstallFailedError ( {
74+ cause,
75+ dir,
76+ } ) ,
77+ } )
78+
4979const resolveEntryPoint = ( name : string , dir : string ) : EntryPoint => {
5080 let entrypoint : Option . Option < string >
5181 try {
@@ -81,7 +111,10 @@ export const layer = Layer.effect(
81111 Effect . gen ( function * ( ) {
82112 yield * flock . acquire ( `npm-install:${ input . dir } ` )
83113 const { Arborist } = yield * Effect . promise ( ( ) => import ( "@npmcli/arborist" ) )
114+ const add = input . add ?? [ ]
115+ const npmOptions = yield * loadOptions ( input . dir )
84116 const arborist = new Arborist ( {
117+ ...npmOptions ,
85118 path : input . dir ,
86119 binLinks : true ,
87120 progress : false ,
@@ -91,14 +124,15 @@ export const layer = Layer.effect(
91124 return yield * Effect . tryPromise ( {
92125 try : ( ) =>
93126 arborist . reify ( {
94- add : input ?. add || [ ] ,
127+ ...npmOptions ,
128+ add,
95129 save : true ,
96130 saveType : "prod" ,
97131 } ) ,
98132 catch : ( cause ) =>
99133 new InstallFailedError ( {
100134 cause,
101- add : input ?. add ,
135+ add,
102136 dir : input . dir ,
103137 } ) ,
104138 } ) as Effect . Effect < ArboristTree , InstallFailedError >
0 commit comments