From 11af6b10350ca478ebbc35b27cfbf75616fb5fca Mon Sep 17 00:00:00 2001 From: logelog <194732487+logelog@users.noreply.github.com> Date: Sat, 11 Jul 2026 18:07:18 +0200 Subject: [PATCH] fix(cli): import plugin artifacts through file URLs --- .changeset/calm-validator-paths.md | 5 +++++ packages/core/src/cli/commands/bundle-utils.ts | 6 ++++++ packages/core/src/cli/commands/bundle.ts | 10 ++++++++-- packages/core/tests/unit/cli/bundle-utils.test.ts | 11 +++++++++++ 4 files changed, 30 insertions(+), 2 deletions(-) create mode 100644 .changeset/calm-validator-paths.md diff --git a/.changeset/calm-validator-paths.md b/.changeset/calm-validator-paths.md new file mode 100644 index 0000000000..a22b2017ea --- /dev/null +++ b/.changeset/calm-validator-paths.md @@ -0,0 +1,5 @@ +--- +"emdash": patch +--- + +Fixes `emdash plugin bundle` and `emdash plugin validate` on Windows by importing generated plugin artifacts through portable file URLs. diff --git a/packages/core/src/cli/commands/bundle-utils.ts b/packages/core/src/cli/commands/bundle-utils.ts index 21b45031ef..2faa6dd769 100644 --- a/packages/core/src/cli/commands/bundle-utils.ts +++ b/packages/core/src/cli/commands/bundle-utils.ts @@ -9,6 +9,7 @@ import { createWriteStream } from "node:fs"; import { readdir, stat, access } from "node:fs/promises"; import { resolve, join } from "node:path"; import { pipeline } from "node:stream/promises"; +import { pathToFileURL } from "node:url"; import { imageSize } from "image-size"; import { packTar } from "modern-tar/fs"; @@ -105,6 +106,11 @@ export async function fileExists(path: string): Promise { } } +/** Convert an absolute build artifact path into a portable ESM import specifier. */ +export function toFileImportSpecifier(path: string): string { + return pathToFileURL(path).href; +} + // ── Image dimension readers ────────────────────────────────────────────────── /** diff --git a/packages/core/src/cli/commands/bundle.ts b/packages/core/src/cli/commands/bundle.ts index 0ab19e16d6..7d1997f5db 100644 --- a/packages/core/src/cli/commands/bundle.ts +++ b/packages/core/src/cli/commands/bundle.ts @@ -37,6 +37,7 @@ import { MAX_SCREENSHOT_HEIGHT, readImageDimensions, resolveSourceEntry, + toFileImportSpecifier, totalBundleBytes, validateBundleSize, } from "./bundle-utils.js"; @@ -202,7 +203,10 @@ export const bundleCommand = defineCommand({ } // Dynamic import of the built plugin - const pluginModule = (await import(mainOutputPath)) as Record; + const pluginModule = (await import(toFileImportSpecifier(mainOutputPath))) as Record< + string, + unknown + >; // Extract manifest from the imported module. // Supports three patterns: @@ -269,7 +273,9 @@ export const bundleCommand = defineCommand({ const backendBaseName = basename(backendEntry).replace(TS_EXT_RE, ""); const backendProbePath = await findBuildOutput(backendProbeDir, backendBaseName); if (backendProbePath) { - const backendModule = (await import(backendProbePath)) as Record; + const backendModule = (await import( + toFileImportSpecifier(backendProbePath) + )) as Record; const standardDef = (backendModule.default ?? {}) as Record; const hooks = standardDef.hooks as Record | undefined; const routes = standardDef.routes as Record | undefined; diff --git a/packages/core/tests/unit/cli/bundle-utils.test.ts b/packages/core/tests/unit/cli/bundle-utils.test.ts index 3f50ca5c51..f9425e385f 100644 --- a/packages/core/tests/unit/cli/bundle-utils.test.ts +++ b/packages/core/tests/unit/cli/bundle-utils.test.ts @@ -22,6 +22,7 @@ import { findNodeBuiltinImports, findBuildOutput, findSourceExports, + toFileImportSpecifier, } from "../../../src/cli/commands/bundle-utils.js"; import type { ResolvedPlugin } from "../../../src/plugins/types.js"; @@ -277,6 +278,16 @@ describe("findBuildOutput", () => { }); }); +describe("toFileImportSpecifier", () => { + it("returns a file URL for an absolute build artifact path", () => { + const artifactPath = join(tmpdir(), "emdash-plugin", "index.mjs"); + const specifier = toFileImportSpecifier(artifactPath); + + expect(new URL(specifier).protocol).toBe("file:"); + expect(specifier).not.toBe(artifactPath); + }); +}); + describe("findNodeBuiltinImports", () => { it("detects require('node:fs') in bundled output", () => { expect(findNodeBuiltinImports(`const fs = require("node:fs");`)).toEqual(["fs"]);