TypeScript/src/compiler/program.ts

2320 lines
120 KiB
TypeScript
Raw Normal View History

/// <reference path="sys.ts" />
/// <reference path="emitter.ts" />
/// <reference path="core.ts" />
namespace ts {
/* @internal */ export let programTime = 0;
2015-03-13 23:03:17 +01:00
/* @internal */ export let emitTime = 0;
/* @internal */ export let ioReadTime = 0;
/* @internal */ export let ioWriteTime = 0;
2015-03-08 05:12:16 +01:00
/** The version of the TypeScript compiler release */
2016-06-27 22:43:07 +02:00
export const version = "2.0.0";
2015-11-04 23:02:33 +01:00
const emptyArray: any[] = [];
2016-06-11 00:44:11 +02:00
const defaultTypeRoots = ["node_modules/@types"];
2015-10-05 21:27:06 +02:00
export function findConfigFile(searchPath: string, fileExists: (fileName: string) => boolean): string {
while (true) {
const fileName = combinePaths(searchPath, "tsconfig.json");
2015-10-05 21:27:06 +02:00
if (fileExists(fileName)) {
return fileName;
}
2015-11-04 23:02:33 +01:00
const parentPath = getDirectoryPath(searchPath);
if (parentPath === searchPath) {
break;
}
searchPath = parentPath;
}
return undefined;
}
export function resolveTripleslashReference(moduleName: string, containingFile: string): string {
2015-11-04 23:02:33 +01:00
const basePath = getDirectoryPath(containingFile);
const referencedFileName = isRootedDiskPath(moduleName) ? moduleName : combinePaths(basePath, moduleName);
return normalizePath(referencedFileName);
}
/* @internal */
export function computeCommonSourceDirectoryOfFilenames(fileNames: string[], currentDirectory: string, getCanonicalFileName: (fileName: string) => string): string {
let commonPathComponents: string[];
const failed = forEach(fileNames, sourceFile => {
// Each file contributes into common source file path
const sourcePathComponents = getNormalizedPathComponents(sourceFile, currentDirectory);
sourcePathComponents.pop(); // The base file name is not part of the common directory path
if (!commonPathComponents) {
// first file
commonPathComponents = sourcePathComponents;
return;
}
for (let i = 0, n = Math.min(commonPathComponents.length, sourcePathComponents.length); i < n; i++) {
if (getCanonicalFileName(commonPathComponents[i]) !== getCanonicalFileName(sourcePathComponents[i])) {
if (i === 0) {
// Failed to find any common path component
return true;
}
// New common path found that is 0 -> i-1
commonPathComponents.length = i;
break;
}
}
// If the sourcePathComponents was shorter than the commonPathComponents, truncate to the sourcePathComponents
if (sourcePathComponents.length < commonPathComponents.length) {
commonPathComponents.length = sourcePathComponents.length;
}
});
// A common path can not be found when paths span multiple drives on windows, for example
if (failed) {
return "";
}
if (!commonPathComponents) { // Can happen when all input files are .d.ts files
return currentDirectory;
}
return getNormalizedPathFromPathComponents(commonPathComponents);
}
2015-11-20 06:33:33 +01:00
function trace(host: ModuleResolutionHost, message: DiagnosticMessage, ...args: any[]): void;
function trace(host: ModuleResolutionHost, message: DiagnosticMessage): void {
host.trace(formatMessage.apply(undefined, arguments));
}
function isTraceEnabled(compilerOptions: CompilerOptions, host: ModuleResolutionHost): boolean {
return compilerOptions.traceResolution && host.trace !== undefined;
2015-11-20 06:33:33 +01:00
}
2016-06-03 15:22:34 +02:00
/* @internal */
export function hasZeroOrOneAsteriskCharacter(str: string): boolean {
let seenAsterisk = false;
for (let i = 0; i < str.length; i++) {
if (str.charCodeAt(i) === CharacterCodes.asterisk) {
if (!seenAsterisk) {
seenAsterisk = true;
}
else {
// have already seen asterisk
return false;
}
}
}
return true;
}
function createResolvedModule(resolvedFileName: string, isExternalLibraryImport: boolean, failedLookupLocations: string[]): ResolvedModuleWithFailedLookupLocations {
return { resolvedModule: resolvedFileName ? { resolvedFileName, isExternalLibraryImport } : undefined, failedLookupLocations };
}
function moduleHasNonRelativeName(moduleName: string): boolean {
if (isRootedDiskPath(moduleName)) {
return false;
}
const i = moduleName.lastIndexOf("./", 1);
const startsWithDotSlashOrDotDotSlash = i === 0 || (i === 1 && moduleName.charCodeAt(0) === CharacterCodes.dot);
return !startsWithDotSlashOrDotDotSlash;
}
interface ModuleResolutionState {
host: ModuleResolutionHost;
compilerOptions: CompilerOptions;
traceEnabled: boolean;
// skip .tsx files if jsx is not enabled
skipTsx: boolean;
}
function tryReadTypesSection(packageJsonPath: string, baseDirectory: string, state: ModuleResolutionState): string {
let jsonContent: { typings?: string, types?: string, main?: string };
try {
const jsonText = state.host.readFile(packageJsonPath);
jsonContent = jsonText ? <{ typings?: string, types?: string, main?: string }>JSON.parse(jsonText) : {};
}
catch (e) {
// gracefully handle if readFile fails or returns not JSON
jsonContent = {};
}
let typesFile: string;
let fieldName: string;
// first try to read content of 'typings' section (backward compatibility)
if (jsonContent.typings) {
if (typeof jsonContent.typings === "string") {
fieldName = "typings";
typesFile = jsonContent.typings;
}
else {
if (state.traceEnabled) {
trace(state.host, Diagnostics.Expected_type_of_0_field_in_package_json_to_be_string_got_1, "typings", typeof jsonContent.typings);
}
}
}
// then read 'types'
if (!typesFile && jsonContent.types) {
if (typeof jsonContent.types === "string") {
fieldName = "types";
typesFile = jsonContent.types;
}
else {
if (state.traceEnabled) {
trace(state.host, Diagnostics.Expected_type_of_0_field_in_package_json_to_be_string_got_1, "types", typeof jsonContent.types);
}
}
}
if (typesFile) {
const typesFilePath = normalizePath(combinePaths(baseDirectory, typesFile));
if (state.traceEnabled) {
trace(state.host, Diagnostics.package_json_has_0_field_1_that_references_2, fieldName, typesFile, typesFilePath);
}
return typesFilePath;
}
// Use the main module for inferring types if no types package specified and the allowJs is set
if (state.compilerOptions.allowJs && jsonContent.main && typeof jsonContent.main === "string") {
if (state.traceEnabled) {
2016-05-24 02:06:48 +02:00
trace(state.host, Diagnostics.No_types_specified_in_package_json_but_allowJs_is_set_so_returning_main_value_of_0, jsonContent.main);
}
const mainFilePath = normalizePath(combinePaths(baseDirectory, jsonContent.main));
return mainFilePath;
}
return undefined;
}
2016-04-05 20:28:50 +02:00
const typeReferenceExtensions = [".d.ts"];
2016-06-13 23:37:07 +02:00
function getEffectiveTypeRoots(options: CompilerOptions, host: ModuleResolutionHost) {
2016-06-28 20:43:07 +02:00
if (options.typeRoots) {
return options.typeRoots;
}
let currentDirectory: string;
if (options.configFilePath) {
currentDirectory = getDirectoryPath(options.configFilePath);
}
else if (host.getCurrentDirectory) {
currentDirectory = host.getCurrentDirectory();
}
if (!currentDirectory) {
return undefined;
}
return map(defaultTypeRoots, d => combinePaths(currentDirectory, d));
2016-06-13 23:37:07 +02:00
}
/**
* @param {string | undefined} containingFile - file that contains type reference directive, can be undefined if containing file is unknown.
* This is possible in case if resolution is performed for directives specified via 'types' parameter. In this case initial path for secondary lookups
* is assumed to be the same as root directory of the project.
*/
export function resolveTypeReferenceDirective(typeReferenceDirectiveName: string, containingFile: string, options: CompilerOptions, host: ModuleResolutionHost): ResolvedTypeReferenceDirectiveWithFailedLookupLocations {
const traceEnabled = isTraceEnabled(options, host);
const moduleResolutionState: ModuleResolutionState = {
compilerOptions: options,
host: host,
skipTsx: true,
traceEnabled
};
2016-06-13 23:37:07 +02:00
const typeRoots = getEffectiveTypeRoots(options, host);
if (traceEnabled) {
if (containingFile === undefined) {
2016-06-11 00:44:11 +02:00
if (typeRoots === undefined) {
trace(host, Diagnostics.Resolving_type_reference_directive_0_containing_file_not_set_root_directory_not_set, typeReferenceDirectiveName);
}
else {
2016-06-11 00:44:11 +02:00
trace(host, Diagnostics.Resolving_type_reference_directive_0_containing_file_not_set_root_directory_1, typeReferenceDirectiveName, typeRoots);
}
}
else {
2016-06-11 00:44:11 +02:00
if (typeRoots === undefined) {
trace(host, Diagnostics.Resolving_type_reference_directive_0_containing_file_1_root_directory_not_set, typeReferenceDirectiveName, containingFile);
}
else {
2016-06-11 00:44:11 +02:00
trace(host, Diagnostics.Resolving_type_reference_directive_0_containing_file_1_root_directory_2, typeReferenceDirectiveName, containingFile, typeRoots);
}
}
}
const failedLookupLocations: string[] = [];
2016-04-05 20:28:50 +02:00
// Check primary library paths
if (typeRoots && typeRoots.length) {
2016-06-11 00:44:11 +02:00
if (traceEnabled) {
2016-06-13 18:33:49 +02:00
trace(host, Diagnostics.Resolving_with_primary_search_path_0, typeRoots.join(", "));
2016-06-11 00:44:11 +02:00
}
2016-06-13 18:33:49 +02:00
const primarySearchPaths = typeRoots;
2016-06-11 00:44:11 +02:00
for (const typeRoot of primarySearchPaths) {
const candidate = combinePaths(typeRoot, typeReferenceDirectiveName);
const candidateDirectory = getDirectoryPath(candidate);
const resolvedFile = loadNodeModuleFromDirectory(typeReferenceExtensions, candidate, failedLookupLocations,
!directoryProbablyExists(candidateDirectory, host), moduleResolutionState);
if (resolvedFile) {
if (traceEnabled) {
trace(host, Diagnostics.Type_reference_directive_0_was_successfully_resolved_to_1_primary_Colon_2, typeReferenceDirectiveName, resolvedFile, true);
}
return {
resolvedTypeReferenceDirective: { primary: true, resolvedFileName: resolvedFile },
failedLookupLocations
};
}
}
}
else {
if (traceEnabled) {
trace(host, Diagnostics.Root_directory_cannot_be_determined_skipping_primary_search_paths);
}
}
let resolvedFile: string;
let initialLocationForSecondaryLookup: string;
if (containingFile) {
initialLocationForSecondaryLookup = getDirectoryPath(containingFile);
}
if (initialLocationForSecondaryLookup !== undefined) {
// check secondary locations
if (traceEnabled) {
trace(host, Diagnostics.Looking_up_in_node_modules_folder_initial_location_0, initialLocationForSecondaryLookup);
}
resolvedFile = loadModuleFromNodeModules(typeReferenceDirectiveName, initialLocationForSecondaryLookup, failedLookupLocations, moduleResolutionState);
if (traceEnabled) {
if (resolvedFile) {
trace(host, Diagnostics.Type_reference_directive_0_was_successfully_resolved_to_1_primary_Colon_2, typeReferenceDirectiveName, resolvedFile, false);
}
else {
trace(host, Diagnostics.Type_reference_directive_0_was_not_resolved, typeReferenceDirectiveName);
}
}
}
else {
if (traceEnabled) {
trace(host, Diagnostics.Containing_file_is_not_specified_and_root_directory_cannot_be_determined_skipping_lookup_in_node_modules_folder);
}
}
return {
resolvedTypeReferenceDirective: resolvedFile
? { primary: false, resolvedFileName: resolvedFile }
: undefined,
failedLookupLocations
};
}
export function resolveModuleName(moduleName: string, containingFile: string, compilerOptions: CompilerOptions, host: ModuleResolutionHost): ResolvedModuleWithFailedLookupLocations {
2015-11-20 06:33:33 +01:00
const traceEnabled = isTraceEnabled(compilerOptions, host);
if (traceEnabled) {
trace(host, Diagnostics.Resolving_module_0_from_1, moduleName, containingFile);
}
let moduleResolution = compilerOptions.moduleResolution;
if (moduleResolution === undefined) {
2016-02-13 00:40:47 +01:00
moduleResolution = getEmitModuleKind(compilerOptions) === ModuleKind.CommonJS ? ModuleResolutionKind.NodeJs : ModuleResolutionKind.Classic;
2015-11-20 06:33:33 +01:00
if (traceEnabled) {
trace(host, Diagnostics.Module_resolution_kind_is_not_specified_using_0, ModuleResolutionKind[moduleResolution]);
}
}
else {
if (traceEnabled) {
trace(host, Diagnostics.Explicitly_specified_module_resolution_kind_Colon_0, ModuleResolutionKind[moduleResolution]);
}
}
2015-11-20 06:33:33 +01:00
let result: ResolvedModuleWithFailedLookupLocations;
2015-08-21 01:13:49 +02:00
switch (moduleResolution) {
2015-11-20 06:33:33 +01:00
case ModuleResolutionKind.NodeJs:
result = nodeModuleNameResolver(moduleName, containingFile, compilerOptions, host);
break;
case ModuleResolutionKind.Classic:
result = classicNameResolver(moduleName, containingFile, compilerOptions, host);
break;
}
if (traceEnabled) {
if (result.resolvedModule) {
trace(host, Diagnostics.Module_name_0_was_successfully_resolved_to_1, moduleName, result.resolvedModule.resolvedFileName);
}
else {
trace(host, Diagnostics.Module_name_0_was_not_resolved, moduleName);
}
}
2015-11-20 06:33:33 +01:00
return result;
}
/*
* Every module resolution kind can has its specific understanding how to load module from a specific path on disk
* I.e. for path '/a/b/c':
* - Node loader will first to try to check if '/a/b/c' points to a file with some supported extension and if this fails
* it will try to load module from directory: directory '/a/b/c' should exist and it should have either 'package.json' with
* 'typings' entry or file 'index' with some supported extension
* - Classic loader will only try to interpret '/a/b/c' as file.
*/
type ResolutionKindSpecificLoader = (candidate: string, extensions: string[], failedLookupLocations: string[], onlyRecordFailures: boolean, state: ModuleResolutionState) => string;
/**
* Any module resolution kind can be augmented with optional settings: 'baseUrl', 'paths' and 'rootDirs' - they are used to
* mitigate differences between design time structure of the project and its runtime counterpart so the same import name
* can be resolved successfully by TypeScript compiler and runtime module loader.
* If these settings are set then loading procedure will try to use them to resolve module name and it can of failure it will
* fallback to standard resolution routine.
*
* - baseUrl - this setting controls how non-relative module names are resolved. If this setting is specified then non-relative
* names will be resolved relative to baseUrl: i.e. if baseUrl is '/a/b' then candidate location to resolve module name 'c/d' will
* be '/a/b/c/d'
* - paths - this setting can only be used when baseUrl is specified. allows to tune how non-relative module names
* will be resolved based on the content of the module name.
* Structure of 'paths' compiler options
* 'paths': {
* pattern-1: [...substitutions],
* pattern-2: [...substitutions],
* ...
* pattern-n: [...substitutions]
* }
* Pattern here is a string that can contain zero or one '*' character. During module resolution module name will be matched against
* all patterns in the list. Matching for patterns that don't contain '*' means that module name must be equal to pattern respecting the case.
* If pattern contains '*' then to match pattern "<prefix>*<suffix>" module name must start with the <prefix> and end with <suffix>.
* <MatchedStar> denotes part of the module name between <prefix> and <suffix>.
* If module name can be matches with multiple patterns then pattern with the longest prefix will be picked.
* After selecting pattern we'll use list of substitutions to get candidate locations of the module and the try to load module
* from the candidate location.
* Substitution is a string that can contain zero or one '*'. To get candidate location from substitution we'll pick every
* substitution in the list and replace '*' with <MatchedStar> string. If candidate location is not rooted it
* will be converted to absolute using baseUrl.
* For example:
* baseUrl: /a/b/c
* "paths": {
* // match all module names
* "*": [
* "*", // use matched name as is,
* // <matched name> will be looked as /a/b/c/<matched name>
*
* "folder1/*" // substitution will convert matched name to 'folder1/<matched name>',
* // since it is not rooted then final candidate location will be /a/b/c/folder1/<matched name>
* ],
* // match module names that start with 'components/'
* "components/*": [ "/root/components/*" ] // substitution will convert /components/folder1/<matched name> to '/root/components/folder1/<matched name>',
* // it is rooted so it will be final candidate location
* }
*
* 'rootDirs' allows the project to be spreaded across multiple locations and resolve modules with relative names as if
* they were in the same location. For example lets say there are two files
* '/local/src/content/file1.ts'
* '/shared/components/contracts/src/content/protocols/file2.ts'
* After bundling content of '/shared/components/contracts/src' will be merged with '/local/src' so
* if file1 has the following import 'import {x} from "./protocols/file2"' it will be resolved successfully in runtime.
* 'rootDirs' provides the way to tell compiler that in order to get the whole project it should behave as if content of all
* root dirs were merged together.
* I.e. for the example above 'rootDirs' will have two entries: [ '/local/src', '/shared/components/contracts/src' ].
* Compiler will first convert './protocols/file2' into absolute path relative to the location of containing file:
* '/local/src/content/protocols/file2' and try to load it - failure.
* Then it will search 'rootDirs' looking for a longest matching prefix of this absolute path and if such prefix is found - absolute path will
* be converted to a path relative to found rootDir entry './content/protocols/file2' (*). As a last step compiler will check all remaining
* entries in 'rootDirs', use them to build absolute path out of (*) and try to resolve module from this location.
*/
function tryLoadModuleUsingOptionalResolutionSettings(moduleName: string, containingDirectory: string, loader: ResolutionKindSpecificLoader,
failedLookupLocations: string[], supportedExtensions: string[], state: ModuleResolutionState): string {
2015-11-20 06:33:33 +01:00
if (moduleHasNonRelativeName(moduleName)) {
return tryLoadModuleUsingBaseUrl(moduleName, loader, failedLookupLocations, supportedExtensions, state);
}
else {
return tryLoadModuleUsingRootDirs(moduleName, containingDirectory, loader, failedLookupLocations, supportedExtensions, state);
2015-11-26 01:41:09 +01:00
}
}
2015-11-26 01:41:09 +01:00
function tryLoadModuleUsingRootDirs(moduleName: string, containingDirectory: string, loader: ResolutionKindSpecificLoader,
failedLookupLocations: string[], supportedExtensions: string[], state: ModuleResolutionState): string {
2015-11-20 06:33:33 +01:00
if (!state.compilerOptions.rootDirs) {
return undefined;
}
if (state.traceEnabled) {
trace(state.host, Diagnostics.rootDirs_option_is_set_using_it_to_resolve_relative_module_name_0, moduleName);
}
const candidate = normalizePath(combinePaths(containingDirectory, moduleName));
2015-11-20 06:33:33 +01:00
let matchedRootDir: string;
let matchedNormalizedPrefix: string;
for (const rootDir of state.compilerOptions.rootDirs) {
// rootDirs are expected to be absolute
// in case of tsconfig.json this will happen automatically - compiler will expand relative names
// using location of tsconfig.json as base location
let normalizedRoot = normalizePath(rootDir);
if (!endsWith(normalizedRoot, directorySeparator)) {
normalizedRoot += directorySeparator;
}
const isLongestMatchingPrefix =
startsWith(candidate, normalizedRoot) &&
(matchedNormalizedPrefix === undefined || matchedNormalizedPrefix.length < normalizedRoot.length);
if (state.traceEnabled) {
trace(state.host, Diagnostics.Checking_if_0_is_the_longest_matching_prefix_for_1_2, normalizedRoot, candidate, isLongestMatchingPrefix);
}
if (isLongestMatchingPrefix) {
matchedNormalizedPrefix = normalizedRoot;
matchedRootDir = rootDir;
}
2015-11-20 06:33:33 +01:00
}
if (matchedNormalizedPrefix) {
if (state.traceEnabled) {
trace(state.host, Diagnostics.Longest_matching_prefix_for_0_is_1, candidate, matchedNormalizedPrefix);
}
const suffix = candidate.substr(matchedNormalizedPrefix.length);
// first - try to load from a initial location
if (state.traceEnabled) {
trace(state.host, Diagnostics.Loading_0_from_the_root_dir_1_candidate_location_2, suffix, matchedNormalizedPrefix, candidate);
}
const resolvedFileName = loader(candidate, supportedExtensions, failedLookupLocations, !directoryProbablyExists(containingDirectory, state.host), state);
if (resolvedFileName) {
return resolvedFileName;
2015-11-20 06:33:33 +01:00
}
if (state.traceEnabled) {
trace(state.host, Diagnostics.Trying_other_entries_in_rootDirs);
}
// then try to resolve using remaining entries in rootDirs
for (const rootDir of state.compilerOptions.rootDirs) {
if (rootDir === matchedRootDir) {
// skip the initially matched entry
continue;
}
const candidate = combinePaths(normalizePath(rootDir), suffix);
if (state.traceEnabled) {
trace(state.host, Diagnostics.Loading_0_from_the_root_dir_1_candidate_location_2, suffix, rootDir, candidate);
2015-11-26 01:41:09 +01:00
}
const baseDirectory = getDirectoryPath(candidate);
const resolvedFileName = loader(candidate, supportedExtensions, failedLookupLocations, !directoryProbablyExists(baseDirectory, state.host), state);
if (resolvedFileName) {
return resolvedFileName;
}
}
if (state.traceEnabled) {
trace(state.host, Diagnostics.Module_resolution_using_rootDirs_has_failed);
}
}
return undefined;
}
2015-11-26 01:41:09 +01:00
function tryLoadModuleUsingBaseUrl(moduleName: string, loader: ResolutionKindSpecificLoader, failedLookupLocations: string[],
supportedExtensions: string[], state: ModuleResolutionState): string {
if (!state.compilerOptions.baseUrl) {
return undefined;
}
if (state.traceEnabled) {
trace(state.host, Diagnostics.baseUrl_option_is_set_to_0_using_this_value_to_resolve_non_relative_module_name_1, state.compilerOptions.baseUrl, moduleName);
}
2015-11-26 01:41:09 +01:00
// string is for exact match
let matchedPattern: Pattern | string | undefined = undefined;
if (state.compilerOptions.paths) {
if (state.traceEnabled) {
trace(state.host, Diagnostics.paths_option_is_specified_looking_for_a_pattern_to_match_module_name_0, moduleName);
2015-11-20 06:33:33 +01:00
}
2016-06-03 15:22:34 +02:00
matchedPattern = matchPatternOrExact(getKeys(state.compilerOptions.paths), moduleName);
}
if (matchedPattern) {
const matchedStar = typeof matchedPattern === "string" ? undefined : matchedText(matchedPattern, moduleName);
const matchedPatternText = typeof matchedPattern === "string" ? matchedPattern : patternText(matchedPattern);
if (state.traceEnabled) {
trace(state.host, Diagnostics.Module_name_0_matched_pattern_1, moduleName, matchedPatternText);
2015-11-20 06:33:33 +01:00
}
for (const subst of state.compilerOptions.paths[matchedPatternText]) {
const path = matchedStar ? subst.replace("*", matchedStar) : subst;
const candidate = normalizePath(combinePaths(state.compilerOptions.baseUrl, path));
if (state.traceEnabled) {
trace(state.host, Diagnostics.Trying_substitution_0_candidate_module_location_Colon_1, subst, path);
2015-11-20 06:33:33 +01:00
}
const resolvedFileName = loader(candidate, supportedExtensions, failedLookupLocations, !directoryProbablyExists(getDirectoryPath(candidate), state.host), state);
if (resolvedFileName) {
return resolvedFileName;
}
}
return undefined;
}
else {
const candidate = normalizePath(combinePaths(state.compilerOptions.baseUrl, moduleName));
2015-11-20 06:33:33 +01:00
if (state.traceEnabled) {
trace(state.host, Diagnostics.Resolving_module_name_0_relative_to_base_url_1_2, moduleName, state.compilerOptions.baseUrl, candidate);
2015-11-20 06:33:33 +01:00
}
return loader(candidate, supportedExtensions, failedLookupLocations, !directoryProbablyExists(getDirectoryPath(candidate), state.host), state);
}
}
/**
* patternStrings contains both pattern strings (containing "*") and regular strings.
* Return an exact match if possible, or a pattern match, or undefined.
* (These are verified by verifyCompilerOptions to have 0 or 1 "*" characters.)
*/
function matchPatternOrExact(patternStrings: string[], candidate: string): string | Pattern | undefined {
const patterns: Pattern[] = [];
for (const patternString of patternStrings) {
const pattern = tryParsePattern(patternString);
if (pattern) {
patterns.push(pattern);
}
else if (patternString === candidate) {
// pattern was matched as is - no need to search further
return patternString;
}
}
return findBestPatternMatch(patterns, _ => _, candidate);
}
function patternText({prefix, suffix}: Pattern): string {
return `${prefix}*${suffix}`;
}
/**
* Given that candidate matches pattern, returns the text matching the '*'.
* E.g.: matchedText(tryParsePattern("foo*baz"), "foobarbaz") === "bar"
*/
function matchedText(pattern: Pattern, candidate: string): string {
Debug.assert(isPatternMatch(pattern, candidate));
return candidate.substr(pattern.prefix.length, candidate.length - pattern.suffix.length);
}
/** Return the object corresponding to the best pattern to match `candidate`. */
2016-06-03 15:22:34 +02:00
/* @internal */
export function findBestPatternMatch<T>(values: T[], getPattern: (value: T) => Pattern, candidate: string): T | undefined {
let matchedValue: T | undefined = undefined;
// use length of prefix as betterness criteria
let longestMatchPrefixLength = -1;
for (const v of values) {
const pattern = getPattern(v);
if (isPatternMatch(pattern, candidate) && pattern.prefix.length > longestMatchPrefixLength) {
longestMatchPrefixLength = pattern.prefix.length;
matchedValue = v;
}
}
return matchedValue;
}
function isPatternMatch({prefix, suffix}: Pattern, candidate: string) {
return candidate.length >= prefix.length + suffix.length &&
startsWith(candidate, prefix) &&
endsWith(candidate, suffix);
}
2016-06-03 15:22:34 +02:00
/* @internal */
export function tryParsePattern(pattern: string): Pattern | undefined {
// This should be verified outside of here and a proper error thrown.
Debug.assert(hasZeroOrOneAsteriskCharacter(pattern));
const indexOfStar = pattern.indexOf("*");
return indexOfStar === -1 ? undefined : {
prefix: pattern.substr(0, indexOfStar),
suffix: pattern.substr(indexOfStar + 1)
};
}
export function nodeModuleNameResolver(moduleName: string, containingFile: string, compilerOptions: CompilerOptions, host: ModuleResolutionHost): ResolvedModuleWithFailedLookupLocations {
2015-11-04 23:02:33 +01:00
const containingDirectory = getDirectoryPath(containingFile);
const supportedExtensions = getSupportedExtensions(compilerOptions);
2015-11-20 06:33:33 +01:00
const traceEnabled = isTraceEnabled(compilerOptions, host);
const failedLookupLocations: string[] = [];
2016-02-23 21:48:31 +01:00
const state = { compilerOptions, host, traceEnabled, skipTsx: false };
let resolvedFileName = tryLoadModuleUsingOptionalResolutionSettings(moduleName, containingDirectory, nodeLoadModuleByRelativeName,
failedLookupLocations, supportedExtensions, state);
let isExternalLibraryImport = false;
if (!resolvedFileName) {
if (moduleHasNonRelativeName(moduleName)) {
if (traceEnabled) {
trace(host, Diagnostics.Loading_module_0_from_node_modules_folder, moduleName);
}
resolvedFileName = loadModuleFromNodeModules(moduleName, containingDirectory, failedLookupLocations, state);
isExternalLibraryImport = resolvedFileName !== undefined;
}
else {
const candidate = normalizePath(combinePaths(containingDirectory, moduleName));
resolvedFileName = nodeLoadModuleByRelativeName(candidate, supportedExtensions, failedLookupLocations, /*onlyRecordFailures*/ false, state);
}
}
if (resolvedFileName && host.realpath) {
const originalFileName = resolvedFileName;
resolvedFileName = normalizePath(host.realpath(resolvedFileName));
2015-11-20 06:33:33 +01:00
if (traceEnabled) {
trace(host, Diagnostics.Resolving_real_path_for_0_result_1, originalFileName, resolvedFileName);
2015-11-20 06:33:33 +01:00
}
}
return createResolvedModule(resolvedFileName, isExternalLibraryImport, failedLookupLocations);
}
function nodeLoadModuleByRelativeName(candidate: string, supportedExtensions: string[], failedLookupLocations: string[],
onlyRecordFailures: boolean, state: ModuleResolutionState): string {
2015-11-20 06:33:33 +01:00
if (state.traceEnabled) {
trace(state.host, Diagnostics.Loading_module_as_file_Slash_folder_candidate_module_location_0, candidate);
}
const resolvedFileName = loadModuleFromFile(candidate, supportedExtensions, failedLookupLocations, onlyRecordFailures, state);
return resolvedFileName || loadNodeModuleFromDirectory(supportedExtensions, candidate, failedLookupLocations, onlyRecordFailures, state);
}
/* @internal */
2016-02-23 21:48:31 +01:00
export function directoryProbablyExists(directoryName: string, host: { directoryExists?: (directoryName: string) => boolean }): boolean {
// if host does not support 'directoryExists' assume that directory will exist
return !host.directoryExists || host.directoryExists(directoryName);
}
/**
* @param {boolean} onlyRecordFailures - if true then function won't try to actually load files but instead record all attempts as failures. This flag is necessary
* in cases when we know upfront that all load attempts will fail (because containing folder does not exists) however we still need to record all failed lookup locations.
*/
function loadModuleFromFile(candidate: string, extensions: string[], failedLookupLocation: string[], onlyRecordFailures: boolean, state: ModuleResolutionState): string {
2016-05-31 20:39:11 +02:00
// First try to keep/add an extension: importing "./foo.ts" can be matched by a file "./foo.ts", and "./foo" by "./foo.d.ts"
2016-05-31 21:58:05 +02:00
const resolvedByAddingOrKeepingExtension = loadModuleFromFileWorker(candidate, extensions, failedLookupLocation, onlyRecordFailures, state);
if (resolvedByAddingOrKeepingExtension) {
return resolvedByAddingOrKeepingExtension;
2016-05-31 20:39:11 +02:00
}
// Then try stripping a ".js" or ".jsx" extension and replacing it with a TypeScript one, e.g. "./foo.js" can be matched by "./foo.ts" or "./foo.d.ts"
if (hasJavaScriptFileExtension(candidate)) {
const extensionless = removeFileExtension(candidate);
if (state.traceEnabled) {
const extension = candidate.substring(extensionless.length);
trace(state.host, Diagnostics.File_name_0_has_a_1_extension_stripping_it, candidate, extension);
}
return loadModuleFromFileWorker(extensionless, extensions, failedLookupLocation, onlyRecordFailures, state);
}
}
2016-05-31 21:58:05 +02:00
2016-05-31 20:39:11 +02:00
function loadModuleFromFileWorker(candidate: string, extensions: string[], failedLookupLocation: string[], onlyRecordFailures: boolean, state: ModuleResolutionState): string {
if (!onlyRecordFailures) {
2016-05-31 21:58:05 +02:00
// check if containing folder exists - if it doesn't then just record failures for all supported extensions without disk probing
const directory = getDirectoryPath(candidate);
if (directory) {
onlyRecordFailures = !directoryProbablyExists(directory, state.host);
}
}
2015-11-09 21:49:36 +01:00
return forEach(extensions, tryLoad);
2015-08-21 01:13:49 +02:00
function tryLoad(ext: string): string {
2016-06-06 21:17:11 +02:00
if (state.skipTsx && isJsxOrTsxExtension(ext)) {
return undefined;
}
2015-11-04 23:02:33 +01:00
const fileName = fileExtensionIs(candidate, ext) ? candidate : candidate + ext;
if (!onlyRecordFailures && state.host.fileExists(fileName)) {
if (state.traceEnabled) {
trace(state.host, Diagnostics.File_0_exist_use_it_as_a_name_resolution_result, fileName);
2015-11-20 06:33:33 +01:00
}
2015-08-21 01:13:49 +02:00
return fileName;
}
else {
if (state.traceEnabled) {
trace(state.host, Diagnostics.File_0_does_not_exist, fileName);
2015-11-20 06:33:33 +01:00
}
2015-08-21 01:13:49 +02:00
failedLookupLocation.push(fileName);
return undefined;
}
}
}
function loadNodeModuleFromDirectory(extensions: string[], candidate: string, failedLookupLocation: string[], onlyRecordFailures: boolean, state: ModuleResolutionState): string {
2015-11-04 23:02:33 +01:00
const packageJsonPath = combinePaths(candidate, "package.json");
const directoryExists = !onlyRecordFailures && directoryProbablyExists(candidate, state.host);
if (directoryExists && state.host.fileExists(packageJsonPath)) {
if (state.traceEnabled) {
trace(state.host, Diagnostics.Found_package_json_at_0, packageJsonPath);
2015-11-20 06:33:33 +01:00
}
const typesFile = tryReadTypesSection(packageJsonPath, candidate, state);
if (typesFile) {
const result = loadModuleFromFile(typesFile, extensions, failedLookupLocation, !directoryProbablyExists(getDirectoryPath(typesFile), state.host), state);
if (result) {
return result;
}
}
2015-11-20 06:33:33 +01:00
else {
if (state.traceEnabled) {
trace(state.host, Diagnostics.package_json_does_not_have_types_field);
}
}
}
else {
if (state.traceEnabled) {
trace(state.host, Diagnostics.File_0_does_not_exist, packageJsonPath);
2015-11-20 06:33:33 +01:00
}
// record package json as one of failed lookup locations - in the future if this file will appear it will invalidate resolution results
failedLookupLocation.push(packageJsonPath);
}
return loadModuleFromFile(combinePaths(candidate, "index"), extensions, failedLookupLocation, !directoryExists, state);
}
function loadModuleFromNodeModulesFolder(moduleName: string, directory: string, failedLookupLocations: string[], state: ModuleResolutionState): string {
const nodeModulesFolder = combinePaths(directory, "node_modules");
const nodeModulesFolderExists = directoryProbablyExists(nodeModulesFolder, state.host);
const candidate = normalizePath(combinePaths(nodeModulesFolder, moduleName));
const supportedExtensions = getSupportedExtensions(state.compilerOptions);
let result = loadModuleFromFile(candidate, supportedExtensions, failedLookupLocations, !nodeModulesFolderExists, state);
if (result) {
return result;
}
result = loadNodeModuleFromDirectory(supportedExtensions, candidate, failedLookupLocations, !nodeModulesFolderExists, state);
if (result) {
return result;
}
}
function loadModuleFromNodeModules(moduleName: string, directory: string, failedLookupLocations: string[], state: ModuleResolutionState): string {
directory = normalizeSlashes(directory);
while (true) {
2015-11-04 23:02:33 +01:00
const baseName = getBaseFileName(directory);
if (baseName !== "node_modules") {
const result =
// first: try to load module as-is
loadModuleFromNodeModulesFolder(moduleName, directory, failedLookupLocations, state) ||
// second: try to load module from the scope '@types'
loadModuleFromNodeModulesFolder(combinePaths("@types", moduleName), directory, failedLookupLocations, state);
if (result) {
return result;
}
}
2015-11-04 23:02:33 +01:00
const parentPath = getDirectoryPath(directory);
if (parentPath === directory) {
break;
}
directory = parentPath;
}
return undefined;
}
export function classicNameResolver(moduleName: string, containingFile: string, compilerOptions: CompilerOptions, host: ModuleResolutionHost): ResolvedModuleWithFailedLookupLocations {
2015-11-20 06:33:33 +01:00
const traceEnabled = isTraceEnabled(compilerOptions, host);
const state = { compilerOptions, host, traceEnabled, skipTsx: !compilerOptions.jsx };
const failedLookupLocations: string[] = [];
const supportedExtensions = getSupportedExtensions(compilerOptions);
let containingDirectory = getDirectoryPath(containingFile);
const resolvedFileName = tryLoadModuleUsingOptionalResolutionSettings(moduleName, containingDirectory, loadModuleFromFile, failedLookupLocations, supportedExtensions, state);
if (resolvedFileName) {
return createResolvedModule(resolvedFileName, /*isExternalLibraryImport*/false, failedLookupLocations);
}
let referencedSourceFile: string;
if (moduleHasNonRelativeName(moduleName)) {
while (true) {
const searchName = normalizePath(combinePaths(containingDirectory, moduleName));
referencedSourceFile = loadModuleFromFile(searchName, supportedExtensions, failedLookupLocations, /*onlyRecordFailures*/ false, state);
if (referencedSourceFile) {
break;
}
const parentPath = getDirectoryPath(containingDirectory);
if (parentPath === containingDirectory) {
break;
}
containingDirectory = parentPath;
}
}
else {
const candidate = normalizePath(combinePaths(containingDirectory, moduleName));
referencedSourceFile = loadModuleFromFile(candidate, supportedExtensions, failedLookupLocations, /*onlyRecordFailures*/ false, state);
}
return referencedSourceFile
2016-02-23 21:48:31 +01:00
? { resolvedModule: { resolvedFileName: referencedSourceFile }, failedLookupLocations }
: { resolvedModule: undefined, failedLookupLocations };
}
2015-08-25 23:34:34 +02:00
/* @internal */
export const defaultInitCompilerOptions: CompilerOptions = {
module: ModuleKind.CommonJS,
2015-11-04 18:08:33 +01:00
target: ScriptTarget.ES5,
noImplicitAny: false,
sourceMap: false,
};
2015-07-27 13:52:57 +02:00
interface OutputFingerprint {
hash: string;
byteOrderMark: boolean;
mtime: Date;
}
export function createCompilerHost(options: CompilerOptions, setParentNodes?: boolean): CompilerHost {
2015-11-04 23:02:33 +01:00
const existingDirectories: Map<boolean> = {};
function getCanonicalFileName(fileName: string): string {
// if underlying system can distinguish between two files whose names differs only in cases then file name already in canonical form.
// otherwise use toLowerCase as a canonical form.
return sys.useCaseSensitiveFileNames ? fileName : fileName.toLowerCase();
}
2015-06-26 01:24:41 +02:00
// returned by CScript sys environment
2015-11-04 23:02:33 +01:00
const unsupportedFileEncodingErrorCode = -2147024809;
function getSourceFile(fileName: string, languageVersion: ScriptTarget, onError?: (message: string) => void): SourceFile {
2015-03-13 23:03:17 +01:00
let text: string;
try {
2015-11-04 23:02:33 +01:00
const start = new Date().getTime();
2015-03-13 23:03:17 +01:00
text = sys.readFile(fileName, options.charset);
ioReadTime += new Date().getTime() - start;
}
catch (e) {
if (onError) {
onError(e.number === unsupportedFileEncodingErrorCode
? createCompilerDiagnostic(Diagnostics.Unsupported_file_encoding).messageText
: e.message);
}
text = "";
}
return text !== undefined ? createSourceFile(fileName, text, languageVersion, setParentNodes) : undefined;
}
function directoryExists(directoryPath: string): boolean {
if (hasProperty(existingDirectories, directoryPath)) {
return true;
}
if (sys.directoryExists(directoryPath)) {
existingDirectories[directoryPath] = true;
return true;
}
return false;
}
function ensureDirectoriesExist(directoryPath: string) {
if (directoryPath.length > getRootLength(directoryPath) && !directoryExists(directoryPath)) {
2015-11-04 23:02:33 +01:00
const parentDirectory = getDirectoryPath(directoryPath);
ensureDirectoriesExist(parentDirectory);
sys.createDirectory(directoryPath);
}
}
2016-02-11 09:38:21 +01:00
let outputFingerprints: Map<OutputFingerprint>;
function writeFileIfUpdated(fileName: string, data: string, writeByteOrderMark: boolean): void {
if (!outputFingerprints) {
outputFingerprints = {};
}
const hash = sys.createHash(data);
const mtimeBefore = sys.getModifiedTime(fileName);
if (mtimeBefore && hasProperty(outputFingerprints, fileName)) {
const fingerprint = outputFingerprints[fileName];
// If output has not been changed, and the file has no external modification
if (fingerprint.byteOrderMark === writeByteOrderMark &&
fingerprint.hash === hash &&
fingerprint.mtime.getTime() === mtimeBefore.getTime()) {
return;
}
2016-02-11 09:38:21 +01:00
}
2016-02-11 09:38:21 +01:00
sys.writeFile(fileName, data, writeByteOrderMark);
2016-02-11 09:38:21 +01:00
const mtimeAfter = sys.getModifiedTime(fileName);
2016-02-11 09:38:21 +01:00
outputFingerprints[fileName] = {
hash,
byteOrderMark: writeByteOrderMark,
mtime: mtimeAfter
};
}
function writeFile(fileName: string, data: string, writeByteOrderMark: boolean, onError?: (message: string) => void) {
try {
2015-11-04 23:02:33 +01:00
const start = new Date().getTime();
ensureDirectoriesExist(getDirectoryPath(normalizePath(fileName)));
2016-02-11 09:38:21 +01:00
if (isWatchSet(options) && sys.createHash && sys.getModifiedTime) {
2016-02-11 09:38:21 +01:00
writeFileIfUpdated(fileName, data, writeByteOrderMark);
}
else {
sys.writeFile(fileName, data, writeByteOrderMark);
}
ioWriteTime += new Date().getTime() - start;
}
catch (e) {
if (onError) {
onError(e.message);
}
}
}
function getDefaultLibLocation(): string {
return getDirectoryPath(normalizePath(sys.getExecutingFilePath()));
}
const newLine = getNewLineCharacter(options);
const realpath = sys.realpath && ((path: string) => sys.realpath(path));
return {
getSourceFile,
getDefaultLibLocation,
getDefaultLibFileName: options => combinePaths(getDefaultLibLocation(), getDefaultLibFileName(options)),
writeFile,
getCurrentDirectory: memoize(() => sys.getCurrentDirectory()),
useCaseSensitiveFileNames: () => sys.useCaseSensitiveFileNames,
getCanonicalFileName,
getNewLine: () => newLine,
2015-08-05 06:22:37 +02:00
fileExists: fileName => sys.fileExists(fileName),
readFile: fileName => sys.readFile(fileName),
2016-01-06 23:16:56 +01:00
trace: (s: string) => sys.write(s + newLine),
directoryExists: directoryName => sys.directoryExists(directoryName),
2016-06-11 00:44:11 +02:00
getDirectories: (path: string) => sys.getDirectories(path),
realpath
};
}
2015-06-18 21:04:26 +02:00
export function getPreEmitDiagnostics(program: Program, sourceFile?: SourceFile, cancellationToken?: CancellationToken): Diagnostic[] {
let diagnostics = program.getOptionsDiagnostics(cancellationToken).concat(
2016-02-23 21:48:31 +01:00
program.getSyntacticDiagnostics(sourceFile, cancellationToken),
program.getGlobalDiagnostics(cancellationToken),
program.getSemanticDiagnostics(sourceFile, cancellationToken));
if (program.getCompilerOptions().declaration) {
diagnostics = diagnostics.concat(program.getDeclarationDiagnostics(sourceFile, cancellationToken));
}
return sortAndDeduplicateDiagnostics(diagnostics);
}
export function flattenDiagnosticMessageText(messageText: string | DiagnosticMessageChain, newLine: string): string {
if (typeof messageText === "string") {
return messageText;
}
else {
2015-03-13 23:03:17 +01:00
let diagnosticChain = messageText;
let result = "";
2015-03-13 23:03:17 +01:00
let indent = 0;
while (diagnosticChain) {
if (indent) {
result += newLine;
2015-03-13 23:03:17 +01:00
for (let i = 0; i < indent; i++) {
result += " ";
}
}
result += diagnosticChain.messageText;
indent++;
diagnosticChain = diagnosticChain.next;
}
return result;
}
}
function loadWithLocalCache<T>(names: string[], containingFile: string, loader: (name: string, containingFile: string) => T): T[] {
if (names.length === 0) {
return [];
}
const resolutions: T[] = [];
const cache: Map<T> = {};
for (const name of names) {
let result: T;
if (hasProperty(cache, name)) {
result = cache[name];
}
else {
result = loader(name, containingFile);
cache[name] = result;
}
resolutions.push(result);
}
return resolutions;
}
2016-06-11 00:44:11 +02:00
function getInferredTypesRoot(options: CompilerOptions, rootFiles: string[], host: CompilerHost) {
return computeCommonSourceDirectoryOfFilenames(rootFiles, host.getCurrentDirectory(), f => host.getCanonicalFileName(f));
}
/**
* Given a set of options and a set of root files, returns the set of type directive names
* that should be included for this program automatically.
* This list could either come from the config file,
* or from enumerating the types root + initial secondary types lookup location.
* More type directives might appear in the program later as a result of loading actual source files;
* this list is only the set of defaults that are implicitly included.
*/
export function getAutomaticTypeDirectiveNames(options: CompilerOptions, rootFiles: string[], host: CompilerHost): string[] {
2016-05-23 23:51:39 +02:00
// Use explicit type list from tsconfig.json
if (options.types) {
return options.types;
}
2016-06-11 00:44:11 +02:00
// Walk the primary type lookup locations
let result: string[] = [];
if (host.directoryExists && host.getDirectories) {
2016-06-13 23:37:07 +02:00
const typeRoots = getEffectiveTypeRoots(options, host);
if (typeRoots) {
for (const root of typeRoots) {
if (host.directoryExists(root)) {
result = result.concat(host.getDirectories(root));
}
2016-06-11 00:44:11 +02:00
}
2016-05-23 23:51:39 +02:00
}
}
2016-06-11 00:44:11 +02:00
return result;
2016-05-23 23:51:39 +02:00
}
export function createProgram(rootNames: string[], options: CompilerOptions, host?: CompilerHost, oldProgram?: Program): Program {
2015-03-13 23:03:17 +01:00
let program: Program;
let files: SourceFile[] = [];
2015-11-18 23:10:53 +01:00
let commonSourceDirectory: string;
let diagnosticsProducingTypeChecker: TypeChecker;
let noDiagnosticsTypeChecker: TypeChecker;
2015-06-12 21:53:24 +02:00
let classifiableNames: Map<string>;
let resolvedTypeReferenceDirectives: Map<ResolvedTypeReferenceDirective> = {};
let fileProcessingDiagnostics = createDiagnosticCollection();
2015-05-01 03:14:53 +02:00
2016-06-21 22:42:02 +02:00
// The below settings are to track if a .js file should be add to the program if loaded via searching under node_modules.
// This works as imported modules are discovered recursively in a depth first manner, specifically:
// - For each root file, findSourceFile is called.
// - This calls processImportedModules for each module imported in the source file.
// - This calls resolveModuleNames, and then calls findSourceFile for each resolved module.
// As all these operations happen - and are nested - within the createProgram call, they close over the below variables.
// The current resolution depth is tracked by incrementing/decrementing as the depth first search progresses.
2016-06-27 05:48:22 +02:00
const maxNodeModulesJsDepth = typeof options.maxNodeModuleJsDepth === "number" ? options.maxNodeModuleJsDepth : 2;
2016-06-21 22:42:02 +02:00
let currentNodeModulesJsDepth = 0;
2016-06-27 05:48:22 +02:00
// If a module has some of its imports skipped due to being at the depth limit under node_modules, then track
// this, as it may be imported at a shallower depth later, and then it will need its skipped imports processed.
const modulesWithElidedImports: Map<boolean> = {};
// Track source files that are JavaScript files found by searching under node_modules, as these shouldn't be compiled.
const jsFilesFoundSearchingNodeModules: Map<boolean> = {};
2015-11-04 23:02:33 +01:00
const start = new Date().getTime();
host = host || createCompilerHost(options);
let skipDefaultLib = options.noLib;
const programDiagnostics = createDiagnosticCollection();
const currentDirectory = host.getCurrentDirectory();
const supportedExtensions = getSupportedExtensions(options);
// Map storing if there is emit blocking diagnostics for given input
2015-11-18 19:48:03 +01:00
const hasEmitBlockingDiagnostics = createFileMap<boolean>(getCanonicalFileName);
let resolveModuleNamesWorker: (moduleNames: string[], containingFile: string) => ResolvedModule[];
if (host.resolveModuleNames) {
resolveModuleNamesWorker = (moduleNames, containingFile) => host.resolveModuleNames(moduleNames, containingFile);
}
else {
const loader = (moduleName: string, containingFile: string) => resolveModuleName(moduleName, containingFile, options, host).resolvedModule;
resolveModuleNamesWorker = (moduleNames, containingFile) => loadWithLocalCache(moduleNames, containingFile, loader);
}
let resolveTypeReferenceDirectiveNamesWorker: (typeDirectiveNames: string[], containingFile: string) => ResolvedTypeReferenceDirective[];
if (host.resolveTypeReferenceDirectives) {
resolveTypeReferenceDirectiveNamesWorker = (typeDirectiveNames, containingFile) => host.resolveTypeReferenceDirectives(typeDirectiveNames, containingFile);
}
else {
const loader = (typesRef: string, containingFile: string) => resolveTypeReferenceDirective(typesRef, containingFile, options, host).resolvedTypeReferenceDirective;
resolveTypeReferenceDirectiveNamesWorker = (typeReferenceDirectiveNames, containingFile) => loadWithLocalCache(typeReferenceDirectiveNames, containingFile, loader);
}
2015-05-01 03:14:53 +02:00
2015-11-04 23:02:33 +01:00
const filesByName = createFileMap<SourceFile>();
// stores 'filename -> file association' ignoring case
// used to track cases when two file names differ only in casing
2015-11-04 23:02:33 +01:00
const filesByNameIgnoreCase = host.useCaseSensitiveFileNames() ? createFileMap<SourceFile>(fileName => fileName.toLowerCase()) : undefined;
if (!tryReuseStructureFromOldProgram()) {
forEach(rootNames, name => processRootFile(name, /*isDefaultLib*/ false));
2016-06-11 00:44:11 +02:00
// load type declarations specified via 'types' argument or implicitly from types/ and node_modules/@types folders
const typeReferences: string[] = getAutomaticTypeDirectiveNames(options, rootNames, host);
2016-05-18 00:41:31 +02:00
if (typeReferences) {
2016-06-11 00:44:11 +02:00
const inferredRoot = getInferredTypesRoot(options, rootNames, host);
const containingFilename = combinePaths(inferredRoot, "__inferred type names__.ts");
const resolutions = resolveTypeReferenceDirectiveNamesWorker(typeReferences, containingFilename);
2016-05-18 00:41:31 +02:00
for (let i = 0; i < typeReferences.length; i++) {
processTypeReferenceDirective(typeReferences[i], resolutions[i]);
}
2015-06-25 02:40:04 +02:00
}
// Do not process the default library if:
// - The '--noLib' flag is used.
// - A 'no-default-lib' reference comment is encountered in
// processing the root files.
if (!skipDefaultLib) {
// If '--lib' is not specified, include default library file according to '--target'
// otherwise, using options specified in '--lib' instead of '--target' default library file
if (!options.lib) {
processRootFile(host.getDefaultLibFileName(options), /*isDefaultLib*/ true);
}
else {
const libDirectory = host.getDefaultLibLocation ? host.getDefaultLibLocation() : getDirectoryPath(host.getDefaultLibFileName(options));
forEach(options.lib, libFileName => {
processRootFile(combinePaths(libDirectory, libFileName), /*isDefaultLib*/ true);
});
}
}
}
2015-05-01 03:14:53 +02:00
// unconditionally set oldProgram to undefined to prevent it from being captured in closure
oldProgram = undefined;
program = {
getRootFileNames: () => rootNames,
getSourceFile,
getSourceFileByPath,
getSourceFiles: () => files,
getCompilerOptions: () => options,
getSyntacticDiagnostics,
getOptionsDiagnostics,
getGlobalDiagnostics,
getSemanticDiagnostics,
getDeclarationDiagnostics,
getTypeChecker,
2015-06-12 21:53:24 +02:00
getClassifiableNames,
getDiagnosticsProducingTypeChecker,
getCommonSourceDirectory,
emit,
getCurrentDirectory: () => currentDirectory,
getNodeCount: () => getDiagnosticsProducingTypeChecker().getNodeCount(),
getIdentifierCount: () => getDiagnosticsProducingTypeChecker().getIdentifierCount(),
getSymbolCount: () => getDiagnosticsProducingTypeChecker().getSymbolCount(),
getTypeCount: () => getDiagnosticsProducingTypeChecker().getTypeCount(),
getFileProcessingDiagnostics: () => fileProcessingDiagnostics,
getResolvedTypeReferenceDirectives: () => resolvedTypeReferenceDirectives
};
verifyCompilerOptions();
programTime += new Date().getTime() - start;
2015-10-12 22:10:54 +02:00
return program;
function getCommonSourceDirectory() {
if (typeof commonSourceDirectory === "undefined") {
if (options.rootDir && checkSourceFilesBelongToPath(files, options.rootDir)) {
// If a rootDir is specified and is valid use it as the commonSourceDirectory
commonSourceDirectory = getNormalizedAbsolutePath(options.rootDir, currentDirectory);
}
else {
commonSourceDirectory = computeCommonSourceDirectory(files);
}
if (commonSourceDirectory && commonSourceDirectory[commonSourceDirectory.length - 1] !== directorySeparator) {
// Make sure directory path ends with directory separator so this string can directly
// used to replace with "" to get the relative path of the source file and the relative path doesn't
// start with / making it rooted path
commonSourceDirectory += directorySeparator;
}
}
return commonSourceDirectory;
}
2015-06-12 21:53:24 +02:00
function getClassifiableNames() {
if (!classifiableNames) {
// Initialize a checker so that all our files are bound.
getTypeChecker();
2015-06-12 21:53:24 +02:00
classifiableNames = {};
2015-11-04 23:02:33 +01:00
for (const sourceFile of files) {
2015-06-12 21:53:24 +02:00
copyMap(sourceFile.classifiableNames, classifiableNames);
}
}
2015-06-12 21:53:24 +02:00
return classifiableNames;
}
function tryReuseStructureFromOldProgram(): boolean {
if (!oldProgram) {
return false;
}
// check properties that can affect structure of the program or module resolution strategy
// if any of these properties has changed - structure cannot be reused
const oldOptions = oldProgram.getCompilerOptions();
if ((oldOptions.module !== options.module) ||
(oldOptions.moduleResolution !== options.moduleResolution) ||
(oldOptions.noResolve !== options.noResolve) ||
(oldOptions.target !== options.target) ||
(oldOptions.noLib !== options.noLib) ||
(oldOptions.jsx !== options.jsx) ||
(oldOptions.allowJs !== options.allowJs) ||
(oldOptions.rootDir !== options.rootDir) ||
(oldOptions.configFilePath !== options.configFilePath) ||
(oldOptions.baseUrl !== options.baseUrl) ||
2016-06-27 05:48:22 +02:00
(oldOptions.maxNodeModuleJsDepth !== options.maxNodeModuleJsDepth) ||
2016-06-13 23:37:07 +02:00
!arrayIsEqualTo(oldOptions.typeRoots, oldOptions.typeRoots) ||
!arrayIsEqualTo(oldOptions.rootDirs, options.rootDirs) ||
!mapIsEqualTo(oldOptions.paths, options.paths)) {
return false;
}
Debug.assert(!oldProgram.structureIsReused);
// there is an old program, check if we can reuse its structure
2015-11-04 23:02:33 +01:00
const oldRootNames = oldProgram.getRootFileNames();
2015-06-25 02:40:04 +02:00
if (!arrayIsEqualTo(oldRootNames, rootNames)) {
return false;
}
if (!arrayIsEqualTo(options.types, oldOptions.types)) {
return false;
}
// check if program source files has changed in the way that can affect structure of the program
2015-11-04 23:02:33 +01:00
const newSourceFiles: SourceFile[] = [];
const filePaths: Path[] = [];
const modifiedSourceFiles: SourceFile[] = [];
2015-11-04 23:02:33 +01:00
for (const oldSourceFile of oldProgram.getSourceFiles()) {
let newSourceFile = host.getSourceFileByPath
? host.getSourceFileByPath(oldSourceFile.fileName, oldSourceFile.path, options.target)
: host.getSourceFile(oldSourceFile.fileName, options.target);
2015-06-25 02:40:04 +02:00
if (!newSourceFile) {
return false;
}
newSourceFile.path = oldSourceFile.path;
filePaths.push(newSourceFile.path);
if (oldSourceFile !== newSourceFile) {
2015-07-09 23:40:33 +02:00
if (oldSourceFile.hasNoDefaultLib !== newSourceFile.hasNoDefaultLib) {
// value of no-default-lib has changed
// this will affect if default library is injected into the list of files
return false;
}
// check tripleslash references
if (!arrayIsEqualTo(oldSourceFile.referencedFiles, newSourceFile.referencedFiles, fileReferenceIsEqualTo)) {
// tripleslash references has changed
return false;
}
// check imports and module augmentations
2015-08-20 00:37:37 +02:00
collectExternalModuleReferences(newSourceFile);
if (!arrayIsEqualTo(oldSourceFile.imports, newSourceFile.imports, moduleNameIsEqualTo)) {
// imports has changed
return false;
}
if (!arrayIsEqualTo(oldSourceFile.moduleAugmentations, newSourceFile.moduleAugmentations, moduleNameIsEqualTo)) {
// moduleAugmentations has changed
return false;
}
if (!arrayIsEqualTo(oldSourceFile.typeReferenceDirectives, newSourceFile.typeReferenceDirectives, fileReferenceIsEqualTo)) {
// 'types' references has changed
return false;
}
const newSourceFilePath = getNormalizedAbsolutePath(newSourceFile.fileName, currentDirectory);
if (resolveModuleNamesWorker) {
2016-01-14 19:56:49 +01:00
const moduleNames = map(concatenate(newSourceFile.imports, newSourceFile.moduleAugmentations), getTextOfLiteral);
const resolutions = resolveModuleNamesWorker(moduleNames, newSourceFilePath);
// ensure that module resolution results are still correct
const resolutionsChanged = hasChangesInResolutions(moduleNames, resolutions, oldSourceFile.resolvedModules, moduleResolutionIsEqualTo);
if (resolutionsChanged) {
return false;
}
}
if (resolveTypeReferenceDirectiveNamesWorker) {
const typesReferenceDirectives = map(newSourceFile.typeReferenceDirectives, x => x.fileName);
const resolutions = resolveTypeReferenceDirectiveNamesWorker(typesReferenceDirectives, newSourceFilePath);
// ensure that types resolutions are still correct
const resolutionsChanged = hasChangesInResolutions(typesReferenceDirectives, resolutions, oldSourceFile.resolvedTypeReferenceDirectiveNames, typeDirectiveIsEqualTo);
if (resolutionsChanged) {
return false;
}
}
// pass the cache of module/types resolutions from the old source file
newSourceFile.resolvedModules = oldSourceFile.resolvedModules;
newSourceFile.resolvedTypeReferenceDirectiveNames = oldSourceFile.resolvedTypeReferenceDirectiveNames;
modifiedSourceFiles.push(newSourceFile);
}
else {
// file has no changes - use it as is
newSourceFile = oldSourceFile;
}
// if file has passed all checks it should be safe to reuse it
newSourceFiles.push(newSourceFile);
}
// update fileName -> file mapping
2015-12-23 00:45:00 +01:00
for (let i = 0, len = newSourceFiles.length; i < len; i++) {
filesByName.set(filePaths[i], newSourceFiles[i]);
}
files = newSourceFiles;
fileProcessingDiagnostics = oldProgram.getFileProcessingDiagnostics();
2015-11-04 23:02:33 +01:00
for (const modifiedFile of modifiedSourceFiles) {
fileProcessingDiagnostics.reattachFileDiagnostics(modifiedFile);
}
resolvedTypeReferenceDirectives = oldProgram.getResolvedTypeReferenceDirectives();
oldProgram.structureIsReused = true;
return true;
}
function getEmitHost(writeFileCallback?: WriteFileCallback): EmitHost {
return {
getCanonicalFileName,
getCommonSourceDirectory: program.getCommonSourceDirectory,
getCompilerOptions: program.getCompilerOptions,
getCurrentDirectory: () => currentDirectory,
getNewLine: () => host.getNewLine(),
getSourceFile: program.getSourceFile,
getSourceFileByPath: program.getSourceFileByPath,
getSourceFiles: program.getSourceFiles,
getFilesFromNodeModules: () => jsFilesFoundSearchingNodeModules,
writeFile: writeFileCallback || (
(fileName, data, writeByteOrderMark, onError, sourceFiles) => host.writeFile(fileName, data, writeByteOrderMark, onError, sourceFiles)),
isEmitBlocked,
};
}
function getDiagnosticsProducingTypeChecker() {
return diagnosticsProducingTypeChecker || (diagnosticsProducingTypeChecker = createTypeChecker(program, /*produceDiagnostics:*/ true));
}
function getTypeChecker() {
return noDiagnosticsTypeChecker || (noDiagnosticsTypeChecker = createTypeChecker(program, /*produceDiagnostics:*/ false));
}
2015-06-18 21:04:26 +02:00
function emit(sourceFile?: SourceFile, writeFileCallback?: WriteFileCallback, cancellationToken?: CancellationToken): EmitResult {
2015-06-18 19:52:19 +02:00
return runWithCancellationToken(() => emitWorker(this, sourceFile, writeFileCallback, cancellationToken));
}
function isEmitBlocked(emitFileName: string): boolean {
return hasEmitBlockingDiagnostics.contains(toPath(emitFileName, currentDirectory, getCanonicalFileName));
}
2015-06-18 21:04:26 +02:00
function emitWorker(program: Program, sourceFile: SourceFile, writeFileCallback: WriteFileCallback, cancellationToken: CancellationToken): EmitResult {
2016-02-17 07:01:28 +01:00
let declarationDiagnostics: Diagnostic[] = [];
if (options.noEmit) {
2016-04-08 00:29:11 +02:00
return { diagnostics: declarationDiagnostics, sourceMaps: undefined, emittedFiles: undefined, emitSkipped: true };
2016-02-17 07:01:28 +01:00
}
// If the noEmitOnError flag is set, then check if we have any errors so far. If so,
2015-07-09 00:35:49 +02:00
// immediately bail out. Note that we pass 'undefined' for 'sourceFile' so that we
// get any preEmit diagnostics, not just the ones
if (options.noEmitOnError) {
2016-02-17 07:57:27 +01:00
const diagnostics = program.getOptionsDiagnostics(cancellationToken).concat(
2016-02-17 07:01:28 +01:00
program.getSyntacticDiagnostics(sourceFile, cancellationToken),
program.getGlobalDiagnostics(cancellationToken),
program.getSemanticDiagnostics(sourceFile, cancellationToken));
if (diagnostics.length === 0 && program.getCompilerOptions().declaration) {
declarationDiagnostics = program.getDeclarationDiagnostics(/*sourceFile*/ undefined, cancellationToken);
}
if (diagnostics.length > 0 || declarationDiagnostics.length > 0) {
return {
diagnostics: concatenate(diagnostics, declarationDiagnostics),
sourceMaps: undefined,
2016-04-08 00:29:11 +02:00
emittedFiles: undefined,
emitSkipped: true
};
}
}
// Create the emit resolver outside of the "emitTime" tracking code below. That way
// any cost associated with it (like type checking) are appropriate associated with
// the type-checking counter.
//
// If the -out option is specified, we should not pass the source file to getEmitResolver.
// This is because in the -out scenario all files need to be emitted, and therefore all
// files need to be type checked. And the way to specify that all files need to be type
// checked is to not pass the file to getEmitResolver.
2015-11-04 23:02:33 +01:00
const emitResolver = getDiagnosticsProducingTypeChecker().getEmitResolver((options.outFile || options.out) ? undefined : sourceFile);
2015-11-04 23:02:33 +01:00
const start = new Date().getTime();
2015-11-04 23:02:33 +01:00
const emitResult = emitFiles(
emitResolver,
getEmitHost(writeFileCallback),
sourceFile);
emitTime += new Date().getTime() - start;
return emitResult;
}
function getSourceFile(fileName: string): SourceFile {
return getSourceFileByPath(toPath(fileName, currentDirectory, getCanonicalFileName));
}
function getSourceFileByPath(path: Path): SourceFile {
return filesByName.get(path);
}
2015-06-18 19:52:19 +02:00
function getDiagnosticsHelper(
2016-02-23 21:48:31 +01:00
sourceFile: SourceFile,
getDiagnostics: (sourceFile: SourceFile, cancellationToken: CancellationToken) => Diagnostic[],
cancellationToken: CancellationToken): Diagnostic[] {
if (sourceFile) {
2015-06-18 19:52:19 +02:00
return getDiagnostics(sourceFile, cancellationToken);
}
2015-11-04 23:02:33 +01:00
const allDiagnostics: Diagnostic[] = [];
forEach(program.getSourceFiles(), sourceFile => {
2015-06-18 19:52:19 +02:00
if (cancellationToken) {
cancellationToken.throwIfCancellationRequested();
}
addRange(allDiagnostics, getDiagnostics(sourceFile, cancellationToken));
});
return sortAndDeduplicateDiagnostics(allDiagnostics);
}
2015-06-18 21:04:26 +02:00
function getSyntacticDiagnostics(sourceFile: SourceFile, cancellationToken: CancellationToken): Diagnostic[] {
2015-06-18 19:52:19 +02:00
return getDiagnosticsHelper(sourceFile, getSyntacticDiagnosticsForFile, cancellationToken);
}
2015-06-18 21:04:26 +02:00
function getSemanticDiagnostics(sourceFile: SourceFile, cancellationToken: CancellationToken): Diagnostic[] {
2015-06-18 19:52:19 +02:00
return getDiagnosticsHelper(sourceFile, getSemanticDiagnosticsForFile, cancellationToken);
}
2015-06-18 21:04:26 +02:00
function getDeclarationDiagnostics(sourceFile: SourceFile, cancellationToken: CancellationToken): Diagnostic[] {
const options = program.getCompilerOptions();
2016-02-24 23:30:21 +01:00
// collect diagnostics from the program only once if either no source file was specified or out/outFile is set (bundled emit)
if (!sourceFile || options.out || options.outFile) {
return getDeclarationDiagnosticsWorker(sourceFile, cancellationToken);
}
else {
return getDiagnosticsHelper(sourceFile, getDeclarationDiagnosticsForFile, cancellationToken);
}
}
2015-06-18 21:04:26 +02:00
function getSyntacticDiagnosticsForFile(sourceFile: SourceFile, cancellationToken: CancellationToken): Diagnostic[] {
return sourceFile.parseDiagnostics;
}
2015-06-18 19:52:19 +02:00
function runWithCancellationToken<T>(func: () => T): T {
try {
return func();
}
catch (e) {
if (e instanceof OperationCanceledException) {
2015-07-09 00:35:49 +02:00
// We were canceled while performing the operation. Because our type checker
2015-06-18 19:52:19 +02:00
// might be a bad state, we need to throw it away.
2015-07-07 00:31:22 +02:00
//
// Note: we are overly aggressive here. We do not actually *have* to throw away
2015-07-07 00:31:22 +02:00
// the "noDiagnosticsTypeChecker". However, for simplicity, i'd like to keep
// the lifetimes of these two TypeCheckers the same. Also, we generally only
// cancel when the user has made a change anyways. And, in that case, we (the
2015-07-09 00:35:49 +02:00
// program instance) will get thrown away anyways. So trying to keep one of
2015-07-07 00:31:22 +02:00
// these type checkers alive doesn't serve much purpose.
2015-06-18 19:52:19 +02:00
noDiagnosticsTypeChecker = undefined;
diagnosticsProducingTypeChecker = undefined;
}
2015-06-18 19:52:19 +02:00
throw e;
}
}
2015-06-18 21:04:26 +02:00
function getSemanticDiagnosticsForFile(sourceFile: SourceFile, cancellationToken: CancellationToken): Diagnostic[] {
2015-06-18 19:52:19 +02:00
return runWithCancellationToken(() => {
2015-11-04 23:02:33 +01:00
const typeChecker = getDiagnosticsProducingTypeChecker();
2015-06-18 19:52:19 +02:00
Debug.assert(!!sourceFile.bindDiagnostics);
2015-11-04 23:02:33 +01:00
const bindDiagnostics = sourceFile.bindDiagnostics;
// For JavaScript files, we don't want to report the normal typescript semantic errors.
// Instead, we just report errors for using TypeScript-only constructs from within a
// JavaScript file.
const checkDiagnostics = isSourceFileJavaScript(sourceFile) ?
getJavaScriptSemanticDiagnosticsForFile(sourceFile, cancellationToken) :
typeChecker.getDiagnostics(sourceFile, cancellationToken);
2015-11-04 23:02:33 +01:00
const fileProcessingDiagnosticsInFile = fileProcessingDiagnostics.getDiagnostics(sourceFile.fileName);
const programDiagnosticsInFile = programDiagnostics.getDiagnostics(sourceFile.fileName);
2015-06-18 19:52:19 +02:00
return bindDiagnostics.concat(checkDiagnostics).concat(fileProcessingDiagnosticsInFile).concat(programDiagnosticsInFile);
2015-06-18 19:52:19 +02:00
});
}
function getJavaScriptSemanticDiagnosticsForFile(sourceFile: SourceFile, cancellationToken: CancellationToken): Diagnostic[] {
return runWithCancellationToken(() => {
const diagnostics: Diagnostic[] = [];
walk(sourceFile);
return diagnostics;
function walk(node: Node): boolean {
if (!node) {
return false;
}
switch (node.kind) {
case SyntaxKind.ImportEqualsDeclaration:
diagnostics.push(createDiagnosticForNode(node, Diagnostics.import_can_only_be_used_in_a_ts_file));
return true;
case SyntaxKind.ExportAssignment:
2016-02-01 22:28:58 +01:00
if ((<ExportAssignment>node).isExportEquals) {
diagnostics.push(createDiagnosticForNode(node, Diagnostics.export_can_only_be_used_in_a_ts_file));
return true;
}
break;
case SyntaxKind.ClassDeclaration:
let classDeclaration = <ClassDeclaration>node;
if (checkModifiers(classDeclaration.modifiers) ||
checkTypeParameters(classDeclaration.typeParameters)) {
return true;
}
break;
case SyntaxKind.HeritageClause:
let heritageClause = <HeritageClause>node;
if (heritageClause.token === SyntaxKind.ImplementsKeyword) {
diagnostics.push(createDiagnosticForNode(node, Diagnostics.implements_clauses_can_only_be_used_in_a_ts_file));
return true;
}
break;
case SyntaxKind.InterfaceDeclaration:
diagnostics.push(createDiagnosticForNode(node, Diagnostics.interface_declarations_can_only_be_used_in_a_ts_file));
return true;
case SyntaxKind.ModuleDeclaration:
diagnostics.push(createDiagnosticForNode(node, Diagnostics.module_declarations_can_only_be_used_in_a_ts_file));
return true;
case SyntaxKind.TypeAliasDeclaration:
diagnostics.push(createDiagnosticForNode(node, Diagnostics.type_aliases_can_only_be_used_in_a_ts_file));
return true;
case SyntaxKind.MethodDeclaration:
case SyntaxKind.MethodSignature:
case SyntaxKind.Constructor:
case SyntaxKind.GetAccessor:
case SyntaxKind.SetAccessor:
case SyntaxKind.FunctionExpression:
case SyntaxKind.FunctionDeclaration:
case SyntaxKind.ArrowFunction:
case SyntaxKind.FunctionDeclaration:
const functionDeclaration = <FunctionLikeDeclaration>node;
if (checkModifiers(functionDeclaration.modifiers) ||
checkTypeParameters(functionDeclaration.typeParameters) ||
checkTypeAnnotation(functionDeclaration.type)) {
return true;
}
break;
case SyntaxKind.VariableStatement:
const variableStatement = <VariableStatement>node;
if (checkModifiers(variableStatement.modifiers)) {
return true;
}
break;
case SyntaxKind.VariableDeclaration:
const variableDeclaration = <VariableDeclaration>node;
if (checkTypeAnnotation(variableDeclaration.type)) {
return true;
}
break;
case SyntaxKind.CallExpression:
case SyntaxKind.NewExpression:
const expression = <CallExpression>node;
if (expression.typeArguments && expression.typeArguments.length > 0) {
const start = expression.typeArguments.pos;
diagnostics.push(createFileDiagnostic(sourceFile, start, expression.typeArguments.end - start,
Diagnostics.type_arguments_can_only_be_used_in_a_ts_file));
return true;
}
break;
case SyntaxKind.Parameter:
const parameter = <ParameterDeclaration>node;
if (parameter.modifiers) {
const start = parameter.modifiers.pos;
diagnostics.push(createFileDiagnostic(sourceFile, start, parameter.modifiers.end - start,
Diagnostics.parameter_modifiers_can_only_be_used_in_a_ts_file));
return true;
}
if (parameter.questionToken) {
2015-10-06 01:42:37 +02:00
diagnostics.push(createDiagnosticForNode(parameter.questionToken, Diagnostics._0_can_only_be_used_in_a_ts_file, "?"));
return true;
}
if (parameter.type) {
diagnostics.push(createDiagnosticForNode(parameter.type, Diagnostics.types_can_only_be_used_in_a_ts_file));
return true;
}
break;
case SyntaxKind.PropertyDeclaration:
const propertyDeclaration = <PropertyDeclaration>node;
if (propertyDeclaration.modifiers) {
for (const modifier of propertyDeclaration.modifiers) {
if (modifier.kind !== SyntaxKind.StaticKeyword) {
diagnostics.push(createDiagnosticForNode(modifier, Diagnostics._0_can_only_be_used_in_a_ts_file, tokenToString(modifier.kind)));
return true;
}
}
}
if (checkTypeAnnotation((<PropertyDeclaration>node).type)) {
return true;
}
break;
case SyntaxKind.EnumDeclaration:
diagnostics.push(createDiagnosticForNode(node, Diagnostics.enum_declarations_can_only_be_used_in_a_ts_file));
return true;
case SyntaxKind.TypeAssertionExpression:
let typeAssertionExpression = <TypeAssertion>node;
diagnostics.push(createDiagnosticForNode(typeAssertionExpression.type, Diagnostics.type_assertion_expressions_can_only_be_used_in_a_ts_file));
return true;
case SyntaxKind.Decorator:
if (!options.experimentalDecorators) {
2016-02-13 17:02:16 +01:00
diagnostics.push(createDiagnosticForNode(node, Diagnostics.Experimental_support_for_decorators_is_a_feature_that_is_subject_to_change_in_a_future_release_Set_the_experimentalDecorators_option_to_remove_this_warning));
}
return true;
}
return forEachChild(node, walk);
}
function checkTypeParameters(typeParameters: NodeArray<TypeParameterDeclaration>): boolean {
if (typeParameters) {
const start = typeParameters.pos;
diagnostics.push(createFileDiagnostic(sourceFile, start, typeParameters.end - start, Diagnostics.type_parameter_declarations_can_only_be_used_in_a_ts_file));
return true;
}
return false;
}
function checkTypeAnnotation(type: TypeNode): boolean {
if (type) {
diagnostics.push(createDiagnosticForNode(type, Diagnostics.types_can_only_be_used_in_a_ts_file));
return true;
}
return false;
}
function checkModifiers(modifiers: ModifiersArray): boolean {
if (modifiers) {
for (const modifier of modifiers) {
switch (modifier.kind) {
case SyntaxKind.PublicKeyword:
case SyntaxKind.PrivateKeyword:
case SyntaxKind.ProtectedKeyword:
case SyntaxKind.ReadonlyKeyword:
case SyntaxKind.DeclareKeyword:
diagnostics.push(createDiagnosticForNode(modifier, Diagnostics._0_can_only_be_used_in_a_ts_file, tokenToString(modifier.kind)));
return true;
// These are all legal modifiers.
case SyntaxKind.StaticKeyword:
case SyntaxKind.ExportKeyword:
case SyntaxKind.ConstKeyword:
case SyntaxKind.DefaultKeyword:
case SyntaxKind.AbstractKeyword:
}
}
}
return false;
}
});
}
function getDeclarationDiagnosticsWorker(sourceFile: SourceFile, cancellationToken: CancellationToken): Diagnostic[] {
2015-06-18 19:52:19 +02:00
return runWithCancellationToken(() => {
const resolver = getDiagnosticsProducingTypeChecker().getEmitResolver(sourceFile, cancellationToken);
// Don't actually write any files since we're just getting diagnostics.
const writeFile: WriteFileCallback = () => { };
return ts.getDeclarationDiagnostics(getEmitHost(writeFile), resolver, sourceFile);
2015-06-18 19:52:19 +02:00
});
}
function getDeclarationDiagnosticsForFile(sourceFile: SourceFile, cancellationToken: CancellationToken): Diagnostic[] {
return isDeclarationFile(sourceFile) ? [] : getDeclarationDiagnosticsWorker(sourceFile, cancellationToken);
}
function getOptionsDiagnostics(): Diagnostic[] {
2015-11-04 23:02:33 +01:00
const allDiagnostics: Diagnostic[] = [];
addRange(allDiagnostics, fileProcessingDiagnostics.getGlobalDiagnostics());
addRange(allDiagnostics, programDiagnostics.getGlobalDiagnostics());
return sortAndDeduplicateDiagnostics(allDiagnostics);
}
function getGlobalDiagnostics(): Diagnostic[] {
2015-11-04 23:02:33 +01:00
const allDiagnostics: Diagnostic[] = [];
addRange(allDiagnostics, getDiagnosticsProducingTypeChecker().getGlobalDiagnostics());
return sortAndDeduplicateDiagnostics(allDiagnostics);
}
function hasExtension(fileName: string): boolean {
return getBaseFileName(fileName).indexOf(".") >= 0;
}
function processRootFile(fileName: string, isDefaultLib: boolean) {
2016-03-02 19:52:16 +01:00
processSourceFile(normalizePath(fileName), isDefaultLib, /*isReference*/ true);
}
function fileReferenceIsEqualTo(a: FileReference, b: FileReference): boolean {
return a.fileName === b.fileName;
}
function moduleNameIsEqualTo(a: LiteralExpression, b: LiteralExpression): boolean {
2015-06-25 02:40:04 +02:00
return a.text === b.text;
}
2016-01-14 19:56:49 +01:00
function getTextOfLiteral(literal: LiteralExpression): string {
return literal.text;
}
function collectExternalModuleReferences(file: SourceFile): void {
if (file.imports) {
return;
}
2015-11-09 21:49:36 +01:00
const isJavaScriptFile = isSourceFileJavaScript(file);
const isExternalModuleFile = isExternalModule(file);
let imports: LiteralExpression[];
let moduleAugmentations: LiteralExpression[];
2015-11-04 23:02:33 +01:00
for (const node of file.statements) {
collectModuleReferences(node, /*inAmbientModule*/ false);
if (isJavaScriptFile) {
collectRequireCalls(node);
}
}
file.imports = imports || emptyArray;
file.moduleAugmentations = moduleAugmentations || emptyArray;
return;
function collectModuleReferences(node: Node, inAmbientModule: boolean): void {
switch (node.kind) {
case SyntaxKind.ImportDeclaration:
case SyntaxKind.ImportEqualsDeclaration:
case SyntaxKind.ExportDeclaration:
let moduleNameExpr = getExternalModuleName(node);
if (!moduleNameExpr || moduleNameExpr.kind !== SyntaxKind.StringLiteral) {
break;
}
if (!(<LiteralExpression>moduleNameExpr).text) {
break;
}
// TypeScript 1.0 spec (April 2014): 12.1.6
// An ExternalImportDeclaration in an AmbientExternalModuleDeclaration may reference other external modules
// only through top - level external module names. Relative external module names are not permitted.
if (!inAmbientModule || !isExternalModuleNameRelative((<LiteralExpression>moduleNameExpr).text)) {
(imports || (imports = [])).push(<LiteralExpression>moduleNameExpr);
}
break;
case SyntaxKind.ModuleDeclaration:
if (isAmbientModule(<ModuleDeclaration>node) && (inAmbientModule || node.flags & NodeFlags.Ambient || isDeclarationFile(file))) {
const moduleName = <LiteralExpression>(<ModuleDeclaration>node).name;
// Ambient module declarations can be interpreted as augmentations for some existing external modules.
// This will happen in two cases:
// - if current file is external module then module augmentation is a ambient module declaration defined in the top level scope
// - if current file is not external module then module augmentation is an ambient module declaration with non-relative module name
// immediately nested in top level ambient module declaration .
if (isExternalModuleFile || (inAmbientModule && !isExternalModuleNameRelative(moduleName.text))) {
(moduleAugmentations || (moduleAugmentations = [])).push(moduleName);
}
else if (!inAmbientModule) {
// An AmbientExternalModuleDeclaration declares an external module.
// This type of declaration is permitted only in the global module.
// The StringLiteral must specify a top - level external module name.
// Relative external module names are not permitted
// NOTE: body of ambient module is always a module block, if it exists
const body = <ModuleBlock>(<ModuleDeclaration>node).body;
if (body) {
for (const statement of body.statements) {
collectModuleReferences(statement, /*inAmbientModule*/ true);
}
}
}
}
}
}
function collectRequireCalls(node: Node): void {
if (isRequireCall(node, /*checkArgumentIsStringLiteral*/true)) {
(imports || (imports = [])).push(<StringLiteral>(<CallExpression>node).arguments[0]);
}
else {
forEachChild(node, collectRequireCalls);
}
}
}
2016-03-02 19:52:16 +01:00
/**
* 'isReference' indicates whether the file was brought in via a reference directive (rather than an import declaration)
*/
function processSourceFile(fileName: string, isDefaultLib: boolean, isReference: boolean, refFile?: SourceFile, refPos?: number, refEnd?: number) {
2015-05-05 08:30:43 +02:00
let diagnosticArgument: string[];
2015-03-13 23:03:17 +01:00
let diagnostic: DiagnosticMessage;
if (hasExtension(fileName)) {
2015-03-27 00:48:17 +01:00
if (!options.allowNonTsExtensions && !forEach(supportedExtensions, extension => fileExtensionIs(host.getCanonicalFileName(fileName), extension))) {
2015-05-05 08:30:43 +02:00
diagnostic = Diagnostics.File_0_has_unsupported_extension_The_only_supported_extensions_are_1;
diagnosticArgument = [fileName, "'" + supportedExtensions.join("', '") + "'"];
}
2016-03-02 19:52:16 +01:00
else if (!findSourceFile(fileName, toPath(fileName, currentDirectory, getCanonicalFileName), isDefaultLib, isReference, refFile, refPos, refEnd)) {
diagnostic = Diagnostics.File_0_not_found;
2015-05-05 08:30:43 +02:00
diagnosticArgument = [fileName];
}
else if (refFile && host.getCanonicalFileName(fileName) === host.getCanonicalFileName(refFile.fileName)) {
diagnostic = Diagnostics.A_file_cannot_have_a_reference_to_itself;
2015-05-05 08:30:43 +02:00
diagnosticArgument = [fileName];
}
}
else {
2016-03-02 19:52:16 +01:00
const nonTsFile: SourceFile = options.allowNonTsExtensions && findSourceFile(fileName, toPath(fileName, currentDirectory, getCanonicalFileName), isDefaultLib, isReference, refFile, refPos, refEnd);
if (!nonTsFile) {
if (options.allowNonTsExtensions) {
diagnostic = Diagnostics.File_0_not_found;
diagnosticArgument = [fileName];
}
2016-03-02 19:52:16 +01:00
else if (!forEach(supportedExtensions, extension => findSourceFile(fileName + extension, toPath(fileName + extension, currentDirectory, getCanonicalFileName), isDefaultLib, isReference, refFile, refPos, refEnd))) {
diagnostic = Diagnostics.File_0_not_found;
fileName += ".ts";
diagnosticArgument = [fileName];
}
}
}
if (diagnostic) {
if (refFile !== undefined && refEnd !== undefined && refPos !== undefined) {
fileProcessingDiagnostics.add(createFileDiagnostic(refFile, refPos, refEnd - refPos, diagnostic, ...diagnosticArgument));
}
else {
fileProcessingDiagnostics.add(createCompilerDiagnostic(diagnostic, ...diagnosticArgument));
}
}
}
function reportFileNamesDifferOnlyInCasingError(fileName: string, existingFileName: string, refFile: SourceFile, refPos: number, refEnd: number): void {
if (refFile !== undefined && refPos !== undefined && refEnd !== undefined) {
fileProcessingDiagnostics.add(createFileDiagnostic(refFile, refPos, refEnd - refPos,
Diagnostics.File_name_0_differs_from_already_included_file_name_1_only_in_casing, fileName, existingFileName));
}
else {
fileProcessingDiagnostics.add(createCompilerDiagnostic(Diagnostics.File_name_0_differs_from_already_included_file_name_1_only_in_casing, fileName, existingFileName));
}
}
// Get source file from normalized fileName
2016-03-02 19:52:16 +01:00
function findSourceFile(fileName: string, path: Path, isDefaultLib: boolean, isReference: boolean, refFile?: SourceFile, refPos?: number, refEnd?: number): SourceFile {
if (filesByName.contains(path)) {
const file = filesByName.get(path);
// try to check if we've already seen this file but with a different casing in path
// NOTE: this only makes sense for case-insensitive file systems
if (file && options.forceConsistentCasingInFileNames && getNormalizedAbsolutePath(file.fileName, currentDirectory) !== getNormalizedAbsolutePath(fileName, currentDirectory)) {
reportFileNamesDifferOnlyInCasingError(fileName, file.fileName, refFile, refPos, refEnd);
}
2016-06-27 05:48:22 +02:00
// See if we need to reprocess the imports due to prior skipped imports
if (file && lookUp(modulesWithElidedImports, file.path)) {
2016-06-27 08:33:46 +02:00
if (currentNodeModulesJsDepth < maxNodeModulesJsDepth) {
2016-06-27 05:48:22 +02:00
modulesWithElidedImports[file.path] = false;
processImportedModules(file, getDirectoryPath(fileName));
}
}
return file;
}
// We haven't looked for this file, do so now and cache result
2015-11-04 23:02:33 +01:00
const file = host.getSourceFile(fileName, options.target, hostErrorMessage => {
if (refFile !== undefined && refPos !== undefined && refEnd !== undefined) {
fileProcessingDiagnostics.add(createFileDiagnostic(refFile, refPos, refEnd - refPos,
Diagnostics.Cannot_read_file_0_Colon_1, fileName, hostErrorMessage));
}
else {
fileProcessingDiagnostics.add(createCompilerDiagnostic(Diagnostics.Cannot_read_file_0_Colon_1, fileName, hostErrorMessage));
}
});
filesByName.set(path, file);
if (file) {
file.path = path;
if (host.useCaseSensitiveFileNames()) {
// for case-sensitive file systems check if we've already seen some file with similar filename ignoring case
const existingFile = filesByNameIgnoreCase.get(path);
if (existingFile) {
reportFileNamesDifferOnlyInCasingError(fileName, existingFile.fileName, refFile, refPos, refEnd);
}
else {
filesByNameIgnoreCase.set(path, file);
}
}
skipDefaultLib = skipDefaultLib || file.hasNoDefaultLib;
2015-11-04 23:02:33 +01:00
const basePath = getDirectoryPath(fileName);
if (!options.noResolve) {
processReferencedFiles(file, basePath, isDefaultLib);
processTypeReferenceDirectives(file);
}
// always process imported modules to record module name resolutions
processImportedModules(file, basePath);
if (isDefaultLib) {
files.unshift(file);
}
else {
files.push(file);
}
}
return file;
}
function processReferencedFiles(file: SourceFile, basePath: string, isDefaultLib: boolean) {
forEach(file.referencedFiles, ref => {
2015-11-04 23:02:33 +01:00
const referencedFileName = resolveTripleslashReference(ref.fileName, file.fileName);
processSourceFile(referencedFileName, isDefaultLib, /*isReference*/ true, file, ref.pos, ref.end);
});
}
function processTypeReferenceDirectives(file: SourceFile) {
const typeDirectives = map(file.typeReferenceDirectives, l => l.fileName);
const resolutions = resolveTypeReferenceDirectiveNamesWorker(typeDirectives, file.fileName);
2016-02-23 21:48:31 +01:00
for (let i = 0; i < typeDirectives.length; i++) {
const ref = file.typeReferenceDirectives[i];
const resolvedTypeReferenceDirective = resolutions[i];
// store resolved type directive on the file
setResolvedTypeReferenceDirective(file, ref.fileName, resolvedTypeReferenceDirective);
processTypeReferenceDirective(ref.fileName, resolvedTypeReferenceDirective, file, ref.pos, ref.end);
}
}
function processTypeReferenceDirective(typeReferenceDirective: string, resolvedTypeReferenceDirective: ResolvedTypeReferenceDirective,
refFile?: SourceFile, refPos?: number, refEnd?: number): void {
// If we already found this library as a primary reference - nothing to do
const previousResolution = resolvedTypeReferenceDirectives[typeReferenceDirective];
if (previousResolution && previousResolution.primary) {
return;
}
let saveResolution = true;
if (resolvedTypeReferenceDirective) {
if (resolvedTypeReferenceDirective.primary) {
// resolved from the primary path
processSourceFile(resolvedTypeReferenceDirective.resolvedFileName, /*isDefaultLib*/ false, /*isReference*/ true, refFile, refPos, refEnd);
2016-02-23 21:48:31 +01:00
}
else {
// If we already resolved to this file, it must have been a secondary reference. Check file contents
// for sameness and possibly issue an error
if (previousResolution) {
const otherFileText = host.readFile(resolvedTypeReferenceDirective.resolvedFileName);
if (otherFileText !== getSourceFile(previousResolution.resolvedFileName).text) {
fileProcessingDiagnostics.add(createDiagnostic(refFile, refPos, refEnd,
Diagnostics.Conflicting_library_definitions_for_0_found_at_1_and_2_Copy_the_correct_file_to_the_typings_folder_to_resolve_this_conflict,
typeReferenceDirective,
resolvedTypeReferenceDirective.resolvedFileName,
previousResolution.resolvedFileName
));
}
// don't overwrite previous resolution result
saveResolution = false;
2016-02-23 21:48:31 +01:00
}
else {
// First resolution of this library
processSourceFile(resolvedTypeReferenceDirective.resolvedFileName, /*isDefaultLib*/ false, /*isReference*/ true, refFile, refPos, refEnd);
2016-02-23 21:48:31 +01:00
}
}
}
else {
2016-06-11 00:44:11 +02:00
fileProcessingDiagnostics.add(createDiagnostic(refFile, refPos, refEnd, Diagnostics.Cannot_find_type_definition_file_for_0, typeReferenceDirective));
}
2016-02-23 21:48:31 +01:00
if (saveResolution) {
resolvedTypeReferenceDirectives[typeReferenceDirective] = resolvedTypeReferenceDirective;
}
}
function createDiagnostic(refFile: SourceFile, refPos: number, refEnd: number, message: DiagnosticMessage, ...args: any[]): Diagnostic {
if (refFile === undefined || refPos === undefined || refEnd === undefined) {
return createCompilerDiagnostic(message, ...args);
}
else {
return createFileDiagnostic(refFile, refPos, refEnd - refPos, message, ...args);
}
2016-02-23 21:48:31 +01:00
}
function getCanonicalFileName(fileName: string): string {
return host.getCanonicalFileName(fileName);
}
2015-08-20 00:37:37 +02:00
function processImportedModules(file: SourceFile, basePath: string) {
collectExternalModuleReferences(file);
if (file.imports.length || file.moduleAugmentations.length) {
2015-06-25 03:12:02 +02:00
file.resolvedModules = {};
2016-01-14 19:56:49 +01:00
const moduleNames = map(concatenate(file.imports, file.moduleAugmentations), getTextOfLiteral);
2015-11-04 23:02:33 +01:00
const resolutions = resolveModuleNamesWorker(moduleNames, getNormalizedAbsolutePath(file.fileName, currentDirectory));
2016-01-12 07:34:38 +01:00
for (let i = 0; i < moduleNames.length; i++) {
2015-11-04 23:02:33 +01:00
const resolution = resolutions[i];
setResolvedModule(file, moduleNames[i], resolution);
2016-06-27 05:48:22 +02:00
const resolvedPath = resolution ? toPath(resolution.resolvedFileName, currentDirectory, getCanonicalFileName) : undefined;
// add file to program only if:
// - resolution was successful
// - noResolve is falsy
2016-05-10 07:05:57 +02:00
// - module name comes from the list of imports
2016-02-13 16:48:25 +01:00
// - it's not a top level JavaScript module that exceeded the search max
2016-06-21 23:00:42 +02:00
const isJsFileUnderNodeModules = resolution && resolution.isExternalLibraryImport &&
2016-06-21 22:42:02 +02:00
hasJavaScriptFileExtension(resolution.resolvedFileName);
2016-06-27 05:48:22 +02:00
2016-06-21 22:42:02 +02:00
if (isJsFileUnderNodeModules) {
2016-06-27 05:48:22 +02:00
jsFilesFoundSearchingNodeModules[resolvedPath] = true;
2016-06-21 22:42:02 +02:00
currentNodeModulesJsDepth++;
}
2016-06-27 05:48:22 +02:00
const elideImport = isJsFileUnderNodeModules && currentNodeModulesJsDepth > maxNodeModulesJsDepth;
const shouldAddFile = resolution && !options.noResolve && i < file.imports.length && !elideImport;
if (elideImport) {
modulesWithElidedImports[file.path] = true;
}
else if (shouldAddFile) {
2016-06-21 22:42:02 +02:00
findSourceFile(resolution.resolvedFileName,
2016-06-27 05:48:22 +02:00
resolvedPath,
2016-06-21 22:42:02 +02:00
/*isDefaultLib*/ false, /*isReference*/ false,
file,
skipTrivia(file.text, file.imports[i].pos),
file.imports[i].end);
}
2016-06-21 22:42:02 +02:00
if (isJsFileUnderNodeModules) {
currentNodeModulesJsDepth--;
}
}
}
else {
// no imports - drop cached module resolutions
file.resolvedModules = undefined;
}
return;
}
2016-02-23 21:48:31 +01:00
function computeCommonSourceDirectory(sourceFiles: SourceFile[]): string {
const fileNames: string[] = [];
for (const file of sourceFiles) {
if (!file.isDeclarationFile) {
fileNames.push(file.fileName);
}
}
return computeCommonSourceDirectoryOfFilenames(fileNames, currentDirectory, getCanonicalFileName);
2016-02-23 21:48:31 +01:00
}
2015-04-19 22:24:39 +02:00
function checkSourceFilesBelongToPath(sourceFiles: SourceFile[], rootDirectory: string): boolean {
2015-04-15 07:11:25 +02:00
let allFilesBelongToPath = true;
2015-04-19 22:24:39 +02:00
if (sourceFiles) {
2015-11-04 23:02:33 +01:00
const absoluteRootDirectoryPath = host.getCanonicalFileName(getNormalizedAbsolutePath(rootDirectory, currentDirectory));
2015-04-15 07:11:25 +02:00
2016-01-16 23:05:46 +01:00
for (const sourceFile of sourceFiles) {
2015-04-15 07:11:25 +02:00
if (!isDeclarationFile(sourceFile)) {
2015-11-04 23:02:33 +01:00
const absoluteSourceFilePath = host.getCanonicalFileName(getNormalizedAbsolutePath(sourceFile.fileName, currentDirectory));
2015-04-15 07:11:25 +02:00
if (absoluteSourceFilePath.indexOf(absoluteRootDirectoryPath) !== 0) {
programDiagnostics.add(createCompilerDiagnostic(Diagnostics.File_0_is_not_under_rootDir_1_rootDir_is_expected_to_contain_all_source_files, sourceFile.fileName, options.rootDir));
2015-04-15 07:11:25 +02:00
allFilesBelongToPath = false;
}
}
}
}
2015-04-15 07:11:25 +02:00
return allFilesBelongToPath;
}
function verifyCompilerOptions() {
if (options.isolatedModules) {
if (options.declaration) {
programDiagnostics.add(createCompilerDiagnostic(Diagnostics.Option_0_cannot_be_specified_with_option_1, "declaration", "isolatedModules"));
}
if (options.noEmitOnError) {
programDiagnostics.add(createCompilerDiagnostic(Diagnostics.Option_0_cannot_be_specified_with_option_1, "noEmitOnError", "isolatedModules"));
}
if (options.out) {
programDiagnostics.add(createCompilerDiagnostic(Diagnostics.Option_0_cannot_be_specified_with_option_1, "out", "isolatedModules"));
}
if (options.outFile) {
programDiagnostics.add(createCompilerDiagnostic(Diagnostics.Option_0_cannot_be_specified_with_option_1, "outFile", "isolatedModules"));
}
}
2015-04-08 09:14:23 +02:00
if (options.inlineSourceMap) {
if (options.sourceMap) {
programDiagnostics.add(createCompilerDiagnostic(Diagnostics.Option_0_cannot_be_specified_with_option_1, "sourceMap", "inlineSourceMap"));
2015-04-08 09:14:23 +02:00
}
if (options.mapRoot) {
programDiagnostics.add(createCompilerDiagnostic(Diagnostics.Option_0_cannot_be_specified_with_option_1, "mapRoot", "inlineSourceMap"));
2015-04-08 09:14:23 +02:00
}
2015-04-21 05:33:31 +02:00
}
if (options.paths && options.baseUrl === undefined) {
programDiagnostics.add(createCompilerDiagnostic(Diagnostics.Option_paths_cannot_be_used_without_specifying_baseUrl_option));
}
if (options.paths) {
for (const key in options.paths) {
if (!hasProperty(options.paths, key)) {
continue;
}
if (!hasZeroOrOneAsteriskCharacter(key)) {
programDiagnostics.add(createCompilerDiagnostic(Diagnostics.Pattern_0_can_have_at_most_one_Asterisk_character, key));
}
2016-04-14 06:16:39 +02:00
if (isArray(options.paths[key])) {
for (const subst of options.paths[key]) {
const typeOfSubst = typeof subst;
if (typeOfSubst === "string") {
if (!hasZeroOrOneAsteriskCharacter(subst)) {
programDiagnostics.add(createCompilerDiagnostic(Diagnostics.Substitution_0_in_pattern_1_in_can_have_at_most_one_Asterisk_character, subst, key));
}
}
else {
programDiagnostics.add(createCompilerDiagnostic(Diagnostics.Substitution_0_for_pattern_1_has_incorrect_type_expected_string_got_2, subst, key, typeOfSubst));
}
}
}
2016-04-14 06:16:39 +02:00
else {
2016-05-19 02:12:59 +02:00
programDiagnostics.add(createCompilerDiagnostic(Diagnostics.Substitutions_for_pattern_0_should_be_an_array, key));
2016-04-14 06:16:39 +02:00
}
}
}
if (!options.sourceMap && !options.inlineSourceMap) {
if (options.inlineSources) {
programDiagnostics.add(createCompilerDiagnostic(Diagnostics.Option_0_can_only_be_used_when_either_option_inlineSourceMap_or_option_sourceMap_is_provided, "inlineSources"));
2015-04-08 09:14:23 +02:00
}
if (options.sourceRoot) {
programDiagnostics.add(createCompilerDiagnostic(Diagnostics.Option_0_can_only_be_used_when_either_option_inlineSourceMap_or_option_sourceMap_is_provided, "sourceRoot"));
}
2015-04-08 09:14:23 +02:00
}
if (options.out && options.outFile) {
programDiagnostics.add(createCompilerDiagnostic(Diagnostics.Option_0_cannot_be_specified_with_option_1, "out", "outFile"));
}
if (options.mapRoot && !options.sourceMap) {
// Error to specify --mapRoot without --sourcemap
programDiagnostics.add(createCompilerDiagnostic(Diagnostics.Option_0_cannot_be_specified_without_specifying_option_1, "mapRoot", "sourceMap"));
}
if (options.declarationDir) {
if (!options.declaration) {
programDiagnostics.add(createCompilerDiagnostic(Diagnostics.Option_0_cannot_be_specified_without_specifying_option_1, "declarationDir", "declaration"));
}
if (options.out || options.outFile) {
programDiagnostics.add(createCompilerDiagnostic(Diagnostics.Option_0_cannot_be_specified_with_option_1, "declarationDir", options.out ? "out" : "outFile"));
}
}
if (options.lib && options.noLib) {
programDiagnostics.add(createCompilerDiagnostic(Diagnostics.Option_0_cannot_be_specified_with_option_1, "lib", "noLib"));
}
2015-11-04 23:02:33 +01:00
const languageVersion = options.target || ScriptTarget.ES3;
const outFile = options.outFile || options.out;
2015-11-04 23:02:33 +01:00
const firstExternalModuleSourceFile = forEach(files, f => isExternalModule(f) ? f : undefined);
if (options.isolatedModules) {
if (options.module === ModuleKind.None && languageVersion < ScriptTarget.ES6) {
programDiagnostics.add(createCompilerDiagnostic(Diagnostics.Option_isolatedModules_can_only_be_used_when_either_option_module_is_provided_or_option_target_is_ES2015_or_higher));
}
2015-11-04 23:02:33 +01:00
const firstNonExternalModuleSourceFile = forEach(files, f => !isExternalModule(f) && !isDeclarationFile(f) ? f : undefined);
if (firstNonExternalModuleSourceFile) {
2015-11-04 23:02:33 +01:00
const span = getErrorSpanForNode(firstNonExternalModuleSourceFile, firstNonExternalModuleSourceFile);
programDiagnostics.add(createFileDiagnostic(firstNonExternalModuleSourceFile, span.start, span.length, Diagnostics.Cannot_compile_namespaces_when_the_isolatedModules_flag_is_provided));
}
}
else if (firstExternalModuleSourceFile && languageVersion < ScriptTarget.ES6 && options.module === ModuleKind.None) {
// We cannot use createDiagnosticFromNode because nodes do not have parents yet
const span = getErrorSpanForNode(firstExternalModuleSourceFile, firstExternalModuleSourceFile.externalModuleIndicator);
2016-05-19 01:37:14 +02:00
programDiagnostics.add(createFileDiagnostic(firstExternalModuleSourceFile, span.start, span.length, Diagnostics.Cannot_use_imports_exports_or_module_augmentations_when_module_is_none));
}
2015-03-12 06:53:36 +01:00
2015-10-05 23:25:48 +02:00
// Cannot specify module gen that isn't amd or system with --out
if (outFile) {
if (options.module && !(options.module === ModuleKind.AMD || options.module === ModuleKind.System)) {
programDiagnostics.add(createCompilerDiagnostic(Diagnostics.Only_amd_and_system_modules_are_supported_alongside_0, options.out ? "out" : "outFile"));
}
else if (options.module === undefined && firstExternalModuleSourceFile) {
const span = getErrorSpanForNode(firstExternalModuleSourceFile, firstExternalModuleSourceFile.externalModuleIndicator);
2016-05-19 01:37:14 +02:00
programDiagnostics.add(createFileDiagnostic(firstExternalModuleSourceFile, span.start, span.length, Diagnostics.Cannot_compile_modules_using_option_0_unless_the_module_flag_is_amd_or_system, options.out ? "out" : "outFile"));
}
}
2015-10-22 00:27:33 +02:00
// there has to be common source directory if user specified --outdir || --sourceRoot
// if user specified --mapRoot, there needs to be common source directory if there would be multiple files being emitted
if (options.outDir || // there is --outDir specified
options.sourceRoot || // there is --sourceRoot specified
options.mapRoot) { // there is --mapRoot specified
// Precalculate and cache the common source directory
const dir = getCommonSourceDirectory();
2015-10-22 00:27:33 +02:00
// If we failed to find a good common directory, but outDir is specified and at least one of our files is on a windows drive/URL/other resource, add a failure
if (options.outDir && dir === "" && forEach(files, file => getRootLength(file.fileName) > 1)) {
2016-02-23 21:48:31 +01:00
programDiagnostics.add(createCompilerDiagnostic(Diagnostics.Cannot_find_the_common_subdirectory_path_for_the_input_files));
2015-11-18 22:19:56 +01:00
}
2015-10-22 00:27:33 +02:00
}
2016-03-12 21:14:00 +01:00
if (!options.noEmit && options.allowJs && options.declaration) {
programDiagnostics.add(createCompilerDiagnostic(Diagnostics.Option_0_cannot_be_specified_with_option_1, "allowJs", "declaration"));
}
2015-06-26 01:24:41 +02:00
2015-06-02 00:01:24 +02:00
if (options.emitDecoratorMetadata &&
!options.experimentalDecorators) {
programDiagnostics.add(createCompilerDiagnostic(Diagnostics.Option_0_cannot_be_specified_without_specifying_option_1, "emitDecoratorMetadata", "experimentalDecorators"));
2015-06-02 00:01:24 +02:00
}
2015-07-09 00:35:49 +02:00
2016-01-08 00:00:50 +01:00
if (options.reactNamespace && !isIdentifier(options.reactNamespace, languageVersion)) {
programDiagnostics.add(createCompilerDiagnostic(Diagnostics.Invalid_value_for_reactNamespace_0_is_not_a_valid_identifier, options.reactNamespace));
2016-01-08 00:00:50 +01:00
}
2015-11-06 05:09:40 +01:00
// If the emit is enabled make sure that every output file is unique and not overwriting any of the input files
if (!options.noEmit && !options.suppressOutputPathCheck) {
const emitHost = getEmitHost();
const emitFilesSeen = createFileMap<boolean>(!host.useCaseSensitiveFileNames() ? key => key.toLocaleLowerCase() : undefined);
forEachExpectedEmitFile(emitHost, (emitFileNames, sourceFiles, isBundledEmit) => {
verifyEmitFilePath(emitFileNames.jsFilePath, emitFilesSeen);
verifyEmitFilePath(emitFileNames.declarationFilePath, emitFilesSeen);
});
}
2015-11-12 01:10:23 +01:00
// Verify that all the emit files are unique and don't overwrite input files
function verifyEmitFilePath(emitFileName: string, emitFilesSeen: FileMap<boolean>) {
if (emitFileName) {
const emitFilePath = toPath(emitFileName, currentDirectory, getCanonicalFileName);
// Report error if the output overwrites input file
2015-11-18 19:48:03 +01:00
if (filesByName.contains(emitFilePath)) {
createEmitBlockingDiagnostics(emitFileName, emitFilePath, Diagnostics.Cannot_write_file_0_because_it_would_overwrite_input_file);
}
// Report error if multiple files write into same file
if (emitFilesSeen.contains(emitFilePath)) {
// Already seen the same emit file - report error
2015-11-18 19:48:03 +01:00
createEmitBlockingDiagnostics(emitFileName, emitFilePath, Diagnostics.Cannot_write_file_0_because_it_would_be_overwritten_by_multiple_input_files);
}
else {
emitFilesSeen.set(emitFilePath, true);
}
}
}
}
2015-11-18 19:48:03 +01:00
function createEmitBlockingDiagnostics(emitFileName: string, emitFilePath: Path, message: DiagnosticMessage) {
hasEmitBlockingDiagnostics.set(toPath(emitFileName, currentDirectory, getCanonicalFileName), true);
2015-11-12 01:10:23 +01:00
programDiagnostics.add(createCompilerDiagnostic(message, emitFileName));
}
}
2015-06-17 20:08:13 +02:00
}