Skip to content

Commit b23503c

Browse files
committed
Merge pull request #7513 from zhengbli/projectSizeLimitFor18-2
Port #7486 to release 1.8
2 parents de7d429 + b0fc9e0 commit b23503c

File tree

8 files changed

+108
-19
lines changed

8 files changed

+108
-19
lines changed

src/compiler/commandLineParser.ts

+5-1
Original file line numberDiff line numberDiff line change
@@ -305,6 +305,10 @@ namespace ts {
305305
name: "noCustomAsyncPromise",
306306
type: "boolean",
307307
experimental: true
308+
},
309+
{
310+
name: "disableSizeLimit",
311+
type: "boolean"
308312
}
309313
];
310314

@@ -534,7 +538,7 @@ namespace ts {
534538
}
535539
else {
536540
// by default exclude node_modules, and any specificied output directory
537-
exclude = ["node_modules"];
541+
exclude = ["node_modules", "bower_components"];
538542
const outDir = json["compilerOptions"] && json["compilerOptions"]["outDir"];
539543
if (outDir) {
540544
exclude.push(outDir);

src/compiler/diagnosticMessages.json

+4
Original file line numberDiff line numberDiff line change
@@ -2661,5 +2661,9 @@
26612661
"Unknown typing option '{0}'.": {
26622662
"category": "Error",
26632663
"code": 17010
2664+
},
2665+
"Too many JavaScript files in the project. Consider specifying the 'exclude' setting in project configuration to limit included source folders. The likely folder to exclude is '{0}'. To disable the project size limit, set the 'disableSizeLimit' compiler option to 'true'.": {
2666+
"category": "Error",
2667+
"code": 17012
26642668
}
26652669
}

src/compiler/program.ts

+34-2
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ namespace ts {
77
/* @internal */ export let emitTime = 0;
88
/* @internal */ export let ioReadTime = 0;
99
/* @internal */ export let ioWriteTime = 0;
10+
/* @internal */ export const maxProgramSizeForNonTsFiles = 20 * 1024 * 1024;
1011

1112
/** The version of the TypeScript compiler release */
1213

@@ -348,6 +349,8 @@ namespace ts {
348349
let diagnosticsProducingTypeChecker: TypeChecker;
349350
let noDiagnosticsTypeChecker: TypeChecker;
350351
let classifiableNames: Map<string>;
352+
const programSizeLimitExceeded = -1;
353+
let programSizeForNonTsFiles = 0;
351354

352355
let skipDefaultLib = options.noLib;
353356
const supportedExtensions = getSupportedExtensions(options);
@@ -394,7 +397,8 @@ namespace ts {
394397
(oldOptions.target !== options.target) ||
395398
(oldOptions.noLib !== options.noLib) ||
396399
(oldOptions.jsx !== options.jsx) ||
397-
(oldOptions.allowJs !== options.allowJs)) {
400+
(oldOptions.allowJs !== options.allowJs) ||
401+
(oldOptions.disableSizeLimit !== options.disableSizeLimit)) {
398402
oldProgram = undefined;
399403
}
400404
}
@@ -442,6 +446,10 @@ namespace ts {
442446

443447
return program;
444448

449+
function exceedProgramSizeLimit() {
450+
return !options.disableSizeLimit && programSizeForNonTsFiles === programSizeLimitExceeded;
451+
}
452+
445453
function getCommonSourceDirectory() {
446454
if (typeof commonSourceDirectory === "undefined") {
447455
if (options.rootDir && checkSourceFilesBelongToPath(files, options.rootDir)) {
@@ -1054,7 +1062,7 @@ namespace ts {
10541062
}
10551063
}
10561064

1057-
if (diagnostic) {
1065+
if (diagnostic && !exceedProgramSizeLimit()) {
10581066
if (refFile !== undefined && refEnd !== undefined && refPos !== undefined) {
10591067
fileProcessingDiagnostics.add(createFileDiagnostic(refFile, refPos, refEnd - refPos, diagnostic, ...diagnosticArgument));
10601068
}
@@ -1087,6 +1095,11 @@ namespace ts {
10871095
return file;
10881096
}
10891097

1098+
const isNonTsFile = !hasTypeScriptFileExtension(fileName);
1099+
if (isNonTsFile && exceedProgramSizeLimit()) {
1100+
return undefined;
1101+
}
1102+
10901103
// We haven't looked for this file, do so now and cache result
10911104
const file = host.getSourceFile(fileName, options.target, hostErrorMessage => {
10921105
if (refFile !== undefined && refPos !== undefined && refEnd !== undefined) {
@@ -1098,6 +1111,25 @@ namespace ts {
10981111
}
10991112
});
11001113

1114+
if (isNonTsFile && !options.disableSizeLimit && file && file.text) {
1115+
programSizeForNonTsFiles += file.text.length;
1116+
if (programSizeForNonTsFiles > maxProgramSizeForNonTsFiles) {
1117+
// If the program size limit was reached when processing a file, this file is
1118+
// likely in the problematic folder than contains too many files.
1119+
// Normally the folder is one level down from the commonSourceDirectory, for example,
1120+
// if the commonSourceDirectory is "/src/", and the last processed path was "/src/node_modules/a/b.js",
1121+
// we should show in the error message "/src/node_modules/".
1122+
const commonSourceDirectory = getCommonSourceDirectory();
1123+
let rootLevelDirectory = path.substring(0, Math.max(commonSourceDirectory.length, path.indexOf(directorySeparator, commonSourceDirectory.length)));
1124+
if (rootLevelDirectory[rootLevelDirectory.length - 1] !== directorySeparator) {
1125+
rootLevelDirectory += directorySeparator;
1126+
}
1127+
programDiagnostics.add(createCompilerDiagnostic(Diagnostics.Too_many_JavaScript_files_in_the_project_Consider_specifying_the_exclude_setting_in_project_configuration_to_limit_included_source_folders_The_likely_folder_to_exclude_is_0_To_disable_the_project_size_limit_set_the_disableSizeLimit_compiler_option_to_true, rootLevelDirectory));
1128+
programSizeForNonTsFiles = programSizeLimitExceeded;
1129+
return undefined;
1130+
}
1131+
}
1132+
11011133
filesByName.set(path, file);
11021134
if (file) {
11031135
file.path = path;

src/compiler/sys.ts

+17-12
Original file line numberDiff line numberDiff line change
@@ -74,7 +74,7 @@ namespace ts {
7474
watchDirectory?(path: string, callback: DirectoryWatcherCallback, recursive?: boolean): FileWatcher;
7575
};
7676

77-
export var sys: System = (function () {
77+
export var sys: System = (function() {
7878

7979
function getWScriptSystem(): System {
8080

@@ -407,8 +407,8 @@ namespace ts {
407407
const watchedFileSet = createWatchedFileSet();
408408

409409
function isNode4OrLater(): boolean {
410-
return parseInt(process.version.charAt(1)) >= 4;
411-
}
410+
return parseInt(process.version.charAt(1)) >= 4;
411+
}
412412

413413
const platform: string = _os.platform();
414414
// win32\win64 are case insensitive platforms, MacOS (darwin) by default is also case insensitive
@@ -477,15 +477,20 @@ namespace ts {
477477
for (const current of files) {
478478
const name = combinePaths(path, current);
479479
if (!contains(exclude, getCanonicalPath(name))) {
480-
const stat = _fs.statSync(name);
481-
if (stat.isFile()) {
482-
if (!extension || fileExtensionIs(name, extension)) {
483-
result.push(name);
480+
// fs.statSync would throw an exception if the file is a symlink
481+
// whose linked file doesn't exist.
482+
try {
483+
const stat = _fs.statSync(name);
484+
if (stat.isFile()) {
485+
if (!extension || fileExtensionIs(name, extension)) {
486+
result.push(name);
487+
}
488+
}
489+
else if (stat.isDirectory()) {
490+
directories.push(name);
484491
}
485492
}
486-
else if (stat.isDirectory()) {
487-
directories.push(name);
488-
}
493+
catch (e) { }
489494
}
490495
}
491496
for (const current of directories) {
@@ -509,7 +514,7 @@ namespace ts {
509514
// and https://github.com/Microsoft/TypeScript/issues/4643), therefore
510515
// if the current node.js version is newer than 4, use `fs.watch` instead.
511516
const watchSet = isNode4OrLater() ? watchedFileSet : pollingWatchedFileSet;
512-
const watchedFile = watchSet.addFile(filePath, callback);
517+
const watchedFile = watchSet.addFile(filePath, callback);
513518
return {
514519
close: () => watchSet.removeFile(watchedFile)
515520
};
@@ -539,7 +544,7 @@ namespace ts {
539544
}
540545
);
541546
},
542-
resolvePath: function (path: string): string {
547+
resolvePath: function(path: string): string {
543548
return _path.resolve(path);
544549
},
545550
fileExists(path: string): boolean {

src/compiler/types.ts

+1
Original file line numberDiff line numberDiff line change
@@ -2437,6 +2437,7 @@ namespace ts {
24372437
allowSyntheticDefaultImports?: boolean;
24382438
allowJs?: boolean;
24392439
noImplicitUseStrict?: boolean;
2440+
disableSizeLimit?: boolean;
24402441
/* @internal */ stripInternal?: boolean;
24412442

24422443
// Skip checking lib.d.ts to help speed up tests.

src/compiler/utilities.ts

+4
Original file line numberDiff line numberDiff line change
@@ -2476,6 +2476,10 @@ namespace ts {
24762476
return forEach(supportedJavascriptExtensions, extension => fileExtensionIs(fileName, extension));
24772477
}
24782478

2479+
export function hasTypeScriptFileExtension(fileName: string) {
2480+
return forEach(supportedTypeScriptExtensions, extension => fileExtensionIs(fileName, extension));
2481+
}
2482+
24792483
/**
24802484
* Replace each instance of non-ascii characters by one, two, three, or four escape sequences
24812485
* representing the UTF-8 encoding of the character, and return the expanded char code list.

src/server/editorServices.ts

+41-3
Original file line numberDiff line numberDiff line change
@@ -1217,10 +1217,45 @@ namespace ts.server {
12171217
}
12181218
else {
12191219
const project = this.createProject(configFilename, projectOptions);
1220+
let programSizeForNonTsFiles = 0;
1221+
1222+
// As the project openning might not be complete if there are too many files,
1223+
// therefore to surface the diagnostics we need to make sure the given client file is opened.
1224+
if (clientFileName) {
1225+
if (this.host.fileExists(clientFileName)) {
1226+
const currentClientFileInfo = this.openFile(clientFileName, /*openedByClient*/ true);
1227+
project.addRoot(currentClientFileInfo);
1228+
if (!hasTypeScriptFileExtension(currentClientFileInfo.fileName) && currentClientFileInfo.content) {
1229+
programSizeForNonTsFiles += currentClientFileInfo.content.length;
1230+
}
1231+
}
1232+
else {
1233+
return { errorMsg: "specified file " + clientFileName + " not found" };
1234+
}
1235+
}
1236+
12201237
for (const rootFilename of projectOptions.files) {
1238+
if (rootFilename === clientFileName) {
1239+
continue;
1240+
}
1241+
12211242
if (this.host.fileExists(rootFilename)) {
1222-
const info = this.openFile(rootFilename, /*openedByClient*/ clientFileName == rootFilename);
1223-
project.addRoot(info);
1243+
if (projectOptions.compilerOptions.disableSizeLimit) {
1244+
const info = this.openFile(rootFilename, /*openedByClient*/ false);
1245+
project.addRoot(info);
1246+
}
1247+
else if (programSizeForNonTsFiles <= maxProgramSizeForNonTsFiles) {
1248+
const info = this.openFile(rootFilename, /*openedByClient*/ false);
1249+
project.addRoot(info);
1250+
if (!hasTypeScriptFileExtension(rootFilename)) {
1251+
programSizeForNonTsFiles += info.content.length;
1252+
}
1253+
}
1254+
else {
1255+
// The project size is too large. Stop loading the files on the server,
1256+
// and let the compiler issue an diagnostic via `createProgram`.
1257+
break;
1258+
}
12241259
}
12251260
else {
12261261
return { errorMsg: "specified file " + rootFilename + " not found" };
@@ -1251,7 +1286,10 @@ namespace ts.server {
12511286
return error;
12521287
}
12531288
else {
1254-
const oldFileNames = project.compilerService.host.roots.map(info => info.fileName);
1289+
// if the project is too large, the root files might not have been all loaded if the total
1290+
// program size reached the upper limit. In that case project.projectOptions.files should
1291+
// be more precise. However this would only happen for configured project.
1292+
const oldFileNames = project.projectOptions ? project.projectOptions.files : project.compilerService.host.roots.map(info => info.fileName);
12551293
const newFileNames = projectOptions.files;
12561294
const fileNamesToRemove = oldFileNames.filter(f => newFileNames.indexOf(f) < 0);
12571295
const fileNamesToAdd = newFileNames.filter(f => oldFileNames.indexOf(f) < 0);

src/services/services.ts

+2-1
Original file line numberDiff line numberDiff line change
@@ -2759,7 +2759,8 @@ namespace ts {
27592759
oldSettings.module !== newSettings.module ||
27602760
oldSettings.noResolve !== newSettings.noResolve ||
27612761
oldSettings.jsx !== newSettings.jsx ||
2762-
oldSettings.allowJs !== newSettings.allowJs);
2762+
oldSettings.allowJs !== newSettings.allowJs ||
2763+
oldSettings.disableSizeLimit !== oldSettings.disableSizeLimit);
27632764

27642765
// Now create a new compiler
27652766
const compilerHost: CompilerHost = {

0 commit comments

Comments
 (0)