@itrocks/route
Version:
Domain-driven route manager with automatic generation, decorators, and static routes
111 lines • 4.16 kB
JavaScript
;
var __importDefault = (this && this.__importDefault) || function (mod) {
return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
const app_dir_1 = require("@itrocks/app-dir");
const node_fs_1 = require("node:fs");
const node_fs_2 = require("node:fs");
const typescript_1 = __importDefault(require("typescript"));
// TODO support aliasing: import { Route as Alias } from './route' ; @Alias('/')
const scanPath = app_dir_1.appDir;
const staticRoutesFile = app_dir_1.appDir + '/static-routes.json';
let source;
try {
source = (0, node_fs_1.readFileSync)(staticRoutesFile) + '';
}
catch {
source = '{}';
}
const routes = JSON.parse(source);
const modules = {};
for (const [path, module] of Object.entries(routes)) {
(modules[module] ?? (modules[module] = [])).push(path);
}
function saveStaticRoutes() {
return (0, node_fs_2.writeFileSync)(staticRoutesFile, JSON.stringify(routes, null, '\t') + '\n');
}
exports.default = () => (context) => (sourceFile) => {
let hasRoute = false;
const module = sourceFile.fileName.slice(scanPath.length, -3);
const validRoutes = {};
function isRoute(node) {
if (!typescript_1.default.isImportDeclaration(node))
return false;
if (!node.importClause)
return false;
const moduleSpecifier = node.moduleSpecifier;
if (moduleSpecifier.text !== '@itrocks/route')
return false;
if (node.importClause.name?.getText() === 'Route') {
return true;
}
const namedBindings = node.importClause.namedBindings;
if (!namedBindings || !typescript_1.default.isNamedImports(namedBindings))
return false;
for (const importSpecifier of namedBindings.elements) {
if (importSpecifier.name.getText() === 'Route') {
return true;
}
}
return false;
}
function routeDecoratorValues(node) {
const routes = [];
if (!typescript_1.default.canHaveDecorators(node))
return [];
for (const decorator of typescript_1.default.getDecorators(node) ?? []) {
if (!typescript_1.default.isCallExpression(decorator.expression))
continue;
if (decorator.expression.expression.getText() !== 'Route')
continue;
const argument = decorator.expression.arguments[0];
if (!argument || !typescript_1.default.isStringLiteral(argument))
continue;
routes.push(argument.text);
}
return routes;
}
const visit = (node) => {
if (hasRoute ||= isRoute(node)) {
for (const path of routeDecoratorValues(node)) {
if (!path)
continue;
validRoutes[path] = module;
if (module === routes[path])
continue;
if (routes[path]) {
const moduleRoutes = modules[routes[path]];
delete moduleRoutes[moduleRoutes.indexOf(path)];
}
(modules[module] ?? (modules[module] = [])).push(path);
routes[path] = module;
}
}
return typescript_1.default.visitEachChild(node, visit, context);
};
const resultNode = typescript_1.default.visitNode(sourceFile, visit);
const moduleRoutes = modules[module];
if (moduleRoutes) {
let deletions = 0;
for (const path of moduleRoutes) {
if (validRoutes[path])
continue;
delete moduleRoutes[moduleRoutes.indexOf(path)];
delete routes[path];
deletions++;
}
if (deletions) {
modules[module] = moduleRoutes.filter(path => path);
if (!modules[module].length) {
delete modules[module];
}
saveStaticRoutes();
}
else if (Object.keys(validRoutes).length) {
saveStaticRoutes();
}
}
return resultNode;
};
//# sourceMappingURL=static-routes-plugin.js.map