UNPKG

@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
"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 () { 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;