Skip to content

format-converter worker not bundled — consumers fail at build time #4

Description

@schnsrw

Found while integrating `@schnsrw/docx-js-editor@1.0.0` into a downstream Vite consumer (Casual Drive). The published `dist/format-converter-HT2IRSL3.mjs` contains:

```js
new Worker(new URL("./format-converter.worker.ts", import.meta.url), { type: "module" })
```

but the `.ts` source isn't shipped in `dist/` — only the compiled `.mjs`. Vite's `vite:worker-import-meta-url` plugin errors at build time:

```
Could not resolve entry module
"node_modules/.../dist/format-converter.worker.ts"
file: node_modules/.../dist/format-converter-HT2IRSL3.mjs
```

The bug exists for any consumer whose bundler honours the `new Worker(new URL(...), import.meta.url)` pattern at build time (Vite, modern webpack with worker-plugin, esbuild's bundler). The format-converter is used for odt / md / txt import paths; consumers that don't trigger those still can't build.

Drive workaround (shipped as a stop-gap)

Vite `transform` hook that rewrites the worker construction to a no-op object:

```ts
{
name: "casual-drive-sdk-worker-shim",
enforce: "pre",
transform(code, id) {
if (!id.includes("@schnsrw/docx-js-editor")) return null;
if (!code.includes("format-converter.worker.ts")) return null;
return code.replace(
/new Worker(new URL(["']./format-converter.worker.ts["'],import.meta.url)\s*,\s*{[^}]*})/g,
"({postMessage(){},addEventListener(){},removeEventListener(){},terminate(){},onmessage:null,onerror:null,onmessageerror:null})"
);
},
}
```

(see CasualOffice/doc-hub@21206da). Works for Drive because the converter is unreachable from the in-Drive embed flow, but it's a downstream hack, not a fix.

Suggested fix

A few patterns that would work in tsup:

  1. Pre-build the worker as a sibling artifact. Add a second `defineConfig` entry that compiles `src/lib/format-converter.worker.ts` into `dist/format-converter.worker.js`, then rewrite the consumer-facing URL to reference the compiled file (e.g. via a custom esbuild plugin that swaps `.ts` for `.js` in worker URLs). Adds an `exports` entry for `./format-converter.worker` so the package.json describes it.

  2. Inline as a Blob URL. Read the worker source at build time, embed as a constant in the main bundle, instantiate `Worker(URL.createObjectURL(new Blob([source], { type: "application/javascript" })))`. Most portable but bloats the main bundle.

  3. `?worker&inline` Vite-style import. Only works for Vite consumers; doesn't help the package's headless-Node or webpack consumers.

(1) is the cleanest. Happy to send a PR if you'd like.

Reproduce

```bash
mkdir bug-repro && cd bug-repro
npm init -y
npm i vite @vitejs/plugin-react react react-dom @schnsrw/docx-js-editor@1.0.0

minimal src/main.tsx that imports anything from @schnsrw/docx-js-editor

npx vite build
```

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions