Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Appearance settings

Commit fe40873

Browse filesBrowse files
authored
Merge pull request microsoft#19786 from Microsoft/directoryRename
Handle the watch when folders are added/removed/renamed in wild card folder
2 parents 57f247e + 3f34525 commit fe40873
Copy full SHA for fe40873

7 files changed

+162-39Lines changed: 162 additions & 39 deletions

File tree

Expand file treeCollapse file tree
Open diff view settings
Filter options
Expand file treeCollapse file tree
Open diff view settings
Collapse file

‎src/compiler/core.ts‎

Copy file name to clipboardExpand all lines: src/compiler/core.ts
+11-6Lines changed: 11 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -3097,10 +3097,9 @@ namespace ts {
30973097
function addOrDeleteFileOrDirectory(fileOrDirectory: string, fileOrDirectoryPath: Path) {
30983098
const existingResult = getCachedFileSystemEntries(fileOrDirectoryPath);
30993099
if (existingResult) {
3100-
// This was a folder already present, remove it if this doesnt exist any more
3101-
if (!host.directoryExists(fileOrDirectory)) {
3102-
cachedReadDirectoryResult.delete(fileOrDirectoryPath);
3103-
}
3100+
// Just clear the cache for now
3101+
// For now just clear the cache, since this could mean that multiple level entries might need to be re-evaluated
3102+
clearCache();
31043103
}
31053104
else {
31063105
// This was earlier a file (hence not in cached directory contents)
@@ -3113,8 +3112,14 @@ namespace ts {
31133112
fileExists: host.fileExists(fileOrDirectoryPath),
31143113
directoryExists: host.directoryExists(fileOrDirectoryPath)
31153114
};
3116-
updateFilesOfFileSystemEntry(parentResult, baseName, fsQueryResult.fileExists);
3117-
updateFileSystemEntry(parentResult.directories, baseName, fsQueryResult.directoryExists);
3115+
if (fsQueryResult.directoryExists || hasEntry(parentResult.directories, baseName)) {
3116+
// Folder added or removed, clear the cache instead of updating the folder and its structure
3117+
clearCache();
3118+
}
3119+
else {
3120+
// No need to update the directory structure, just files
3121+
updateFilesOfFileSystemEntry(parentResult, baseName, fsQueryResult.fileExists);
3122+
}
31183123
return fsQueryResult;
31193124
}
31203125
}
Collapse file

‎src/compiler/watch.ts‎

Copy file name to clipboardExpand all lines: src/compiler/watch.ts
+23-14Lines changed: 23 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -230,7 +230,7 @@ namespace ts {
230230

231231
function createWatchMode(rootFileNames: string[], compilerOptions: CompilerOptions, watchingHost?: WatchingSystemHost, configFileName?: string, configFileSpecs?: ConfigFileSpecs, configFileWildCardDirectories?: MapLike<WatchDirectoryFlags>, optionsToExtendForConfigFile?: CompilerOptions) {
232232
let program: Program;
233-
let needsReload: boolean; // true if the config file changed and needs to reload it from the disk
233+
let reloadLevel: ConfigFileProgramReloadLevel; // level to indicate if the program needs to be reloaded from config file/just filenames etc
234234
let missingFilesMap: Map<FileWatcher>; // Map of file watchers for the missing files
235235
let watchedWildcardDirectories: Map<WildcardDirectoryWatcher>; // map of watchers for the wild card directories in the config file
236236
let timerToUpdateProgram: any; // timer callback to recompile the program
@@ -488,25 +488,38 @@ namespace ts {
488488

489489
function scheduleProgramReload() {
490490
Debug.assert(!!configFileName);
491-
needsReload = true;
491+
reloadLevel = ConfigFileProgramReloadLevel.Full;
492492
scheduleProgramUpdate();
493493
}
494494

495495
function updateProgram() {
496496
timerToUpdateProgram = undefined;
497497
reportWatchDiagnostic(createCompilerDiagnostic(Diagnostics.File_change_detected_Starting_incremental_compilation));
498498

499-
if (needsReload) {
500-
reloadConfigFile();
499+
switch (reloadLevel) {
500+
case ConfigFileProgramReloadLevel.Partial:
501+
return reloadFileNamesFromConfigFile();
502+
case ConfigFileProgramReloadLevel.Full:
503+
return reloadConfigFile();
504+
default:
505+
return synchronizeProgram();
501506
}
502-
else {
503-
synchronizeProgram();
507+
}
508+
509+
function reloadFileNamesFromConfigFile() {
510+
const result = getFileNamesFromConfigSpecs(configFileSpecs, getDirectoryPath(configFileName), compilerOptions, directoryStructureHost);
511+
if (!configFileSpecs.filesSpecs && result.fileNames.length === 0) {
512+
reportDiagnostic(getErrorForNoInputFiles(configFileSpecs, configFileName));
504513
}
514+
rootFileNames = result.fileNames;
515+
516+
// Update the program
517+
synchronizeProgram();
505518
}
506519

507520
function reloadConfigFile() {
508521
writeLog(`Reloading config file: ${configFileName}`);
509-
needsReload = false;
522+
reloadLevel = ConfigFileProgramReloadLevel.None;
510523

511524
const cachedHost = directoryStructureHost as CachedDirectoryStructureHost;
512525
cachedHost.clearCache();
@@ -611,18 +624,14 @@ namespace ts {
611624

612625
// If the the added or created file or directory is not supported file name, ignore the file
613626
// But when watched directory is added/removed, we need to reload the file list
614-
if (fileOrDirectoryPath !== directory && !isSupportedSourceFileName(fileOrDirectory, compilerOptions)) {
627+
if (fileOrDirectoryPath !== directory && hasExtension(fileOrDirectoryPath) && !isSupportedSourceFileName(fileOrDirectory, compilerOptions)) {
615628
writeLog(`Project: ${configFileName} Detected file add/remove of non supported extension: ${fileOrDirectory}`);
616629
return;
617630
}
618631

619632
// Reload is pending, do the reload
620-
if (!needsReload) {
621-
const result = getFileNamesFromConfigSpecs(configFileSpecs, getDirectoryPath(configFileName), compilerOptions, directoryStructureHost);
622-
if (!configFileSpecs.filesSpecs && result.fileNames.length === 0) {
623-
reportDiagnostic(getErrorForNoInputFiles(configFileSpecs, configFileName));
624-
}
625-
rootFileNames = result.fileNames;
633+
if (reloadLevel !== ConfigFileProgramReloadLevel.Full) {
634+
reloadLevel = ConfigFileProgramReloadLevel.Partial;
626635

627636
// Schedule Update the program
628637
scheduleProgramUpdate();
Collapse file

‎src/compiler/watchUtilities.ts‎

Copy file name to clipboardExpand all lines: src/compiler/watchUtilities.ts
+8Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,14 @@
22

33
/* @internal */
44
namespace ts {
5+
export enum ConfigFileProgramReloadLevel {
6+
None,
7+
/** Update the file name list from the disk */
8+
Partial,
9+
/** Reload completely by re-reading contents of config file from disk and updating program */
10+
Full
11+
}
12+
513
/**
614
* Updates the existing missing file watches with the new set of missing files after new program is created
715
*/
Collapse file

‎src/harness/unittests/tsserverProjectSystem.ts‎

Copy file name to clipboardExpand all lines: src/harness/unittests/tsserverProjectSystem.ts
+56-3Lines changed: 56 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -2766,6 +2766,7 @@ namespace ts.projectSystem {
27662766
watchedRecursiveDirectories.push(`${root}/a/b/src`, `${root}/a/b/node_modules`);
27672767
checkWatchedDirectories(host, watchedRecursiveDirectories, /*recursive*/ true);
27682768
});
2769+
27692770
});
27702771

27712772
describe("Proper errors", () => {
@@ -2868,6 +2869,58 @@ namespace ts.projectSystem {
28682869
verifyNonExistentFile(/*useProjectRoot*/ false);
28692870
});
28702871
});
2872+
2873+
it("folder rename updates project structure and reports no errors", () => {
2874+
const projectDir = "/a/b/projects/myproject";
2875+
const app: FileOrFolder = {
2876+
path: `${projectDir}/bar/app.ts`,
2877+
content: "class Bar implements foo.Foo { getFoo() { return ''; } get2() { return 1; } }"
2878+
};
2879+
const foo: FileOrFolder = {
2880+
path: `${projectDir}/foo/foo.ts`,
2881+
content: "declare namespace foo { interface Foo { get2(): number; getFoo(): string; } }"
2882+
};
2883+
const configFile: FileOrFolder = {
2884+
path: `${projectDir}/tsconfig.json`,
2885+
content: JSON.stringify({ compilerOptions: { module: "none", targer: "es5" }, exclude: ["node_modules"] })
2886+
};
2887+
const host = createServerHost([app, foo, configFile]);
2888+
const session = createSession(host, { canUseEvents: true, });
2889+
const projectService = session.getProjectService();
2890+
2891+
session.executeCommandSeq<protocol.OpenRequest>({
2892+
command: server.CommandNames.Open,
2893+
arguments: { file: app.path, }
2894+
});
2895+
checkNumberOfProjects(projectService, { configuredProjects: 1 });
2896+
assert.isDefined(projectService.configuredProjects.get(configFile.path));
2897+
verifyErrorsInApp();
2898+
2899+
host.renameFolder(`${projectDir}/foo`, `${projectDir}/foo2`);
2900+
host.runQueuedTimeoutCallbacks();
2901+
host.runQueuedTimeoutCallbacks();
2902+
verifyErrorsInApp();
2903+
2904+
function verifyErrorsInApp() {
2905+
host.clearOutput();
2906+
const expectedSequenceId = session.getNextSeq();
2907+
session.executeCommandSeq<protocol.GeterrRequest>({
2908+
command: server.CommandNames.Geterr,
2909+
arguments: {
2910+
delay: 0,
2911+
files: [app.path]
2912+
}
2913+
});
2914+
host.checkTimeoutQueueLengthAndRun(1);
2915+
checkErrorMessage(host, "syntaxDiag", { file: app.path, diagnostics: [] });
2916+
host.clearOutput();
2917+
2918+
host.runQueuedImmediateCallbacks();
2919+
checkErrorMessage(host, "semanticDiag", { file: app.path, diagnostics: [] });
2920+
checkCompleteEvent(host, 2, expectedSequenceId);
2921+
host.clearOutput();
2922+
}
2923+
});
28712924
});
28722925

28732926
describe("autoDiscovery", () => {
@@ -5656,8 +5709,8 @@ namespace ts.projectSystem {
56565709
{ path: "/a/b/node_modules/.staging/lodash-b0733faa/index.js", content: "module.exports = require('./lodash');" },
56575710
{ path: "/a/b/node_modules/.staging/typescript-8493ea5d/package.json.3017591594" }
56585711
].map(getRootedFileOrFolder));
5659-
// Since we didnt add any supported extension file, there wont be any timeout scheduled
5660-
verifyAfterPartialOrCompleteNpmInstall(0);
5712+
// Since we added/removed folder, scheduled project update
5713+
verifyAfterPartialOrCompleteNpmInstall(2);
56615714

56625715
// Remove file "/a/b/node_modules/.staging/typescript-8493ea5d/package.json.3017591594"
56635716
filesAndFoldersToAdd.length--;
@@ -5678,7 +5731,7 @@ namespace ts.projectSystem {
56785731
{ path: "/a/b/node_modules/.staging/rxjs-22375c61/testing" },
56795732
{ path: "/a/b/node_modules/.staging/rxjs-22375c61/package.json.2252192041", content: "{\n \"_args\": [\n [\n {\n \"raw\": \"rxjs@^5.4.2\",\n \"scope\": null,\n \"escapedName\": \"rxjs\",\n \"name\": \"rxjs\",\n \"rawSpec\": \"^5.4.2\",\n \"spec\": \">=5.4.2 <6.0.0\",\n \"type\": \"range\"\n },\n \"C:\\\\Users\\\\shkamat\\\\Desktop\\\\app\"\n ]\n ],\n \"_from\": \"rxjs@>=5.4.2 <6.0.0\",\n \"_id\": \"rxjs@5.4.3\",\n \"_inCache\": true,\n \"_location\": \"/rxjs\",\n \"_nodeVersion\": \"7.7.2\",\n \"_npmOperationalInternal\": {\n \"host\": \"s3://npm-registry-packages\",\n \"tmp\": \"tmp/rxjs-5.4.3.tgz_1502407898166_0.6800217325799167\"\n },\n \"_npmUser\": {\n \"name\": \"blesh\",\n \"email\": \"ben@benlesh.com\"\n },\n \"_npmVersion\": \"5.3.0\",\n \"_phantomChildren\": {},\n \"_requested\": {\n \"raw\": \"rxjs@^5.4.2\",\n \"scope\": null,\n \"escapedName\": \"rxjs\",\n \"name\": \"rxjs\",\n \"rawSpec\": \"^5.4.2\",\n \"spec\": \">=5.4.2 <6.0.0\",\n \"type\": \"range\"\n },\n \"_requiredBy\": [\n \"/\"\n ],\n \"_resolved\": \"https://registry.npmjs.org/rxjs/-/rxjs-5.4.3.tgz\",\n \"_shasum\": \"0758cddee6033d68e0fd53676f0f3596ce3d483f\",\n \"_shrinkwrap\": null,\n \"_spec\": \"rxjs@^5.4.2\",\n \"_where\": \"C:\\\\Users\\\\shkamat\\\\Desktop\\\\app\",\n \"author\": {\n \"name\": \"Ben Lesh\",\n \"email\": \"ben@benlesh.com\"\n },\n \"bugs\": {\n \"url\": \"https://github.com/ReactiveX/RxJS/issues\"\n },\n \"config\": {\n \"commitizen\": {\n \"path\": \"cz-conventional-changelog\"\n }\n },\n \"contributors\": [\n {\n \"name\": \"Ben Lesh\",\n \"email\": \"ben@benlesh.com\"\n },\n {\n \"name\": \"Paul Taylor\",\n \"email\": \"paul.e.taylor@me.com\"\n },\n {\n \"name\": \"Jeff Cross\",\n \"email\": \"crossj@google.com\"\n },\n {\n \"name\": \"Matthew Podwysocki\",\n \"email\": \"matthewp@microsoft.com\"\n },\n {\n \"name\": \"OJ Kwon\",\n \"email\": \"kwon.ohjoong@gmail.com\"\n },\n {\n \"name\": \"Andre Staltz\",\n \"email\": \"andre@staltz.com\"\n }\n ],\n \"dependencies\": {\n \"symbol-observable\": \"^1.0.1\"\n },\n \"description\": \"Reactive Extensions for modern JavaScript\",\n \"devDependencies\": {\n \"babel-polyfill\": \"^6.23.0\",\n \"benchmark\": \"^2.1.0\",\n \"benchpress\": \"2.0.0-beta.1\",\n \"chai\": \"^3.5.0\",\n \"color\": \"^0.11.1\",\n \"colors\": \"1.1.2\",\n \"commitizen\": \"^2.8.6\",\n \"coveralls\": \"^2.11.13\",\n \"cz-conventional-changelog\": \"^1.2.0\",\n \"danger\": \"^1.1.0\",\n \"doctoc\": \"^1.0.0\",\n \"escape-string-regexp\": \"^1.0.5 \",\n \"esdoc\": \"^0.4.7\",\n \"eslint\": \"^3.8.0\",\n \"fs-extra\": \"^2.1.2\",\n \"get-folder-size\": \"^1.0.0\",\n \"glob\": \"^7.0.3\",\n \"gm\": \"^1.22.0\",\n \"google-closure-compiler-js\": \"^20170218.0.0\",\n \"gzip-size\": \"^3.0.0\",\n \"http-server\": \"^0.9.0\",\n \"husky\": \"^0.13.3\",\n \"lint-staged\": \"3.2.5\",\n \"lodash\": \"^4.15.0\",\n \"madge\": \"^1.4.3\",\n \"markdown-doctest\": \"^0.9.1\",\n \"minimist\": \"^1.2.0\",\n \"mkdirp\": \"^0.5.1\",\n \"mocha\": \"^3.0.2\",\n \"mocha-in-sauce\": \"0.0.1\",\n \"npm-run-all\": \"^4.0.2\",\n \"npm-scripts-info\": \"^0.3.4\",\n \"nyc\": \"^10.2.0\",\n \"opn-cli\": \"^3.1.0\",\n \"platform\": \"^1.3.1\",\n \"promise\": \"^7.1.1\",\n \"protractor\": \"^3.1.1\",\n \"rollup\": \"0.36.3\",\n \"rollup-plugin-inject\": \"^2.0.0\",\n \"rollup-plugin-node-resolve\": \"^2.0.0\",\n \"rx\": \"latest\",\n \"rxjs\": \"latest\",\n \"shx\": \"^0.2.2\",\n \"sinon\": \"^2.1.0\",\n \"sinon-chai\": \"^2.9.0\",\n \"source-map-support\": \"^0.4.0\",\n \"tslib\": \"^1.5.0\",\n \"tslint\": \"^4.4.2\",\n \"typescript\": \"~2.0.6\",\n \"typings\": \"^2.0.0\",\n \"validate-commit-msg\": \"^2.14.0\",\n \"watch\": \"^1.0.1\",\n \"webpack\": \"^1.13.1\",\n \"xmlhttprequest\": \"1.8.0\"\n },\n \"directories\": {},\n \"dist\": {\n \"integrity\": \"sha512-fSNi+y+P9ss+EZuV0GcIIqPUK07DEaMRUtLJvdcvMyFjc9dizuDjere+A4V7JrLGnm9iCc+nagV/4QdMTkqC4A==\",\n \"shasum\": \"0758cddee6033d68e0fd53676f0f3596ce3d483f\",\n \"tarball\": \"https://registry.npmjs.org/rxjs/-/rxjs-5.4.3.tgz\"\n },\n \"engines\": {\n \"npm\": \">=2.0.0\"\n },\n \"homepage\": \"https://github.com/ReactiveX/RxJS\",\n \"keywords\": [\n \"Rx\",\n \"RxJS\",\n \"ReactiveX\",\n \"ReactiveExtensions\",\n \"Streams\",\n \"Observables\",\n \"Observable\",\n \"Stream\",\n \"ES6\",\n \"ES2015\"\n ],\n \"license\": \"Apache-2.0\",\n \"lint-staged\": {\n \"*.@(js)\": [\n \"eslint --fix\",\n \"git add\"\n ],\n \"*.@(ts)\": [\n \"tslint --fix\",\n \"git add\"\n ]\n },\n \"main\": \"Rx.js\",\n \"maintainers\": [\n {\n \"name\": \"blesh\",\n \"email\": \"ben@benlesh.com\"\n }\n ],\n \"name\": \"rxjs\",\n \"optionalDependencies\": {},\n \"readme\": \"ERROR: No README data found!\",\n \"repository\": {\n \"type\": \"git\",\n \"url\": \"git+ssh://git@github.com/ReactiveX/RxJS.git\"\n },\n \"scripts-info\": {\n \"info\": \"List available script\",\n \"build_all\": \"Build all packages (ES6, CJS, UMD) and generate packages\",\n \"build_cjs\": \"Build CJS package with clean up existing build, copy source into dist\",\n \"build_es6\": \"Build ES6 package with clean up existing build, copy source into dist\",\n \"build_closure_core\": \"Minify Global core build using closure compiler\",\n \"build_global\": \"Build Global package, then minify build\",\n \"build_perf\": \"Build CJS & Global build, run macro performance test\",\n \"build_test\": \"Build CJS package & test spec, execute mocha test runner\",\n \"build_cover\": \"Run lint to current code, build CJS & test spec, execute test coverage\",\n \"build_docs\": \"Build ES6 & global package, create documentation using it\",\n \"build_spec\": \"Build test specs\",\n \"check_circular_dependencies\": \"Check codebase has circular dependencies\",\n \"clean_spec\": \"Clean up existing test spec build output\",\n \"clean_dist_cjs\": \"Clean up existing CJS package output\",\n \"clean_dist_es6\": \"Clean up existing ES6 package output\",\n \"clean_dist_global\": \"Clean up existing Global package output\",\n \"commit\": \"Run git commit wizard\",\n \"compile_dist_cjs\": \"Compile codebase into CJS module\",\n \"compile_module_es6\": \"Compile codebase into ES6\",\n \"cover\": \"Execute test coverage\",\n \"lint_perf\": \"Run lint against performance test suite\",\n \"lint_spec\": \"Run lint against test spec\",\n \"lint_src\": \"Run lint against source\",\n \"lint\": \"Run lint against everything\",\n \"perf\": \"Run macro performance benchmark\",\n \"perf_micro\": \"Run micro performance benchmark\",\n \"test_mocha\": \"Execute mocha test runner against existing test spec build\",\n \"test_browser\": \"Execute mocha test runner on browser against existing test spec build\",\n \"test\": \"Clean up existing test spec build, build test spec and execute mocha test runner\",\n \"tests2png\": \"Generate marble diagram image from test spec\",\n \"watch\": \"Watch codebase, trigger compile when source code changes\"\n },\n \"typings\": \"Rx.d.ts\",\n \"version\": \"5.4.3\"\n}\n" }
56805733
].map(getRootedFileOrFolder));
5681-
verifyAfterPartialOrCompleteNpmInstall(0);
5734+
verifyAfterPartialOrCompleteNpmInstall(2);
56825735

56835736
// remove /a/b/node_modules/.staging/rxjs-22375c61/package.json.2252192041
56845737
filesAndFoldersToAdd.length--;

0 commit comments

Comments
0 (0)
Morty Proxy This is a proxified and sanitized view of the page, visit original site.