Compare commits

..

1 Commits

Author SHA1 Message Date
Ronald A. Richardson
0bcd9ec165 fix: always generate extension loader files even when no extensions are installed
When no extensions are installed, generateExtensionFiles() previously
returned early after cleaning up the app/extensions directory. This left
the directory absent, causing a fatal module resolution error at runtime:

  Error: Could not find module `@fleetbase/console/extensions`
  imported from `@fleetbase/ember-core/services/universe/extension-manager`

The extension-manager service in @fleetbase/ember-core has a hard static
import:

  import { getExtensionLoader } from '@fleetbase/console/extensions';

Ember's module resolver maps this to app/extensions/index.js. Because
that file was never created in the zero-extension scenario, the entire
application failed to boot.

Changes:
- Remove the early return when extensions.length === 0.
- Restructure generateExtensionFiles() so that generateExtensionLoaders(),
  generateRouter(), and generateExtensionsManifest() are always called,
  regardless of whether any extensions are discovered.
- generateExtensionShims() is still only called when extensions > 0, as
  there are no shim files to write in the empty case.
- When no extensions are found, generateExtensionLoaders() writes an
  app/extensions/index.js with an empty EXTENSION_LOADERS = {} object,
  satisfying the module dependency without registering any loaders.
- generateExtensionsManifest() writes public/extensions.json with an
  empty array [], which is the correct state for zero extensions.

Additionally, add null-guards to the recast AST visitor inside
generateRouter() to prevent a potential TypeError when traversing call
expressions whose callee is not a MemberExpression (e.g. bare require()
or import() calls that have no .property).
2026-02-28 03:44:37 -05:00

View File

@@ -59,27 +59,24 @@ module.exports = {
const extensions = await this.getExtensions();
if (extensions.length === 0) {
console.log('[Fleetbase] No extensions found');
return;
if (extensions.length > 0) {
console.log(`[Fleetbase] Discovered ${extensions.length} extension(s)`);
extensions.forEach((ext) => {
console.log(`[Fleetbase] - ${ext.name} (v${ext.version})`);
});
console.log('');
// Generate extension shims (only needed when extensions are present)
this.generateExtensionShims(extensions);
} else {
console.log('[Fleetbase] No extensions found — generating empty extension loader to satisfy module dependencies.');
}
console.log(`[Fleetbase] Discovered ${extensions.length} extension(s)`);
extensions.forEach((ext) => {
console.log(`[Fleetbase] - ${ext.name} (v${ext.version})`);
});
console.log('');
// Generate extension shims
this.generateExtensionShims(extensions);
// Generate extension loaders
// Always generate loaders, router, and manifest so that
// @fleetbase/console/extensions (app/extensions/index.js) always exists
// and the build does not fail when zero extensions are installed.
this.generateExtensionLoaders(extensions);
// Generate router
this.generateRouter(extensions);
// Generate manifest
this.generateExtensionsManifest(extensions);
},
@@ -227,7 +224,7 @@ export default getExtensionLoader;
recast.visit(ast, {
visitCallExpression(path) {
if (path.value.type === 'CallExpression' && path.value.callee.property.name === 'route' && path.value.arguments[0].value === 'console') {
if (path.value.type === 'CallExpression' && path.value.callee.property && path.value.callee.property.name === 'route' && path.value.arguments[0] && path.value.arguments[0].value === 'console') {
let functionExpression;
// Find the function expression
@@ -270,7 +267,7 @@ export default getExtensionLoader;
}
}
if (path.value.type === 'CallExpression' && path.value.callee.property.name === 'map') {
if (path.value.type === 'CallExpression' && path.value.callee.property && path.value.callee.property.name === 'map') {
let functionExpression;
path.value.arguments.forEach((arg) => {