UNPKG

@nodeboot/aot

Version:

Node-Boot module for Ahead-of-Time (AOT) compilation. Generates node-boot beans and OpenAPI schemas at compile time

195 lines 8.46 kB
"use strict"; var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) { if (k2 === undefined) k2 = k; var desc = Object.getOwnPropertyDescriptor(m, k); if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) { desc = { enumerable: true, get: function() { return m[k]; } }; } Object.defineProperty(o, k2, desc); }) : (function(o, m, k, k2) { if (k2 === undefined) k2 = k; o[k2] = m[k]; })); var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) { Object.defineProperty(o, "default", { enumerable: true, value: v }); }) : function(o, v) { o["default"] = v; }); var __importStar = (this && this.__importStar) || function (mod) { if (mod && mod.__esModule) return mod; var result = {}; if (mod != null) for (var k in mod) if (k !== "default" && Object.prototype.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k); __setModuleDefault(result, mod); return result; }; var __importDefault = (this && this.__importDefault) || function (mod) { return (mod && mod.__esModule) ? mod : { "default": mod }; }; Object.defineProperty(exports, "__esModule", { value: true }); exports.EnableComponentScan = EnableComponentScan; const fs_1 = __importDefault(require("fs")); const path_1 = __importDefault(require("path")); const decorators_main_1 = require("../decorators.main"); const console = __importStar(require("node:console")); /** * @EnableComponentScan * * A **Node-Boot** class decorator that performs **automatic component registration** * by scanning compiled JavaScript files for known decorators (e.g., `@Controller`, `@Service`) * or loading from a prebuilt bean manifest (`node-boot-beans.json`). * * Designed to work in tandem with the `node-boot-aot-beans.js` script for faster startup, * this decorator streamlines the bootstrapping process of your Node-Boot application by * importing all necessary beans automatically. * * --- * * ### How It Works: * 1. **Production Mode (`dist/`)**: * - Looks for `dist/node-boot-beans.json` (generated at build time). * - Dynamically imports all listed files unless they're already cached. * * 2. **Development Mode (`src/`) or Fallback**: * - If no JSON file is found or custom decorators are provided, * it performs a full recursive scan of the codebase (defaults to `dist/`). * - Loads files that contain relevant decorators. * * --- * * ### Features: * - ✅ **AOT-Aware**: Prioritizes reading precomputed metadata (`node-boot-beans.json`) for fast startup. * - 🔍 **Dynamic Scanning Fallback**: Falls back to live scanning when needed (e.g., during dev or missing beans file). * - ⚡ **Smart Importing**: Prevents redundant imports using `require.cache`. * - 🧩 **Extensible**: Accepts custom decorators via `options.customDecorators`. * - 🛡 **Robust Logging**: Gives informative logs during both scanning and importing. * * --- * * ### Options: * ```ts * { * customDecorators: Function[]; // Add support for custom component decorators * } * ``` * * ### Usage: * ```ts * @EnableComponentScan() * @NodeBootApplication() * export class MyApp implements NodeBootApp { * start(): Promise<NodeBootAppView> { * return NodeBoot.run(ExpressServer); * } * } * ``` * * ### Environment Behavior: * - If `NODE_ENV !== "production"` → verbose logs enabled, source scanning more likely. * - Automatically resolves correct path depending on whether the app is running inside `dist/`. * * --- * * @author * Manuel Santos <ney.br.santos@gmail.com> */ function EnableComponentScan(options) { function scanDecorators() { const additionalDecorators = options ? options.customDecorators.map(decorator => decorator.name) : []; return [...decorators_main_1.MAIN_DECORATORS, ...additionalDecorators]; } return function () { const decorators = scanDecorators(); const verbose = process.env["NODE_ENV"] !== "production"; let basePath; // Check if the current working directory (`process.cwd()`) already contains "dist" if (process.cwd().includes("dist")) { basePath = process.cwd(); // If inside dist, just use the current working directory } else { basePath = path_1.default.resolve(process.cwd(), "dist"); // Otherwise, resolve the path to dist/ } const beansFilePath = path_1.default.join(basePath, "node-boot-beans.json"); console.log(`============================= Node-Boot Beans Resolution =============================`); console.log(`🔍 Checking for pre-built beans file: ${beansFilePath}`); function verboseLog(log) { if (verbose) { log(); } } // If custom decorators are provided it should do active scanning if (!options?.customDecorators && fs_1.default.existsSync(beansFilePath)) { try { const beans = JSON.parse(fs_1.default.readFileSync(beansFilePath, "utf-8")); if (Array.isArray(beans) && beans.length > 0) { console.log(`🚀 Beans file found. Importing beans...`); beans.forEach(beanPath => { const absolutePath = path_1.default.join(basePath, beanPath); if (!require.cache[absolutePath]) { verboseLog(() => console.log(`📦 Importing beans from : ${absolutePath}`)); require(absolutePath); } else { verboseLog(() => console.log(`⚡ Skipping already imported module: ${absolutePath}`)); } }); console.log(`${beans.length} application beans successfully imported`); return; // Exit early since beans.json was successfully used } } catch (error) { console.error(`⚠️ Error reading beans file, falling back to active scanning:`, error); } } console.log(`⚠️ Beans file not found or invalid. Performing active scanning...`); let scannedBeans = 0; function fileContainsRelevantDecorator(filePath) { try { const content = fs_1.default.readFileSync(filePath, "utf-8"); return decorators.some(decorator => content.includes(`${decorator})`)); } catch (error) { console.error(`❌ Error reading file: ${filePath}`, error); return false; } } function importFilesFromDir(dir) { const files = fs_1.default.readdirSync(dir); for (const file of files) { const fullPath = path_1.default.join(dir, file); const isDirectory = fs_1.default.statSync(fullPath).isDirectory(); if (isDirectory) { importFilesFromDir(fullPath); } else { if (file === "index.js" || file.endsWith(".d.ts") || !file.endsWith(".js")) { continue; } if (!fileContainsRelevantDecorator(fullPath)) { continue; } // increment beans counter scannedBeans++; if (require.cache[fullPath]) { verboseLog(() => console.log(`⚡ Skipping already imported beans for file: ${fullPath}`)); continue; } try { verboseLog(() => console.log(`📦 Importing beans from file: ${fullPath}`)); require(fullPath); } catch (error) { console.error(`⚠️ Failed to import ${fullPath}:`, error); } } } } if (fs_1.default.existsSync(basePath)) { importFilesFromDir(basePath); console.log(`${scannedBeans} application beans successfully imported`); } else { console.error(`❌ Scan path does not exist: ${basePath}`); } }; } //# sourceMappingURL=EnableComponentScan.js.map