Skip to content

Commit 1f50056

Browse files
committed
Merge remote-tracking branch 'refs/remotes/Microsoft/master'
2 parents 603ba89 + 1045f3b commit 1f50056

File tree

48 files changed

+890
-67
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

48 files changed

+890
-67
lines changed

package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -75,7 +75,7 @@
7575
"through2": "latest",
7676
"travis-fold": "latest",
7777
"ts-node": "latest",
78-
"tslint": "next",
78+
"tslint": "4.0.0-dev.3",
7979
"typescript": "next"
8080
},
8181
"scripts": {

src/compiler/checker.ts

Lines changed: 14 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -4612,8 +4612,8 @@ namespace ts {
46124612
// the modifiers type is T. Otherwise, the modifiers type is {}.
46134613
const declaredType = <MappedType>getTypeFromMappedTypeNode(type.declaration);
46144614
const constraint = getConstraintTypeFromMappedType(declaredType);
4615-
const extendedConstraint = constraint.flags & TypeFlags.TypeParameter ? getConstraintOfTypeParameter(<TypeParameter>constraint) : constraint;
4616-
type.modifiersType = extendedConstraint.flags & TypeFlags.Index ? instantiateType((<IndexType>extendedConstraint).type, type.mapper || identityMapper) : emptyObjectType;
4615+
const extendedConstraint = constraint && constraint.flags & TypeFlags.TypeParameter ? getConstraintOfTypeParameter(<TypeParameter>constraint) : constraint;
4616+
type.modifiersType = extendedConstraint && extendedConstraint.flags & TypeFlags.Index ? instantiateType((<IndexType>extendedConstraint).type, type.mapper || identityMapper) : emptyObjectType;
46174617
}
46184618
}
46194619
return type.modifiersType;
@@ -7730,8 +7730,11 @@ namespace ts {
77307730
}
77317731
}
77327732
}
7733-
else if (relation !== identityRelation && isEmptyObjectType(resolveStructuredTypeMembers(<ObjectType>target))) {
7734-
return Ternary.True;
7733+
else if (relation !== identityRelation) {
7734+
const resolved = resolveStructuredTypeMembers(<ObjectType>target);
7735+
if (isEmptyObjectType(resolved) || resolved.stringIndexInfo && resolved.stringIndexInfo.type.flags & TypeFlags.Any) {
7736+
return Ternary.True;
7737+
}
77357738
}
77367739
return Ternary.False;
77377740
}
@@ -21845,8 +21848,13 @@ namespace ts {
2184521848

2184621849
function checkGrammarNumericLiteral(node: NumericLiteral): boolean {
2184721850
// Grammar checking
21848-
if (node.isOctalLiteral && languageVersion >= ScriptTarget.ES5) {
21849-
return grammarErrorOnNode(node, Diagnostics.Octal_literals_are_not_available_when_targeting_ECMAScript_5_and_higher);
21851+
if (node.isOctalLiteral) {
21852+
if (languageVersion >= ScriptTarget.ES5) {
21853+
return grammarErrorOnNode(node, Diagnostics.Octal_literals_are_not_available_when_targeting_ECMAScript_5_and_higher_Use_the_syntax_0o_0, node.text);
21854+
}
21855+
if (isChildOfLiteralType(node)) {
21856+
return grammarErrorOnNode(node, Diagnostics.Octal_literal_types_must_use_ES2015_syntax_Use_the_syntax_0o_0, node.text);
21857+
}
2185021858
}
2185121859
}
2185221860

src/compiler/diagnosticMessages.json

Lines changed: 13 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -227,7 +227,7 @@
227227
"category": "Error",
228228
"code": 1084
229229
},
230-
"Octal literals are not available when targeting ECMAScript 5 and higher.": {
230+
"Octal literals are not available when targeting ECMAScript 5 and higher. Use the syntax '0o{0}'.": {
231231
"category": "Error",
232232
"code": 1085
233233
},
@@ -2685,6 +2685,10 @@
26852685
"category": "Message",
26862686
"code": 6080
26872687
},
2688+
"File '{0}' has an unsupported extension, so skipping it.": {
2689+
"category": "Message",
2690+
"code": 6081
2691+
},
26882692
"Only 'amd' and 'system' modules are supported alongside --{0}.": {
26892693
"category": "Error",
26902694
"code": 6082
@@ -2945,6 +2949,10 @@
29452949
"category": "Message",
29462950
"code": 6146
29472951
},
2952+
"Resolution for module '{0}' was found in cache": {
2953+
"category": "Message",
2954+
"code": 6147
2955+
},
29482956
"Variable '{0}' implicitly has an '{1}' type.": {
29492957
"category": "Error",
29502958
"code": 7005
@@ -3234,5 +3242,9 @@
32343242
"Add {0} to existing import declaration from {1}": {
32353243
"category": "Message",
32363244
"code": 90015
3245+
},
3246+
"Octal literal types must use ES2015 syntax. Use the syntax '0o{0}'.": {
3247+
"category": "Error",
3248+
"code": 8017
32373249
}
32383250
}

src/compiler/moduleNameResolver.ts

Lines changed: 85 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -47,11 +47,6 @@ namespace ts {
4747
return resolved.path;
4848
}
4949

50-
/** Create Resolved from a file with unknown extension. */
51-
function resolvedFromAnyFile(path: string): Resolved | undefined {
52-
return { path, extension: extensionFromPath(path) };
53-
}
54-
5550
/** Adds `isExernalLibraryImport` to a Resolved to get a ResolvedModule. */
5651
function resolvedModuleFromResolved({ path, extension }: Resolved, isExternalLibraryImport: boolean): ResolvedModuleFull {
5752
return { resolvedFileName: path, extension, isExternalLibraryImport };
@@ -71,7 +66,8 @@ namespace ts {
7166
traceEnabled: boolean;
7267
}
7368

74-
function tryReadTypesSection(extensions: Extensions, packageJsonPath: string, baseDirectory: string, state: ModuleResolutionState): string {
69+
/** Reads from "main" or "types"/"typings" depending on `extensions`. */
70+
function tryReadPackageJsonMainOrTypes(extensions: Extensions, packageJsonPath: string, baseDirectory: string, state: ModuleResolutionState): string {
7571
const jsonContent = readJson(packageJsonPath, state.host);
7672

7773
switch (extensions) {
@@ -293,33 +289,69 @@ namespace ts {
293289
return result;
294290
}
295291

296-
export function resolveModuleName(moduleName: string, containingFile: string, compilerOptions: CompilerOptions, host: ModuleResolutionHost): ResolvedModuleWithFailedLookupLocations {
292+
/**
293+
* Cached module resolutions per containing directory.
294+
* This assumes that any module id will have the same resolution for sibling files located in the same folder.
295+
*/
296+
export interface ModuleResolutionCache {
297+
getOrCreateCacheForDirectory(directoryName: string): Map<ResolvedModuleWithFailedLookupLocations>;
298+
}
299+
300+
export function createModuleResolutionCache(currentDirectory: string, getCanonicalFileName: (s: string) => string) {
301+
const map = createFileMap<Map<ResolvedModuleWithFailedLookupLocations>>();
302+
303+
return { getOrCreateCacheForDirectory };
304+
305+
function getOrCreateCacheForDirectory(directoryName: string) {
306+
const path = toPath(directoryName, currentDirectory, getCanonicalFileName);
307+
let perFolderCache = map.get(path);
308+
if (!perFolderCache) {
309+
perFolderCache = createMap<ResolvedModuleWithFailedLookupLocations>();
310+
map.set(path, perFolderCache);
311+
}
312+
return perFolderCache;
313+
}
314+
}
315+
316+
export function resolveModuleName(moduleName: string, containingFile: string, compilerOptions: CompilerOptions, host: ModuleResolutionHost, cache?: ModuleResolutionCache): ResolvedModuleWithFailedLookupLocations {
297317
const traceEnabled = isTraceEnabled(compilerOptions, host);
298318
if (traceEnabled) {
299319
trace(host, Diagnostics.Resolving_module_0_from_1, moduleName, containingFile);
300320
}
321+
const perFolderCache = cache && cache.getOrCreateCacheForDirectory(getDirectoryPath(containingFile));
322+
let result = perFolderCache && perFolderCache[moduleName];
301323

302-
let moduleResolution = compilerOptions.moduleResolution;
303-
if (moduleResolution === undefined) {
304-
moduleResolution = getEmitModuleKind(compilerOptions) === ModuleKind.CommonJS ? ModuleResolutionKind.NodeJs : ModuleResolutionKind.Classic;
324+
if (result) {
305325
if (traceEnabled) {
306-
trace(host, Diagnostics.Module_resolution_kind_is_not_specified_using_0, ModuleResolutionKind[moduleResolution]);
326+
trace(host, Diagnostics.Resolution_for_module_0_was_found_in_cache, moduleName);
307327
}
308328
}
309329
else {
310-
if (traceEnabled) {
311-
trace(host, Diagnostics.Explicitly_specified_module_resolution_kind_Colon_0, ModuleResolutionKind[moduleResolution]);
330+
let moduleResolution = compilerOptions.moduleResolution;
331+
if (moduleResolution === undefined) {
332+
moduleResolution = getEmitModuleKind(compilerOptions) === ModuleKind.CommonJS ? ModuleResolutionKind.NodeJs : ModuleResolutionKind.Classic;
333+
if (traceEnabled) {
334+
trace(host, Diagnostics.Module_resolution_kind_is_not_specified_using_0, ModuleResolutionKind[moduleResolution]);
335+
}
336+
}
337+
else {
338+
if (traceEnabled) {
339+
trace(host, Diagnostics.Explicitly_specified_module_resolution_kind_Colon_0, ModuleResolutionKind[moduleResolution]);
340+
}
341+
}
342+
343+
switch (moduleResolution) {
344+
case ModuleResolutionKind.NodeJs:
345+
result = nodeModuleNameResolver(moduleName, containingFile, compilerOptions, host);
346+
break;
347+
case ModuleResolutionKind.Classic:
348+
result = classicNameResolver(moduleName, containingFile, compilerOptions, host);
349+
break;
312350
}
313-
}
314351

315-
let result: ResolvedModuleWithFailedLookupLocations;
316-
switch (moduleResolution) {
317-
case ModuleResolutionKind.NodeJs:
318-
result = nodeModuleNameResolver(moduleName, containingFile, compilerOptions, host);
319-
break;
320-
case ModuleResolutionKind.Classic:
321-
result = classicNameResolver(moduleName, containingFile, compilerOptions, host);
322-
break;
352+
if (perFolderCache) {
353+
perFolderCache[moduleName] = result;
354+
}
323355
}
324356

325357
if (traceEnabled) {
@@ -678,18 +710,21 @@ namespace ts {
678710
if (state.traceEnabled) {
679711
trace(state.host, Diagnostics.Found_package_json_at_0, packageJsonPath);
680712
}
681-
const typesFile = tryReadTypesSection(extensions, packageJsonPath, candidate, state);
682-
if (typesFile) {
683-
const onlyRecordFailures = !directoryProbablyExists(getDirectoryPath(typesFile), state.host);
713+
const mainOrTypesFile = tryReadPackageJsonMainOrTypes(extensions, packageJsonPath, candidate, state);
714+
if (mainOrTypesFile) {
715+
const onlyRecordFailures = !directoryProbablyExists(getDirectoryPath(mainOrTypesFile), state.host);
684716
// A package.json "typings" may specify an exact filename, or may choose to omit an extension.
685-
const fromFile = tryFile(typesFile, failedLookupLocations, onlyRecordFailures, state);
686-
if (fromFile) {
687-
// Note: this would allow a package.json to specify a ".js" file as typings. Maybe that should be forbidden.
688-
return resolvedFromAnyFile(fromFile);
717+
const fromExactFile = tryFile(mainOrTypesFile, failedLookupLocations, onlyRecordFailures, state);
718+
if (fromExactFile) {
719+
const resolved = fromExactFile && resolvedIfExtensionMatches(extensions, fromExactFile);
720+
if (resolved) {
721+
return resolved;
722+
}
723+
trace(state.host, Diagnostics.File_0_has_an_unsupported_extension_so_skipping_it, fromExactFile);
689724
}
690-
const x = tryAddingExtensions(typesFile, Extensions.TypeScript, failedLookupLocations, onlyRecordFailures, state);
691-
if (x) {
692-
return x;
725+
const resolved = tryAddingExtensions(mainOrTypesFile, Extensions.TypeScript, failedLookupLocations, onlyRecordFailures, state);
726+
if (resolved) {
727+
return resolved;
693728
}
694729
}
695730
else {
@@ -709,6 +744,24 @@ namespace ts {
709744
return loadModuleFromFile(extensions, combinePaths(candidate, "index"), failedLookupLocations, !directoryExists, state);
710745
}
711746

747+
/** Resolve from an arbitrarily specified file. Return `undefined` if it has an unsupported extension. */
748+
function resolvedIfExtensionMatches(extensions: Extensions, path: string): Resolved | undefined {
749+
const extension = tryGetExtensionFromPath(path);
750+
return extension !== undefined && extensionIsOk(extensions, extension) ? { path, extension } : undefined;
751+
}
752+
753+
/** True if `extension` is one of the supported `extensions`. */
754+
function extensionIsOk(extensions: Extensions, extension: Extension): boolean {
755+
switch (extensions) {
756+
case Extensions.JavaScript:
757+
return extension === Extension.Js || extension === Extension.Jsx;
758+
case Extensions.TypeScript:
759+
return extension === Extension.Ts || extension === Extension.Tsx || extension === Extension.Dts;
760+
case Extensions.DtsOnly:
761+
return extension === Extension.Dts;
762+
}
763+
}
764+
712765
function pathToPackageJson(directory: string): string {
713766
return combinePaths(directory, "package.json");
714767
}

src/compiler/program.ts

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -325,6 +325,7 @@ namespace ts {
325325
// Map storing if there is emit blocking diagnostics for given input
326326
const hasEmitBlockingDiagnostics = createFileMap<boolean>(getCanonicalFileName);
327327

328+
let moduleResolutionCache: ModuleResolutionCache;
328329
let resolveModuleNamesWorker: (moduleNames: string[], containingFile: string) => ResolvedModuleFull[];
329330
if (host.resolveModuleNames) {
330331
resolveModuleNamesWorker = (moduleNames, containingFile) => host.resolveModuleNames(moduleNames, containingFile).map(resolved => {
@@ -338,7 +339,8 @@ namespace ts {
338339
});
339340
}
340341
else {
341-
const loader = (moduleName: string, containingFile: string) => resolveModuleName(moduleName, containingFile, options, host).resolvedModule;
342+
moduleResolutionCache = createModuleResolutionCache(currentDirectory, x => host.getCanonicalFileName(x));
343+
const loader = (moduleName: string, containingFile: string) => resolveModuleName(moduleName, containingFile, options, host, moduleResolutionCache).resolvedModule;
342344
resolveModuleNamesWorker = (moduleNames, containingFile) => loadWithLocalCache(moduleNames, containingFile, loader);
343345
}
344346

@@ -391,6 +393,9 @@ namespace ts {
391393
}
392394
}
393395

396+
// unconditionally set moduleResolutionCache to undefined to avoid unnecessary leaks
397+
moduleResolutionCache = undefined;
398+
394399
// unconditionally set oldProgram to undefined to prevent it from being captured in closure
395400
oldProgram = undefined;
396401

src/compiler/utilities.ts

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -732,6 +732,16 @@ namespace ts {
732732
return false;
733733
}
734734

735+
export function isChildOfLiteralType(node: Node): boolean {
736+
while (node) {
737+
if (node.kind === SyntaxKind.LiteralType) {
738+
return true;
739+
}
740+
node = node.parent;
741+
}
742+
return false;
743+
}
744+
735745
// Warning: This has the same semantics as the forEach family of functions,
736746
// in that traversal terminates in the event that 'visitor' supplies a truthy value.
737747
export function forEachReturnStatement<T>(body: Block, visitor: (stmt: ReturnStatement) => T): T {

src/harness/fourslash.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -341,6 +341,7 @@ namespace FourSlash {
341341
insertSpaceAfterCommaDelimiter: true,
342342
insertSpaceAfterSemicolonInForStatements: true,
343343
insertSpaceBeforeAndAfterBinaryOperators: true,
344+
insertSpaceAfterConstructor: false,
344345
insertSpaceAfterKeywordsInControlFlowStatements: true,
345346
insertSpaceAfterFunctionKeywordForAnonymousFunctions: false,
346347
insertSpaceAfterOpeningAndBeforeClosingNonemptyParenthesis: false,

src/harness/unittests/tsserverProjectSystem.ts

Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1840,6 +1840,41 @@ namespace ts.projectSystem {
18401840
assert.isFalse(service.externalProjects[0].languageServiceEnabled, "language service should be disabled - 2");
18411841
});
18421842

1843+
it("files are properly detached when language service is disabled", () => {
1844+
const f1 = {
1845+
path: "/a/app.js",
1846+
content: "var x = 1"
1847+
};
1848+
const f2 = {
1849+
path: "/a/largefile.js",
1850+
content: ""
1851+
};
1852+
const f3 = {
1853+
path: "/a/lib.js",
1854+
content: "var x = 1"
1855+
};
1856+
const config = {
1857+
path: "/a/tsconfig.json",
1858+
content: JSON.stringify({ compilerOptions: { allowJs: true } })
1859+
};
1860+
const host = createServerHost([f1, f2, f3, config]);
1861+
const originalGetFileSize = host.getFileSize;
1862+
host.getFileSize = (filePath: string) =>
1863+
filePath === f2.path ? server.maxProgramSizeForNonTsFiles + 1 : originalGetFileSize.call(host, filePath);
1864+
1865+
const projectService = createProjectService(host);
1866+
projectService.openClientFile(f1.path);
1867+
projectService.checkNumberOfProjects({ configuredProjects: 1 });
1868+
1869+
projectService.closeClientFile(f1.path);
1870+
projectService.checkNumberOfProjects({});
1871+
1872+
for (const f of [f2, f3]) {
1873+
const scriptInfo = projectService.getScriptInfoForNormalizedPath(server.toNormalizedPath(f.path));
1874+
assert.equal(scriptInfo.containingProjects.length, 0, `expect 0 containing projects for '${f.path}'`)
1875+
}
1876+
});
1877+
18431878
it("language service disabled events are triggered", () => {
18441879
const f1 = {
18451880
path: "/a/app.js",

src/server/project.ts

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -257,8 +257,9 @@ namespace ts.server {
257257
info.detachFromProject(this);
258258
}
259259
}
260-
else {
261-
// release all root files
260+
if (!this.program || !this.languageServiceEnabled) {
261+
// release all root files either if there is no program or language service is disabled.
262+
// in the latter case set of root files can be larger than the set of files in program.
262263
for (const root of this.rootFiles) {
263264
root.detachFromProject(this);
264265
}

src/server/protocol.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2194,12 +2194,14 @@ namespace ts.server.protocol {
21942194
insertSpaceAfterCommaDelimiter?: boolean;
21952195
insertSpaceAfterSemicolonInForStatements?: boolean;
21962196
insertSpaceBeforeAndAfterBinaryOperators?: boolean;
2197+
insertSpaceAfterConstructor?: boolean;
21972198
insertSpaceAfterKeywordsInControlFlowStatements?: boolean;
21982199
insertSpaceAfterFunctionKeywordForAnonymousFunctions?: boolean;
21992200
insertSpaceAfterOpeningAndBeforeClosingNonemptyParenthesis?: boolean;
22002201
insertSpaceAfterOpeningAndBeforeClosingNonemptyBrackets?: boolean;
22012202
insertSpaceAfterOpeningAndBeforeClosingTemplateStringBraces?: boolean;
22022203
insertSpaceAfterOpeningAndBeforeClosingJsxExpressionBraces?: boolean;
2204+
insertSpaceBeforeFunctionParenthesis?: boolean;
22032205
placeOpenBraceOnNewLineForFunctions?: boolean;
22042206
placeOpenBraceOnNewLineForControlBlocks?: boolean;
22052207
}

0 commit comments

Comments
 (0)