Skip to content

Commit bb6e950

Browse files
Fixed handling of (sub)projects with multiple tsconfig files in one directory
1 parent cc471e1 commit bb6e950

File tree

13 files changed

+168
-229
lines changed

13 files changed

+168
-229
lines changed

typescript/src/core/extractor.ts

Lines changed: 26 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -22,19 +22,23 @@ export async function processProjectsAndOutputResult(scanRoot: string, options:
2222
const processedProjects = await processProjects(scanRoot);
2323

2424
// output JSON file
25-
const json = JSON.stringify(processedProjects.map(p => p.toJSON()), (_, value) => {
26-
if(typeof value === 'bigint') {
27-
return value.toString();
28-
} else if(typeof value === 'object' && value instanceof Map) {
29-
return Object.fromEntries(Array.from(value.entries()))
30-
} else {
31-
return value;
32-
}
33-
}, options.prettyPrint ? 2 : undefined);
25+
const json = JSON.stringify(
26+
processedProjects.map((p) => p.toJSON()),
27+
(_, value) => {
28+
if (typeof value === "bigint") {
29+
return value.toString();
30+
} else if (typeof value === "object" && value instanceof Map) {
31+
return Object.fromEntries(Array.from(value.entries()));
32+
} else {
33+
return value;
34+
}
35+
},
36+
options.prettyPrint ? 2 : undefined,
37+
);
3438

3539
let dirPath = path.join(scanRoot, ".reports", "jqa");
36-
let filePath = path.join(dirPath, 'ts-output.json');
37-
fs.mkdir(dirPath, {recursive: true}, (errDir) => {
40+
let filePath = path.join(dirPath, "ts-output.json");
41+
fs.mkdir(dirPath, { recursive: true }, (errDir) => {
3842
if (errDir) {
3943
console.log("Could not create directory: " + dirPath);
4044
} else {
@@ -55,15 +59,15 @@ export async function processProjects(scanRoot: string): Promise<LCEProject[]> {
5559

5660
// process projects
5761
const processedProjects: LCEProject[] = [];
58-
for (let i = 0; i < projects.length; i++){
62+
for (let i = 0; i < projects.length; i++) {
5963
const project = projects[i];
6064
console.log("Processing project " + i + " of " + projects.length);
6165
processedProjects.push(await processProject(project));
6266
}
6367

6468
// post-processing projects
65-
console.log("Post-Processing Results...")
66-
for(const postProcessor of POST_PROCESSORS) {
69+
console.log("Post-Processing Results...");
70+
for (const postProcessor of POST_PROCESSORS) {
6771
postProcessor.postProcess(processedProjects);
6872
}
6973

@@ -86,22 +90,22 @@ export async function processProject(project: LCEProjectInfo): Promise<LCEProjec
8690

8791
// Traverse and process all individual project files
8892
const traverser = new AstTraverser();
89-
for (let i = 0; i < fileList.length; i++){
90-
progressBar.update(i+1);
93+
for (let i = 0; i < fileList.length; i++) {
94+
progressBar.update(i + 1);
9195
const file = fileList[i];
9296

9397
const frStartTime = process.hrtime();
9498
const code: string = fs.readFileSync(file, "utf8");
9599
const frEndTime = process.hrtime();
96-
fileReadingTime += (frEndTime[0] + frEndTime[1]/10**9) - (frStartTime[0] + frStartTime[1]/10**9);
100+
fileReadingTime += frEndTime[0] + frEndTime[1] / 10 ** 9 - (frStartTime[0] + frStartTime[1] / 10 ** 9);
97101

98102
try {
99-
const {ast, services} = parseAndGenerateServices(code, {
103+
const { ast, services } = parseAndGenerateServices(code, {
100104
loc: true,
101105
range: true,
102106
tokens: false,
103107
filePath: file,
104-
project: path.join(project.projectPath, "tsconfig.json"),
108+
project: project.configPath,
105109
});
106110
if (!services.program) {
107111
continue;
@@ -118,7 +122,7 @@ export async function processProject(project: LCEProjectInfo): Promise<LCEProjec
118122
};
119123

120124
concepts = mergeConceptMaps(concepts, unifyConceptMap(traverser.traverse(globalContext), globalContext.sourceFilePathAbsolute));
121-
} catch(e) {
125+
} catch (e) {
122126
console.log("Error occurred while processing file: " + file);
123127
console.log(e);
124128
}
@@ -127,12 +131,9 @@ export async function processProject(project: LCEProjectInfo): Promise<LCEProjec
127131
const normalizedConcepts: Map<string, LCEConcept[]> = unifyConceptMap(concepts, "").get("") ?? new Map();
128132

129133
const endTime = process.hrtime();
130-
const diffTime = (endTime[0] + endTime[1]/10**9) - (startTime[0] + startTime[1]/10**9);
134+
const diffTime = endTime[0] + endTime[1] / 10 ** 9 - (startTime[0] + startTime[1] / 10 ** 9);
131135
console.log("Finished analyzing project files.");
132136
console.log("Runtime: " + diffTime.toFixed(3) + "s (" + fileReadingTime.toFixed(3) + "s reading files)");
133137

134-
return new LCEProject(
135-
project,
136-
normalizedConcepts
137-
);
138+
return new LCEProject(project, normalizedConcepts);
138139
}

typescript/src/core/post-processors/exports.post-processor.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -105,7 +105,7 @@ export class ExportsPostProcessor extends PostProcessor {
105105
// if import source is a node module identifier try to resolve it
106106
let resolvedModulePath;
107107
try {
108-
resolvedModulePath = NodeUtils.resolveImportPath(exp.importSource, projectInfo.rootPath, exp.sourceFilePathAbsolute);
108+
resolvedModulePath = NodeUtils.resolveImportPath(exp.importSource, projectInfo, exp.sourceFilePathAbsolute);
109109
} catch (e) {
110110
console.error(`Error: Could not resolve module: ${exp.importSource}`);
111111
console.error(`\toccurred at ${exp.sourceFilePathAbsolute}}`);

typescript/src/core/processors/import-declaration.processor.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -45,7 +45,7 @@ export class ImportDeclarationProcessor extends Processor {
4545
try {
4646
const resolvedModulePath = NodeUtils.resolveImportPath(
4747
ModulePathUtils.extractFQNPath(target.globalFqn),
48-
globalContext.projectInfo.projectPath,
48+
globalContext.projectInfo,
4949
globalContext.sourceFilePathAbsolute,
5050
);
5151
const targetDeclName = ModulePathUtils.extractFQNIdentifier(target.globalFqn);

typescript/src/core/project.ts

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -6,20 +6,20 @@ import { LCEConcept } from "./concept";
66
export interface LCEProjectInfo {
77

88
/**
9-
* Either identical to `projectPath`, or, if configured in the `tsconfig.json`, the absolute path to the `rootDir`.
9+
* Either identical to directory path containing the `tsconfig.json`, or, if configured in the `tsconfig.json`, the absolute path to the `rootDir`.
1010
* All local FQNs are relative to this path.
1111
* Multiple projects may have the same root path.
1212
*/
1313
rootPath: string;
1414

1515
/**
16-
* The absolute path to the directory containing the tsconfig.json (it may be equal to or a subdirectory to `rootPath`)
16+
* The absolute path to the `tsconfig.json` (may have different name) of the project
1717
* This path is unique for every project.
1818
*/
19-
projectPath: string;
19+
configPath: string;
2020

2121
/**
22-
* The absolute paths to all subprojects (including transitive ones) that have a `tsconfig.json`.
22+
* The absolute paths to all `tsconfig.json` files of subprojects (including transitive ones).
2323
* The term "subproject" is used synonymous to project references defined in the `tsconfig.json` of the main project.
2424
*/
2525
subProjectPaths: string[];
@@ -46,7 +46,7 @@ export class LCEProject {
4646

4747
return {
4848
rootPath: this.projectInfo.rootPath,
49-
projectPath: this.projectInfo.projectPath,
49+
projectPath: this.projectInfo.configPath,
5050
subProjectPaths: this.projectInfo.subProjectPaths,
5151
sourceFilePaths: this.projectInfo.sourceFilePaths,
5252
concepts: Object.fromEntries(jsonConcepts)

typescript/src/core/utils/modulepath.utils.ts

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -205,12 +205,12 @@ export class ModulePathUtils {
205205
* @returns whether the path is outside the project (or one of its subprojects) or not
206206
*/
207207
static isExternal(modulePath: string, projectInfo: LCEProjectInfo, projects: LCEProject[]): boolean {
208-
let moduleIndex: ModuleIndex | undefined = this.moduleIndexes.get(projectInfo.projectPath);
208+
let moduleIndex: ModuleIndex | undefined = this.moduleIndexes.get(projectInfo.configPath);
209209
if(!moduleIndex) {
210210
moduleIndex = new Map();
211211
// create module index by registering all processed modules for project and all subprojects
212212
for(const subproject of projects) {
213-
if(!projectInfo.subProjectPaths.includes(subproject.projectInfo.projectPath) && subproject.projectInfo.projectPath !== projectInfo.projectPath) {
213+
if(!projectInfo.subProjectPaths.includes(subproject.projectInfo.configPath) && subproject.projectInfo.configPath !== projectInfo.configPath) {
214214
continue;
215215
}
216216

@@ -224,7 +224,7 @@ export class ModulePathUtils {
224224
}
225225
}
226226
}
227-
this.moduleIndexes.set(projectInfo.projectPath, moduleIndex)
227+
this.moduleIndexes.set(projectInfo.configPath, moduleIndex)
228228
}
229229

230230
const pathType = this.getPathType(modulePath);

typescript/src/core/utils/node.utils.ts

Lines changed: 9 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ import path from "path";
33
import { FileUtils } from "./file.utils";
44
import * as fs from "fs";
55
import ts, { ParsedCommandLine } from "typescript";
6+
import { LCEProjectInfo } from "../project";
67

78
export class NodeUtils {
89
private static packageMappings = new Map();
@@ -56,11 +57,11 @@ export class NodeUtils {
5657
*
5758
* NOTE: Throws Error on failure of both resolution methods.
5859
*/
59-
public static resolveImportPath(importPath: string, projectPath: string, sourceFilePathAbsolute: string): string {
60-
if (!this.tsConfigs.has(projectPath)) {
61-
this.tsConfigs.set(projectPath, this.parseTsConfig(projectPath));
60+
public static resolveImportPath(importPath: string, projectInfo: LCEProjectInfo, sourceFilePathAbsolute: string): string {
61+
if (!this.tsConfigs.has(projectInfo.configPath)) {
62+
this.tsConfigs.set(projectInfo.configPath, this.parseTsConfig(projectInfo.configPath));
6263
}
63-
const tsconfig = this.tsConfigs.get(projectPath)!;
64+
const tsconfig = this.tsConfigs.get(projectInfo.configPath)!;
6465

6566
let tsResolvedModule: string | undefined;
6667
try {
@@ -72,7 +73,7 @@ export class NodeUtils {
7273
} else {
7374
let jsResolvedModule: string | undefined;
7475
try {
75-
jsResolvedModule = require.resolve(importPath, { paths: [projectPath] });
76+
jsResolvedModule = require.resolve(importPath, { paths: [projectInfo.rootPath] });
7677
} catch (e) {}
7778
if (jsResolvedModule) {
7879
return FileUtils.normalizePath(jsResolvedModule);
@@ -91,15 +92,15 @@ export class NodeUtils {
9192
getDirectories: ts.sys.getDirectories,
9293
};
9394

94-
private static parseTsConfig(projectRootPath: string): ParsedCommandLine {
95-
const configFile = ts.readConfigFile(path.join(projectRootPath, "tsconfig.json"), ts.sys.readFile);
95+
private static parseTsConfig(configPath: string): ParsedCommandLine {
96+
const configFile = ts.readConfigFile(configPath, ts.sys.readFile);
9697
const parseConfigHost: ts.ParseConfigHost = {
9798
useCaseSensitiveFileNames: ts.sys.useCaseSensitiveFileNames,
9899
readDirectory: ts.sys.readDirectory,
99100
fileExists: ts.sys.fileExists,
100101
readFile: ts.sys.readFile,
101102
};
102103

103-
return ts.parseJsonConfigFileContent(configFile.config, parseConfigHost, projectRootPath, {});
104+
return ts.parseJsonConfigFileContent(configFile.config, parseConfigHost, path.dirname(configPath), {});
104105
}
105106
}

typescript/src/core/utils/project.utils.ts

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,7 @@ export class ProjectUtils {
2424
public static normalizeProjectInfo(projectInfo: LCEProjectInfo): LCEProjectInfo {
2525
return {
2626
rootPath: FileUtils.normalizePath(projectInfo.rootPath),
27-
projectPath: FileUtils.normalizePath(projectInfo.projectPath),
27+
configPath: FileUtils.normalizePath(projectInfo.configPath),
2828
subProjectPaths: projectInfo.subProjectPaths.map(path => FileUtils.normalizePath(path)),
2929
sourceFilePaths: projectInfo.sourceFilePaths.map(path => FileUtils.normalizePath(path))
3030
}
@@ -60,10 +60,10 @@ export class ProjectUtils {
6060
// filter out duplicates
6161
const seen: string[] = [];
6262
return result.filter(pi => {
63-
if(seen.includes(pi.projectPath)) {
63+
if(seen.includes(pi.configPath)) {
6464
return false;
6565
} else {
66-
seen.push(pi.projectPath);
66+
seen.push(pi.configPath);
6767
return true;
6868
}
6969
});
@@ -85,7 +85,7 @@ export class ProjectUtils {
8585
for (const ref of tsConfig.projectReferences) {
8686
const referencedConfigFileName = this.getConfigFileName(ref.path);
8787
const subProjectInfos = this.getProjectInfo(path.dirname(referencedConfigFileName), path.basename(referencedConfigFileName));
88-
subProjectPaths.push(...subProjectInfos.map(spi => FileUtils.normalizePath(spi.projectPath)));
88+
subProjectPaths.push(...subProjectInfos.map(spi => FileUtils.normalizePath(spi.configPath)));
8989
result.push(...subProjectInfos);
9090
}
9191
}
@@ -98,7 +98,7 @@ export class ProjectUtils {
9898

9999
result.push({
100100
rootPath: FileUtils.normalizePath(rootPath),
101-
projectPath: FileUtils.normalizePath(projectPath),
101+
configPath: FileUtils.normalizePath(path.join(projectPath, configFileName)),
102102
subProjectPaths: subProjectPaths,
103103
sourceFilePaths: tsConfig.fileNames.map(fn => FileUtils.normalizePath(fn))
104104
});

0 commit comments

Comments
 (0)