@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
JavaScript
;
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