Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Relax import/export elision rules for separate compilation #2550

Merged
merged 10 commits into from
Mar 31, 2015
19 changes: 16 additions & 3 deletions src/compiler/checker.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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) ||
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

i think we can just make this the default behavior. the only cases we would be breaking are error cases, that in the past we elided modules imports, and now we will be writing them. is that correct?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

done

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

actually I've re-checked - this might lead to cascading errors so I'll keep relaxed behavior specific to separateCompilation mode

(target !== unknownSymbol && target.flags & SymbolFlags.Value && !isConstEnumOrConstEnumOnlyModule(target));

if (markAlias) {
markAliasSymbolAsReferenced(symbol);
}
}
}

Expand Down Expand Up @@ -9747,7 +9753,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);
}
Expand Down Expand Up @@ -11266,11 +11274,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);
}
Expand Down
1 change: 1 addition & 0 deletions src/compiler/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1587,6 +1587,7 @@ module ts {
target?: ScriptTarget;
version?: boolean;
watch?: boolean;
separateCompilation?: boolean;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

i think we need to make use of const enums from an ambient declaration an error while using this module, also consider using of internal modules on the global scope as an error..

also we need to make that available on the command line, as an experimental flag.

/* @internal */ stripInternal?: boolean;
[option: string]: string | number | boolean;
}
Expand Down
71 changes: 71 additions & 0 deletions src/services/services.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1635,6 +1635,77 @@ module ts {
sourceFile.scriptSnapshot = scriptSnapshot;
}

export function transpile(input: string, compilerOptions?: CompilerOptions, fileName?: string, syntaxErrors?: Diagnostic[]): string {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We need some comments on what this function is about and the constraints of using it.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

done

let options = compilerOptions ? ts.clone(compilerOptions) : getDefaultCompilerOptions();
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

u do not need the ts. prefix


// Single file transformation is only guaranteed to be correct inside an external module.
// External modules have their own scope and cannot contribute to internal modules outside their
// scope.
if (options.target !== ScriptTarget.ES6 && (!options.module || options.module === ModuleKind.None)) {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We need to check that whatever we parse is an external module as well. else it should be an error.

// TODO: add errors
}

// In single file transformation the compiler does not have access to declaration sites.
// Const enum property accesses will not be detected if they are not in the same file as the enum.
// thus they are going to be emitted as normal propety access. To ensure correct behaviour at runtime,
// we need to generate the actual enum object so that the property 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.
options.allowNonTsExtensions = true;

// enable relaxed emit rules
options.separateCompilation = true;

// We are not resolving references or modules, or even including the default library. Disabling
// emit on error will block generating the output and has no meaningful use here.
options.noEmitOnError = false;

// No resolution requests will be honored anyways. So do not do it
options.noResolve = true;

// TODO (vladima): this should be enabled after adding support to inlinable source maps
options.sourceMap = false;

// Parse
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

As per our discussion today, for transpile function:
sourceMap == true // should be an error
declaration == true // should be an error
noEmitOnError == true // should be an error
perserveConstEnum == false // should be an error
out == true // should be an error

Now we need a good name for the flag :)

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

done except for the flag name

var inputFileName = fileName || "module.ts";
var sourceFile = ts.createSourceFile(inputFileName, input, options.target);

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

u do not need the ts

// 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) => {
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"
};

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);
Expand Down