Skip to content

Commit

Permalink
Allow allowJs and declaration to be used together
Browse files Browse the repository at this point in the history
This intorduces a new symbol-based declaration emitter - currently this
is only used for JSON and JavaScript, as the output is likely worse than
what the other declaration emitter is capable of. In addition, it is
still incomplete - it does not yet support serializaing namespaces.
  • Loading branch information
weswigham committed Jul 12, 2019
1 parent 37f2e59 commit be85d75
Show file tree
Hide file tree
Showing 102 changed files with 6,455 additions and 194 deletions.
591 changes: 590 additions & 1 deletion src/compiler/checker.ts

Large diffs are not rendered by default.

12 changes: 5 additions & 7 deletions src/compiler/emitter.ts
Original file line number Diff line number Diff line change
Expand Up @@ -97,9 +97,7 @@ namespace ts {
comparePaths(sourceFile.fileName, ownOutputFilePath, host.getCurrentDirectory(), !host.useCaseSensitiveFileNames()) === Comparison.EqualTo;
const jsFilePath = options.emitDeclarationOnly || isJsonEmittedToSameLocation ? undefined : ownOutputFilePath;
const sourceMapFilePath = !jsFilePath || isJsonSourceFile(sourceFile) ? undefined : getSourceMapFilePath(jsFilePath, options);
// For legacy reasons (ie, we have baselines capturing the behavior), js files don't report a .d.ts output path - this would only matter if `declaration` and `allowJs` were both on, which is currently an error
const isJs = isSourceFileJS(sourceFile);
const declarationFilePath = ((forceDtsPaths || getEmitDeclarations(options)) && !isJs) ? getDeclarationEmitOutputFilePath(sourceFile.fileName, host) : undefined;
const declarationFilePath = (forceDtsPaths || getEmitDeclarations(options)) ? getDeclarationEmitOutputFilePath(sourceFile.fileName, host) : undefined;
const declarationMapPath = declarationFilePath && getAreDeclarationMapsEnabled(options) ? declarationFilePath + ".map" : undefined;
return { jsFilePath, sourceMapFilePath, declarationFilePath, declarationMapPath, buildInfoPath: undefined };
}
Expand Down Expand Up @@ -351,17 +349,16 @@ namespace ts {
declarationFilePath: string | undefined,
declarationMapPath: string | undefined,
relativeToBuildInfo: (path: string) => string) {
if (!sourceFileOrBundle || !(declarationFilePath && !isInJSFile(sourceFileOrBundle))) {
if (!sourceFileOrBundle || !declarationFilePath) {
return;
}
const sourceFiles = isSourceFile(sourceFileOrBundle) ? [sourceFileOrBundle] : sourceFileOrBundle.sourceFiles;
// Setup and perform the transformation to retrieve declarations from the input files
const nonJsFiles = filter(sourceFiles, isSourceFileNotJS);
const inputListOrBundle = (compilerOptions.outFile || compilerOptions.out) ? [createBundle(nonJsFiles, !isSourceFile(sourceFileOrBundle) ? sourceFileOrBundle.prepends : undefined)] : nonJsFiles;
const inputListOrBundle = (compilerOptions.outFile || compilerOptions.out) ? [createBundle(sourceFiles, !isSourceFile(sourceFileOrBundle) ? sourceFileOrBundle.prepends : undefined)] : sourceFiles;
if (emitOnlyDtsFiles && !getEmitDeclarations(compilerOptions)) {
// Checker wont collect the linked aliases since thats only done when declaration is enabled.
// Do that here when emitting only dts files
nonJsFiles.forEach(collectLinkedAliases);
sourceFiles.forEach(collectLinkedAliases);
}
const declarationTransform = transformNodes(resolver, host, compilerOptions, inputListOrBundle, declarationTransformers, /*allowDtsFiles*/ false);
if (length(declarationTransform.diagnostics)) {
Expand Down Expand Up @@ -615,6 +612,7 @@ namespace ts {
getAllAccessorDeclarations: notImplemented,
getSymbolOfExternalModuleSpecifier: notImplemented,
isBindingCapturedByNode: notImplemented,
getDeclarationStatementsForSourceFile: notImplemented,
};

/*@internal*/
Expand Down
5 changes: 5 additions & 0 deletions src/compiler/factory.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2139,6 +2139,11 @@ namespace ts {
: node;
}

/* @internal */
export function createEmptyExports() {
return createExportDeclaration(/*decorators*/ undefined, /*modifiers*/ undefined, createNamedExports([]), /*moduleSpecifier*/ undefined);
}

export function createNamedExports(elements: ReadonlyArray<ExportSpecifier>) {
const node = <NamedExports>createSynthesizedNode(SyntaxKind.NamedExports);
node.elements = createNodeArray(elements);
Expand Down
4 changes: 0 additions & 4 deletions src/compiler/program.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2906,10 +2906,6 @@ namespace ts {
}
}

if (!options.noEmit && options.allowJs && getEmitDeclarations(options)) {
createDiagnosticForOptionName(Diagnostics.Option_0_cannot_be_specified_with_option_1, "allowJs", getEmitDeclarationOptionName(options));
}

if (options.checkJs && !options.allowJs) {
programDiagnostics.add(createCompilerDiagnostic(Diagnostics.Option_0_cannot_be_specified_without_specifying_option_1, "checkJs", "allowJs"));
}
Expand Down
23 changes: 9 additions & 14 deletions src/compiler/transformers/declarations.ts
Original file line number Diff line number Diff line change
@@ -1,11 +1,8 @@
/*@internal*/
namespace ts {
export function getDeclarationDiagnostics(host: EmitHost, resolver: EmitResolver, file: SourceFile | undefined): DiagnosticWithLocation[] | undefined {
if (file && isSourceFileJS(file)) {
return []; // No declaration diagnostics for js for now
}
const compilerOptions = host.getCompilerOptions();
const result = transformNodes(resolver, host, compilerOptions, file ? [file] : filter(host.getSourceFiles(), isSourceFileNotJS), [transformDeclarations], /*allowDtsFiles*/ false);
const result = transformNodes(resolver, host, compilerOptions, file ? [file] : host.getSourceFiles(), [transformDeclarations], /*allowDtsFiles*/ false);
return result.diagnostics;
}

Expand Down Expand Up @@ -190,15 +187,11 @@ namespace ts {
}
}

function createEmptyExports() {
return createExportDeclaration(/*decorators*/ undefined, /*modifiers*/ undefined, createNamedExports([]), /*moduleSpecifier*/ undefined);
}

function transformRoot(node: Bundle): Bundle;
function transformRoot(node: SourceFile): SourceFile;
function transformRoot(node: SourceFile | Bundle): SourceFile | Bundle;
function transformRoot(node: SourceFile | Bundle) {
if (node.kind === SyntaxKind.SourceFile && (node.isDeclarationFile || isSourceFileJS(node))) {
if (node.kind === SyntaxKind.SourceFile && node.isDeclarationFile) {
return node;
}

Expand All @@ -209,7 +202,10 @@ namespace ts {
let hasNoDefaultLib = false;
const bundle = createBundle(map(node.sourceFiles,
sourceFile => {
if (sourceFile.isDeclarationFile || isSourceFileJS(sourceFile)) return undefined!; // Omit declaration files from bundle results, too // TODO: GH#18217
if (sourceFile.isDeclarationFile) return undefined!; // Omit declaration files from bundle results, too // TODO: GH#18217
if (isSourceFileJS(sourceFile)) {
return updateSourceFileNode(sourceFile, resolver.getDeclarationStatementsForSourceFile(sourceFile, declarationEmitNodeBuilderFlags, symbolTracker) || emptyArray);
}
hasNoDefaultLib = hasNoDefaultLib || sourceFile.hasNoDefaultLib;
currentSourceFile = sourceFile;
enclosingDeclaration = sourceFile;
Expand Down Expand Up @@ -257,6 +253,9 @@ namespace ts {
refs.forEach(referenceVisitor);
return bundle;
}
else if (isSourceFileJS(node)) {
return updateSourceFileNode(node, resolver.getDeclarationStatementsForSourceFile(node, declarationEmitNodeBuilderFlags, symbolTracker) || emptyArray);
}

// Single source file
needsDeclare = true;
Expand Down Expand Up @@ -710,10 +709,6 @@ namespace ts {
return isAnyImportOrReExport(result) || isExportAssignment(result) || hasModifier(result, ModifierFlags.Export);
}

function needsScopeMarker(result: LateVisibilityPaintedStatement | ExportAssignment) {
return !isAnyImportOrReExport(result) && !isExportAssignment(result) && !hasModifier(result, ModifierFlags.Export) && !isAmbientModule(result);
}

function visitDeclarationSubtree(input: Node): VisitResult<Node> {
if (shouldStripInternal(input)) return;
if (isDeclaration(input)) {
Expand Down
7 changes: 7 additions & 0 deletions src/compiler/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3620,6 +3620,7 @@ namespace ts {
getAllAccessorDeclarations(declaration: AccessorDeclaration): AllAccessorDeclarations;
getSymbolOfExternalModuleSpecifier(node: StringLiteralLike): Symbol | undefined;
isBindingCapturedByNode(node: Node, decl: VariableDeclaration | BindingElement): boolean;
getDeclarationStatementsForSourceFile(node: SourceFile, flags: NodeBuilderFlags, tracker: SymbolTracker): Statement[] | undefined;
}

export const enum SymbolFlags {
Expand Down Expand Up @@ -3700,6 +3701,12 @@ namespace ts {

ClassMember = Method | Accessor | Property,

/* @internal */
ExportSupportsDefaultModifier = Class | Function | Interface,

/* @internal */
ExportDoesNotSupportDefaultModifier = ~ExportSupportsDefaultModifier,

/* @internal */
// The set of things we consider semantically classifiable. Used to speed up the LS during
// classification.
Expand Down
21 changes: 20 additions & 1 deletion src/compiler/utilities.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2561,10 +2561,14 @@ namespace ts {
}

export function exportAssignmentIsAlias(node: ExportAssignment | BinaryExpression): boolean {
const e = isExportAssignment(node) ? node.expression : node.right;
const e = getExportAssignmentExpression(node);
return isEntityNameExpression(e) || isClassExpression(e);
}

export function getExportAssignmentExpression(node: ExportAssignment | BinaryExpression): Expression {
return isExportAssignment(node) ? node.expression : node.right;
}

export function getEffectiveBaseTypeNode(node: ClassLikeDeclaration | InterfaceDeclaration) {
const baseType = getClassExtendsHeritageElement(node);
if (baseType && isInJSFile(node)) {
Expand Down Expand Up @@ -6656,6 +6660,21 @@ namespace ts {
return false;
}

/* @internal */
export function isScopeMarker(node: Node) {
return isExportAssignment(node) || isExportDeclaration(node);
}

/* @internal */
export function hasScopeMarker(statements: ReadonlyArray<Statement>) {
return some(statements, isScopeMarker);
}

/* @internal */
export function needsScopeMarker(result: Statement) {
return !isAnyImportOrReExport(result) && !isExportAssignment(result) && !hasModifier(result, ModifierFlags.Export) && !isAmbientModule(result);
}

/* @internal */
export function isForInOrOfStatement(node: Node): node is ForInOrOfStatement {
return node.kind === SyntaxKind.ForInStatement || node.kind === SyntaxKind.ForOfStatement;
Expand Down
21 changes: 13 additions & 8 deletions src/harness/compiler.ts
Original file line number Diff line number Diff line change
Expand Up @@ -219,14 +219,19 @@ namespace compiler {
return vpath.changeExtension(path, ext);
}

public getNumberOfJsFiles() {
let count = this.js.size;
this.js.forEach(document => {
if (ts.fileExtensionIs(document.file, ts.Extension.Json)) {
count--;
}
});
return count;
public getNumberOfJsFiles(includeJson: boolean) {
if (includeJson) {
return this.js.size;
}
else {
let count = this.js.size;
this.js.forEach(document => {
if (ts.fileExtensionIs(document.file, ts.Extension.Json)) {
count--;
}
});
return count;
}
}
}

Expand Down
6 changes: 3 additions & 3 deletions src/harness/harness.ts
Original file line number Diff line number Diff line change
Expand Up @@ -883,7 +883,7 @@ namespace Harness {
throw new Error("Only declaration files should be generated when emitDeclarationOnly:true");
}
}
else if (result.dts.size !== result.getNumberOfJsFiles()) {
else if (result.dts.size !== result.getNumberOfJsFiles(/*includeJson*/ true)) {
throw new Error("There were no errors and declFiles generated did not match number of js files generated");
}
}
Expand All @@ -902,7 +902,7 @@ namespace Harness {
if (vpath.isDeclaration(file.unitName) || vpath.isJson(file.unitName)) {
dtsFiles.push(file);
}
else if (vpath.isTypeScript(file.unitName)) {
else if (vpath.isTypeScript(file.unitName) || (vpath.isJavaScript(file.unitName) && options.allowJs)) {
const declFile = findResultCodeFile(file.unitName);
if (declFile && !findUnit(declFile.file, declInputFiles) && !findUnit(declFile.file, declOtherFiles)) {
dtsFiles.push({ unitName: declFile.file, content: utils.removeByteOrderMark(declFile.text) });
Expand Down Expand Up @@ -1267,7 +1267,7 @@ namespace Harness {
return;
}
else if (options.sourceMap || declMaps) {
if (result.maps.size !== (result.getNumberOfJsFiles() * (declMaps && options.sourceMap ? 2 : 1))) {
if (result.maps.size !== ((options.sourceMap ? result.getNumberOfJsFiles(/*includeJson*/ false) : 0) + (declMaps ? result.getNumberOfJsFiles(/*includeJson*/ true) : 0))) {
throw new Error("Number of sourcemap files should be same as js files.");
}

Expand Down
7 changes: 6 additions & 1 deletion src/testRunner/unittests/tsbuild/resolveJsonModule.ts
Original file line number Diff line number Diff line change
Expand Up @@ -47,7 +47,12 @@ namespace ts {
export default hello.hello`);
const allExpectedOutputs = ["/src/dist/src/index.js", "/src/dist/src/index.d.ts", "/src/dist/src/index.json"];
verifyProjectWithResolveJsonModuleWithFs(fs, "/src/tsconfig_withIncludeOfJson.json", allExpectedOutputs);
verifyProjectWithResolveJsonModuleWithFs(
fs,
"/src/tsconfig_withIncludeOfJson.json",
allExpectedOutputs,
[Diagnostics.Cannot_write_file_0_because_it_would_be_overwritten_by_multiple_input_files, "/src/dist/src/index.d.ts"]
);
});

it("with resolveJsonModule and files containing json file", () => {
Expand Down
9 changes: 5 additions & 4 deletions src/testRunner/unittests/tscWatch/programUpdates.ts
Original file line number Diff line number Diff line change
Expand Up @@ -885,8 +885,8 @@ namespace ts.tscWatch {
// More comment`;
const configFileContentAfterComment = `
"compilerOptions": {
"allowJs": true,
"declaration": true
"inlineSourceMap": true,
"mapRoot": "./"
}
}`;
const configFileContentWithComment = configFileContentBeforeComment + configFileContentComment + configFileContentAfterComment;
Expand All @@ -900,8 +900,9 @@ namespace ts.tscWatch {
const host = createWatchedSystem(files);
const watch = createWatchOfConfigFile(configFile.path, host);
const errors = () => [
getDiagnosticOfFile(watch().getCompilerOptions().configFile!, configFile.content.indexOf('"allowJs"'), '"allowJs"'.length, Diagnostics.Option_0_cannot_be_specified_with_option_1, "allowJs", "declaration"),
getDiagnosticOfFile(watch().getCompilerOptions().configFile!, configFile.content.indexOf('"declaration"'), '"declaration"'.length, Diagnostics.Option_0_cannot_be_specified_with_option_1, "allowJs", "declaration")
getDiagnosticOfFile(watch().getCompilerOptions().configFile!, configFile.content.indexOf('"inlineSourceMap"'), '"inlineSourceMap"'.length, Diagnostics.Option_0_cannot_be_specified_with_option_1, "mapRoot", "inlineSourceMap"),
getDiagnosticOfFile(watch().getCompilerOptions().configFile!, configFile.content.indexOf('"mapRoot"'), '"mapRoot"'.length, Diagnostics.Option_0_cannot_be_specified_with_option_1, "mapRoot", "inlineSourceMap"),
getDiagnosticOfFile(watch().getCompilerOptions().configFile!, configFile.content.indexOf('"mapRoot"'), '"mapRoot"'.length, Diagnostics.Option_0_cannot_be_specified_without_specifying_option_1_or_option_2, "mapRoot", "sourceMap", "declarationMap")
];
const intialErrors = errors();
checkOutputErrorsInitial(host, intialErrors);
Expand Down
9 changes: 5 additions & 4 deletions src/testRunner/unittests/tsserver/projectErrors.ts
Original file line number Diff line number Diff line change
Expand Up @@ -849,8 +849,8 @@ declare module '@custom/plugin' {
// comment`;
const configFileContentAfterComment = `
"compilerOptions": {
"allowJs": true,
"declaration": true
"inlineSourceMap": true,
"mapRoot": "./"
}
}`;
const configFileContentWithComment = configFileContentBeforeComment + configFileContentComment + configFileContentAfterComment;
Expand All @@ -874,7 +874,7 @@ declare module '@custom/plugin' {
seq: 2,
arguments: { file: configFile.path, projectFileName: projectName, includeLinePosition: true }
}).response as ReadonlyArray<server.protocol.DiagnosticWithLinePosition>;
assert.isTrue(diags.length === 2);
assert.isTrue(diags.length === 3);

configFile.content = configFileContentWithoutCommentLine;
host.reloadFS([file, configFile]);
Expand All @@ -885,10 +885,11 @@ declare module '@custom/plugin' {
seq: 2,
arguments: { file: configFile.path, projectFileName: projectName, includeLinePosition: true }
}).response as ReadonlyArray<server.protocol.DiagnosticWithLinePosition>;
assert.isTrue(diagsAfterEdit.length === 2);
assert.isTrue(diagsAfterEdit.length === 3);

verifyDiagnostic(diags[0], diagsAfterEdit[0]);
verifyDiagnostic(diags[1], diagsAfterEdit[1]);
verifyDiagnostic(diags[2], diagsAfterEdit[2]);

function verifyDiagnostic(beforeEditDiag: server.protocol.DiagnosticWithLinePosition, afterEditDiag: server.protocol.DiagnosticWithLinePosition) {
assert.equal(beforeEditDiag.message, afterEditDiag.message);
Expand Down
Loading

0 comments on commit be85d75

Please sign in to comment.