Refactor npm config handling (#24565)
This commit is contained in:
13
packages/core/test/fixture/tmpdir.ts
Normal file
13
packages/core/test/fixture/tmpdir.ts
Normal file
@@ -0,0 +1,13 @@
|
||||
import fs from "fs/promises"
|
||||
import { tmpdir as osTmpdir } from "os"
|
||||
import path from "path"
|
||||
|
||||
export const tmpdir = async () => {
|
||||
const dir = await fs.mkdtemp(path.join(osTmpdir(), "opencode-core-test-"))
|
||||
return {
|
||||
path: dir,
|
||||
async [Symbol.asyncDispose]() {
|
||||
await fs.rm(dir, { recursive: true, force: true })
|
||||
},
|
||||
}
|
||||
}
|
||||
51
packages/core/test/npm-config.test.ts
Normal file
51
packages/core/test/npm-config.test.ts
Normal file
@@ -0,0 +1,51 @@
|
||||
import path from "path"
|
||||
import { describe, expect, test } from "bun:test"
|
||||
import { Effect } from "effect"
|
||||
import { NpmConfig } from "@opencode-ai/core/npm-config"
|
||||
import { tmpdir } from "./fixture/tmpdir"
|
||||
|
||||
describe("NpmConfig.load", () => {
|
||||
test("reads registry from project .npmrc", async () => {
|
||||
await using tmp = await tmpdir()
|
||||
await Bun.write(path.join(tmp.path, ".npmrc"), "registry=https://registry.example.test/\n")
|
||||
|
||||
const config = await Effect.runPromise(NpmConfig.load(tmp.path))
|
||||
|
||||
expect(config.registry).toBe("https://registry.example.test/")
|
||||
})
|
||||
|
||||
test("reads scoped registries from project .npmrc", async () => {
|
||||
await using tmp = await tmpdir()
|
||||
await Bun.write(path.join(tmp.path, ".npmrc"), "@acme:registry=https://npm.acme.test/\n")
|
||||
|
||||
const config = await Effect.runPromise(NpmConfig.load(tmp.path))
|
||||
|
||||
expect(config["@acme:registry"]).toBe("https://npm.acme.test/")
|
||||
})
|
||||
|
||||
test("flattens boolean and list options", async () => {
|
||||
await using tmp = await tmpdir()
|
||||
await Bun.write(path.join(tmp.path, ".npmrc"), "ignore-scripts=true\nomit[]=dev\nomit[]=optional\n")
|
||||
|
||||
const config = await Effect.runPromise(NpmConfig.load(tmp.path))
|
||||
|
||||
expect(config.ignoreScripts).toBe(true)
|
||||
expect(config.omit).toEqual(["dev", "optional"])
|
||||
})
|
||||
})
|
||||
|
||||
describe("NpmConfig.registry", () => {
|
||||
test("normalizes configured registry without trailing slash", async () => {
|
||||
await using tmp = await tmpdir()
|
||||
await Bun.write(path.join(tmp.path, ".npmrc"), "registry=https://registry.example.test/\n")
|
||||
|
||||
await expect(Effect.runPromise(NpmConfig.registry(tmp.path))).resolves.toBe("https://registry.example.test")
|
||||
})
|
||||
|
||||
test("leaves configured registry without trailing slash unchanged", async () => {
|
||||
await using tmp = await tmpdir()
|
||||
await Bun.write(path.join(tmp.path, ".npmrc"), "registry=https://registry.example.test\n")
|
||||
|
||||
await expect(Effect.runPromise(NpmConfig.registry(tmp.path))).resolves.toBe("https://registry.example.test")
|
||||
})
|
||||
})
|
||||
56
packages/core/test/npm.test.ts
Normal file
56
packages/core/test/npm.test.ts
Normal file
@@ -0,0 +1,56 @@
|
||||
import fs from "fs/promises"
|
||||
import path from "path"
|
||||
import { describe, expect, test } from "bun:test"
|
||||
import { Npm } from "@opencode-ai/core/npm"
|
||||
import { tmpdir } from "./fixture/tmpdir"
|
||||
|
||||
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", () => {
|
||||
expect(Npm.sanitize("@opencode/acme")).toBe("@opencode/acme")
|
||||
expect(Npm.sanitize("@opencode/acme@1.0.0")).toBe("@opencode/acme@1.0.0")
|
||||
expect(Npm.sanitize("prettier")).toBe("prettier")
|
||||
})
|
||||
|
||||
test("handles git https specs", () => {
|
||||
const spec = "acme@git+https://github.com/opencode/acme.git"
|
||||
const expected = win ? "acme@git+https_//github.com/opencode/acme.git" : spec
|
||||
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