Support type=module v2 addons in webpack builds - #2780
Support type=module v2 addons in webpack builds#2780NullVoxPopuli-ai-agent wants to merge 2 commits into
Conversation
Two coordinated fixes:
1. @embroider/webpack: add a module rule that opts .js files owned by v2
addons back into webpack's regular javascript/auto handling. When a v2
addon sets "type": "module" in its package.json, webpack otherwise
applies strict ESM semantics to the addon's files, which breaks them in
three ways that non-type=module addons never hit:
- default-importing one of the CommonJS modules we externalize (like
the /@embroider/ext-cjs/ virtual modules) yields the module's exports
object rather than its default export, because strict ESM importers
don't get __esModule interop (embroider-build#1774)
- import specifiers must be fully-specified, so directory imports and
the relative extensionless es-compat2 import emitted by
@embroider/macros importSync fail to resolve (embroider-build#1672)
- require() is not allowed in strict ESM, so the require() calls that
importSync compiles to get left for the runtime AMD loader (which
can't resolve relative paths) instead of being handled by webpack
2. @embroider/macros: emit fully-specified (extension-bearing) import
paths for our es-compat2 and runtime helper modules, so they resolve
under strict ESM semantics in any bundler.
Fixes embroider-build#1672. Part of the type=module quest embroider-build#1773. Supersedes the macros
half of embroider-build#1906.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
|
Note on CI: every That's pre-existing drift, not caused by this change: the Locally (against the committed lockfile, ember-source 6.12), |
|
looks like we haven't fixed (don't plan to fix?) release scenarios |
The v1 addon gets rewritten to v2 during compat, so this exercises the request path where an import from a strict-ESM file lands in a rewritten package (the suspected embroider-build#1674 mechanism), plus default-import interop through that path. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
|
Added explicit coverage for the "type=module v2 addon depends on a v1 addon" case: |
Part of the type=module quest issue: #1773. Fixes #1672. Should also fix #1774 and likely #1674 (explanation below). Supersedes the macros half of #1906 and resolves the blocker that PR was stuck on.
Problem
When a v2 addon sets
"type": "module"in its package.json, webpack applies strict ESM semantics to all of the addon's.jsfiles, which breaks them in three ways that non-type=module addons never hit:Lost
__esModuleinterop for externalized modules ([type=module] _ember_component_template_only is not a function #1774). Default-importing one of the CommonJS modules we externalize — e.g. the/@embroider/ext-cjs/virtual modules that stand in for@ember/component/template-onlywhen it isn't resolvable statically — yields the module's exports object instead of its default export, because webpack (correctly, per spec) skips__esModuleinterop for strict-ESM importers. At runtime:_ember_component_template_only__WEBPACK_IMPORTED_MODULE_x__ is not a function.Fully-specified resolution (
importSyncbreaks the builds of apps consuming type=module packages #1672). Import specifiers inside the addon must carry file extensions, so directory imports fail, and so does the relative extensionlesses-compat2import that@embroider/macrosemits when compilingimportSync():require()is not allowed in strict ESM, so therequire()calls thatimportSync()compiles to are not treated as webpack dependencies and get left for the runtime AMD loader, which can't resolve relative paths. This was the remaining blocker on@embroider/macrosand type=module #1906's test (Could not find module ./side-effecting.js imported from (require)).This is also very likely the mechanism behind #1674: a request originating in a strict-ESM addon file carries
fullySpecifiedresolve options that survive being rehomed into a rewritten package (see the analysis in #1572 (comment)), producing theCan't resolve '.'error. With the addon's files back injavascript/auto, those resolve options are never created in the first place.Solution
@embroider/webpack: add a module rule that opts.jsfiles owned by v2 addons back intotype: 'javascript/auto'withresolve: { fullySpecified: false }. This is exactly the treatment every non-type=module v2 addon already gets (their.jsfiles default tojavascript/autobecause their package.json has notypefield), so type=module addons now behave identically to all other v2 addons. Non-addon packages are unaffected and keep spec-compliant strict-ESM behavior.@embroider/macros: emit fully-specified (extension-bearing) paths for oures-compat2andruntimehelper modules (embroider-mode only; the classic AMD specifier is unchanged). This keeps the emitted code correct under strict ESM semantics in any bundler, independent of the webpack rule.Testing
v2-addon-type-module-test.ts: atype=modulev2 addon (withaddon-main.cjs) covering a template-only component, a@glimmer/componentsubclass, an internal directory import, andimportSync()of a relative module. Runs in bothsafeandoptimizedembroider modes. All pass onrelease,lts_3_28, andlts_5_12; a control run without the two source fixes fails with the strict-ESM errors quoted above.v2-addon-test.tsandmacro-test.tsrelease scenarios (includingCLASSIC=trueruns) all pass.app-embroider-3-lib-import-syncapp builds and passes with these packages linked in.The same interop/fully-specified fix for ember-auto-import is embroider-build/ember-auto-import#718.
🤖 Generated with Claude Code