Skip to content

Reuse Module Resolutions from Unchanged Files #15261

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 24 commits into from
May 2, 2017

Conversation

aozgaa
Copy link
Contributor

@aozgaa aozgaa commented Apr 18, 2017

sheetalkamat and others added 6 commits April 18, 2017 14:10
Previously it assumed valueDeclaration was present, which is not true
for type aliases.
Previously it crashed when it assumed valueDeclaration was always
defined.
Andy Hanson and others added 4 commits April 19, 2017 07:07
…rst-decl-not-valueDecl

mergeSymbol uses first declaration, not valueDeclaration
Use base tsconfig's compileOnSave option if tsconfig.json doesnt have it specified
None = 0,
NoOldProgram = 1,
ModuleResolutionOptions = 2,
RootNames = 3,
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

1 << 3, etc


for (const oldSourceFile of oldProgram.getSourceFiles()) {
let newSourceFile = host.getSourceFileByPath
? host.getSourceFileByPath(oldSourceFile.fileName, oldSourceFile.path, options.target)
: host.getSourceFile(oldSourceFile.fileName, options.target);

if (!newSourceFile) {
return false;
structuralChanges |= StructuralChangesFromOldProgram.SourceFileRemoved;
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

SourceFileRemoved is in your CannotReuseResolution set, so why collect flags and continue here rather than just return?

return false;
else {
// file has no changes - use it as is
newSourceFile = oldSourceFile;
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It's a bit hard to tell in this diff... but is this in the else clause to if (oldSourceFile !== newSourceFile)? If so, this statement seems like a no-op.

/* @internal */ structureIsReused?: StructureIsReused;
}

export const enum StructureIsReused {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

/* @internal */

if (!oldProgramState && !file.ambientModuleNames.length) {
// if old program state is not supplied and file does not contain locally defined ambient modules
// then the best we can do is fallback to the default logic
function createOldProgramState(
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

just inline that. no need to call a function that just wraps the arguments in a literal.

@@ -6,6 +6,23 @@ namespace ts {
const emptyArray: any[] = [];
const ignoreDiagnosticCommentRegEx = /(^\s*$)|(^\s*\/\/\/?\s*(@ts-ignore)?)/;


const enum StructuralChangesFromOldProgram {
None = 0,
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

let's keep this as a tri-state enum

// ensure that module resolution results are still correct
const resolutionsChanged = hasChangesInResolutions(moduleNames, resolutions, oldSourceFile.resolvedModules, moduleResolutionIsEqualTo);
if (resolutionsChanged) {
return false;
return StructuralChangesFromOldProgram.Imports | StructuralChangesFromOldProgram.ModuleAugmentations;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

you want to continue here instead of return to allow for copying the state of unchanged files.

// - in the old program module name was resolved to ambient module whose declaration is in non-modified file
// TODO: if we want to reuse resolutions more aggressively, refine this to check for whether the
// text of the corresponding modulenames has changed.
if (file === oldSourceFile) {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

can you add a test for the npm install case:

  • file1.ts => import * as a from "a";
  • semantic errors
  • Add new file node_modules\a\index.d.ts
  • make an edit in file2.ts
  • no semantic errors

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Added.


// there is an old program, check if we can reuse its structure
const oldRootNames = oldProgram.getRootFileNames();
if (!arrayIsEqualTo(oldRootNames, rootNames)) {
return false;
oldProgram.structureIsReused = StructureIsReused.Not;
return StructureIsReused.Not;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

return oldProgram.structureIsReused = StructureIsReused.Not;

}

if (!arrayIsEqualTo(options.types, oldOptions.types)) {
return false;
oldProgram.structureIsReused = StructureIsReused.Not;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

return oldProgram.structureIsReused = StructureIsReused.Not;


/* @internal */
export const enum StructureIsReused {
Completely,
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Start at 1 or make Not === 0

@@ -123,7 +123,7 @@ namespace ts {
/* @internal */
export function hasChangesInResolutions<T>(names: string[], newResolutions: T[], oldResolutions: Map<T>, comparer: (oldResolution: T, newResolution: T) => boolean): boolean {
if (names.length !== newResolutions.length) {
return false;
return true;
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Figure out how to hit this in a test

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

(or change to an assert)

Copy link
Contributor Author

@aozgaa aozgaa May 1, 2017

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

hasChangesInResolutions is called on resolutions obtained from resolveModuleNamesReusingOldState and resolveTypeReferenceDirectiveNamesWorker.
Both guarantee in their implementations that the array they return is the same length as names.

Changing to an assert.

},
"Reusing resolution of module '{0}' to file '{1}' from old program.": {
"category": "Message",
"code": 618
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Did you mean 618 here? Seems kind low ;-)

if (!oldProgramState) {
return false;
}
// TODO: if we change our policy of rechecking failed lookups on each program create,
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Minor nit: But some of the TODO comments aren't really TODOs (in the sense that work is incomplete), but just a note for any future changes. Just remove the "TODO: " from these if there is no work still to do.

@aozgaa aozgaa merged commit 2150a77 into microsoft:master May 2, 2017
@microsoft microsoft locked and limited conversation to collaborators Jun 14, 2018
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

7 participants