@jqassistant/ts-lce
Version:
Tool to extract language concepts from a TypeScript codebase and export them to a JSON file.
195 lines (194 loc) • 11.5 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 () {
var ownKeys = function(o) {
ownKeys = Object.getOwnPropertyNames || function (o) {
var ar = [];
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
return ar;
};
return ownKeys(o);
};
return function (mod) {
if (mod && mod.__esModule) return mod;
var result = {};
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
__setModuleDefault(result, mod);
return result;
};
})();
Object.defineProperty(exports, "__esModule", { value: true });
exports.ExportsPostProcessor = void 0;
const post_processor_1 = require("../post-processor");
const export_declaration_concept_1 = require("../concepts/export-declaration.concept");
const typescript_module_concept_1 = require("../concepts/typescript-module.concept");
const modulepath_utils_1 = require("../utils/modulepath.utils");
const externals_concept_1 = require("../concepts/externals.concept");
const dependency_concept_1 = require("../concepts/dependency.concept");
const node_utils_1 = require("../utils/node.utils");
const fs = __importStar(require("fs"));
class ExportsPostProcessor extends post_processor_1.PostProcessor {
postProcess(projects) {
for (const project of projects) {
const concepts = project.concepts;
const modules = (concepts.get(typescript_module_concept_1.LCEModule.conceptId) ?? []);
const externalModules = (concepts.get(externals_concept_1.LCEExternalModule.conceptId) ?? []);
const allExports = (concepts.get(export_declaration_concept_1.LCEExportDeclaration.conceptId) ?? []);
const newExports = [];
for (const module of modules) {
newExports.push(...this.getAllModuleExports(concepts, allExports, module.fqn.globalFqn, externalModules, project.projectInfo, projects));
}
concepts.set(export_declaration_concept_1.LCEExportDeclaration.conceptId, newExports);
}
}
getAllModuleExports(concepts, exports, modulePathAbsolute, externalModules, projectInfo, projects) {
const result = [];
const stats = fs.statSync(modulePathAbsolute);
if (stats.isDirectory()) {
modulePathAbsolute += "/index.ts";
}
const rawExports = this.filterExportsForModule(exports, modulePathAbsolute);
for (const exp of rawExports) {
if (exp.importSource) {
// re-export
if (!modulepath_utils_1.ModulePathUtils.isExternal(exp.importSource, projectInfo, projects)) {
// internal re-export: try to resolve export tree
const moduleExports = this.getAllModuleExports(concepts, exports, exp.importSource, externalModules, projectInfo, projects);
if (exp.kind === "namespace") {
// namespace re-export: convert all namespace exports into individual export declarations
if (exp.globalDeclFqn) {
for (const moduleExport of moduleExports) {
let identifier = moduleExport.alias ?? moduleExport.identifier;
if (moduleExport.isDefault) {
identifier = "default";
}
result.push(new export_declaration_concept_1.LCEExportDeclaration(identifier, exp.alias ? `${exp.alias}.${identifier}` : undefined, moduleExport.globalDeclFqn, undefined, moduleExport.isDefault, moduleExport.kind, modulePathAbsolute));
}
this.addDependency(concepts, modulePathAbsolute, exp.importSource, "module");
}
}
else {
// named re-export
let originalExport = this.findSingleModuleExport(moduleExports, exp.identifier);
if (originalExport) {
result.push(new export_declaration_concept_1.LCEExportDeclaration(exp.identifier, exp.alias, originalExport.globalDeclFqn, undefined, exp.isDefault, originalExport.kind, modulePathAbsolute));
if (originalExport.globalDeclFqn) {
this.addDependency(concepts, modulePathAbsolute, originalExport.globalDeclFqn);
}
}
else {
console.error(`Error: could not find exported declaration "${exp.identifier}" in "${exp.importSource}": Ignoring export...`);
console.error(`\toccurred at ${exp.sourceFilePathAbsolute}}`);
}
}
}
else {
// external re-export: link to external declaration(s)
let importSource = exp.importSource;
let externalImportModule = externalModules.find((em) => em.fqn.globalFqn === exp.importSource);
if (!externalImportModule) {
// if import source is a node module identifier try to resolve it
let resolvedModulePath;
try {
resolvedModulePath = node_utils_1.NodeUtils.resolveImportPath(exp.importSource, projectInfo, exp.sourceFilePathAbsolute);
}
catch (e) {
console.error(`Error: Could not resolve module: ${exp.importSource}`);
console.error(`\toccurred at ${exp.sourceFilePathAbsolute}}`);
}
if (resolvedModulePath) {
const packageName = node_utils_1.NodeUtils.getPackageNameForPath(projectInfo.rootPath, resolvedModulePath);
if (packageName) {
externalImportModule = externalModules.find((em) => em.fqn.globalFqn === packageName);
}
if (!externalImportModule) {
importSource = modulepath_utils_1.ModulePathUtils.normalize(projectInfo.rootPath, resolvedModulePath);
externalImportModule = externalModules.find((em) => em.fqn.globalFqn === importSource);
}
if (!externalImportModule) {
// TODO: refine this or find existing mechanism that solves the problem of .d.ts to .js mapping
const potentialDTSPath = resolvedModulePath.replace("node_modules/", "node_modules/@types/").replace(".js", ".d.ts");
importSource = modulepath_utils_1.ModulePathUtils.normalize(projectInfo.rootPath, potentialDTSPath);
externalImportModule = externalModules.find((em) => em.fqn.globalFqn === importSource);
}
}
}
if (externalImportModule) {
const externalDeclarations = externalImportModule.declarations;
if (exp.kind === "namespace") {
// namespace re-export: export all known external declarations of external module
for (const eDecl of externalDeclarations) {
result.push(new export_declaration_concept_1.LCEExportDeclaration(eDecl.name, exp.alias ? `${exp.alias}.${eDecl.name}` : undefined, eDecl.fqn.globalFqn, undefined, false, // technically incorrect, but not relevant for graph generation
"value", // - || -
modulePathAbsolute));
}
this.addDependency(concepts, modulePathAbsolute, importSource, "module");
}
else {
// named re-export of single external dependency
let eDecl = externalDeclarations.find((ed) => ed.name === exp.identifier);
if (!eDecl) {
// TODO: this solution can't handle multiple namespaces in the same file: solve namespace problems
eDecl = externalDeclarations.find((ed) => ed.name.endsWith(`.${exp.identifier}`));
}
if (eDecl) {
result.push(new export_declaration_concept_1.LCEExportDeclaration(eDecl.name, exp.alias, eDecl.fqn.globalFqn, undefined, exp.isDefault, exp.kind, modulePathAbsolute));
this.addDependency(concepts, modulePathAbsolute, eDecl.fqn.globalFqn);
}
else {
console.error(`Error: external declaration with identifier "${exp.identifier}" in module "${exp.importSource}" could not be found: Ignoring export...`);
}
}
}
else {
console.error(`Error: external module "${exp.importSource}" for re-export of "${exp.identifier}" could not be found. Ignoring export...`);
console.error(`\toccurred at ${exp.sourceFilePathAbsolute}}`);
}
}
}
else {
result.push(exp);
}
}
return result;
}
filterExportsForModule(exports, modulePath) {
return exports.filter((ed) => ed.sourceFilePathAbsolute === modulePath);
}
findSingleModuleExport(moduleExports, name) {
for (const me of moduleExports) {
if (name === "default") {
if (me.isDefault) {
return me;
}
}
else {
const meName = me.alias ?? me.identifier;
if (meName === name) {
return me;
}
}
}
}
addDependency(concepts, source, target, targetType = "declaration") {
const dependencies = (concepts.get(dependency_concept_1.LCEDependency.conceptId) ?? []);
dependencies.push(new dependency_concept_1.LCEDependency(target, targetType, source, "module", 1));
concepts.set(dependency_concept_1.LCEDependency.conceptId, dependencies);
}
}
exports.ExportsPostProcessor = ExportsPostProcessor;