Skip to content

fix: IOEXT-1726 aio app dev fails when project package.json has "type": "module"#230

Closed
pru55e11 wants to merge 2 commits into
adobe:masterfrom
pru55e11:fix/ioext-1726-esm-sibling-package-json
Closed

fix: IOEXT-1726 aio app dev fails when project package.json has "type": "module"#230
pru55e11 wants to merge 2 commits into
adobe:masterfrom
pru55e11:fix/ioext-1726-esm-sibling-package-json

Conversation

@pru55e11
Copy link
Copy Markdown
Contributor

@pru55e11 pru55e11 commented May 14, 2026

Summary

Fixes IOEXT-1726: every web action returns 400 "<action> action not found, or does not export main" from aio app dev when the user's project package.json declares "type": "module". aio app deploy is unaffected.

Reproduction (with @adobe/aio-cli@11.1.0, @adobe/aio-lib-runtime@7.3.0, Node 22 or 24):

git clone https://github.com/adobe/commerce-checkout-starter-kit
cd commerce-checkout-starter-kit
npm install
# remove `require-adobe-auth: true` from the `info` action in app.config.yaml
aio app dev -e application
curl -k https://localhost:9080/api/v1/web/commerce-checkout-starter-kit/info
# {"error":"Response is not valid 'message/http'. info action not found, or does not export main"}

Root cause

prepareToBuildAction writes a webpack bundle to <dist>/<pkg>/<action>-temp/index.js with libraryTarget: 'commonjs2', but does not drop a sibling package.json. Node decides the bundle's module type by walking up to the closest package.json, which is the user's project root. When that file has "type": "module", Node parses the CommonJS bundle as ESM. @adobe/aio-cli-plugin-app-dev's defaultActionLoader in run-dev.js calls require(actionPath), which throws, gets caught, and surfaces the misleading "<action> action not found, or does not export main" message.

aio app deploy is unaffected because the deployed bundle runs in an OpenWhisk Node container with no parent package.json to interfere.

Fix

After webpack writes the bundle, drop a sibling package.json pinning the bundle to CommonJS. This is a one-line change inside prepareToBuildAction's non-directory branch:

fs.writeJsonSync(path.join(tempBuildDir, 'package.json'), { type: 'commonjs' })

The sibling is the closest ancestor package.json Node finds when resolving the bundled index.js, so it wins regardless of the project's setting. The change is invisible to deploy: the OpenWhisk Node runtime treats bundled .js actions as CJS already, and an explicit { type: 'commonjs' } is consistent with the bundle's actual format.

Scope

This fixes single-file (webpack-bundled) actions, which is the case in the bug report. Directory-style actions written in ESM would also need the dev plugin's loader to switch from require() to dynamic import(); tracked separately as ACNA-4604.

Test plan

  • New unit test in test/build.actions.test.js asserts prepareToBuildAction writes <temp>/package.json with { type: 'commonjs' } next to the bundled index.js
  • Full test suite passes (457/457, 100% coverage maintained)
  • Manual repro per IOEXT-1726:
    • Without patch: curl /info → HTTP 400, "info action not found, or does not export main"
    • With patch: curl /info → HTTP 200 ✓
  • No deploy regression: deploy path is unchanged for the OpenWhisk runtime; the extra package.json in the zip just makes the bundle's CJS-ness explicit.

Risk

Very low. The change only touches output produced for webpack-bundled (single-file) actions. Existing tests continue to pass; the new test locks in the contract.

…oad as CJS

Webpack emits the action bundle with libraryTarget: 'commonjs2', but Node walks
up to the closest package.json to decide the module type for the bundled
index.js. If the user's project package.json sets "type": "module", Node parses
the CJS bundle as ESM and aio-cli-plugin-app-dev's require()-based loader fails
with "<action> action not found, or does not export main", breaking aio app dev
for every web action.

aio app deploy is unaffected because the deployed bundle runs in an OpenWhisk
Node container where there is no parent package.json to interfere.

Drop a sibling package.json in the webpack temp output that pins the bundle to
CommonJS, regardless of the project's module type.

This fixes the single-file (webpack-bundled) action case. Directory-style
actions written in native ESM remain a separate, larger problem requiring a
loader refactor in aio-cli-plugin-app-dev.
@codecov
Copy link
Copy Markdown

codecov Bot commented May 14, 2026

Codecov Report

✅ All modified and coverable lines are covered by tests.

📢 Thoughts on this report? Let us know!

@pru55e11
Copy link
Copy Markdown
Contributor Author

reopening from the upstream branch

@pru55e11 pru55e11 closed this May 22, 2026
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