Skip to content

fix: resolve cli package.json without import.meta.resolve - #428

Draft
ianwremmel-ai-agent wants to merge 3 commits into
mainfrom
clc-1133-fix-tool-tools-commonjs-build-which-compiles
Draft

ianwremmel-ai-agent wants to merge 3 commits into
mainfrom
clc-1133-fix-tool-tools-commonjs-build-which-compiles

Conversation

@ianwremmel-ai-agent

Copy link
Copy Markdown
Collaborator

tool-tool's src/tool.mjs is compiled to CommonJS as well as ESM. esbuild turns import.meta into an empty object under cjs, so import.meta.resolve("@code-like-a-carpenter/cli") became import_meta.resolve(...) in dist/cjs/tool.cjs — a TypeError for anything reaching addAsCliPlugin through the built CJS executor. executors.json points at the cjs build whenever codegen:tool ran with src/index.mjs absent (create-nodes.mjs derives buildBeforeRun from that check), which is the state tool-foundation/executors.json is committed in.

esbuild flagged this the whole time as an [empty-import-meta] warning, which does not fail the build and so kept scrolling past.

Two changes:

  • Resolve @code-like-a-carpenter/cli/package.json through createRequire(rootPkgPath). Works in both output formats, and keeps the property the old comment insisted on: it does not need the CLI entrypoint to have been built, because package.json is itself the resolution target and always exists. Resolving the subpath directly also drops the findUp walk back up from the entrypoint.
  • Set esbuild logOverride so empty-import-meta is an error, so a format-incompatible construct cannot land again. tool.mjs was the only first-party source that tripped it.

Ticket: https://linear.app/code-like-a-carpenter/issue/CLC-1133/fix-tool-tools-commonjs-build-which-compiles-importmetaresolve-away

@linear-code

linear-code Bot commented Sep 7, 2026

Copy link
Copy Markdown

CLC-1133

@ianwremmel-ai-agent

ianwremmel-ai-agent commented Sep 7, 2026

Copy link
Copy Markdown
Collaborator Author

Plan

  • Replace import.meta.resolve in tool-tool/src/tool.mjs with a form that survives the cjs build, keeping the "resolves before the entrypoint is built" property.
  • Promote esbuild empty-import-meta to an error in @clc/nx:esbuild.
  • Verify nx run-many --target=build emits no empty-import-meta warning.
  • Verify dist/cjs/tool.cjs contains no import_meta = {}.
  • Verify codegen:tool still runs for all six tool packages.
  • Verify the new guard actually fails a build (planted an import.meta offender; nx exited 1).
  • Two adversarial review passes; findings triaged.
  • Rebase onto current main and push.
  • Re-verify the deliverable on a fresh build (2026-09-09 resume pass). Build exit 0, no empty-import-meta, no import_meta binding in dist/cjs, CLI package.json subpath resolves.
  • CI green — blocked on CLC-1138 / fix(ci): shard example tests in-repo instead of via check-run-reporter #437, which removes the dead check-run-reporter split step that reddens test-integration on main. fix(ci): shard example tests in-repo instead of via check-run-reporter #437 is still an open draft and still red. Rebase onto main once it lands.
  • Copilot review — gated on CI.
  • Operator review — gated on CI, and on a human, since the agent account cannot approve a PR it authored and main requires one approving review.

Parked as human-blocked on 2026-09-09: both remaining items need a person, and no reply has come in two days.

Verification

  • nx run-many --target=build --skip-nx-cache: 408 tasks, exit 0, zero empty-import-meta output.
  • codegen:tool ran for tool-deps, tool-foundation, tool-inliner, tool-json-schema, tool-stack, tool-tool; git status clean afterward, so addAsCliPlugin rewrote cli/package.json at the right path with identical content.
  • Guard check: adding a file with import.meta.url to tool-tool/src makes build:cjs fail with ERROR ... [empty-import-meta]. Removed afterward.
  • Blast radius of the guard is zero: the only remaining source import.meta is @clc/nx/executors/esbuild/executor.mjs:49, and @clc/nx never registers the esbuild target (create-nodes.mjs:72).
  • The property the ticket insisted on is preserved, tested directly. With packages/@code-like-a-carpenter/cli/dist moved aside, createRequire(rootPkgPath).resolve('@code-like-a-carpenter/cli/package.json') still resolves, while resolving the bare entrypoint fails MODULE_NOT_FOUND. Resolving the ./package.json subpath is exactly what sidesteps the existence check the original comment warned about.

Review findings triaged

