From a51f0bf8bbca289f692e046cecd18670fd5cf6dd Mon Sep 17 00:00:00 2001 From: Vladimir Matveev Date: Wed, 25 Mar 2015 23:18:58 -0700 Subject: [PATCH] added relaxed emit rules for separate compilation --- src/compiler/checker.ts | 19 ++++++++-- src/compiler/types.ts | 1 + src/services/services.ts | 80 ++++++++++++++++++++++++++++++++++++++++ 3 files changed, 97 insertions(+), 3 deletions(-) diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts index 6fdaac79cea..9765ceee4a9 100644 --- a/src/compiler/checker.ts +++ b/src/compiler/checker.ts @@ -713,8 +713,14 @@ module ts { function markExportAsReferenced(node: ImportEqualsDeclaration | ExportAssignment | ExportSpecifier) { let symbol = getSymbolOfNode(node); let target = resolveAlias(symbol); - if (target && target !== unknownSymbol && target.flags & SymbolFlags.Value && !isConstEnumOrConstEnumOnlyModule(target)) { - markAliasSymbolAsReferenced(symbol); + if (target) { + let markAlias = + (target === unknownSymbol && compilerOptions.separateCompilation) || + (target !== unknownSymbol && target.flags & SymbolFlags.Value && !isConstEnumOrConstEnumOnlyModule(target)); + + if (markAlias) { + markAliasSymbolAsReferenced(symbol); + } } } @@ -9745,7 +9751,9 @@ module ts { checkKindsOfPropertyMemberOverrides(type, baseType); } + } + if (type.baseTypes.length || (baseTypeNode && compilerOptions.separateCompilation)) { // Check that base type can be evaluated as expression checkExpressionOrQualifiedName(baseTypeNode.typeName); } @@ -11264,11 +11272,16 @@ module ts { // parent is not source file or it is not reference to internal module return false; } - return isAliasResolvedToValue(getSymbolOfNode(node)); + + var isValue = isAliasResolvedToValue(getSymbolOfNode(node)); + return isValue && node.moduleReference && !nodeIsMissing(node.moduleReference); } function isAliasResolvedToValue(symbol: Symbol): boolean { let target = resolveAlias(symbol); + if (target === unknownSymbol && compilerOptions.separateCompilation) { + return true; + } // const enums and modules that contain only const enums are not considered values from the emit perespective return target !== unknownSymbol && target.flags & SymbolFlags.Value && !isConstEnumOrConstEnumOnlyModule(target); } diff --git a/src/compiler/types.ts b/src/compiler/types.ts index e5697f9f372..9482339d09b 100644 --- a/src/compiler/types.ts +++ b/src/compiler/types.ts @@ -1587,6 +1587,7 @@ module ts { target?: ScriptTarget; version?: boolean; watch?: boolean; + separateCompilation?: boolean; /* @internal */ stripInternal?: boolean; /* @internal */ preserveNewLines?: boolean; /* @internal */ cacheDownlevelForOfLength?: boolean; diff --git a/src/services/services.ts b/src/services/services.ts index 41ffc04e5ae..5f8ab267e7d 100644 --- a/src/services/services.ts +++ b/src/services/services.ts @@ -1635,6 +1635,86 @@ module ts { sourceFile.scriptSnapshot = scriptSnapshot; } + export function transpile(input: string, compilerOptions?: CompilerOptions, fileName?: string, syntaxErrors?: Diagnostic[]): string { + let options = compilerOptions ? ts.clone(compilerOptions) : getDefaultCompilerOptions(); + + // Single file transformation is only guranteed to be correct if inside an external module. + // External modules have thier own scope and can not contribute to internal modules outside their + // scope. + if (options.target !== ScriptTarget.ES6 && (!options.module || options.module === ModuleKind.None)) { + // add errors + } + + // In sigle file transformation the compiler does not have access to declaration sites. + // Const enum property access will not be detected if they are not in the same file as the enum. + // thus they are goign to be emitted as normal propety access. To ensure correct behaviour at runtime, + // we need to generare the actual enum object so that the proprty accesses do not fail. + options.preserveConstEnums = true; + + // No reason to get declarations, we are only returning javascript + options.declaration = false; + + // Filename can be non-ts file. We are not locating any modules as well, so allow + // non-ts extensions + options.allowNonTsExtensions = true; + + // enable relaxed emit rules + options.separateCompilation = true; + + // We are not resolving references or modules, or even including the library. Disabling + // emit on error will block generating the output and has no meaningful use here. + options.noEmitOnError = false; + + // No resolution requests will be honered anyways. So do not do it + options.noResolve = true; + + // TODO (vladima): add inlineSourceMap once it is checked in + //if (options.sourceMap) { + // // We need to return a single string, so inline the sourceMap in the output + // options.inlineSourceMap = true;; + //} + + // Parse + var inputFileName = fileName || "module.ts"; + var sourceFile = ts.createSourceFile(inputFileName, input, options.target); + + // Store syntactic diagnostics + if (syntaxErrors && sourceFile.parseDiagnostics) { + syntaxErrors.push(...sourceFile.parseDiagnostics); + } + + // Output + let outputText: string; + + // Create a compilerHost object to allow the compiler to read and write files + var compilerHost: CompilerHost = { + getSourceFile: (fileName, target) => fileName === inputFileName ? sourceFile : undefined, + writeFile: (name, text, writeByteOrderMark) => { + if (ts.fileExtensionIs(name, ".js")) { + Debug.assert(outputText === undefined, "Unexpected multiple outputs for the file: " + name); + outputText = text; + } + }, + getDefaultLibFileName: () => "lib.d.ts", + useCaseSensitiveFileNames: () => false, + getCanonicalFileName: fileName => fileName, + getCurrentDirectory: () => "", + getNewLine: () => "\r\n" + }; + + // Note: The emitter needs a the checker to walk the file, so we will create a program for this + // though single file transformation does not really need this. First we need to drop the emitter + // dependcy on the checker and then implement a new resolver that does not do the full check. + var program = ts.createProgram([inputFileName], options, compilerHost); + + // Emit + program.emit(); + + Debug.assert(outputText !== undefined, "Output generation failed"); + + return outputText; + } + export function createLanguageServiceSourceFile(fileName: string, scriptSnapshot: IScriptSnapshot, scriptTarget: ScriptTarget, version: string, setNodeParents: boolean): SourceFile { let sourceFile = createSourceFile(fileName, scriptSnapshot.getText(0, scriptSnapshot.getLength()), scriptTarget, setNodeParents); setSourceFileFields(sourceFile, scriptSnapshot, version);