TypeScript/src/compiler/program.ts

844 lines
40 KiB
TypeScript
Raw Normal View History

/// <reference path="sys.ts" />
/// <reference path="emitter.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 */
export const version = "1.5.3";
var emptyArray: any[] = [];
export function findConfigFile(searchPath: string): string {
var fileName = "tsconfig.json";
while (true) {
if (sys.fileExists(fileName)) {
return fileName;
}
var parentPath = getDirectoryPath(searchPath);
if (parentPath === searchPath) {
break;
}
searchPath = parentPath;
fileName = "../" + fileName;
}
return undefined;
}
export function createCompilerHost(options: CompilerOptions, setParentNodes?: boolean): CompilerHost {
2015-03-13 23:03:17 +01:00
let currentDirectory: string;
let 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();
}
// returned by CScript sys environment
2015-03-13 23:03:17 +01:00
let 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-03-13 23:03:17 +01:00
let start = new Date().getTime();
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)) {
let parentDirectory = getDirectoryPath(directoryPath);
ensureDirectoriesExist(parentDirectory);
sys.createDirectory(directoryPath);
}
}
function writeFile(fileName: string, data: string, writeByteOrderMark: boolean, onError?: (message: string) => void) {
try {
var start = new Date().getTime();
ensureDirectoriesExist(getDirectoryPath(normalizePath(fileName)));
sys.writeFile(fileName, data, writeByteOrderMark);
ioWriteTime += new Date().getTime() - start;
}
catch (e) {
if (onError) {
onError(e.message);
}
}
}
const newLine = getNewLineCharacter(options);
return {
getSourceFile,
getDefaultLibFileName: options => combinePaths(getDirectoryPath(normalizePath(sys.getExecutingFilePath())), getDefaultLibFileName(options)),
writeFile,
getCurrentDirectory: () => currentDirectory || (currentDirectory = sys.getCurrentDirectory()),
useCaseSensitiveFileNames: () => sys.useCaseSensitiveFileNames,
getCanonicalFileName,
getNewLine: () => newLine
};
}
export function getPreEmitDiagnostics(program: Program, sourceFile?: SourceFile): Diagnostic[] {
2015-06-18 20:00:50 +02:00
let diagnostics = program.getOptionsDiagnostics().concat(
program.getSyntacticDiagnostics(sourceFile),
program.getGlobalDiagnostics(),
program.getSemanticDiagnostics(sourceFile));
if (program.getCompilerOptions().declaration) {
diagnostics.concat(program.getDeclarationDiagnostics(sourceFile));
}
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;
}
}
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[] = [];
let diagnostics = createDiagnosticCollection();
2015-06-17 20:08:13 +02:00
2015-03-13 23:03:17 +01:00
let commonSourceDirectory: string;
let diagnosticsProducingTypeChecker: TypeChecker;
let noDiagnosticsTypeChecker: TypeChecker;
2015-06-12 21:53:24 +02:00
let classifiableNames: Map<string>;
let skipDefaultLib = options.noLib;
2015-05-01 03:14:53 +02:00
let start = new Date().getTime();
host = host || createCompilerHost(options);
2015-05-01 03:14:53 +02:00
let filesByName = createFileMap<SourceFile>(fileName => host.getCanonicalFileName(fileName));
// if old program was provided by has different target module kind - assume that it cannot be reused
// different module kind can lead to different way of resolving modules
if (oldProgram && oldProgram.getCompilerOptions().module !== options.module) {
oldProgram = undefined;
}
if (!tryReuseStructureFromOldProgram()) {
forEach(rootNames, name => processRootFile(name, false));
// 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) {
processRootFile(host.getDefaultLibFileName(options), true);
}
}
2015-05-01 03:14:53 +02:00
verifyCompilerOptions();
programTime += new Date().getTime() - start;
program = {
getRootFileNames: () => rootNames,
getSourceFile: getSourceFile,
getSourceFiles: () => files,
getCompilerOptions: () => options,
getSyntacticDiagnostics,
getOptionsDiagnostics,
getGlobalDiagnostics,
getSemanticDiagnostics,
getDeclarationDiagnostics,
getTypeChecker,
2015-06-12 21:53:24 +02:00
getClassifiableNames,
getDiagnosticsProducingTypeChecker,
getCommonSourceDirectory: () => commonSourceDirectory,
emit,
getCurrentDirectory: () => host.getCurrentDirectory(),
getNodeCount: () => getDiagnosticsProducingTypeChecker().getNodeCount(),
getIdentifierCount: () => getDiagnosticsProducingTypeChecker().getIdentifierCount(),
getSymbolCount: () => getDiagnosticsProducingTypeChecker().getSymbolCount(),
getTypeCount: () => getDiagnosticsProducingTypeChecker().getTypeCount(),
};
return program;
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 = {};
for (let 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 (!host.hasChanges) {
// host does not support method 'hasChanges'
return false;
}
if (!oldProgram) {
return false;
}
Debug.assert(!oldProgram.structureIsReused);
// there is an old program, check if we can reuse its structure
let oldRootNames = oldProgram.getRootFileNames();
if (rootNames.length !== oldRootNames.length) {
// different amount of root names - structure cannot be reused
return false;
}
for (let i = 0; i < rootNames.length; i++) {
if (oldRootNames[i] !== rootNames[i]) {
// different order of root names - structure cannot be reused
return false;
}
}
// check if program source files has changed in the way that can affect structure of the program
let newSourceFiles: SourceFile[] = [];
for (let oldSourceFile of oldProgram.getSourceFiles()) {
let newSourceFile: SourceFile;
if (host.hasChanges(oldSourceFile)) {
newSourceFile = host.getSourceFile(oldSourceFile.fileName, options.target);
if (!newSourceFile) {
return false;
}
// check tripleslash references
if (!arrayIsEqualTo(oldSourceFile.referencedFiles, newSourceFile.referencedFiles, fileReferenceIsEqualTo)) {
// tripleslash references has changed
return false;
}
// check imports
collectExternalModuleReferences(newSourceFile);
if (!arrayIsEqualTo(oldSourceFile.imports, newSourceFile.imports, moduleNameIsEqualTo)) {
// imports has changed
return false;
}
// pass the cache of module resolutions from the old source file
newSourceFile.resolvedModules = oldSourceFile.resolvedModules;
}
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
for (let file of newSourceFiles) {
filesByName.set(file.fileName, file);
}
files = newSourceFiles;
oldProgram.structureIsReused = true;
return true;
}
function getEmitHost(writeFileCallback?: WriteFileCallback): EmitHost {
return {
getCanonicalFileName: fileName => host.getCanonicalFileName(fileName),
getCommonSourceDirectory: program.getCommonSourceDirectory,
getCompilerOptions: program.getCompilerOptions,
getCurrentDirectory: () => host.getCurrentDirectory(),
getNewLine: () => host.getNewLine(),
getSourceFile: program.getSourceFile,
getSourceFiles: program.getSourceFiles,
writeFile: writeFileCallback || (
(fileName, data, writeByteOrderMark, onError) => host.writeFile(fileName, data, writeByteOrderMark, onError)),
};
}
function getDiagnosticsProducingTypeChecker() {
return diagnosticsProducingTypeChecker || (diagnosticsProducingTypeChecker = createTypeChecker(program, /*produceDiagnostics:*/ true));
}
function getTypeChecker() {
return noDiagnosticsTypeChecker || (noDiagnosticsTypeChecker = createTypeChecker(program, /*produceDiagnostics:*/ false));
}
function emit(sourceFile?: SourceFile, writeFileCallback?: WriteFileCallback): EmitResult {
// If the noEmitOnError flag is set, then check if we have any errors so far. If so,
// immediately bail out.
if (options.noEmitOnError && getPreEmitDiagnostics(this).length > 0) {
return { diagnostics: [], sourceMaps: 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.
let emitResolver = getDiagnosticsProducingTypeChecker().getEmitResolver(options.out ? undefined : sourceFile);
2015-03-13 23:03:17 +01:00
let start = new Date().getTime();
2015-03-13 23:03:17 +01:00
let emitResult = emitFiles(
emitResolver,
getEmitHost(writeFileCallback),
sourceFile);
emitTime += new Date().getTime() - start;
return emitResult;
}
function getSourceFile(fileName: string) {
return filesByName.get(fileName);
}
function getDiagnosticsHelper(sourceFile: SourceFile, getDiagnostics: (sourceFile: SourceFile) => Diagnostic[]): Diagnostic[] {
if (sourceFile) {
return getDiagnostics(sourceFile);
}
2015-03-13 23:03:17 +01:00
let allDiagnostics: Diagnostic[] = [];
forEach(program.getSourceFiles(), sourceFile => {
addRange(allDiagnostics, getDiagnostics(sourceFile));
});
return sortAndDeduplicateDiagnostics(allDiagnostics);
}
2015-02-06 02:24:46 +01:00
function getSyntacticDiagnostics(sourceFile?: SourceFile): Diagnostic[] {
return getDiagnosticsHelper(sourceFile, getSyntacticDiagnosticsForFile);
}
2015-02-06 02:24:46 +01:00
function getSemanticDiagnostics(sourceFile?: SourceFile): Diagnostic[] {
return getDiagnosticsHelper(sourceFile, getSemanticDiagnosticsForFile);
}
function getDeclarationDiagnostics(sourceFile?: SourceFile): Diagnostic[] {
return getDiagnosticsHelper(sourceFile, getDeclarationDiagnosticsForFile);
}
function getSyntacticDiagnosticsForFile(sourceFile: SourceFile): Diagnostic[] {
return sourceFile.parseDiagnostics;
}
function getSemanticDiagnosticsForFile(sourceFile: SourceFile): Diagnostic[] {
2015-03-13 23:03:17 +01:00
let typeChecker = getDiagnosticsProducingTypeChecker();
Debug.assert(!!sourceFile.bindDiagnostics);
2015-03-13 23:03:17 +01:00
let bindDiagnostics = sourceFile.bindDiagnostics;
let checkDiagnostics = typeChecker.getDiagnostics(sourceFile);
let programDiagnostics = diagnostics.getDiagnostics(sourceFile.fileName);
return bindDiagnostics.concat(checkDiagnostics).concat(programDiagnostics);
}
function getDeclarationDiagnosticsForFile(sourceFile: SourceFile): Diagnostic[] {
if (!isDeclarationFile(sourceFile)) {
let resolver = getDiagnosticsProducingTypeChecker().getEmitResolver(sourceFile);
// Don't actually write any files since we're just getting diagnostics.
var writeFile: WriteFileCallback = () => { };
return ts.getDeclarationDiagnostics(getEmitHost(writeFile), resolver, sourceFile);
}
}
function getOptionsDiagnostics(): Diagnostic[] {
let allDiagnostics: Diagnostic[] = [];
addRange(allDiagnostics, diagnostics.getGlobalDiagnostics());
return sortAndDeduplicateDiagnostics(allDiagnostics);
}
function getGlobalDiagnostics(): Diagnostic[] {
2015-03-13 23:03:17 +01:00
let 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) {
processSourceFile(normalizePath(fileName), isDefaultLib);
}
function fileReferenceIsEqualTo(a: FileReference, b: FileReference): boolean {
return a.fileName === b.fileName;
}
function moduleNameIsEqualTo(a: LiteralExpression, b: LiteralExpression): boolean {
return a.text ===b.text;
}
function collectExternalModuleReferences(file: SourceFile): void {
if (file.imports) {
return;
}
let imports: LiteralExpression[];
for (let node of file.statements) {
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;
}
(imports || (imports = [])).push(<LiteralExpression>moduleNameExpr);
break;
case SyntaxKind.ModuleDeclaration:
if ((<ModuleDeclaration>node).name.kind === SyntaxKind.StringLiteral && (node.flags & NodeFlags.Ambient || isDeclarationFile(file))) {
// TypeScript 1.0 spec (April 2014): 12.1.6
// 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
forEachChild((<ModuleDeclaration>node).body, node => {
if (isExternalModuleImportEqualsDeclaration(node) &&
getExternalModuleImportEqualsDeclarationExpression(node).kind === SyntaxKind.StringLiteral) {
let moduleName = <LiteralExpression>getExternalModuleImportEqualsDeclarationExpression(node);
// TypeScript 1.0 spec (April 2014): 12.1.6
// An ExternalImportDeclaration in anAmbientExternalModuleDeclaration may reference other external modules
// only through top - level external module names. Relative external module names are not permitted.
if (moduleName) {
(imports || (imports = [])).push(moduleName);
}
}
});
}
break;
}
}
file.imports = imports || emptyArray;
}
function processSourceFile(fileName: string, isDefaultLib: boolean, refFile?: SourceFile, refPos?: number, refEnd?: number) {
2015-03-13 23:03:17 +01:00
let start: number;
let length: number;
2015-05-05 08:30:43 +02:00
let extensions: string;
let diagnosticArgument: string[];
if (refEnd !== undefined && refPos !== undefined) {
2015-03-13 23:03:17 +01:00
start = refPos;
length = refEnd - refPos;
}
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("', '") + "'"];
}
else if (!findSourceFile(fileName, isDefaultLib, 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 {
var nonTsFile: SourceFile = options.allowNonTsExtensions && findSourceFile(fileName, isDefaultLib, refFile, refPos, refEnd);
if (!nonTsFile) {
if (options.allowNonTsExtensions) {
diagnostic = Diagnostics.File_0_not_found;
diagnosticArgument = [fileName];
}
else if (!forEach(supportedExtensions, extension => findSourceFile(fileName + extension, isDefaultLib, refFile, refPos, refEnd))) {
diagnostic = Diagnostics.File_0_not_found;
fileName += ".ts";
diagnosticArgument = [fileName];
}
}
}
if (diagnostic) {
if (refFile) {
2015-05-05 08:30:43 +02:00
diagnostics.add(createFileDiagnostic(refFile, start, length, diagnostic, ...diagnosticArgument));
}
else {
2015-05-05 08:30:43 +02:00
diagnostics.add(createCompilerDiagnostic(diagnostic, ...diagnosticArgument));
}
}
}
// Get source file from normalized fileName
function findSourceFile(fileName: string, isDefaultLib: boolean, refFile?: SourceFile, refStart?: number, refLength?: number): SourceFile {
let canonicalName = host.getCanonicalFileName(normalizeSlashes(fileName));
if (filesByName.contains(canonicalName)) {
// We've already looked for this file, use cached result
return getSourceFileFromCache(fileName, canonicalName, /*useAbsolutePath*/ false);
}
else {
2015-03-13 23:03:17 +01:00
let normalizedAbsolutePath = getNormalizedAbsolutePath(fileName, host.getCurrentDirectory());
let canonicalAbsolutePath = host.getCanonicalFileName(normalizedAbsolutePath);
if (filesByName.contains(canonicalAbsolutePath)) {
return getSourceFileFromCache(normalizedAbsolutePath, canonicalAbsolutePath, /*useAbsolutePath*/ true);
}
// We haven't looked for this file, do so now and cache result
let file = host.getSourceFile(fileName, options.target, hostErrorMessage => {
if (refFile) {
diagnostics.add(createFileDiagnostic(refFile, refStart, refLength,
Diagnostics.Cannot_read_file_0_Colon_1, fileName, hostErrorMessage));
}
else {
diagnostics.add(createCompilerDiagnostic(Diagnostics.Cannot_read_file_0_Colon_1, fileName, hostErrorMessage));
}
});
filesByName.set(canonicalName, file);
if (file) {
skipDefaultLib = skipDefaultLib || file.hasNoDefaultLib;
// Set the source file for normalized absolute path
filesByName.set(canonicalAbsolutePath, file);
if (!options.noResolve) {
2015-03-13 23:03:17 +01:00
let basePath = getDirectoryPath(fileName);
processReferencedFiles(file, basePath);
processImportedModules(file, basePath);
}
if (isDefaultLib) {
file.isDefaultLib = true;
files.unshift(file);
}
else {
files.push(file);
}
}
2015-03-13 23:03:17 +01:00
return file;
}
function getSourceFileFromCache(fileName: string, canonicalName: string, useAbsolutePath: boolean): SourceFile {
let file = filesByName.get(canonicalName);
if (file && host.useCaseSensitiveFileNames()) {
2015-03-13 23:03:17 +01:00
let sourceFileName = useAbsolutePath ? getNormalizedAbsolutePath(file.fileName, host.getCurrentDirectory()) : file.fileName;
if (canonicalName !== sourceFileName) {
diagnostics.add(createFileDiagnostic(refFile, refStart, refLength,
Diagnostics.File_name_0_differs_from_already_included_file_name_1_only_in_casing, fileName, sourceFileName));
}
}
return file;
}
}
function processReferencedFiles(file: SourceFile, basePath: string) {
forEach(file.referencedFiles, ref => {
2015-03-13 23:03:17 +01:00
let referencedFileName = isRootedDiskPath(ref.fileName) ? ref.fileName : combinePaths(basePath, ref.fileName);
processSourceFile(normalizePath(referencedFileName), /* isDefaultLib */ false, file, ref.pos, ref.end);
});
}
function processImportedModules(file: SourceFile, basePath: string) {
collectExternalModuleReferences(file);
if (file.imports.length) {
let allImportsInTheCache = false;
// try to grab existing module resolutions from the old source file
let oldSourceFile: SourceFile = oldProgram && oldProgram.getSourceFile(file.fileName);
if (oldSourceFile) {
file.resolvedModules = oldSourceFile.resolvedModules;
// check that all imports are contained in resolved modules cache
// if at least one of imports in not in the cache - cache needs to be reinitialized
checkImports: {
if (file.resolvedModules) {
for (let moduleName of file.imports) {
if (!hasResolvedModuleName(file, moduleName)) {
break checkImports;
}
}
allImportsInTheCache = true;
}
}
}
if (!allImportsInTheCache) {
// initialize resolvedModules with empty map
// old module resolutions still can be used to avoid actual file system probing
let resolvedModules = file.resolvedModules;
file.resolvedModules = {};
for (let moduleName of file.imports) {
resolveModule(moduleName, resolvedModules);
}
}
}
else {
// no imports - drop cached module resolutions
file.resolvedModules = undefined;
}
return;
2015-02-09 02:33:45 +01:00
function findModuleSourceFile(fileName: string, nameLiteral: Expression) {
return findSourceFile(fileName, /* isDefaultLib */ false, file, nameLiteral.pos, nameLiteral.end - nameLiteral.pos);
}
function resolveModule(moduleNameExpr: LiteralExpression, existingResolutions: Map<string>): void {
let searchPath = basePath;
let searchName: string;
if (existingResolutions && hasProperty(existingResolutions, moduleNameExpr.text)) {
let fileName = existingResolutions[moduleNameExpr.text];
// use existing resolution
setResolvedModuleName(file, moduleNameExpr, fileName);
if (fileName) {
findModuleSourceFile(fileName, moduleNameExpr);
}
return;
}
while (true) {
searchName = normalizePath(combinePaths(searchPath, moduleNameExpr.text));
let referencedSourceFile = forEach(supportedExtensions, extension => findModuleSourceFile(searchName + extension, moduleNameExpr));
if (referencedSourceFile) {
setResolvedModuleName(file, moduleNameExpr, referencedSourceFile.fileName);
return;
}
let parentPath = getDirectoryPath(searchPath);
if (parentPath === searchPath) {
break;
}
searchPath = parentPath;
}
// mark reference as non-resolved
setResolvedModuleName(file, moduleNameExpr, undefined);
}
}
2015-04-17 01:35:48 +02:00
function computeCommonSourceDirectory(sourceFiles: SourceFile[]): string {
let commonPathComponents: string[];
2015-04-15 07:11:25 +02:00
let currentDirectory = host.getCurrentDirectory();
forEach(files, sourceFile => {
// Each file contributes into common source file path
2015-04-15 07:11:25 +02:00
if (isDeclarationFile(sourceFile)) {
return;
}
2015-04-15 07:11:25 +02:00
let sourcePathComponents = getNormalizedPathComponents(sourceFile.fileName, currentDirectory);
2015-04-20 23:23:24 +02:00
sourcePathComponents.pop(); // The base file name is not part of the common directory path
if (!commonPathComponents) {
// first file
commonPathComponents = sourcePathComponents;
return;
}
2015-04-20 23:23:24 +02:00
for (let i = 0, n = Math.min(commonPathComponents.length, sourcePathComponents.length); i < n; i++) {
if (commonPathComponents[i] !== sourcePathComponents[i]) {
if (i === 0) {
diagnostics.add(createCompilerDiagnostic(Diagnostics.Cannot_find_the_common_subdirectory_path_for_the_input_files));
return;
}
2015-04-15 07:11:25 +02:00
2015-04-20 23:23:24 +02:00
// New common path found that is 0 -> i-1
commonPathComponents.length = i;
break;
}
}
2015-04-20 23:23:24 +02:00
// If the sourcePathComponents was shorter than the commonPathComponents, truncate to the sourcePathComponents
if (sourcePathComponents.length < commonPathComponents.length) {
commonPathComponents.length = sourcePathComponents.length;
2015-04-15 07:11:25 +02:00
}
});
2015-04-15 07:11:25 +02:00
return getNormalizedPathFromPathComponents(commonPathComponents);
}
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-04-15 07:11:25 +02:00
let currentDirectory = host.getCurrentDirectory();
let absoluteRootDirectoryPath = host.getCanonicalFileName(getNormalizedAbsolutePath(rootDirectory, currentDirectory));
2015-04-19 22:24:39 +02:00
for (var sourceFile of sourceFiles) {
2015-04-15 07:11:25 +02:00
if (!isDeclarationFile(sourceFile)) {
let absoluteSourceFilePath = host.getCanonicalFileName(getNormalizedAbsolutePath(sourceFile.fileName, currentDirectory));
if (absoluteSourceFilePath.indexOf(absoluteRootDirectoryPath) !== 0) {
2015-04-17 01:35:48 +02:00
diagnostics.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.sourceMap) {
diagnostics.add(createCompilerDiagnostic(Diagnostics.Option_sourceMap_cannot_be_specified_with_option_isolatedModules));
}
if (options.declaration) {
diagnostics.add(createCompilerDiagnostic(Diagnostics.Option_declaration_cannot_be_specified_with_option_isolatedModules));
}
if (options.noEmitOnError) {
diagnostics.add(createCompilerDiagnostic(Diagnostics.Option_noEmitOnError_cannot_be_specified_with_option_isolatedModules));
}
if (options.out) {
diagnostics.add(createCompilerDiagnostic(Diagnostics.Option_out_cannot_be_specified_with_option_isolatedModules));
}
}
2015-04-08 09:14:23 +02:00
if (options.inlineSourceMap) {
if (options.sourceMap) {
diagnostics.add(createCompilerDiagnostic(Diagnostics.Option_sourceMap_cannot_be_specified_with_option_inlineSourceMap));
}
if (options.mapRoot) {
diagnostics.add(createCompilerDiagnostic(Diagnostics.Option_mapRoot_cannot_be_specified_with_option_inlineSourceMap));
}
if (options.sourceRoot) {
diagnostics.add(createCompilerDiagnostic(Diagnostics.Option_sourceRoot_cannot_be_specified_with_option_inlineSourceMap));
}
2015-04-21 05:33:31 +02:00
}
if (options.inlineSources) {
if (!options.sourceMap && !options.inlineSourceMap) {
diagnostics.add(createCompilerDiagnostic(Diagnostics.Option_inlineSources_can_only_be_used_when_either_option_inlineSourceMap_or_option_sourceMap_is_provided));
2015-04-08 09:14:23 +02:00
}
}
if (!options.sourceMap && (options.mapRoot || options.sourceRoot)) {
// Error to specify --mapRoot or --sourceRoot without mapSourceFiles
if (options.mapRoot) {
2015-05-11 23:52:02 +02:00
diagnostics.add(createCompilerDiagnostic(Diagnostics.Option_mapRoot_cannot_be_specified_without_specifying_sourceMap_option));
}
if (options.sourceRoot) {
2015-05-11 23:52:02 +02:00
diagnostics.add(createCompilerDiagnostic(Diagnostics.Option_sourceRoot_cannot_be_specified_without_specifying_sourceMap_option));
}
return;
}
let languageVersion = options.target || ScriptTarget.ES3;
2015-03-13 23:03:17 +01:00
let firstExternalModuleSourceFile = forEach(files, f => isExternalModule(f) ? f : undefined);
if (options.isolatedModules) {
if (!options.module && languageVersion < ScriptTarget.ES6) {
diagnostics.add(createCompilerDiagnostic(Diagnostics.Option_isolatedModules_can_only_be_used_when_either_option_module_is_provided_or_option_target_is_ES6_or_higher));
2015-03-12 06:53:36 +01:00
}
let firstNonExternalModuleSourceFile = forEach(files, f => !isExternalModule(f) && !isDeclarationFile(f) ? f : undefined);
if (firstNonExternalModuleSourceFile) {
let span = getErrorSpanForNode(firstNonExternalModuleSourceFile, firstNonExternalModuleSourceFile);
diagnostics.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) {
// We cannot use createDiagnosticFromNode because nodes do not have parents yet
let span = getErrorSpanForNode(firstExternalModuleSourceFile, firstExternalModuleSourceFile.externalModuleIndicator);
2015-04-27 03:29:37 +02:00
diagnostics.add(createFileDiagnostic(firstExternalModuleSourceFile, span.start, span.length, Diagnostics.Cannot_compile_modules_unless_the_module_flag_is_provided));
2015-03-12 06:53:36 +01:00
}
// Cannot specify module gen target when in es6 or above
if (options.module && languageVersion >= ScriptTarget.ES6) {
diagnostics.add(createCompilerDiagnostic(Diagnostics.Cannot_compile_modules_into_commonjs_amd_system_or_umd_when_targeting_ES6_or_higher));
}
// 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 and there would be multiple js files generated
(!options.out || firstExternalModuleSourceFile !== undefined))) {
2015-04-15 07:11:25 +02:00
if (options.rootDir && checkSourceFilesBelongToPath(files, options.rootDir)) {
// If a rootDir is specified and is valid use it as the commonSourceDirectory
commonSourceDirectory = getNormalizedAbsolutePath(options.rootDir, host.getCurrentDirectory());
}
else {
// Compute the commonSourceDirectory from the input files
2015-04-17 01:35:48 +02:00
commonSourceDirectory = computeCommonSourceDirectory(files);
2015-04-15 07:11:25 +02:00
}
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;
}
}
if (options.noEmit) {
if (options.out || options.outDir) {
diagnostics.add(createCompilerDiagnostic(Diagnostics.Option_noEmit_cannot_be_specified_with_option_out_or_outDir));
}
if (options.declaration) {
diagnostics.add(createCompilerDiagnostic(Diagnostics.Option_noEmit_cannot_be_specified_with_option_declaration));
}
}
2015-06-02 00:01:24 +02:00
if (options.emitDecoratorMetadata &&
!options.experimentalDecorators) {
diagnostics.add(createCompilerDiagnostic(Diagnostics.Option_experimentalDecorators_must_also_be_specified_when_option_emitDecoratorMetadata_is_specified));
}
}
}
2015-06-17 20:08:13 +02:00
}