Two passes on Codex (spec-aware and spec-blind), a distinct model family from the authoring one. Both independently returned the same single finding and no functional defects.

Acted on:

  • tool.mjs:71 — the comment claimed the package-json executor emits "./package.json" "for every package". Overbroad: examples take configExample (package-json/executor.mjs:29), which builds no exports map, and @clc/nx returns before the target is registered (create-nodes.mjs:72). Dropped the clause; the load-bearing precondition is just that the CLI exports it. Fixed in 3862cd8.

Carried over from the earlier pass:

  • Executor comment claimed the breakage was "silent" and that any import.meta use is a TypeError. Both wrong — esbuild warned, and property reads yield undefined. Comment rewritten.
  • The first commit message asserted a live failing path I could not substantiate. Rewritten to name the actual reachable path.

Dismissed:

  • codegen:tool writes cli/package.json but declares only {projectRoot}/package.json as an nx output, so a cache hit skips the registration. Real, but pre-existing and unchanged by this diff.
  • Neither package has tests. Matches their existing state; adding a first harness is out of scope.

Annotations

All 18 dismissed as pre-existing and untouched by this diff, with rationale recorded per annotation: no-explicit-any lint in foundation-runtime, logger, and types/*.d.ts; GitHub's Node 20 runner deprecation notices; and the error annotations downstream of the test-integration failure now tracked by CLC-1138.

@nx-cloud

nx-cloud Bot commented Sep 7, 2026

Copy link
Copy Markdown

View your CI Pipeline Execution ↗ for commit 3862cd8

Command Status Duration Result
nx run-many --target build ✅ Succeeded 39s View ↗

💡 Verify your cache is correct by running tasks in a sandbox. Read docs ↗


☁️ Nx Cloud last updated this comment at 2026-09-07 22:27:56 UTC

@codecov

codecov Bot commented Sep 7, 2026

Copy link
Copy Markdown

Codecov Report

✅ All modified and coverable lines are covered by tests.
✅ Project coverage is 80.41%. Comparing base (5584eeb) to head (3862cd8).

Additional details and impacted files
@@           Coverage Diff           @@
##             main     #428   +/-   ##
=======================================
  Coverage   80.41%   80.41%           
=======================================
  Files          23       23           
  Lines         730      730           
  Branches      182      182           
=======================================
  Hits          587      587           
  Misses        140      140           
  Partials        3        3           

☔ View full report in Codecov by Harness.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.
  • 📦 JS Bundle Analysis: Save yourself from yourself by tracking and limiting bundle sizes in JS merges.

tool-tool's src/tool.mjs is compiled to CommonJS as well as ESM. esbuild
turns `import.meta` into an empty object under cjs, so
`import.meta.resolve('@code-like-a-carpenter/cli')` became
`import_meta.resolve(...)` in dist/cjs/tool.cjs — a TypeError for anything
that reaches addAsCliPlugin through the built CJS executor. executors.json
points at the cjs build whenever codegen:tool ran with src/index.mjs absent
(create-nodes.mjs sets buildBeforeRun from that check), which is the state
tool-foundation/executors.json is committed in.

Resolve `@code-like-a-carpenter/cli/package.json` through
createRequire(rootPkgPath) instead. That works in both output formats and
keeps the property the old comment called out: it does not depend on the
CLI entrypoint having been built, because package.json is itself the
resolution target and always exists. Resolving the subpath directly also
drops the findUp walk back up from the entrypoint.

Set esbuild's logOverride so `empty-import-meta` is an error, making a
format-incompatible construct fail @clc/nx:esbuild instead of scrolling
past as a warning. tool.mjs was the only first-party source that tripped
it; @clc/nx itself is exempt because create-nodes gives it no build target.
The comment said the package-json executor emits "./package.json" for
every package. Examples take configExample, which does not build an
exports map, and @clc/nx never registers the target at all. The
precondition that matters is that the CLI itself exports it, so state
only that.
@ianwremmel-ai-agent
ianwremmel-ai-agent force-pushed the clc-1133-fix-tool-tools-commonjs-build-which-compiles branch from 1130f12 to 3862cd8 Compare September 7, 2026 22:25
@ianwremmel-ai-agent

Copy link
Copy Markdown
Collaborator Author

CI blocker: test-integration is red on main, not from this diff

All five test-integration shards fail here, and they fail identically on main. Run 34163408910 (head 5584eeb, current main) fails on exactly the same five shards and nothing else.

Chain:

  1. The check-run-reporter/action split step returns an empty steps.split.outputs.tests.
  2. ./scripts/crr-sam deploy is therefore called with no arguments.
  3. scripts/crr-sam:26 is for testfile in "$@", so with no arguments testfile is never assigned.
  4. scripts/crr-sam:35 dereferences $testfile inside the for projectName in $projects loop. Under set -u that aborts: ./scripts/crr-sam: line 35: testfile: unbound variable.
  5. The job dies before running a test, so no reports/junit/**/*.xml is written, which produces the trailing Could not find any report files matching glob annotation.

