fix: support ESM templates in templates:install (require → dynamic import) - #83
AnujVishwakarma-src wants to merge 2 commits into
Conversation
templates:install loaded the template generator via require(templatePath) and passed the result straight to env.instantiate(). For an ESM template (type:module) require() either throws ERR_REQUIRE_ESM (Node without require(esm)) or, on newer Node, returns the module namespace object rather than the generator class — so env.instantiate() received a non-constructor and threw 'constructor is not a constructor' (e.g. running @adobe/generator-app-excshell once it shipped as ESM). Load the template resiliently: require() a CommonJS template, fall back to dynamic import() on ERR_REQUIRE_ESM, and unwrap a .default export in both cases. Works for CommonJS and ESM templates. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
|
/upload-coverage |
|
Coverage artifact not found. Re-run CI first, then comment |
Codecov Report✅ All modified and coverable lines are covered by tests. 📢 Thoughts on this report? Let us know! |
…on (#84) The ESM 4.x releases broke 'aio app add extension' on the released aio-cli: the Template Registry installs this template at @latest and app-templates loads it with require(), which cannot instantiate an ESM generator ('constructor is not a constructor'). Revert the source to CommonJS (the 3.0.0 tree) and publish as 5.0.0 so @latest is CJS again. Temporary until aio-cli-plugin-app-templates loads templates via import() (adobe/aio-cli-plugin-app-templates#83) ships in a new aio-cli; this package will then be re-migrated to ESM. generator-aio-app@10 keeps the ESM 4.1.0 via its ^4 range (unaffected by this major). Co-authored-by: Anuj Vishwakarma <298213938+AnujVishwakarma-src@users.noreply.github.com> Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…on (#68) The ESM 3.x releases broke 'aio app add extension' on the released aio-cli: the Template Registry installs this template at @latest and app-templates loads it with require(), which cannot instantiate an ESM generator ('constructor is not a constructor'). Revert the source to CommonJS (the 2.0.0 tree) and publish as 4.0.0 so @latest is CJS again. Temporary until aio-cli-plugin-app-templates loads templates via import() (adobe/aio-cli-plugin-app-templates#83) ships in a new aio-cli; this package will then be re-migrated to ESM. generator-aio-app@10 keeps the ESM 3.1.0 via its ^3 range (unaffected by this major). Co-authored-by: Anuj Vishwakarma <298213938+AnujVishwakarma-src@users.noreply.github.com> Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
|
Findings P1 / P2: ERR_REQUIRE_ASYNC_MODULE is not handled In src/commands/templates/install.js, the fallback only catches: if (e.code === 'ERR_REQUIRE_ESM') { On newer Node versions, require() can load synchronous ESM directly. However, if the ESM template or anything in its import graph uses top-level await, Node throws ERR_REQUIRE_ASYNC_MODULE, not ERR_REQUIRE_ESM. Node explicitly documents that such modules must be loaded with import(). That means an otherwise valid ESM Yeoman template can still fail in the exact code path this PR is intended to fix. Suggested change: } catch (e) { I would treat this as the main review comment. |
Addresses review: on newer Node, require() loads sync ESM directly, but an ESM template (or anything in its import graph) using top-level await throws ERR_REQUIRE_ASYNC_MODULE (not ERR_REQUIRE_ESM). Handle both codes so such a template still loads via dynamic import(). Adds a fixture with top-level await + a test covering the async-ESM path. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
|
Good catch — addressed in
if (e.code === 'ERR_REQUIRE_ESM' || e.code === 'ERR_REQUIRE_ASYNC_MODULE') {
templateModule = await import(pathToFileURL(templatePath).href)
} else {
throw e
}So an ESM template that uses top-level await (which throws Added |
|
/upload-coverage |
Problem
aio app add extension→templates:installruns the selected template with:When a template ships as ESM (
type: module),require(templatePath):ERR_REQUIRE_ESMon Node withoutrequire(esm), or{ __esModule, default: <class> }— not the generator class.Either way
env.instantiate()gets a non-constructor and throwsError: constructor is not a constructor. This is hit today by@adobe/generator-app-excshelland@adobe/generator-app-asset-compute(both now ESM), since the Template Registry installs templates at@latest, bypassing semver pins.Fix
Load the template resiliently and unwrap the default export (works for both CommonJS and ESM templates):
Tests
ERR_REQUIRE_ESM→ dynamicimport(), real ESM fixture) instantiates the default-export class.ERR_REQUIRE_ESMrequire error propagates.🤖 Generated with Claude Code