diff --git a/cli/deno.json b/cli/deno.json index d318d601..1adb3968 100644 --- a/cli/deno.json +++ b/cli/deno.json @@ -1,6 +1,6 @@ { "name": "@molt/cli", - "version": "0.19.1", + "version": "0.19.2", "exports": { ".": "./main.ts" }, diff --git a/core/deno.json b/core/deno.json index f700f61b..fa85344d 100644 --- a/core/deno.json +++ b/core/deno.json @@ -1,6 +1,6 @@ { "name": "@molt/core", - "version": "0.19.1", + "version": "0.19.2", "exports": { ".": "./mod.ts", "./bumps": "./bumps.ts", diff --git a/core/locks.ts b/core/locks.ts index fc764aae..c1b798d4 100644 --- a/core/locks.ts +++ b/core/locks.ts @@ -248,7 +248,7 @@ async function getJsrDependencies( export async function extract( lockfile: LockfileJson, dependency: DependencySpec, -): Promise { +): Promise { return isRemote(dependency) ? await extractRemote(lockfile, dependency) : extractPackage(lockfile, dependency as DependencySpec<"jsr" | "npm">); @@ -257,7 +257,7 @@ export async function extract( async function extractRemote( lock: LockfileJson, dep: DependencySpec<"http" | "https">, -): Promise { +): Promise { const reqs = Object.keys(lock.remote).filter((req) => req.startsWith(stringify(dep)) ); @@ -265,6 +265,7 @@ async function extractRemote( const graph = await createGraph(req); return graph.modules.map((mod) => mod.specifier); }))).flat(); + if (!deps.length) return; return { version: VERSION, remote: filterValues( @@ -277,11 +278,12 @@ async function extractRemote( function extractPackage( lock: LockfileJson, dep: DependencySpec<"jsr" | "npm">, -): LockfileJson { +): LockfileJson | undefined { const name = stringify(dep, "kind", "name", "constraint"); const lockfile = parseFromJson("", lock); lockfile.setWorkspaceConfig({ dependencies: [name] }); const json = lockfile.toJson(); + if (!json.packages) return; json.remote = {}; return json; } diff --git a/core/locks_test.ts b/core/locks_test.ts index fbc8dac7..b7a83b49 100644 --- a/core/locks_test.ts +++ b/core/locks_test.ts @@ -246,6 +246,12 @@ describe("extract", () => { beforeEach(() => fs.mock()); afterEach(() => fs.dispose()); + it("should return undefined for an unlocked package", async () => { + const dep = parse("jsr:@std/testing@^0.222.0"); + const lock = await extract(LOCKFILE, dep); + assertEquals(lock, undefined); + }); + it("should extract the partial lock for a jsr package from a lockfile", async () => { const dep = parse("jsr:@std/assert@^0.222.0"); const lock = await extract(LOCKFILE, dep); diff --git a/core/mod.ts b/core/mod.ts index e170faaa..1073f91a 100644 --- a/core/mod.ts +++ b/core/mod.ts @@ -84,7 +84,7 @@ class LockContext { get merged(): LockfileJson { return this.reqs.map((req) => - this.created.get(req) ?? this.extracted.get(req)! + this.created.get(req) ?? this.extracted.get(req) ?? {} as LockfileJson // @ts-ignore allow passing concrete types to deepMerge ).reduce((prev, curr) => deepMerge(prev, curr), Lock.empty); } @@ -159,7 +159,7 @@ export async function collect( const lockfile = Lock.parse(await Deno.readTextFile(options.lock!)); for (const req of reqs) { const lock = await Lock.extract(lockfile, parse(req)); - locks.extracted.set(req, lock); + if (lock) locks.extracted.set(req, lock); } } const ctx = new Context(reqs, refs, locks); @@ -217,11 +217,11 @@ class Update implements UpdateI { const bumped = bump.constraint ? { ...dep, constraint: bump.constraint } : dep; - - const locked = this.#ctx.locks.extracted.get(req)!; - const lock = await Lock.create(bumped, bump.lock, locked); - this.#ctx.locks.created.set(req, lock); - + const locked = this.#ctx.locks.extracted.get(req); + if (locked) { + const lock = await Lock.create(bumped, bump.lock, locked); + this.#ctx.locks.created.set(req, lock); + } await Deno.writeTextFile( this.#ctx.locks.source, Lock.format(this.#ctx.locks.merged), diff --git a/core/mod_test.ts b/core/mod_test.ts index e12a4e03..7d9919b6 100644 --- a/core/mod_test.ts +++ b/core/mod_test.ts @@ -130,4 +130,48 @@ describe("core", () => { lock: "deno.lock", }); }); + + it("should handle an non-locked dependency", async () => { + await Deno.writeTextFile( + "deno.json", + dedent` + { + "imports": { + "@conventional-commits/parser": "npm:@conventional-commits/parser@^0.3.0", + "@luca/flag": "jsr:@luca/flag@^1.0.0" + } + } + `, + ); + const deps = await collect({ config: "deno.json", lock: "deno.lock" }); + assertEquals(deps.length, 2); + const [dep] = deps; + const update = await dep.check(); + assertExists(update); + assertEquals(update.dep.name, "@conventional-commits/parser"); + await update.write(); + assertEquals(await Deno.readTextFile("mod.ts"), MOD_TS); + assertEquals( + await Deno.readTextFile("deno.json"), + dedent` + { + "imports": { + "@conventional-commits/parser": "npm:@conventional-commits/parser@^0.4.0", + "@luca/flag": "jsr:@luca/flag@^1.0.0" + } + } + `, + ); + assertEquals(await Deno.readTextFile("deno.lock"), DENO_LOCK); + await update.commit(); + assertSpyCallArg(git, 0, 1, { + args: [ + "commit", + "-m", + "bump @conventional-commits/parser to ^0.4.0", + "deno.json", + ], + lock: undefined, + }); + }); });