Skip to content

fix(core): load the config as ESM in CommonJS projects - #69

Merged
Hebilicious merged 2 commits into
mainfrom
fix/config-module-format
Sep 24, 2026
Merged

Hebilicious merged 2 commits into
mainfrom
fix/config-module-format

Conversation

@Hebilicious

Copy link
Copy Markdown
Owner

Fixes a config load failure reported after 0.8.0 shipped.

A project whose package.json declares "type": "commonjs" makes Node evaluate cssforge.config.ts as CommonJS with module syntax detection turned off, so the documented export default defineConfig({...}) failed and neither the CLI nor the bundler plugin could load the config. Reproduced against the published 0.8.0 with a Vite build: [cssforge] Could not load virtual:cssforge.css: Could not load the CSS Forge config at ..., no CSS emitted.

The loader now evaluates project TypeScript in the config graph as ESM, so the config format no longer depends on the consumer's type field. Its message also includes the failure it caught, which bundlers previously hid behind the config path.

Verification: a "type": "commonjs" Vite project builds and emits the tokens with the fix, and fails without it; cssforge:test, typecheck, format, readme-check, and jsr-dry-run pass. The regression test fails when the new load hook is removed.

A project that declares `"type": "commonjs"` makes Node read a `.ts` config as
CommonJS with module syntax detection off, so `export default` failed and the
CLI and the bundler plugin could not load the config at all.

The loader now evaluates project TypeScript in the config graph as ESM, and its
message includes the failure it caught, which bundlers showed only as a config
path.
@changeset-bot

changeset-bot Bot commented Sep 24, 2026 •

Copy link
Copy Markdown

🦋 Changeset detected

Latest commit: 38f13e5

The changes in this PR will be included in the next version bump.

This PR includes changesets to release 1 package
Name Type
@hebilicious/cssforge Patch

Not sure what this means? Click here to learn what changesets are.

Click here if you're a maintainer who wants to add another changeset to this PR

@cloudflare-workers-and-pages

cloudflare-workers-and-pages Bot commented Sep 24, 2026 •

Copy link
Copy Markdown

Deploying with  Cloudflare Workers  Cloudflare Workers

The latest updates on your project. Learn more about integrating Git with Workers.

Status Name Latest Commit Preview URL Updated (UTC)
✅ Deployment successful!
View logs
cssforge 38f13e5 Commit Preview URL

Branch Preview URL
Sep 24 2026, 01:47 PM

Forcing the ESM format for every project TypeScript file also hit the package's
own sources when they run from source, which broke the JSR smoke test under
Deno. The override now applies only when the nearest package.json declares
CommonJS and the file is written as ESM, so a Deno run, a `"type": "module"`
project, and a CommonJS-authored config all keep their own format.
@Hebilicious
Hebilicious merged commit 444bfc5 into main Sep 24, 2026
5 checks passed
Hebilicious added a commit that referenced this pull request Sep 24, 2026
Cleans up the loader change in #69, which worked but did more than it
needed to.

`packages/cssforge/src/loader.ts` no longer walks package.json files,
parses them, or guesses the module system from source text. It asks Node
for the format Node chose and overrides only `commonjs-typescript`:

```ts
const loaded = nextLoad(url, context);
if (loaded.format !== "commonjs-typescript") return loaded;

return {
	format: "module-typescript",
	source: loaded.source ?? readFileSync(fileURLToPath(parsed), "utf8"),
	shortCircuit: true,
};
```

That drops `packageTypeOf`, its cache, and the `usesEsmSyntax` regex,
whose false positive silently switched a CommonJS-authored file to ESM,
so 67 lines become 15 and the rule reads as one sentence: TypeScript in
the config graph loads as ESM when Node would read it as CommonJS.

Behavior change: a CommonJS-authored `.ts` module inside a CommonJS
package now loads as ESM, matching the documented `export default`
config shape. That is the trade for removing the heuristic, and the
changeset says so.

Also adds the review rule to AGENTS.md: a pull request is never merged
without an explicit instruction for that pull request, release pull
requests included.

Verification: the loader test now covers `"type": "commonjs"`, `"type":
"module"`, and an absent type field, and fails when the load hook is
removed; `cssforge:test`, `typecheck`, `format`, `readme-check`, and
`jsr-dry-run` pass; the JSR smoke passes with `--require-deno`; the
example browser suite passes 5/5; and a Vite project built against
locally packed 0.8.1 plus the plugin emits the tokens under all three
type fields.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant