mirror of
https://github.com/microsoft/TypeScript.git
synced 2026-02-15 03:23:08 -06:00
Layer the compiler so that every layer only depends on hte layers below it.
The layering is now: types core scanner parser binder checker emitter program
This commit is contained in:
parent
5c4c08a667
commit
4aa361d4bf
@ -1,10 +1,6 @@
|
||||
/// <reference path="types.ts"/>
|
||||
/// <reference path="core.ts"/>
|
||||
/// <reference path="scanner.ts"/>
|
||||
/// <reference path="parser.ts"/>
|
||||
|
||||
module ts {
|
||||
|
||||
export const enum ModuleInstanceState {
|
||||
NonInstantiated = 0,
|
||||
Instantiated = 1,
|
||||
|
||||
@ -1,10 +1,4 @@
|
||||
/// <reference path="types.ts"/>
|
||||
/// <reference path="core.ts"/>
|
||||
/// <reference path="scanner.ts"/>
|
||||
/// <reference path="parser.ts"/>
|
||||
/// <reference path="binder.ts"/>
|
||||
/// <reference path="emitter.ts"/>
|
||||
/// <reference path="utilities.ts"/>
|
||||
|
||||
module ts {
|
||||
var nextSymbolId = 1;
|
||||
@ -16,7 +10,6 @@ module ts {
|
||||
/// If fullTypeCheck === false, the typechecker can take shortcuts and skip checks that only produce errors.
|
||||
/// NOTE: checks that somehow affect decisions being made during typechecking should be executed in both cases.
|
||||
export function createTypeChecker(program: Program, fullTypeCheck: boolean): TypeChecker {
|
||||
|
||||
var Symbol = objectAllocator.getSymbolConstructor();
|
||||
var Type = objectAllocator.getTypeConstructor();
|
||||
var Signature = objectAllocator.getSignatureConstructor();
|
||||
@ -27,6 +20,7 @@ module ts {
|
||||
var emptySymbols: SymbolTable = {};
|
||||
|
||||
var compilerOptions = program.getCompilerOptions();
|
||||
var emitResolver = createResolver();
|
||||
|
||||
var checker: TypeChecker = {
|
||||
getProgram: () => program,
|
||||
@ -36,9 +30,7 @@ module ts {
|
||||
getTypeCount: () => typeCount,
|
||||
isUndefinedSymbol: symbol => symbol === undefinedSymbol,
|
||||
isArgumentsSymbol: symbol => symbol === argumentsSymbol,
|
||||
emitFiles: invokeEmitter,
|
||||
getDiagnostics,
|
||||
getDeclarationDiagnostics,
|
||||
getGlobalDiagnostics,
|
||||
getTypeOfSymbolAtLocation,
|
||||
getDeclaredTypeOfSymbol,
|
||||
@ -66,6 +58,7 @@ module ts {
|
||||
getAliasedSymbol: resolveImport,
|
||||
hasEarlyErrors,
|
||||
isEmitBlocked,
|
||||
getEmitResolver: () => emitResolver,
|
||||
};
|
||||
|
||||
var undefinedSymbol = createSymbol(SymbolFlags.Property | SymbolFlags.Transient, "undefined");
|
||||
@ -8945,12 +8938,6 @@ module ts {
|
||||
return getSortedDiagnostics();
|
||||
}
|
||||
|
||||
function getDeclarationDiagnostics(targetSourceFile: SourceFile): Diagnostic[] {
|
||||
var resolver = createResolver();
|
||||
checkSourceFile(targetSourceFile);
|
||||
return ts.getDeclarationDiagnostics(program, resolver, targetSourceFile);
|
||||
}
|
||||
|
||||
function getGlobalDiagnostics(): Diagnostic[] {
|
||||
return filter(getSortedDiagnostics(), d => !d.file);
|
||||
}
|
||||
@ -9575,11 +9562,6 @@ module ts {
|
||||
};
|
||||
}
|
||||
|
||||
function invokeEmitter(targetSourceFile?: SourceFile) {
|
||||
var resolver = createResolver();
|
||||
return emitFiles(resolver, targetSourceFile);
|
||||
}
|
||||
|
||||
function initializeTypeChecker() {
|
||||
// Bind all source files and propagate errors
|
||||
forEach(program.getSourceFiles(), file => {
|
||||
|
||||
@ -1,8 +1,4 @@
|
||||
/// <reference path="types.ts"/>
|
||||
/// <reference path="core.ts"/>
|
||||
/// <reference path="scanner.ts"/>
|
||||
/// <reference path="parser.ts"/>
|
||||
/// <reference path="binder.ts"/>
|
||||
/// <reference path="checker.ts"/>
|
||||
|
||||
module ts {
|
||||
interface EmitTextWriter {
|
||||
|
||||
@ -1,5 +1,3 @@
|
||||
/// <reference path="types.ts"/>
|
||||
/// <reference path="core.ts"/>
|
||||
/// <reference path="scanner.ts"/>
|
||||
/// <reference path="utilities.ts"/>
|
||||
|
||||
@ -260,79 +258,6 @@ module ts {
|
||||
}
|
||||
}
|
||||
|
||||
// TODO (drosen, mhegazy): Move to a more appropriate file.
|
||||
export function createCompilerHost(options: CompilerOptions): CompilerHost {
|
||||
var currentDirectory: string;
|
||||
var 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
|
||||
var unsupportedFileEncodingErrorCode = -2147024809;
|
||||
|
||||
function getSourceFile(filename: string, languageVersion: ScriptTarget, onError?: (message: string) => void): SourceFile {
|
||||
try {
|
||||
var text = sys.readFile(filename, options.charset);
|
||||
}
|
||||
catch (e) {
|
||||
if (onError) {
|
||||
onError(e.number === unsupportedFileEncodingErrorCode ?
|
||||
createCompilerDiagnostic(Diagnostics.Unsupported_file_encoding).messageText :
|
||||
e.message);
|
||||
}
|
||||
text = "";
|
||||
}
|
||||
|
||||
return text !== undefined ? createSourceFile(filename, text, languageVersion) : undefined;
|
||||
}
|
||||
|
||||
function writeFile(fileName: string, data: string, writeByteOrderMark: boolean, onError?: (message: string) => void) {
|
||||
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)) {
|
||||
var parentDirectory = getDirectoryPath(directoryPath);
|
||||
ensureDirectoriesExist(parentDirectory);
|
||||
sys.createDirectory(directoryPath);
|
||||
}
|
||||
}
|
||||
|
||||
try {
|
||||
ensureDirectoriesExist(getDirectoryPath(normalizePath(fileName)));
|
||||
sys.writeFile(fileName, data, writeByteOrderMark);
|
||||
}
|
||||
catch (e) {
|
||||
if (onError) {
|
||||
onError(e.message);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return {
|
||||
getSourceFile,
|
||||
getDefaultLibFilename: options => combinePaths(getDirectoryPath(normalizePath(sys.getExecutingFilePath())), options.target === ScriptTarget.ES6 ? "lib.es6.d.ts" : "lib.d.ts"),
|
||||
writeFile,
|
||||
getCurrentDirectory: () => currentDirectory || (currentDirectory = sys.getCurrentDirectory()),
|
||||
useCaseSensitiveFileNames: () => sys.useCaseSensitiveFileNames,
|
||||
getCanonicalFileName,
|
||||
getNewLine: () => sys.newLine
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
const enum ParsingContext {
|
||||
SourceElements, // Elements in source file
|
||||
ModuleElements, // Elements in module declaration
|
||||
@ -5265,275 +5190,4 @@ module ts {
|
||||
return grammarErrorOnFirstToken(node, Diagnostics.yield_expressions_are_not_currently_supported);
|
||||
}
|
||||
}
|
||||
|
||||
export function createProgram(rootNames: string[], options: CompilerOptions, host: CompilerHost): Program {
|
||||
var program: Program;
|
||||
var files: SourceFile[] = [];
|
||||
var filesByName: Map<SourceFile> = {};
|
||||
var errors: Diagnostic[] = [];
|
||||
var seenNoDefaultLib = options.noLib;
|
||||
var commonSourceDirectory: string;
|
||||
|
||||
forEach(rootNames, name => processRootFile(name, false));
|
||||
if (!seenNoDefaultLib) {
|
||||
processRootFile(host.getDefaultLibFilename(options), true);
|
||||
}
|
||||
verifyCompilerOptions();
|
||||
errors.sort(compareDiagnostics);
|
||||
program = {
|
||||
getSourceFile: getSourceFile,
|
||||
getSourceFiles: () => files,
|
||||
getCompilerOptions: () => options,
|
||||
getCompilerHost: () => host,
|
||||
getDiagnostics: getDiagnostics,
|
||||
getGlobalDiagnostics: getGlobalDiagnostics,
|
||||
getTypeChecker: fullTypeCheckMode => createTypeChecker(program, fullTypeCheckMode),
|
||||
getCommonSourceDirectory: () => commonSourceDirectory,
|
||||
};
|
||||
return program;
|
||||
|
||||
function getSourceFile(filename: string) {
|
||||
filename = host.getCanonicalFileName(filename);
|
||||
return hasProperty(filesByName, filename) ? filesByName[filename] : undefined;
|
||||
}
|
||||
|
||||
function getDiagnostics(sourceFile?: SourceFile): Diagnostic[] {
|
||||
return sourceFile ? filter(errors, e => e.file === sourceFile) : errors;
|
||||
}
|
||||
|
||||
function getGlobalDiagnostics(): Diagnostic[] {
|
||||
return filter(errors, e => !e.file);
|
||||
}
|
||||
|
||||
function hasExtension(filename: string): boolean {
|
||||
return getBaseFilename(filename).indexOf(".") >= 0;
|
||||
}
|
||||
|
||||
function processRootFile(filename: string, isDefaultLib: boolean) {
|
||||
processSourceFile(normalizePath(filename), isDefaultLib);
|
||||
}
|
||||
|
||||
function processSourceFile(filename: string, isDefaultLib: boolean, refFile?: SourceFile, refPos?: number, refEnd?: number) {
|
||||
if (refEnd !== undefined && refPos !== undefined) {
|
||||
var start = refPos;
|
||||
var length = refEnd - refPos;
|
||||
}
|
||||
var diagnostic: DiagnosticMessage;
|
||||
if (hasExtension(filename)) {
|
||||
if (!options.allowNonTsExtensions && !fileExtensionIs(filename, ".ts")) {
|
||||
diagnostic = Diagnostics.File_0_must_have_extension_ts_or_d_ts;
|
||||
}
|
||||
else if (!findSourceFile(filename, isDefaultLib, refFile, refPos, refEnd)) {
|
||||
diagnostic = Diagnostics.File_0_not_found;
|
||||
}
|
||||
else if (refFile && host.getCanonicalFileName(filename) === host.getCanonicalFileName(refFile.filename)) {
|
||||
diagnostic = Diagnostics.A_file_cannot_have_a_reference_to_itself;
|
||||
}
|
||||
}
|
||||
else {
|
||||
if (!(findSourceFile(filename + ".ts", isDefaultLib, refFile, refPos, refEnd) || findSourceFile(filename + ".d.ts", isDefaultLib, refFile, refPos, refEnd))) {
|
||||
diagnostic = Diagnostics.File_0_not_found;
|
||||
filename += ".ts";
|
||||
}
|
||||
}
|
||||
|
||||
if (diagnostic) {
|
||||
if (refFile) {
|
||||
errors.push(createFileDiagnostic(refFile, start, length, diagnostic, filename));
|
||||
}
|
||||
else {
|
||||
errors.push(createCompilerDiagnostic(diagnostic, filename));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Get source file from normalized filename
|
||||
function findSourceFile(filename: string, isDefaultLib: boolean, refFile?: SourceFile, refStart?: number, refLength?: number): SourceFile {
|
||||
var canonicalName = host.getCanonicalFileName(filename);
|
||||
if (hasProperty(filesByName, canonicalName)) {
|
||||
// We've already looked for this file, use cached result
|
||||
return getSourceFileFromCache(filename, canonicalName, /*useAbsolutePath*/ false);
|
||||
}
|
||||
else {
|
||||
var normalizedAbsolutePath = getNormalizedAbsolutePath(filename, host.getCurrentDirectory());
|
||||
var canonicalAbsolutePath = host.getCanonicalFileName(normalizedAbsolutePath);
|
||||
if (hasProperty(filesByName, canonicalAbsolutePath)) {
|
||||
return getSourceFileFromCache(normalizedAbsolutePath, canonicalAbsolutePath, /*useAbsolutePath*/ true);
|
||||
}
|
||||
|
||||
// We haven't looked for this file, do so now and cache result
|
||||
var file = filesByName[canonicalName] = host.getSourceFile(filename, options.target, hostErrorMessage => {
|
||||
errors.push(createFileDiagnostic(refFile, refStart, refLength,
|
||||
Diagnostics.Cannot_read_file_0_Colon_1, filename, hostErrorMessage));
|
||||
});
|
||||
if (file) {
|
||||
seenNoDefaultLib = seenNoDefaultLib || file.hasNoDefaultLib;
|
||||
|
||||
// Set the source file for normalized absolute path
|
||||
filesByName[canonicalAbsolutePath] = file;
|
||||
|
||||
if (!options.noResolve) {
|
||||
var basePath = getDirectoryPath(filename);
|
||||
processReferencedFiles(file, basePath);
|
||||
processImportedModules(file, basePath);
|
||||
}
|
||||
if (isDefaultLib) {
|
||||
files.unshift(file);
|
||||
}
|
||||
else {
|
||||
files.push(file);
|
||||
}
|
||||
forEach(file.getSyntacticDiagnostics(), e => {
|
||||
errors.push(e);
|
||||
});
|
||||
}
|
||||
}
|
||||
return file;
|
||||
|
||||
function getSourceFileFromCache(filename: string, canonicalName: string, useAbsolutePath: boolean): SourceFile {
|
||||
var file = filesByName[canonicalName];
|
||||
if (file && host.useCaseSensitiveFileNames()) {
|
||||
var sourceFileName = useAbsolutePath ? getNormalizedAbsolutePath(file.filename, host.getCurrentDirectory()) : file.filename;
|
||||
if (canonicalName !== sourceFileName) {
|
||||
errors.push(createFileDiagnostic(refFile, refStart, refLength,
|
||||
Diagnostics.Filename_0_differs_from_already_included_filename_1_only_in_casing, filename, sourceFileName));
|
||||
}
|
||||
}
|
||||
return file;
|
||||
}
|
||||
}
|
||||
|
||||
function processReferencedFiles(file: SourceFile, basePath: string) {
|
||||
forEach(file.referencedFiles, ref => {
|
||||
var 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) {
|
||||
forEach(file.statements, node => {
|
||||
if (isExternalModuleImportDeclaration(node) &&
|
||||
getExternalModuleImportDeclarationExpression(node).kind === SyntaxKind.StringLiteral) {
|
||||
|
||||
var nameLiteral = <LiteralExpression>getExternalModuleImportDeclarationExpression(node);
|
||||
var moduleName = nameLiteral.text;
|
||||
if (moduleName) {
|
||||
var searchPath = basePath;
|
||||
while (true) {
|
||||
var searchName = normalizePath(combinePaths(searchPath, moduleName));
|
||||
if (findModuleSourceFile(searchName + ".ts", nameLiteral) || findModuleSourceFile(searchName + ".d.ts", nameLiteral)) {
|
||||
break;
|
||||
}
|
||||
|
||||
var parentPath = getDirectoryPath(searchPath);
|
||||
if (parentPath === searchPath) {
|
||||
break;
|
||||
}
|
||||
searchPath = parentPath;
|
||||
}
|
||||
}
|
||||
}
|
||||
else if (node.kind === SyntaxKind.ModuleDeclaration && (<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 (isExternalModuleImportDeclaration(node) &&
|
||||
getExternalModuleImportDeclarationExpression(node).kind === SyntaxKind.StringLiteral) {
|
||||
|
||||
var nameLiteral = <LiteralExpression>getExternalModuleImportDeclarationExpression(node);
|
||||
var moduleName = nameLiteral.text;
|
||||
if (moduleName) {
|
||||
// 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.
|
||||
var searchName = normalizePath(combinePaths(basePath, moduleName));
|
||||
var tsFile = findModuleSourceFile(searchName + ".ts", nameLiteral);
|
||||
if (!tsFile) {
|
||||
findModuleSourceFile(searchName + ".d.ts", nameLiteral);
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
function findModuleSourceFile(filename: string, nameLiteral: LiteralExpression) {
|
||||
return findSourceFile(filename, /* isDefaultLib */ false, file, nameLiteral.pos, nameLiteral.end - nameLiteral.pos);
|
||||
}
|
||||
}
|
||||
|
||||
function verifyCompilerOptions() {
|
||||
if (!options.sourceMap && (options.mapRoot || options.sourceRoot)) {
|
||||
// Error to specify --mapRoot or --sourceRoot without mapSourceFiles
|
||||
if (options.mapRoot) {
|
||||
errors.push(createCompilerDiagnostic(Diagnostics.Option_mapRoot_cannot_be_specified_without_specifying_sourcemap_option));
|
||||
}
|
||||
if (options.sourceRoot) {
|
||||
errors.push(createCompilerDiagnostic(Diagnostics.Option_sourceRoot_cannot_be_specified_without_specifying_sourcemap_option));
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
var firstExternalModule = forEach(files, f => isExternalModule(f) ? f : undefined);
|
||||
if (firstExternalModule && options.module === ModuleKind.None) {
|
||||
// We cannot use createDiagnosticFromNode because nodes do not have parents yet
|
||||
var externalModuleErrorSpan = getErrorSpanForNode(firstExternalModule.externalModuleIndicator);
|
||||
var errorStart = skipTrivia(firstExternalModule.text, externalModuleErrorSpan.pos);
|
||||
var errorLength = externalModuleErrorSpan.end - errorStart;
|
||||
errors.push(createFileDiagnostic(firstExternalModule, errorStart, errorLength, Diagnostics.Cannot_compile_external_modules_unless_the_module_flag_is_provided));
|
||||
}
|
||||
|
||||
// there has to be common source directory if user specified --outdir || --sourcRoot
|
||||
// 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 || firstExternalModule !== undefined))) {
|
||||
|
||||
var commonPathComponents: string[];
|
||||
forEach(files, sourceFile => {
|
||||
// Each file contributes into common source file path
|
||||
if (!(sourceFile.flags & NodeFlags.DeclarationFile)
|
||||
&& !fileExtensionIs(sourceFile.filename, ".js")) {
|
||||
var sourcePathComponents = getNormalizedPathComponents(sourceFile.filename, host.getCurrentDirectory());
|
||||
sourcePathComponents.pop(); // FileName is not part of directory
|
||||
if (commonPathComponents) {
|
||||
for (var i = 0; i < Math.min(commonPathComponents.length, sourcePathComponents.length); i++) {
|
||||
if (commonPathComponents[i] !== sourcePathComponents[i]) {
|
||||
if (i === 0) {
|
||||
errors.push(createCompilerDiagnostic(Diagnostics.Cannot_find_the_common_subdirectory_path_for_the_input_files));
|
||||
return;
|
||||
}
|
||||
|
||||
// New common path found that is 0 -> i-1
|
||||
commonPathComponents.length = i;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// If the fileComponent path completely matched and less than already found update the length
|
||||
if (sourcePathComponents.length < commonPathComponents.length) {
|
||||
commonPathComponents.length = sourcePathComponents.length;
|
||||
}
|
||||
}
|
||||
else {
|
||||
// first file
|
||||
commonPathComponents = sourcePathComponents;
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
commonSourceDirectory = getNormalizedPathFromPathComponents(commonPathComponents);
|
||||
if (commonSourceDirectory) {
|
||||
// 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;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
374
src/compiler/program.ts
Normal file
374
src/compiler/program.ts
Normal file
@ -0,0 +1,374 @@
|
||||
/// <reference path="sys.ts" />
|
||||
/// <reference path="emitter.ts" />
|
||||
|
||||
module ts {
|
||||
// TODO (drosen, mhegazy): Move to a more appropriate file.
|
||||
export function createCompilerHost(options: CompilerOptions): CompilerHost {
|
||||
var currentDirectory: string;
|
||||
var 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
|
||||
var unsupportedFileEncodingErrorCode = -2147024809;
|
||||
|
||||
function getSourceFile(filename: string, languageVersion: ScriptTarget, onError?: (message: string) => void): SourceFile {
|
||||
try {
|
||||
var text = sys.readFile(filename, options.charset);
|
||||
}
|
||||
catch (e) {
|
||||
if (onError) {
|
||||
onError(e.number === unsupportedFileEncodingErrorCode ?
|
||||
createCompilerDiagnostic(Diagnostics.Unsupported_file_encoding).messageText :
|
||||
e.message);
|
||||
}
|
||||
text = "";
|
||||
}
|
||||
|
||||
return text !== undefined ? createSourceFile(filename, text, languageVersion) : undefined;
|
||||
}
|
||||
|
||||
function writeFile(fileName: string, data: string, writeByteOrderMark: boolean, onError?: (message: string) => void) {
|
||||
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)) {
|
||||
var parentDirectory = getDirectoryPath(directoryPath);
|
||||
ensureDirectoriesExist(parentDirectory);
|
||||
sys.createDirectory(directoryPath);
|
||||
}
|
||||
}
|
||||
|
||||
try {
|
||||
ensureDirectoriesExist(getDirectoryPath(normalizePath(fileName)));
|
||||
sys.writeFile(fileName, data, writeByteOrderMark);
|
||||
}
|
||||
catch (e) {
|
||||
if (onError) {
|
||||
onError(e.message);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return {
|
||||
getSourceFile,
|
||||
getDefaultLibFilename: options => combinePaths(getDirectoryPath(normalizePath(sys.getExecutingFilePath())), options.target === ScriptTarget.ES6 ? "lib.es6.d.ts" : "lib.d.ts"),
|
||||
writeFile,
|
||||
getCurrentDirectory: () => currentDirectory || (currentDirectory = sys.getCurrentDirectory()),
|
||||
useCaseSensitiveFileNames: () => sys.useCaseSensitiveFileNames,
|
||||
getCanonicalFileName,
|
||||
getNewLine: () => sys.newLine
|
||||
};
|
||||
}
|
||||
|
||||
export function createProgram(rootNames: string[], options: CompilerOptions, host: CompilerHost): Program {
|
||||
var program: Program;
|
||||
var files: SourceFile[] = [];
|
||||
var filesByName: Map<SourceFile> = {};
|
||||
var errors: Diagnostic[] = [];
|
||||
var seenNoDefaultLib = options.noLib;
|
||||
var commonSourceDirectory: string;
|
||||
|
||||
forEach(rootNames, name => processRootFile(name, false));
|
||||
if (!seenNoDefaultLib) {
|
||||
processRootFile(host.getDefaultLibFilename(options), true);
|
||||
}
|
||||
verifyCompilerOptions();
|
||||
errors.sort(compareDiagnostics);
|
||||
|
||||
var fullTypeChecker: TypeChecker;
|
||||
var nonFullTypeChecker: TypeChecker;
|
||||
|
||||
function getTypeChecker(full: boolean) {
|
||||
if (full) {
|
||||
return fullTypeChecker || (fullTypeChecker = createTypeChecker(program, full));
|
||||
}
|
||||
else {
|
||||
return nonFullTypeChecker || (nonFullTypeChecker = createTypeChecker(program, full));
|
||||
}
|
||||
}
|
||||
|
||||
program = {
|
||||
getSourceFile: getSourceFile,
|
||||
getSourceFiles: () => files,
|
||||
getCompilerOptions: () => options,
|
||||
getCompilerHost: () => host,
|
||||
getDiagnostics: getDiagnostics,
|
||||
getGlobalDiagnostics: getGlobalDiagnostics,
|
||||
getDeclarationDiagnostics: getDeclarationDiagnostics,
|
||||
getTypeChecker,
|
||||
getCommonSourceDirectory: () => commonSourceDirectory,
|
||||
emitFiles: invokeEmitter
|
||||
};
|
||||
return program;
|
||||
|
||||
function getDeclarationDiagnostics(targetSourceFile: SourceFile): Diagnostic[]{
|
||||
var fullTypeChecker = getTypeChecker(/*full:*/ true);
|
||||
fullTypeChecker.getDiagnostics(targetSourceFile);
|
||||
var resolver = fullTypeChecker.getEmitResolver();
|
||||
return ts.getDeclarationDiagnostics(program, resolver, targetSourceFile);
|
||||
}
|
||||
|
||||
function invokeEmitter(targetSourceFile?: SourceFile) {
|
||||
var resolver = getTypeChecker(/*full:*/ true).getEmitResolver();
|
||||
return emitFiles(resolver, targetSourceFile);
|
||||
}
|
||||
|
||||
function getSourceFile(filename: string) {
|
||||
filename = host.getCanonicalFileName(filename);
|
||||
return hasProperty(filesByName, filename) ? filesByName[filename] : undefined;
|
||||
}
|
||||
|
||||
function getDiagnostics(sourceFile?: SourceFile): Diagnostic[] {
|
||||
return sourceFile ? filter(errors, e => e.file === sourceFile) : errors;
|
||||
}
|
||||
|
||||
function getGlobalDiagnostics(): Diagnostic[] {
|
||||
return filter(errors, e => !e.file);
|
||||
}
|
||||
|
||||
function hasExtension(filename: string): boolean {
|
||||
return getBaseFilename(filename).indexOf(".") >= 0;
|
||||
}
|
||||
|
||||
function processRootFile(filename: string, isDefaultLib: boolean) {
|
||||
processSourceFile(normalizePath(filename), isDefaultLib);
|
||||
}
|
||||
|
||||
function processSourceFile(filename: string, isDefaultLib: boolean, refFile?: SourceFile, refPos?: number, refEnd?: number) {
|
||||
if (refEnd !== undefined && refPos !== undefined) {
|
||||
var start = refPos;
|
||||
var length = refEnd - refPos;
|
||||
}
|
||||
var diagnostic: DiagnosticMessage;
|
||||
if (hasExtension(filename)) {
|
||||
if (!options.allowNonTsExtensions && !fileExtensionIs(filename, ".ts")) {
|
||||
diagnostic = Diagnostics.File_0_must_have_extension_ts_or_d_ts;
|
||||
}
|
||||
else if (!findSourceFile(filename, isDefaultLib, refFile, refPos, refEnd)) {
|
||||
diagnostic = Diagnostics.File_0_not_found;
|
||||
}
|
||||
else if (refFile && host.getCanonicalFileName(filename) === host.getCanonicalFileName(refFile.filename)) {
|
||||
diagnostic = Diagnostics.A_file_cannot_have_a_reference_to_itself;
|
||||
}
|
||||
}
|
||||
else {
|
||||
if (!(findSourceFile(filename + ".ts", isDefaultLib, refFile, refPos, refEnd) || findSourceFile(filename + ".d.ts", isDefaultLib, refFile, refPos, refEnd))) {
|
||||
diagnostic = Diagnostics.File_0_not_found;
|
||||
filename += ".ts";
|
||||
}
|
||||
}
|
||||
|
||||
if (diagnostic) {
|
||||
if (refFile) {
|
||||
errors.push(createFileDiagnostic(refFile, start, length, diagnostic, filename));
|
||||
}
|
||||
else {
|
||||
errors.push(createCompilerDiagnostic(diagnostic, filename));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Get source file from normalized filename
|
||||
function findSourceFile(filename: string, isDefaultLib: boolean, refFile?: SourceFile, refStart?: number, refLength?: number): SourceFile {
|
||||
var canonicalName = host.getCanonicalFileName(filename);
|
||||
if (hasProperty(filesByName, canonicalName)) {
|
||||
// We've already looked for this file, use cached result
|
||||
return getSourceFileFromCache(filename, canonicalName, /*useAbsolutePath*/ false);
|
||||
}
|
||||
else {
|
||||
var normalizedAbsolutePath = getNormalizedAbsolutePath(filename, host.getCurrentDirectory());
|
||||
var canonicalAbsolutePath = host.getCanonicalFileName(normalizedAbsolutePath);
|
||||
if (hasProperty(filesByName, canonicalAbsolutePath)) {
|
||||
return getSourceFileFromCache(normalizedAbsolutePath, canonicalAbsolutePath, /*useAbsolutePath*/ true);
|
||||
}
|
||||
|
||||
// We haven't looked for this file, do so now and cache result
|
||||
var file = filesByName[canonicalName] = host.getSourceFile(filename, options.target, hostErrorMessage => {
|
||||
errors.push(createFileDiagnostic(refFile, refStart, refLength,
|
||||
Diagnostics.Cannot_read_file_0_Colon_1, filename, hostErrorMessage));
|
||||
});
|
||||
if (file) {
|
||||
seenNoDefaultLib = seenNoDefaultLib || file.hasNoDefaultLib;
|
||||
|
||||
// Set the source file for normalized absolute path
|
||||
filesByName[canonicalAbsolutePath] = file;
|
||||
|
||||
if (!options.noResolve) {
|
||||
var basePath = getDirectoryPath(filename);
|
||||
processReferencedFiles(file, basePath);
|
||||
processImportedModules(file, basePath);
|
||||
}
|
||||
if (isDefaultLib) {
|
||||
files.unshift(file);
|
||||
}
|
||||
else {
|
||||
files.push(file);
|
||||
}
|
||||
forEach(file.getSyntacticDiagnostics(), e => {
|
||||
errors.push(e);
|
||||
});
|
||||
}
|
||||
}
|
||||
return file;
|
||||
|
||||
function getSourceFileFromCache(filename: string, canonicalName: string, useAbsolutePath: boolean): SourceFile {
|
||||
var file = filesByName[canonicalName];
|
||||
if (file && host.useCaseSensitiveFileNames()) {
|
||||
var sourceFileName = useAbsolutePath ? getNormalizedAbsolutePath(file.filename, host.getCurrentDirectory()) : file.filename;
|
||||
if (canonicalName !== sourceFileName) {
|
||||
errors.push(createFileDiagnostic(refFile, refStart, refLength,
|
||||
Diagnostics.Filename_0_differs_from_already_included_filename_1_only_in_casing, filename, sourceFileName));
|
||||
}
|
||||
}
|
||||
return file;
|
||||
}
|
||||
}
|
||||
|
||||
function processReferencedFiles(file: SourceFile, basePath: string) {
|
||||
forEach(file.referencedFiles, ref => {
|
||||
var 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) {
|
||||
forEach(file.statements, node => {
|
||||
if (isExternalModuleImportDeclaration(node) &&
|
||||
getExternalModuleImportDeclarationExpression(node).kind === SyntaxKind.StringLiteral) {
|
||||
|
||||
var nameLiteral = <LiteralExpression>getExternalModuleImportDeclarationExpression(node);
|
||||
var moduleName = nameLiteral.text;
|
||||
if (moduleName) {
|
||||
var searchPath = basePath;
|
||||
while (true) {
|
||||
var searchName = normalizePath(combinePaths(searchPath, moduleName));
|
||||
if (findModuleSourceFile(searchName + ".ts", nameLiteral) || findModuleSourceFile(searchName + ".d.ts", nameLiteral)) {
|
||||
break;
|
||||
}
|
||||
|
||||
var parentPath = getDirectoryPath(searchPath);
|
||||
if (parentPath === searchPath) {
|
||||
break;
|
||||
}
|
||||
searchPath = parentPath;
|
||||
}
|
||||
}
|
||||
}
|
||||
else if (node.kind === SyntaxKind.ModuleDeclaration && (<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 (isExternalModuleImportDeclaration(node) &&
|
||||
getExternalModuleImportDeclarationExpression(node).kind === SyntaxKind.StringLiteral) {
|
||||
|
||||
var nameLiteral = <LiteralExpression>getExternalModuleImportDeclarationExpression(node);
|
||||
var moduleName = nameLiteral.text;
|
||||
if (moduleName) {
|
||||
// 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.
|
||||
var searchName = normalizePath(combinePaths(basePath, moduleName));
|
||||
var tsFile = findModuleSourceFile(searchName + ".ts", nameLiteral);
|
||||
if (!tsFile) {
|
||||
findModuleSourceFile(searchName + ".d.ts", nameLiteral);
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
function findModuleSourceFile(filename: string, nameLiteral: LiteralExpression) {
|
||||
return findSourceFile(filename, /* isDefaultLib */ false, file, nameLiteral.pos, nameLiteral.end - nameLiteral.pos);
|
||||
}
|
||||
}
|
||||
|
||||
function verifyCompilerOptions() {
|
||||
if (!options.sourceMap && (options.mapRoot || options.sourceRoot)) {
|
||||
// Error to specify --mapRoot or --sourceRoot without mapSourceFiles
|
||||
if (options.mapRoot) {
|
||||
errors.push(createCompilerDiagnostic(Diagnostics.Option_mapRoot_cannot_be_specified_without_specifying_sourcemap_option));
|
||||
}
|
||||
if (options.sourceRoot) {
|
||||
errors.push(createCompilerDiagnostic(Diagnostics.Option_sourceRoot_cannot_be_specified_without_specifying_sourcemap_option));
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
var firstExternalModule = forEach(files, f => isExternalModule(f) ? f : undefined);
|
||||
if (firstExternalModule && options.module === ModuleKind.None) {
|
||||
// We cannot use createDiagnosticFromNode because nodes do not have parents yet
|
||||
var externalModuleErrorSpan = getErrorSpanForNode(firstExternalModule.externalModuleIndicator);
|
||||
var errorStart = skipTrivia(firstExternalModule.text, externalModuleErrorSpan.pos);
|
||||
var errorLength = externalModuleErrorSpan.end - errorStart;
|
||||
errors.push(createFileDiagnostic(firstExternalModule, errorStart, errorLength, Diagnostics.Cannot_compile_external_modules_unless_the_module_flag_is_provided));
|
||||
}
|
||||
|
||||
// there has to be common source directory if user specified --outdir || --sourcRoot
|
||||
// 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 || firstExternalModule !== undefined))) {
|
||||
|
||||
var commonPathComponents: string[];
|
||||
forEach(files, sourceFile => {
|
||||
// Each file contributes into common source file path
|
||||
if (!(sourceFile.flags & NodeFlags.DeclarationFile)
|
||||
&& !fileExtensionIs(sourceFile.filename, ".js")) {
|
||||
var sourcePathComponents = getNormalizedPathComponents(sourceFile.filename, host.getCurrentDirectory());
|
||||
sourcePathComponents.pop(); // FileName is not part of directory
|
||||
if (commonPathComponents) {
|
||||
for (var i = 0; i < Math.min(commonPathComponents.length, sourcePathComponents.length); i++) {
|
||||
if (commonPathComponents[i] !== sourcePathComponents[i]) {
|
||||
if (i === 0) {
|
||||
errors.push(createCompilerDiagnostic(Diagnostics.Cannot_find_the_common_subdirectory_path_for_the_input_files));
|
||||
return;
|
||||
}
|
||||
|
||||
// New common path found that is 0 -> i-1
|
||||
commonPathComponents.length = i;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// If the fileComponent path completely matched and less than already found update the length
|
||||
if (sourcePathComponents.length < commonPathComponents.length) {
|
||||
commonPathComponents.length = sourcePathComponents.length;
|
||||
}
|
||||
}
|
||||
else {
|
||||
// first file
|
||||
commonPathComponents = sourcePathComponents;
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
commonSourceDirectory = getNormalizedPathFromPathComponents(commonPathComponents);
|
||||
if (commonSourceDirectory) {
|
||||
// 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;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -1,4 +1,3 @@
|
||||
/// <reference path="types.ts"/>
|
||||
/// <reference path="core.ts"/>
|
||||
/// <reference path="diagnosticInformationMap.generated.ts"/>
|
||||
|
||||
|
||||
@ -1,11 +1,4 @@
|
||||
/// <reference path="core.ts"/>
|
||||
/// <reference path="sys.ts"/>
|
||||
/// <reference path="types.ts"/>
|
||||
/// <reference path="scanner.ts"/>
|
||||
/// <reference path="parser.ts"/>
|
||||
/// <reference path="binder.ts"/>
|
||||
/// <reference path="checker.ts"/>
|
||||
/// <reference path="emitter.ts"/>
|
||||
/// <reference path="program.ts"/>
|
||||
/// <reference path="commandLineParser.ts"/>
|
||||
|
||||
module ts {
|
||||
@ -298,7 +291,7 @@ module ts {
|
||||
}
|
||||
else {
|
||||
var emitStart = new Date().getTime();
|
||||
var emitOutput = checker.emitFiles();
|
||||
var emitOutput = program.emitFiles();
|
||||
var emitErrors = emitOutput.diagnostics;
|
||||
exitStatus = emitOutput.emitResultStatus;
|
||||
var reportStart = new Date().getTime();
|
||||
|
||||
@ -1,5 +1,3 @@
|
||||
/// <reference path="core.ts"/>
|
||||
|
||||
module ts {
|
||||
export interface Map<T> {
|
||||
[index: string]: T;
|
||||
@ -914,10 +912,14 @@ module ts {
|
||||
getSourceFiles(): SourceFile[];
|
||||
getCompilerOptions(): CompilerOptions;
|
||||
getCompilerHost(): CompilerHost;
|
||||
|
||||
getDiagnostics(sourceFile?: SourceFile): Diagnostic[];
|
||||
getGlobalDiagnostics(): Diagnostic[];
|
||||
getDeclarationDiagnostics(sourceFile: SourceFile): Diagnostic[];
|
||||
|
||||
getTypeChecker(fullTypeCheckMode: boolean): TypeChecker;
|
||||
getCommonSourceDirectory(): string;
|
||||
emitFiles(targetSourceFile?: SourceFile): EmitResult;
|
||||
}
|
||||
|
||||
export interface SourceMapSpan {
|
||||
@ -959,14 +961,13 @@ module ts {
|
||||
|
||||
export interface TypeChecker {
|
||||
getProgram(): Program;
|
||||
getEmitResolver(): EmitResolver;
|
||||
getDiagnostics(sourceFile?: SourceFile): Diagnostic[];
|
||||
getDeclarationDiagnostics(sourceFile: SourceFile): Diagnostic[];
|
||||
getGlobalDiagnostics(): Diagnostic[];
|
||||
getNodeCount(): number;
|
||||
getIdentifierCount(): number;
|
||||
getSymbolCount(): number;
|
||||
getTypeCount(): number;
|
||||
emitFiles(targetSourceFile?: SourceFile): EmitResult;
|
||||
getTypeOfSymbolAtLocation(symbol: Symbol, node: Node): Type;
|
||||
getDeclaredTypeOfSymbol(symbol: Symbol): Type;
|
||||
getPropertiesOfType(type: Type): Symbol[];
|
||||
|
||||
@ -2202,7 +2202,7 @@ module FourSlash {
|
||||
if (errors.length > 0) {
|
||||
throw new Error('Error compiling ' + fileName + ': ' + errors.map(e => e.messageText).join('\r\n'));
|
||||
}
|
||||
checker.emitFiles();
|
||||
program.emitFiles();
|
||||
result = result || ''; // Might have an empty fourslash file
|
||||
|
||||
// Compile and execute the test
|
||||
|
||||
@ -16,8 +16,6 @@
|
||||
|
||||
/// <reference path='..\services\services.ts' />
|
||||
/// <reference path='..\services\shims.ts' />
|
||||
/// <reference path='..\compiler\core.ts' />
|
||||
/// <reference path='..\compiler\sys.ts' />
|
||||
/// <reference path='external\mocha.d.ts'/>
|
||||
/// <reference path='external\chai.d.ts'/>
|
||||
/// <reference path='sourceMapRecorder.ts'/>
|
||||
@ -1053,7 +1051,7 @@ module Harness {
|
||||
// only emit if there weren't parse errors
|
||||
var emitResult: ts.EmitResult;
|
||||
if (!isEmitBlocked) {
|
||||
emitResult = checker.emitFiles();
|
||||
emitResult = program.emitFiles();
|
||||
}
|
||||
|
||||
var errors: HarnessDiagnostic[] = [];
|
||||
|
||||
@ -132,7 +132,7 @@ class ProjectRunner extends RunnerBase {
|
||||
if (!errors.length) {
|
||||
var checker = program.getTypeChecker(/*fullTypeCheck*/ true);
|
||||
errors = checker.getDiagnostics();
|
||||
var emitResult = checker.emitFiles();
|
||||
var emitResult = program.emitFiles();
|
||||
errors = ts.concatenate(errors, emitResult.diagnostics);
|
||||
sourceMapData = emitResult.sourceMaps;
|
||||
|
||||
|
||||
@ -1,8 +1,4 @@
|
||||
/// <reference path="..\compiler\types.ts"/>
|
||||
/// <reference path="..\compiler\core.ts"/>
|
||||
/// <reference path="..\compiler\scanner.ts"/>
|
||||
/// <reference path="..\compiler\parser.ts"/>
|
||||
/// <reference path="..\compiler\checker.ts"/>
|
||||
/// <reference path="..\compiler\program.ts"/>
|
||||
|
||||
/// <reference path='breakpoints.ts' />
|
||||
/// <reference path='outliningElementsCollector.ts' />
|
||||
@ -13,7 +9,6 @@
|
||||
/// <reference path='formatting\smartIndenter.ts' />
|
||||
|
||||
module ts {
|
||||
|
||||
export var servicesVersion = "0.4"
|
||||
|
||||
export interface Node {
|
||||
@ -2435,7 +2430,7 @@ module ts {
|
||||
var allDiagnostics = checker.getDiagnostics(targetSourceFile);
|
||||
if (compilerOptions.declaration) {
|
||||
// If '-d' is enabled, check for emitter error. One example of emitter error is export class implements non-export interface
|
||||
allDiagnostics = allDiagnostics.concat(checker.getDeclarationDiagnostics(targetSourceFile));
|
||||
allDiagnostics = allDiagnostics.concat(program.getDeclarationDiagnostics(targetSourceFile));
|
||||
}
|
||||
return allDiagnostics
|
||||
}
|
||||
@ -4870,7 +4865,7 @@ module ts {
|
||||
// Initialize writer for CompilerHost.writeFile
|
||||
writer = getEmitOutputWriter;
|
||||
|
||||
var emitOutput = getFullTypeCheckChecker().emitFiles(sourceFile);
|
||||
var emitOutput = program.emitFiles(sourceFile);
|
||||
|
||||
// Reset writer back to undefined to make sure that we produce an error message if CompilerHost.writeFile method is called when we are not in getEmitOutput
|
||||
writer = undefined;
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user