Skip to content

Commit b988bc9

Browse files
author
Andy
authored
Merge pull request microsoft#10303 from Microsoft/not_needed_types
Allow `"typings": null`
2 parents 9ac13ab + df739fd commit b988bc9

File tree

8 files changed

+84
-51
lines changed

8 files changed

+84
-51
lines changed

src/compiler/program.ts

+44-41
Original file line numberDiff line numberDiff line change
@@ -119,49 +119,31 @@ namespace ts {
119119
}
120120

121121
function tryReadTypesSection(packageJsonPath: string, baseDirectory: string, state: ModuleResolutionState): string {
122-
let jsonContent: { typings?: string, types?: string, main?: string };
123-
try {
124-
const jsonText = state.host.readFile(packageJsonPath);
125-
jsonContent = jsonText ? <{ typings?: string, types?: string, main?: string }>JSON.parse(jsonText) : {};
126-
}
127-
catch (e) {
128-
// gracefully handle if readFile fails or returns not JSON
129-
jsonContent = {};
130-
}
131-
132-
let typesFile: string;
133-
let fieldName: string;
134-
// first try to read content of 'typings' section (backward compatibility)
135-
if (jsonContent.typings) {
136-
if (typeof jsonContent.typings === "string") {
137-
fieldName = "typings";
138-
typesFile = jsonContent.typings;
139-
}
140-
else {
141-
if (state.traceEnabled) {
142-
trace(state.host, Diagnostics.Expected_type_of_0_field_in_package_json_to_be_string_got_1, "typings", typeof jsonContent.typings);
122+
const jsonContent = readJson(packageJsonPath, state.host);
123+
124+
function tryReadFromField(fieldName: string) {
125+
if (hasProperty(jsonContent, fieldName)) {
126+
const typesFile = (<any>jsonContent)[fieldName];
127+
if (typeof typesFile === "string") {
128+
const typesFilePath = normalizePath(combinePaths(baseDirectory, typesFile));
129+
if (state.traceEnabled) {
130+
trace(state.host, Diagnostics.package_json_has_0_field_1_that_references_2, fieldName, typesFile, typesFilePath);
131+
}
132+
return typesFilePath;
143133
}
144-
}
145-
}
146-
// then read 'types'
147-
if (!typesFile && jsonContent.types) {
148-
if (typeof jsonContent.types === "string") {
149-
fieldName = "types";
150-
typesFile = jsonContent.types;
151-
}
152-
else {
153-
if (state.traceEnabled) {
154-
trace(state.host, Diagnostics.Expected_type_of_0_field_in_package_json_to_be_string_got_1, "types", typeof jsonContent.types);
134+
else {
135+
if (state.traceEnabled) {
136+
trace(state.host, Diagnostics.Expected_type_of_0_field_in_package_json_to_be_string_got_1, fieldName, typeof typesFile);
137+
}
155138
}
156139
}
157140
}
158-
if (typesFile) {
159-
const typesFilePath = normalizePath(combinePaths(baseDirectory, typesFile));
160-
if (state.traceEnabled) {
161-
trace(state.host, Diagnostics.package_json_has_0_field_1_that_references_2, fieldName, typesFile, typesFilePath);
162-
}
141+
142+
const typesFilePath = tryReadFromField("typings") || tryReadFromField("types");
143+
if (typesFilePath) {
163144
return typesFilePath;
164145
}
146+
165147
// Use the main module for inferring types if no types package specified and the allowJs is set
166148
if (state.compilerOptions.allowJs && jsonContent.main && typeof jsonContent.main === "string") {
167149
if (state.traceEnabled) {
@@ -173,6 +155,17 @@ namespace ts {
173155
return undefined;
174156
}
175157

158+
function readJson(path: string, host: ModuleResolutionHost): { typings?: string, types?: string, main?: string } {
159+
try {
160+
const jsonText = host.readFile(path);
161+
return jsonText ? JSON.parse(jsonText) : {};
162+
}
163+
catch (e) {
164+
// gracefully handle if readFile fails or returns not JSON
165+
return {};
166+
}
167+
}
168+
176169
const typeReferenceExtensions = [".d.ts"];
177170

178171
function getEffectiveTypeRoots(options: CompilerOptions, host: ModuleResolutionHost) {
@@ -717,7 +710,7 @@ namespace ts {
717710
}
718711

719712
function loadNodeModuleFromDirectory(extensions: string[], candidate: string, failedLookupLocation: string[], onlyRecordFailures: boolean, state: ModuleResolutionState): string {
720-
const packageJsonPath = combinePaths(candidate, "package.json");
713+
const packageJsonPath = pathToPackageJson(candidate);
721714
const directoryExists = !onlyRecordFailures && directoryProbablyExists(candidate, state.host);
722715
if (directoryExists && state.host.fileExists(packageJsonPath)) {
723716
if (state.traceEnabled) {
@@ -747,6 +740,10 @@ namespace ts {
747740
return loadModuleFromFile(combinePaths(candidate, "index"), extensions, failedLookupLocation, !directoryExists, state);
748741
}
749742

743+
function pathToPackageJson(directory: string): string {
744+
return combinePaths(directory, "package.json");
745+
}
746+
750747
function loadModuleFromNodeModulesFolder(moduleName: string, directory: string, failedLookupLocations: string[], state: ModuleResolutionState): string {
751748
const nodeModulesFolder = combinePaths(directory, "node_modules");
752749
const nodeModulesFolderExists = directoryProbablyExists(nodeModulesFolder, state.host);
@@ -1070,15 +1067,21 @@ namespace ts {
10701067
}
10711068

10721069
// Walk the primary type lookup locations
1073-
let result: string[] = [];
1070+
const result: string[] = [];
10741071
if (host.directoryExists && host.getDirectories) {
10751072
const typeRoots = getEffectiveTypeRoots(options, host);
10761073
if (typeRoots) {
10771074
for (const root of typeRoots) {
10781075
if (host.directoryExists(root)) {
10791076
for (const typeDirectivePath of host.getDirectories(root)) {
1080-
// Return just the type directive names
1081-
result = result.concat(getBaseFileName(normalizePath(typeDirectivePath)));
1077+
const normalized = normalizePath(typeDirectivePath);
1078+
const packageJsonPath = pathToPackageJson(combinePaths(root, normalized));
1079+
// tslint:disable-next-line:no-null-keyword
1080+
const isNotNeededPackage = host.fileExists(packageJsonPath) && readJson(packageJsonPath, host).typings === null;
1081+
if (!isNotNeededPackage) {
1082+
// Return just the type directive names
1083+
result.push(getBaseFileName(normalized));
1084+
}
10821085
}
10831086
}
10841087
}

src/harness/compilerRunner.ts

+1-1
Original file line numberDiff line numberDiff line change
@@ -52,7 +52,7 @@ class CompilerBaselineRunner extends RunnerBase {
5252
private makeUnitName(name: string, root: string) {
5353
const path = ts.toPath(name, root, (fileName) => Harness.Compiler.getCanonicalFileName(fileName));
5454
const pathStart = ts.toPath(Harness.IO.getCurrentDirectory(), "", (fileName) => Harness.Compiler.getCanonicalFileName(fileName));
55-
return path.replace(pathStart, "/");
55+
return pathStart ? path.replace(pathStart, "/") : path;
5656
};
5757

5858
public checkTestCodeOutput(fileName: string) {

src/harness/fourslash.ts

+10-9
Original file line numberDiff line numberDiff line change
@@ -2395,13 +2395,14 @@ ${code}
23952395
// Comment line, check for global/file @options and record them
23962396
const match = optionRegex.exec(line.substr(2));
23972397
if (match) {
2398-
const fileMetadataNamesIndex = fileMetadataNames.indexOf(match[1]);
2398+
const [key, value] = match.slice(1);
2399+
const fileMetadataNamesIndex = fileMetadataNames.indexOf(key);
23992400
if (fileMetadataNamesIndex === -1) {
24002401
// Check if the match is already existed in the global options
2401-
if (globalOptions[match[1]] !== undefined) {
2402-
throw new Error("Global Option : '" + match[1] + "' is already existed");
2402+
if (globalOptions[key] !== undefined) {
2403+
throw new Error(`Global option '${key}' already exists`);
24032404
}
2404-
globalOptions[match[1]] = match[2];
2405+
globalOptions[key] = value;
24052406
}
24062407
else {
24072408
if (fileMetadataNamesIndex === fileMetadataNames.indexOf(metadataOptionNames.fileName)) {
@@ -2416,12 +2417,12 @@ ${code}
24162417
resetLocalData();
24172418
}
24182419

2419-
currentFileName = basePath + "/" + match[2];
2420-
currentFileOptions[match[1]] = match[2];
2420+
currentFileName = basePath + "/" + value;
2421+
currentFileOptions[key] = value;
24212422
}
24222423
else {
24232424
// Add other fileMetadata flag
2424-
currentFileOptions[match[1]] = match[2];
2425+
currentFileOptions[key] = value;
24252426
}
24262427
}
24272428
}
@@ -2509,7 +2510,7 @@ ${code}
25092510
}
25102511

25112512
const marker: Marker = {
2512-
fileName: fileName,
2513+
fileName,
25132514
position: location.position,
25142515
data: markerValue
25152516
};
@@ -2526,7 +2527,7 @@ ${code}
25262527

25272528
function recordMarker(fileName: string, location: LocationInformation, name: string, markerMap: MarkerMap, markers: Marker[]): Marker {
25282529
const marker: Marker = {
2529-
fileName: fileName,
2530+
fileName,
25302531
position: location.position
25312532
};
25322533

Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
//// [tests/cases/conformance/typings/typingsLookup2.ts] ////
2+
3+
//// [package.json]
4+
{ "typings": null }
5+
6+
//// [a.ts]
7+
8+
9+
//// [a.js]
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
=== /a.ts ===
2+
3+
No type information for this code.
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
[]
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
=== /a.ts ===
2+
3+
No type information for this code.
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
// @traceResolution: true
2+
// @noImplicitReferences: true
3+
// @currentDirectory: /
4+
// This tests that an @types package with `"typings": null` is not automatically included.
5+
// (If it were, this test would break because there are no typings to be found.)
6+
7+
// @filename: /tsconfig.json
8+
{}
9+
10+
// @filename: /node_modules/@types/angular2/package.json
11+
{ "typings": null }
12+
13+
// @filename: /a.ts

0 commit comments

Comments
 (0)