mirror of
https://github.com/anomalyco/opencode.git
synced 2026-06-01 19:05:38 +00:00
fix(npm): respect npmrc config (#24001)
This commit is contained in:
@@ -1,8 +1,11 @@
|
|||||||
export * as Npm from "."
|
export * as Npm from "."
|
||||||
|
|
||||||
import path from "path"
|
import path from "path"
|
||||||
|
import { fileURLToPath } from "url"
|
||||||
import npa from "npm-package-arg"
|
import npa from "npm-package-arg"
|
||||||
import semver from "semver"
|
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 { Effect, Schema, Context, Layer, Option, FileSystem } from "effect"
|
||||||
import { NodeFileSystem } from "@effect/platform-node"
|
import { NodeFileSystem } from "@effect/platform-node"
|
||||||
import { AppFileSystem } from "@opencode-ai/shared/filesystem"
|
import { AppFileSystem } from "@opencode-ai/shared/filesystem"
|
||||||
@@ -40,12 +43,39 @@ export interface Interface {
|
|||||||
export class Service extends Context.Service<Service, Interface>()("@opencode/Npm") {}
|
export class Service extends Context.Service<Service, Interface>()("@opencode/Npm") {}
|
||||||
|
|
||||||
const illegal = process.platform === "win32" ? new Set(["<", ">", ":", '"', "|", "?", "*"]) : undefined
|
const illegal = process.platform === "win32" ? new Set(["<", ">", ":", '"', "|", "?", "*"]) : undefined
|
||||||
|
const npmPath = fileURLToPath(new URL("../..", import.meta.url))
|
||||||
|
|
||||||
export function sanitize(pkg: string) {
|
export function sanitize(pkg: string) {
|
||||||
if (!illegal) return pkg
|
if (!illegal) return pkg
|
||||||
return Array.from(pkg, (char) => (illegal.has(char) || char.charCodeAt(0) < 32 ? "_" : char)).join("")
|
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 => {
|
const resolveEntryPoint = (name: string, dir: string): EntryPoint => {
|
||||||
let entrypoint: Option.Option<string>
|
let entrypoint: Option.Option<string>
|
||||||
try {
|
try {
|
||||||
@@ -81,7 +111,10 @@ export const layer = Layer.effect(
|
|||||||
Effect.gen(function* () {
|
Effect.gen(function* () {
|
||||||
yield* flock.acquire(`npm-install:${input.dir}`)
|
yield* flock.acquire(`npm-install:${input.dir}`)
|
||||||
const { Arborist } = yield* Effect.promise(() => import("@npmcli/arborist"))
|
const { Arborist } = yield* Effect.promise(() => import("@npmcli/arborist"))
|
||||||
|
const add = input.add ?? []
|
||||||
|
const npmOptions = yield* loadOptions(input.dir)
|
||||||
const arborist = new Arborist({
|
const arborist = new Arborist({
|
||||||
|
...npmOptions,
|
||||||
path: input.dir,
|
path: input.dir,
|
||||||
binLinks: true,
|
binLinks: true,
|
||||||
progress: false,
|
progress: false,
|
||||||
@@ -91,14 +124,15 @@ export const layer = Layer.effect(
|
|||||||
return yield* Effect.tryPromise({
|
return yield* Effect.tryPromise({
|
||||||
try: () =>
|
try: () =>
|
||||||
arborist.reify({
|
arborist.reify({
|
||||||
add: input?.add || [],
|
...npmOptions,
|
||||||
|
add,
|
||||||
save: true,
|
save: true,
|
||||||
saveType: "prod",
|
saveType: "prod",
|
||||||
}),
|
}),
|
||||||
catch: (cause) =>
|
catch: (cause) =>
|
||||||
new InstallFailedError({
|
new InstallFailedError({
|
||||||
cause,
|
cause,
|
||||||
add: input?.add,
|
add,
|
||||||
dir: input.dir,
|
dir: input.dir,
|
||||||
}),
|
}),
|
||||||
}) as Effect.Effect<ArboristTree, InstallFailedError>
|
}) 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 { describe, expect, test } from "bun:test"
|
||||||
import { Npm } from "../src/npm"
|
import { Npm } from "../src/npm"
|
||||||
|
import { tmpdir } from "./fixture/fixture"
|
||||||
|
|
||||||
const win = process.platform === "win32"
|
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", () => {
|
describe("Npm.sanitize", () => {
|
||||||
test("keeps normal scoped package specs unchanged", () => {
|
test("keeps normal scoped package specs unchanged", () => {
|
||||||
@@ -16,3 +27,29 @@ describe("Npm.sanitize", () => {
|
|||||||
expect(Npm.sanitize(spec)).toBe(expected)
|
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