mirror of
https://github.com/fleetbase/fleetbase.git
synced 2025-12-19 14:18:57 +00:00
feat: Generate app/extensions/index.js with new format and cleanup
- Changed extension loader generation to create app/extensions/index.js - New format uses direct imports instead of dynamic imports - Added getExtensionLoader helper function - Removed unused plugins directory - Removed old extension-loaders.generated.js file - Cleaner and simpler loader structure
This commit is contained in:
@@ -1,14 +0,0 @@
|
||||
/**
|
||||
* AUTO-GENERATED FILE - DO NOT EDIT
|
||||
* Generated by prebuild.js
|
||||
*
|
||||
* This file provides a build-time map of extension loaders to avoid
|
||||
* dynamic require() calls which are incompatible with Embroider.
|
||||
*
|
||||
* Each loader is a function that returns a dynamic import() of the
|
||||
* local app-level shim module (not the addon module directly).
|
||||
*/
|
||||
|
||||
export const EXTENSION_LOADERS = {
|
||||
'@fleetbase/fleetops-engine': () => import('../extensions/fleetops'),
|
||||
};
|
||||
@@ -136,12 +136,13 @@ module.exports = {
|
||||
},
|
||||
|
||||
generateExtensionLoaders(extensions) {
|
||||
const utilsDir = path.join(this.project.root, 'app', 'utils');
|
||||
const extensionsDir = path.join(this.project.root, 'app', 'extensions');
|
||||
|
||||
if (!fs.existsSync(utilsDir)) {
|
||||
fs.mkdirSync(utilsDir, { recursive: true });
|
||||
if (!fs.existsSync(extensionsDir)) {
|
||||
fs.mkdirSync(extensionsDir, { recursive: true });
|
||||
}
|
||||
|
||||
const imports = [];
|
||||
const loaders = {};
|
||||
|
||||
extensions.forEach((extension) => {
|
||||
@@ -152,22 +153,30 @@ module.exports = {
|
||||
}
|
||||
|
||||
const mountPath = extension.fleetbase?.route || this.getExtensionMountPath(extension.name);
|
||||
loaders[extension.name] = `() => import('@fleetbase/console/extensions/${mountPath}')`;
|
||||
const camelCaseName = mountPath.replace(/-([a-z])/g, (g) => g[1].toUpperCase());
|
||||
|
||||
imports.push(`import ${camelCaseName} from './${mountPath}';`);
|
||||
loaders[extension.name] = `() => ${camelCaseName}`;
|
||||
});
|
||||
|
||||
const loadersContent = `// Auto-generated extension loaders
|
||||
const loadersContent = `${imports.join('\n')}
|
||||
|
||||
export const EXTENSION_LOADERS = {
|
||||
${Object.entries(loaders)
|
||||
.map(([name, loader]) => ` '${name}': ${loader}`)
|
||||
.join(',\n')}
|
||||
};
|
||||
|
||||
export default EXTENSION_LOADERS;
|
||||
export const getExtensionLoader = (packageName) => {
|
||||
return EXTENSION_LOADERS[packageName];
|
||||
};
|
||||
|
||||
export default getExtensionLoader;
|
||||
`;
|
||||
|
||||
const loadersFile = path.join(utilsDir, 'extension-loaders.generated.js');
|
||||
const loadersFile = path.join(extensionsDir, 'index.js');
|
||||
fs.writeFileSync(loadersFile, loadersContent, 'utf8');
|
||||
console.log('[fleetbase-extensions-generator] ✓ Generated app/utils/extension-loaders.generated.js');
|
||||
console.log('[fleetbase-extensions-generator] ✓ Generated app/extensions/index.js');
|
||||
},
|
||||
|
||||
generateRouter(extensions) {
|
||||
|
||||
@@ -1,127 +0,0 @@
|
||||
const Plugin = require('broccoli-plugin');
|
||||
const fs = require('fs');
|
||||
const path = require('path');
|
||||
const fg = require('fast-glob');
|
||||
|
||||
/**
|
||||
* ExtensionDiscoveryPlugin
|
||||
*
|
||||
* Discovers Fleetbase extensions from node_modules by scanning package.json files
|
||||
* for packages with 'fleetbase-extension' and 'ember-engine' keywords.
|
||||
*
|
||||
* This plugin runs during build and caches the discovered extensions for use
|
||||
* by other plugins (router generation, extension loaders, etc.)
|
||||
*/
|
||||
class ExtensionDiscoveryPlugin extends Plugin {
|
||||
constructor(inputNodes, options = {}) {
|
||||
super(inputNodes, {
|
||||
annotation: options.annotation || 'ExtensionDiscoveryPlugin',
|
||||
persistentOutput: true,
|
||||
});
|
||||
|
||||
this.projectRoot = options.projectRoot || process.cwd();
|
||||
}
|
||||
|
||||
/**
|
||||
* Get only specific properties from an object
|
||||
*/
|
||||
only(subject, props = []) {
|
||||
const keys = Object.keys(subject);
|
||||
const result = {};
|
||||
|
||||
for (let i = 0; i < keys.length; i++) {
|
||||
const key = keys[i];
|
||||
if (props.includes(key)) {
|
||||
result[key] = subject[key];
|
||||
}
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
* Discover all Fleetbase extensions from node_modules
|
||||
*/
|
||||
async discoverExtensions() {
|
||||
const extensions = [];
|
||||
const seenPackages = new Set();
|
||||
|
||||
const results = await fg([
|
||||
'node_modules/*/package.json',
|
||||
'node_modules/*/*/package.json'
|
||||
], {
|
||||
cwd: this.projectRoot,
|
||||
absolute: true
|
||||
});
|
||||
|
||||
for (const packagePath of results) {
|
||||
let packageData = null;
|
||||
|
||||
try {
|
||||
const packageJson = fs.readFileSync(packagePath, 'utf8');
|
||||
packageData = JSON.parse(packageJson);
|
||||
} catch (e) {
|
||||
console.warn(`[ExtensionDiscovery] Could not parse package.json at ${packagePath}:`, e.message);
|
||||
continue;
|
||||
}
|
||||
|
||||
// Check if this is a Fleetbase extension
|
||||
if (!packageData ||
|
||||
!packageData.keywords ||
|
||||
!packageData.keywords.includes('fleetbase-extension') ||
|
||||
!packageData.keywords.includes('ember-engine')) {
|
||||
continue;
|
||||
}
|
||||
|
||||
// Skip duplicates
|
||||
if (seenPackages.has(packageData.name)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
seenPackages.add(packageData.name);
|
||||
|
||||
// Extract relevant metadata
|
||||
const extension = this.only(packageData, [
|
||||
'name',
|
||||
'description',
|
||||
'version',
|
||||
'fleetbase',
|
||||
'keywords',
|
||||
'license',
|
||||
'repository'
|
||||
]);
|
||||
|
||||
extensions.push(extension);
|
||||
}
|
||||
|
||||
return extensions;
|
||||
}
|
||||
|
||||
async build() {
|
||||
console.log('[ExtensionDiscovery] ========================================');
|
||||
console.log('[ExtensionDiscovery] Starting extension discovery...');
|
||||
console.log('[ExtensionDiscovery] Project root:', this.projectRoot);
|
||||
console.log('[ExtensionDiscovery] Output path:', this.outputPath);
|
||||
|
||||
const extensions = await this.discoverExtensions();
|
||||
|
||||
// Write extensions to cache file
|
||||
const cacheFile = path.join(this.outputPath, 'extensions.json');
|
||||
fs.writeFileSync(
|
||||
cacheFile,
|
||||
JSON.stringify(extensions, null, 2),
|
||||
'utf8'
|
||||
);
|
||||
|
||||
console.log('[ExtensionDiscovery] ========================================');
|
||||
console.log('[ExtensionDiscovery] ✓ Discovery complete');
|
||||
console.log('[ExtensionDiscovery] Found', extensions.length, 'extension(s):');
|
||||
extensions.forEach(ext => {
|
||||
console.log('[ExtensionDiscovery] -', ext.name + '@' + ext.version);
|
||||
});
|
||||
console.log('[ExtensionDiscovery] Wrote extensions.json to:', cacheFile);
|
||||
console.log('[ExtensionDiscovery] ========================================');
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = ExtensionDiscoveryPlugin;
|
||||
@@ -1,126 +0,0 @@
|
||||
const Plugin = require('broccoli-plugin');
|
||||
const fs = require('fs');
|
||||
const path = require('path');
|
||||
|
||||
/**
|
||||
* ExtensionLoadersGeneratorPlugin
|
||||
*
|
||||
* Generates app/utils/extension-loaders.generated.js with a map of extension names
|
||||
* to dynamic import functions.
|
||||
*
|
||||
* Output example:
|
||||
* export const EXTENSION_LOADERS = {
|
||||
* '@fleetbase/fleetops-engine': () => import('../extensions/fleetops'),
|
||||
* };
|
||||
*
|
||||
* This file is imported by the ExtensionManager service to lazy-load extension setup code.
|
||||
*/
|
||||
class ExtensionLoadersGeneratorPlugin extends Plugin {
|
||||
constructor(inputNodes, options = {}) {
|
||||
super(inputNodes, {
|
||||
annotation: options.annotation || 'ExtensionLoadersGeneratorPlugin',
|
||||
persistentOutput: true,
|
||||
});
|
||||
|
||||
this.projectRoot = options.projectRoot || process.cwd();
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the mount path for an extension
|
||||
*/
|
||||
getExtensionMountPath(extensionName) {
|
||||
const segments = extensionName.split('/');
|
||||
let mountName = segments[1] || segments[0];
|
||||
return mountName.replace('-engine', '');
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if an extension has an addon/extension.js file
|
||||
*/
|
||||
hasExtensionFile(extensionName) {
|
||||
const extensionPath = path.join(
|
||||
this.projectRoot,
|
||||
'node_modules',
|
||||
extensionName,
|
||||
'addon',
|
||||
'extension.js'
|
||||
);
|
||||
return fs.existsSync(extensionPath);
|
||||
}
|
||||
|
||||
/**
|
||||
* Generate the extension-loaders.generated.js content
|
||||
*/
|
||||
generateLoadersContent(extensions) {
|
||||
const lines = [
|
||||
'/**',
|
||||
' * AUTO-GENERATED FILE - DO NOT EDIT',
|
||||
' * Generated by ExtensionLoadersGeneratorPlugin',
|
||||
' *',
|
||||
' * This file contains a map of extension names to dynamic import functions.',
|
||||
' * The ExtensionManager service uses this to lazy-load extension setup code',
|
||||
' * without loading the entire engine bundle.',
|
||||
' */',
|
||||
'',
|
||||
'export const EXTENSION_LOADERS = {',
|
||||
];
|
||||
|
||||
let loaderCount = 0;
|
||||
|
||||
for (const extension of extensions) {
|
||||
const pkgName = extension.name;
|
||||
|
||||
if (!this.hasExtensionFile(pkgName)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
const mountName = this.getExtensionMountPath(pkgName);
|
||||
lines.push(` '${pkgName}': () => import('../extensions/${mountName}'),`);
|
||||
loaderCount++;
|
||||
}
|
||||
|
||||
lines.push('};');
|
||||
lines.push('');
|
||||
|
||||
return {
|
||||
content: lines.join('\n'),
|
||||
count: loaderCount
|
||||
};
|
||||
}
|
||||
|
||||
async build() {
|
||||
console.log('[ExtensionLoadersGenerator] ========================================');
|
||||
console.log('[ExtensionLoadersGenerator] Starting loaders generation...');
|
||||
console.log('[ExtensionLoadersGenerator] Project root:', this.projectRoot);
|
||||
console.log('[ExtensionLoadersGenerator] Output path:', this.outputPath);
|
||||
|
||||
// Read discovered extensions from cache
|
||||
const extensionsCacheFile = path.join(this.inputPaths[0], 'extensions.json');
|
||||
if (!fs.existsSync(extensionsCacheFile)) {
|
||||
console.warn('[ExtensionLoadersGenerator] No extensions cache found, skipping');
|
||||
return;
|
||||
}
|
||||
|
||||
const extensions = JSON.parse(fs.readFileSync(extensionsCacheFile, 'utf8'));
|
||||
|
||||
// Create output directory
|
||||
const outputDir = path.join(this.outputPath, 'utils');
|
||||
if (!fs.existsSync(outputDir)) {
|
||||
fs.mkdirSync(outputDir, { recursive: true });
|
||||
}
|
||||
|
||||
// Generate loaders file
|
||||
const { content, count } = this.generateLoadersContent(extensions);
|
||||
const outputPath = path.join(outputDir, 'extension-loaders.generated.js');
|
||||
|
||||
fs.writeFileSync(outputPath, content, 'utf8');
|
||||
|
||||
console.log('[ExtensionLoadersGenerator] ========================================');
|
||||
console.log('[ExtensionLoadersGenerator] ✓ Loaders generation complete');
|
||||
console.log('[ExtensionLoadersGenerator] Generated extension-loaders.generated.js with', count, 'loader(s)');
|
||||
console.log('[ExtensionLoadersGenerator] Output file:', outputPath);
|
||||
console.log('[ExtensionLoadersGenerator] ========================================');
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = ExtensionLoadersGeneratorPlugin;
|
||||
@@ -1,123 +0,0 @@
|
||||
const Plugin = require('broccoli-plugin');
|
||||
const fs = require('fs');
|
||||
const path = require('path');
|
||||
|
||||
/**
|
||||
* ExtensionShimGeneratorPlugin
|
||||
*
|
||||
* Generates app/extensions/*.js shim files by INLINING the extension.js code
|
||||
* from each engine's addon directory. This enables code splitting - the extension
|
||||
* setup code can be dynamically imported without loading the entire engine bundle.
|
||||
*
|
||||
* This is critical for performance: extension setup runs at boot, but engines
|
||||
* are lazy-loaded only when their routes are accessed.
|
||||
*/
|
||||
class ExtensionShimGeneratorPlugin extends Plugin {
|
||||
constructor(inputNodes, options = {}) {
|
||||
super(inputNodes, {
|
||||
annotation: options.annotation || 'Generate Extension Shims',
|
||||
persistentOutput: true,
|
||||
needsCache: false
|
||||
});
|
||||
|
||||
this.projectRoot = options.projectRoot || process.cwd();
|
||||
}
|
||||
|
||||
async build() {
|
||||
console.log('[ExtensionShimGenerator] ========================================');
|
||||
console.log('[ExtensionShimGenerator] Starting shim generation...');
|
||||
console.log('[ExtensionShimGenerator] Project root:', this.projectRoot);
|
||||
console.log('[ExtensionShimGenerator] Output path:', this.outputPath);
|
||||
|
||||
const extensionsJsonPath = path.join(this.inputPaths[0], 'extensions.json');
|
||||
console.log('[ExtensionShimGenerator] Reading extensions from:', extensionsJsonPath);
|
||||
// Check if extensions.json exists
|
||||
if (!fs.existsSync(extensionsJsonPath)) {
|
||||
console.warn('[ExtensionShimGenerator] extensions.json not found, skipping');
|
||||
return;
|
||||
}
|
||||
|
||||
// Read discovered extensions
|
||||
const extensions = JSON.parse(fs.readFileSync(extensionsJsonPath, 'utf8'));
|
||||
|
||||
// Create extensions output directory
|
||||
const extensionsDir = path.join(this.outputPath, 'extensions');
|
||||
if (!fs.existsSync(extensionsDir)) {
|
||||
fs.mkdirSync(extensionsDir, { recursive: true });
|
||||
}
|
||||
|
||||
let shimCount = 0;
|
||||
|
||||
for (const extension of extensions) {
|
||||
const pkgName = extension.name;
|
||||
const mountName = this.#getExtensionMountPath(pkgName);
|
||||
|
||||
// Path to extension.js in the engine's addon directory
|
||||
const extensionPath = path.join(
|
||||
this.projectRoot,
|
||||
'node_modules',
|
||||
pkgName,
|
||||
'addon',
|
||||
'extension.js'
|
||||
);
|
||||
|
||||
// Check if extension.js exists
|
||||
if (!fs.existsSync(extensionPath)) {
|
||||
console.log(`[ExtensionShimGenerator] No extension.js found for ${pkgName}, skipping`);
|
||||
continue;
|
||||
}
|
||||
|
||||
// Read the extension code
|
||||
let extensionCode;
|
||||
try {
|
||||
extensionCode = fs.readFileSync(extensionPath, 'utf8');
|
||||
} catch (error) {
|
||||
console.error(`[ExtensionShimGenerator] Failed to read extension.js for ${pkgName}:`, error.message);
|
||||
continue;
|
||||
}
|
||||
|
||||
// Generate shim filename (e.g., 'fleetops.js')
|
||||
const shimPath = path.join(extensionsDir, `${mountName}.js`);
|
||||
|
||||
// INLINE the extension code instead of re-exporting
|
||||
// This is critical: we copy the code so it can be bundled by ember-auto-import
|
||||
// without requiring the engine to be loaded
|
||||
const shimContent = `// GENERATED BY extension-shim-generator.js - DO NOT EDIT
|
||||
// Extension setup for ${pkgName}
|
||||
//
|
||||
// This file contains the inlined extension setup code from the engine.
|
||||
// It is generated at build time to enable dynamic import without loading
|
||||
// the entire engine bundle.
|
||||
|
||||
${extensionCode}
|
||||
`;
|
||||
|
||||
try {
|
||||
fs.writeFileSync(shimPath, shimContent, 'utf8');
|
||||
shimCount++;
|
||||
console.log(`[ExtensionShimGenerator] ✓ Inlined extension code for ${pkgName} → ${mountName}.js`);
|
||||
} catch (error) {
|
||||
console.error(`[ExtensionShimGenerator] Failed to write shim for ${pkgName}:`, error.message);
|
||||
}
|
||||
}
|
||||
|
||||
console.log('[ExtensionShimGenerator] ========================================');
|
||||
console.log('[ExtensionShimGenerator] ✓ Shim generation complete');
|
||||
console.log('[ExtensionShimGenerator] Generated', shimCount, 'extension shim file(s)');
|
||||
console.log('[ExtensionShimGenerator] Output directory:', path.join(this.outputPath, 'extensions'));
|
||||
console.log('[ExtensionShimGenerator] ========================================');
|
||||
}
|
||||
|
||||
/**
|
||||
* Extract the mount path from an extension package name
|
||||
* @param {string} extensionName - e.g., '@fleetbase/fleetops-engine'
|
||||
* @returns {string} - e.g., 'fleetops'
|
||||
*/
|
||||
#getExtensionMountPath(extensionName) {
|
||||
const segments = extensionName.split('/');
|
||||
let mountName = segments[1] || segments[0];
|
||||
return mountName.replace('-engine', '');
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = ExtensionShimGeneratorPlugin;
|
||||
@@ -1,215 +0,0 @@
|
||||
const Plugin = require('broccoli-plugin');
|
||||
const fs = require('fs');
|
||||
const path = require('path');
|
||||
const recast = require('recast');
|
||||
const babelParser = require('recast/parsers/babel');
|
||||
const builders = recast.types.builders;
|
||||
|
||||
/**
|
||||
* RouterGeneratorPlugin
|
||||
*
|
||||
* Automatically mounts discovered extensions in the Ember router.
|
||||
* Reads router.map.js template and generates app/router.js with all engine mounts.
|
||||
*
|
||||
* Handles two types of mounts:
|
||||
* 1. Console extensions: Mounted under /console route
|
||||
* 2. Root extensions: Mounted at root level (fleetbase.mount === 'root')
|
||||
*
|
||||
* Uses AST manipulation to preserve existing router code and only add new mounts.
|
||||
*/
|
||||
class RouterGeneratorPlugin extends Plugin {
|
||||
constructor(inputNodes, options = {}) {
|
||||
super(inputNodes, {
|
||||
annotation: options.annotation || 'RouterGeneratorPlugin',
|
||||
persistentOutput: true,
|
||||
});
|
||||
|
||||
this.projectRoot = options.projectRoot || process.cwd();
|
||||
this.routerMapFile = options.routerMapFile || path.join(this.projectRoot, 'router.map.js');
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the mount path for an extension
|
||||
*/
|
||||
getExtensionMountPath(extensionName) {
|
||||
const segments = extensionName.split('/');
|
||||
let mountName = segments[1] || segments[0];
|
||||
return mountName.replace('-engine', '');
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the route path for an extension
|
||||
* Uses fleetbase.route if specified, otherwise uses mount path
|
||||
*/
|
||||
getExtensionRoute(extension) {
|
||||
if (extension.fleetbase && extension.fleetbase.route) {
|
||||
return extension.fleetbase.route;
|
||||
}
|
||||
return this.getExtensionMountPath(extension.name);
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if an engine is already mounted in the AST
|
||||
*/
|
||||
isEngineMounted(functionBody, engineName) {
|
||||
return functionBody.some(statement => {
|
||||
if (statement.type !== 'ExpressionStatement') return false;
|
||||
const expr = statement.expression;
|
||||
if (expr.type !== 'CallExpression') return false;
|
||||
if (!expr.arguments || expr.arguments.length === 0) return false;
|
||||
return expr.arguments[0].value === engineName;
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Create an AST node for mounting an engine
|
||||
*/
|
||||
createMountExpression(engineName, route) {
|
||||
return builders.expressionStatement(
|
||||
builders.callExpression(
|
||||
builders.memberExpression(
|
||||
builders.thisExpression(),
|
||||
builders.identifier('mount')
|
||||
),
|
||||
[
|
||||
builders.literal(engineName),
|
||||
builders.objectExpression([
|
||||
builders.property('init', builders.identifier('as'), builders.literal(route)),
|
||||
builders.property('init', builders.identifier('path'), builders.literal(route)),
|
||||
])
|
||||
]
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Add console extensions to the /console route
|
||||
*/
|
||||
addConsoleExtensions(ast, extensions) {
|
||||
let addedCount = 0;
|
||||
|
||||
recast.visit(ast, {
|
||||
visitCallExpression: (path) => {
|
||||
const node = path.value;
|
||||
|
||||
// Look for this.route('console', function() { ... })
|
||||
if (node.type === 'CallExpression' &&
|
||||
node.callee.property &&
|
||||
node.callee.property.name === 'route' &&
|
||||
node.arguments[0] &&
|
||||
node.arguments[0].value === 'console') {
|
||||
|
||||
// Find the function expression
|
||||
const functionExpression = node.arguments.find(arg => arg.type === 'FunctionExpression');
|
||||
|
||||
if (functionExpression) {
|
||||
// Add each console extension
|
||||
extensions.forEach(extension => {
|
||||
const route = this.getExtensionRoute(extension);
|
||||
|
||||
// Check if already mounted
|
||||
if (!this.isEngineMounted(functionExpression.body.body, extension.name)) {
|
||||
functionExpression.body.body.push(
|
||||
this.createMountExpression(extension.name, route)
|
||||
);
|
||||
addedCount++;
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
return false; // Don't traverse children
|
||||
}
|
||||
});
|
||||
|
||||
return addedCount;
|
||||
}
|
||||
|
||||
/**
|
||||
* Add root extensions to the root map
|
||||
*/
|
||||
addRootExtensions(ast, extensions) {
|
||||
let addedCount = 0;
|
||||
|
||||
recast.visit(ast, {
|
||||
visitCallExpression: (path) => {
|
||||
const node = path.value;
|
||||
|
||||
// Look for Router.map(function() { ... })
|
||||
if (node.type === 'CallExpression' &&
|
||||
node.callee.property &&
|
||||
node.callee.property.name === 'map') {
|
||||
|
||||
// Find the function expression
|
||||
const functionExpression = node.arguments.find(arg => arg.type === 'FunctionExpression');
|
||||
|
||||
if (functionExpression) {
|
||||
// Add each root extension
|
||||
extensions.forEach(extension => {
|
||||
const route = this.getExtensionRoute(extension);
|
||||
|
||||
// Check if already mounted
|
||||
if (!this.isEngineMounted(functionExpression.body.body, extension.name)) {
|
||||
functionExpression.body.body.push(
|
||||
this.createMountExpression(extension.name, route)
|
||||
);
|
||||
addedCount++;
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
return false; // Don't traverse children
|
||||
}
|
||||
});
|
||||
|
||||
return addedCount;
|
||||
}
|
||||
|
||||
async build() {
|
||||
console.log('[RouterGenerator] Generating app/router.js...');
|
||||
|
||||
// Read discovered extensions from cache
|
||||
const extensionsCacheFile = path.join(this.inputPaths[0], 'extensions.json');
|
||||
if (!fs.existsSync(extensionsCacheFile)) {
|
||||
console.warn('[RouterGenerator] No extensions cache found, skipping');
|
||||
return;
|
||||
}
|
||||
|
||||
const extensions = JSON.parse(fs.readFileSync(extensionsCacheFile, 'utf8'));
|
||||
|
||||
// Separate console and root extensions
|
||||
const consoleExtensions = extensions.filter(ext =>
|
||||
!ext.fleetbase || ext.fleetbase.mount !== 'root'
|
||||
);
|
||||
const rootExtensions = extensions.filter(ext =>
|
||||
ext.fleetbase && ext.fleetbase.mount === 'root'
|
||||
);
|
||||
|
||||
// Read router.map.js template
|
||||
if (!fs.existsSync(this.routerMapFile)) {
|
||||
console.error('[RouterGenerator] router.map.js not found at:', this.routerMapFile);
|
||||
return;
|
||||
}
|
||||
|
||||
const routerFileContents = fs.readFileSync(this.routerMapFile, 'utf8');
|
||||
|
||||
// Parse the router file
|
||||
const ast = recast.parse(routerFileContents, { parser: babelParser });
|
||||
|
||||
// Add extensions to the AST
|
||||
const consoleAdded = this.addConsoleExtensions(ast, consoleExtensions);
|
||||
const rootAdded = this.addRootExtensions(ast, rootExtensions);
|
||||
|
||||
// Generate the output code
|
||||
const output = recast.print(ast, { quote: 'single' }).code;
|
||||
|
||||
// Write to output path
|
||||
const outputPath = path.join(this.outputPath, 'router.js');
|
||||
fs.writeFileSync(outputPath, output, 'utf8');
|
||||
|
||||
console.log(`[RouterGenerator] Generated app/router.js (${consoleAdded} console mounts, ${rootAdded} root mounts)`);
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = RouterGeneratorPlugin;
|
||||
Reference in New Issue
Block a user