fix: Correct Broccoli plugin wiring to merge into app.trees.app

- Merge generatedAppTree into app.trees.app before app.toTree()
- Fix Funnel srcDir/destDir configuration
- Remove prebuild extension generation (now handled by Broccoli)
- Add extensionManifestTree to expose dist/extensions.json
- Files now treated as real app source for ember-auto-import
This commit is contained in:
roncodes
2025-11-27 01:12:36 -05:00
parent a0fc1ce402
commit edba6c8396
2 changed files with 96 additions and 60 deletions

View File

@@ -4,6 +4,7 @@
const EmberApp = require('ember-cli/lib/broccoli/ember-app');
const Funnel = require('broccoli-funnel');
const writeFile = require('broccoli-file-creator');
const mergeTrees = require('broccoli-merge-trees');
const postcssImport = require('postcss-import');
const postcssPresetEnv = require('postcss-preset-env');
const postcssEach = require('postcss-each');
@@ -15,6 +16,12 @@ const tailwind = require('tailwindcss');
const toBoolean = require('./config/utils/to-boolean');
const environment = process.env.EMBER_ENV;
// Import Broccoli plugins
const ExtensionDiscoveryPlugin = require('./lib/build-plugins/extension-discovery');
const ExtensionShimGeneratorPlugin = require('./lib/build-plugins/extension-shim-generator');
const ExtensionLoadersGeneratorPlugin = require('./lib/build-plugins/extension-loaders-generator');
const RouterGeneratorPlugin = require('./lib/build-plugins/router-generator');
module.exports = function (defaults) {
const app = new EmberApp(defaults, {
storeConfigInMeta: false,
@@ -68,7 +75,92 @@ module.exports = function (defaults) {
},
});
// Extension shims are generated by prebuild.js before ember build
// Step 1: Create extension discovery and generator instances
const extensionDiscovery = new ExtensionDiscoveryPlugin([], {
projectRoot: __dirname,
annotation: "Discover Fleetbase Extensions",
});
const extensionShims = new ExtensionShimGeneratorPlugin([extensionDiscovery], {
projectRoot: __dirname,
annotation: "Generate Extension Shims",
});
const extensionLoaders = new ExtensionLoadersGeneratorPlugin([extensionDiscovery], {
projectRoot: __dirname,
annotation: "Generate Extension Loaders",
});
const router = new RouterGeneratorPlugin([extensionDiscovery], {
projectRoot: __dirname,
annotation: "Generate Router with Extension Mounts",
});
// Step 2: Build a "generated app" tree
// The plugin outputs currently contain files relative to their own outputPath:
// - extensionShims output: extensions/*.js
// - extensionLoaders output: utils/extension-loaders.generated.js
// - router output: router.js
//
// We want these to appear in the app source tree as:
// - app/extensions/*.js
// - app/utils/extension-loaders.generated.js
// - app/router.js
//
// To achieve this, we build a merged tree whose paths mirror app/ structure:
const generatedAppTree = mergeTrees(
[
// app/extensions/*.js
// extensionShims writes to outputPath/extensions/*.js
// We want these files at app/extensions/*.js in the final tree
new Funnel(extensionShims, {
srcDir: "extensions",
destDir: "extensions",
}),
// app/utils/extension-loaders.generated.js
// extensionLoaders writes to outputPath/utils/*.js
new Funnel(extensionLoaders, {
srcDir: "utils",
destDir: "utils",
}),
// app/router.js
// router writes to outputPath/router.js
new Funnel(router, {
srcDir: "/",
destDir: "/",
}),
],
{
overwrite: true,
annotation: "Merge Extension Generated Files into app tree",
}
);
// Step 3: Merge generatedAppTree into app.trees.app
// Before calling app.toTree(), we must ensure that app.trees.app includes the generated files.
app.trees.app = mergeTrees(
[app.trees.app, generatedAppTree],
{ overwrite: true }
);
// This makes Ember treat the generated files as if they had been in the app/ directory from the beginning.
// Step 4: Expose extensions.json as dist/extensions.json
// We also want the discovery manifest to be accessible at dist/extensions.json. To do this, we
// funnel the discovery plugin output:
const extensionManifestTree = new Funnel(extensionDiscovery, {
srcDir: "/",
files: ["extensions.json"],
destDir: "/",
});
// Now, extensionManifestTree will place extensions.json at the root of the final dist output.
// Step 5: Final app.toTree() call
// Instead of passing the "extensions" tree into app.toTree(), we now pass only the trees that
// represent additional *top-level* output (such as the manifest and runtimeConfigTree).
// Runtime config
let runtimeConfigTree;
@@ -81,5 +173,7 @@ module.exports = function (defaults) {
});
}
return app.toTree([runtimeConfigTree].filter(Boolean));
return app.toTree(
[extensionManifestTree, runtimeConfigTree].filter(Boolean)
);
};

View File

@@ -173,62 +173,4 @@ function getRouterFileContents() {
const output = recast.print(ast, { quote: 'single' }).code;
fs.writeFileSync(path.join(__dirname, 'app/router.js'), output);
// Generate extension shims
console.log('[Prebuild] Generating extension shims...');
generateExtensionShims(extensions);
})();
function generateExtensionShims(extensions) {
const extensionsDir = path.join(__dirname, 'app', 'extensions');
const utilsDir = path.join(__dirname, 'app', 'utils');
// Create directories
fs.mkdirSync(extensionsDir, { recursive: true });
fs.mkdirSync(utilsDir, { recursive: true });
const loaderEntries = [];
// Generate shim for each extension that has extension.js
for (const extension of extensions) {
const extensionPath = path.join(__dirname, 'node_modules', extension.name, 'addon', 'extension.js');
if (fs.existsSync(extensionPath)) {
try {
// Read the extension code
const extensionCode = fs.readFileSync(extensionPath, 'utf8');
// Generate shim content
const shimContent = `// AUTO-GENERATED - DO NOT EDIT
// Extension shim for ${extension.name}
${extensionCode}
`;
// Write shim file
const mountName = getExtensionMountPath(extension.name);
const shimPath = path.join(extensionsDir, `${mountName}.js`);
fs.writeFileSync(shimPath, shimContent, 'utf8');
// Add to loader map
loaderEntries.push(` '${extension.name}': () => import('@fleetbase/console/extensions/${mountName}')`);
console.log(`[Prebuild] ✓ Generated shim: app/extensions/${mountName}.js`);
} catch (error) {
console.error(`[Prebuild] Failed to generate shim for ${extension.name}:`, error.message);
}
}
}
// Generate extension loaders file
const loadersContent = `// AUTO-GENERATED - DO NOT EDIT
// Extension loader map for dynamic imports
export const EXTENSION_LOADERS = {
${loaderEntries.join(',\n')}
};
`;
const loadersPath = path.join(utilsDir, 'extension-loaders.js');
fs.writeFileSync(loadersPath, loadersContent, 'utf8');
console.log(`[Prebuild] ✓ Generated ${loaderEntries.length} extension shims and loader map`);
}