mirror of
https://github.com/anomalyco/opencode.git
synced 2026-05-18 10:07:58 +00:00
fix(npm): respect npmrc config (#24001)
This commit is contained in:
@@ -1,8 +1,11 @@
|
||||
export * as Npm from "."
|
||||
|
||||
import path from "path"
|
||||
import { fileURLToPath } from "url"
|
||||
import npa from "npm-package-arg"
|
||||
import semver from "semver"
|
||||
import Config from "@npmcli/config"
|
||||
import { definitions, flatten, nerfDarts, shorthands } from "@npmcli/config/lib/definitions/index.js"
|
||||
import { Effect, Schema, Context, Layer, Option, FileSystem } from "effect"
|
||||
import { NodeFileSystem } from "@effect/platform-node"
|
||||
import { AppFileSystem } from "@opencode-ai/shared/filesystem"
|
||||
@@ -40,12 +43,39 @@ export interface Interface {
|
||||
export class Service extends Context.Service<Service, Interface>()("@opencode/Npm") {}
|
||||
|
||||
const illegal = process.platform === "win32" ? new Set(["<", ">", ":", '"', "|", "?", "*"]) : undefined
|
||||
const npmPath = fileURLToPath(new URL("../..", import.meta.url))
|
||||
|
||||
export function sanitize(pkg: string) {
|
||||
if (!illegal) return pkg
|
||||
return Array.from(pkg, (char) => (illegal.has(char) || char.charCodeAt(0) < 32 ? "_" : char)).join("")
|
||||
}
|
||||
|
||||
const loadOptions = (dir: string) =>
|
||||
Effect.tryPromise({
|
||||
try: async () => {
|
||||
const config = new Config({
|
||||
npmPath,
|
||||
cwd: dir,
|
||||
env: { ...process.env },
|
||||
argv: [process.execPath, process.execPath],
|
||||
execPath: process.execPath,
|
||||
platform: process.platform,
|
||||
definitions,
|
||||
flatten,
|
||||
nerfDarts,
|
||||
shorthands,
|
||||
warn: false,
|
||||
})
|
||||
await config.load()
|
||||
return config.flat
|
||||
},
|
||||
catch: (cause) =>
|
||||
new InstallFailedError({
|
||||
cause,
|
||||
dir,
|
||||
}),
|
||||
})
|
||||
|
||||
const resolveEntryPoint = (name: string, dir: string): EntryPoint => {
|
||||
let entrypoint: Option.Option<string>
|
||||
try {
|
||||
@@ -81,7 +111,10 @@ export const layer = Layer.effect(
|
||||
Effect.gen(function* () {
|
||||
yield* flock.acquire(`npm-install:${input.dir}`)
|
||||
const { Arborist } = yield* Effect.promise(() => import("@npmcli/arborist"))
|
||||
const add = input.add ?? []
|
||||
const npmOptions = yield* loadOptions(input.dir)
|
||||
const arborist = new Arborist({
|
||||
...npmOptions,
|
||||
path: input.dir,
|
||||
binLinks: true,
|
||||
progress: false,
|
||||
@@ -91,14 +124,15 @@ export const layer = Layer.effect(
|
||||
return yield* Effect.tryPromise({
|
||||
try: () =>
|
||||
arborist.reify({
|
||||
add: input?.add || [],
|
||||
...npmOptions,
|
||||
add,
|
||||
save: true,
|
||||
saveType: "prod",
|
||||
}),
|
||||
catch: (cause) =>
|
||||
new InstallFailedError({
|
||||
cause,
|
||||
add: input?.add,
|
||||
add,
|
||||
dir: input.dir,
|
||||
}),
|
||||
}) as Effect.Effect<ArboristTree, InstallFailedError>
|
||||
|
||||
@@ -1,7 +1,18 @@
|
||||
import fs from "fs/promises"
|
||||
import path from "path"
|
||||
import { describe, expect, test } from "bun:test"
|
||||
import { Npm } from "../src/npm"
|
||||
import { tmpdir } from "./fixture/fixture"
|
||||
|
||||
const win = process.platform === "win32"
|
||||
const writePackage = (dir: string, pkg: Record<string, unknown>) =>
|
||||
Bun.write(
|
||||
path.join(dir, "package.json"),
|
||||
JSON.stringify({
|
||||
version: "1.0.0",
|
||||
...pkg,
|
||||
}),
|
||||
)
|
||||
|
||||
describe("Npm.sanitize", () => {
|
||||
test("keeps normal scoped package specs unchanged", () => {
|
||||
@@ -16,3 +27,29 @@ describe("Npm.sanitize", () => {
|
||||
expect(Npm.sanitize(spec)).toBe(expected)
|
||||
})
|
||||
})
|
||||
|
||||
describe("Npm.install", () => {
|
||||
test("respects omit from project .npmrc", async () => {
|
||||
await using tmp = await tmpdir()
|
||||
|
||||
await writePackage(tmp.path, {
|
||||
name: "fixture",
|
||||
dependencies: {
|
||||
"prod-pkg": "file:./prod-pkg",
|
||||
},
|
||||
devDependencies: {
|
||||
"dev-pkg": "file:./dev-pkg",
|
||||
},
|
||||
})
|
||||
await Bun.write(path.join(tmp.path, ".npmrc"), "omit=dev\n")
|
||||
await fs.mkdir(path.join(tmp.path, "prod-pkg"))
|
||||
await fs.mkdir(path.join(tmp.path, "dev-pkg"))
|
||||
await writePackage(path.join(tmp.path, "prod-pkg"), { name: "prod-pkg" })
|
||||
await writePackage(path.join(tmp.path, "dev-pkg"), { name: "dev-pkg" })
|
||||
|
||||
await Npm.install(tmp.path)
|
||||
|
||||
await expect(fs.stat(path.join(tmp.path, "node_modules", "prod-pkg"))).resolves.toBeDefined()
|
||||
await expect(fs.stat(path.join(tmp.path, "node_modules", "dev-pkg"))).rejects.toThrow()
|
||||
})
|
||||
})
|
||||
|
||||
Reference in New Issue
Block a user