The last green main run was a3b5cb3 on 2025-11-21, so this looks like the check-run-reporter split going empty in the interim rather than a code change.

I have not touched it. Papering over step 3 (defaulting testfile) would make deployed non-empty and turn the job green while still deploying and testing nothing, which is worse than the current failure. The real fix depends on whether the check-run-reporter service and CHECK_RUN_REPORTER_TOKEN are still provisioned — I can't determine that from here.

Question: how do you want this handled? Options as I see them:

  • File and fix the crr-sam / check-run-reporter breakage as its own ticket, and land this PR after that.
  • Merge this PR red on test-integration, since the failure predates it.

Holding this PR in draft until you say which.

Where this PR stands otherwise

Gates 2-5 are clear: no merge conflicts, no actionable threads, remaining annotations are pre-existing no-explicit-any lint on files this diff does not touch plus GitHub's Node 20 runner deprecation notices. Every other check is green, including build, lint, test-unit, TSC, and both Codecov gates.

Verification of the ticket's "done when", run locally:

  • nx run-many --target=build --skip-nx-cache -> 408 tasks, exit 0, no empty-import-meta output.
  • dist/cjs/tool.cjs contains no import_meta binding.
  • codegen:tool runs for all six tool packages, working tree clean afterward.
  • The new guard bites: planting an import.meta.url reference in tool-tool/src makes @code-like-a-carpenter/tool-tool:build:cjs fail. Reverted after the check.
  • The property the ticket insisted on is preserved. With packages/@code-like-a-carpenter/cli/dist moved aside, createRequire(rootPkgPath).resolve('@code-like-a-carpenter/cli/package.json') still resolves, while resolving the bare entrypoint fails with MODULE_NOT_FOUND. Resolving the ./package.json subpath is what sidesteps the existence check the original comment warned about.

@ianwremmel-ai-agent

Copy link
Copy Markdown
Collaborator Author

Answering my own question above: the first option was taken. The test-integration breakage is now CLC-1138, and PR #437 (fix(ci): shard example tests in-repo instead of via check-run-reporter) is in flight against it. Check Run Reporter dropped the test-splitting API, so #437 computes each matrix shard in-repo and deletes the split step.

This PR waits for #437 to land, then rebases onto main for a green test-integration. Nothing in this diff changes in the meantime.

Annotation triage, all dismissed with rationale recorded:

@ianwremmel-ai-agent

Copy link
Copy Markdown
Collaborator Author

Resume pass: re-verified, still blocked on a human

Nothing has moved since 2026-09-08. Head is still 3862cd8, there are no reviews, and there is no operator reply here or on CLC-1133.

Re-verified the deliverable this pass with a fresh build (nx run @code-like-a-carpenter/tool-tool:build --skip-nx-cache, 71 tasks, exit 0):

  • No empty-import-meta error, so the new logOverride guard passes against real sources rather than only against a planted offender.
  • dist/cjs/*.cjs has no import_meta binding. A bare grep import_meta tool.cjs returns two hits, but both are the import_metadata require at lines 47 and 49 matching as a substring, not an import.meta shim.
  • createRequire(<repo>/package.json).resolve('@code-like-a-carpenter/cli/package.json') resolves, which is the property the original comment protected.
  • The only import.meta left under packages/ is @clc/nx/executors/esbuild/executor.mjs:49, inside a template string, and @clc/nx never registers the esbuild target for itself.

The branch is 8 commits behind main. All eight are Dependabot bumps and none touches tool.mjs or the esbuild executor, so I have not rebased: it would spend a CI run that fails identically.

What is blocking

  1. test-integration is red on main itself, with the same ./scripts/crr-sam: line 35: testfile: unbound variable. The fix is CLC-1138 / fix(ci): shard example tests in-repo instead of via check-run-reporter #437, which is still an open draft and still red.
  2. main's ruleset requires one approving review. This PR was authored by the agent account, which cannot approve its own PR and has no bypass.

Both need a person: land #437, then approve this one. Parking it here rather than polling again — three requests over two days have gone unanswered, so another wait cycle adds nothing.

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