Skip to content

Allow export default const enum, export default enum, export default … #18628

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

Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
The table of contents is too big for display.
Diff view
Diff view
  •  
  •  
  •  
13 changes: 11 additions & 2 deletions src/compiler/binder.ts
Original file line number Diff line number Diff line change
Expand Up @@ -426,6 +426,15 @@ namespace ts {
symbol = createSymbol(SymbolFlags.None, name);
}
}
else if (isDefaultExport && parent && symbol.declarations && symbol.declarations.length) {
Copy link
Member

Choose a reason for hiding this comment

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

Suggested change
else if (isDefaultExport && parent && symbol.declarations && symbol.declarations.length) {
else if (isDefaultExport && parent && some(symbol.declarations)) {

// && parent because we only want to check the export default symbol.
// if there is already an export default declaration, make sure that their local names are the same.
if (symbol.declarations[0].localSymbol !== node.localSymbol) {
const message = Diagnostics.Merged_default_exports_must_have_the_same_name;
file.bindDiagnostics.push(createDiagnosticForNode(getNameOfDeclaration(symbol.declarations[0]) || symbol.declarations[0], message));
file.bindDiagnostics.push(createDiagnosticForNode(getNameOfDeclaration(node) || node, message));
}
}
}

addDeclarationToSymbol(symbol, node, includes);
Expand Down Expand Up @@ -472,8 +481,8 @@ namespace ts {
}
const exportKind = symbolFlags & SymbolFlags.Value ? SymbolFlags.ExportValue : 0;
const local = declareSymbol(container.locals!, /*parent*/ undefined, node, exportKind, symbolExcludes);
local.exportSymbol = declareSymbol(container.symbol.exports!, container.symbol, node, symbolFlags, symbolExcludes);
node.localSymbol = local;
local.exportSymbol = declareSymbol(container.symbol.exports!, container.symbol, node, symbolFlags, symbolExcludes);
return local;
}
else {
Expand Down Expand Up @@ -1381,7 +1390,7 @@ namespace ts {
function bindJSDocTypeAlias(node: JSDocTypedefTag | JSDocCallbackTag) {
if (node.fullName) {
setParentPointers(node, node.fullName);
}
}
Copy link
Member

Choose a reason for hiding this comment

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

The indentation here is wrong.

}

function bindCallExpressionFlow(node: CallExpression) {
Expand Down
5 changes: 4 additions & 1 deletion src/compiler/diagnosticMessages.json
Original file line number Diff line number Diff line change
Expand Up @@ -2489,7 +2489,10 @@
"category": "Error",
"code": 2734
},

"Merged default exports must have the same name.": {
"category": "Error",
"code": 2735
},
"Import declaration '{0}' is using private name '{1}'.": {
"category": "Error",
"code": 4000
Expand Down
72 changes: 46 additions & 26 deletions src/compiler/parser.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1414,11 +1414,11 @@ namespace ts {
case SyntaxKind.ExportKeyword:
nextToken();
if (token() === SyntaxKind.DefaultKeyword) {
return lookAhead(nextTokenCanFollowDefaultKeyword);
return lookAhead(nextTokenCanFollowDefaultModifier);
}
return token() !== SyntaxKind.AsteriskToken && token() !== SyntaxKind.AsKeyword && token() !== SyntaxKind.OpenBraceToken && canFollowModifier();
case SyntaxKind.DefaultKeyword:
return nextTokenCanFollowDefaultKeyword();
return nextTokenCanFollowDefaultModifier();
case SyntaxKind.StaticKeyword:
case SyntaxKind.GetKeyword:
case SyntaxKind.SetKeyword:
Expand All @@ -1441,12 +1441,37 @@ namespace ts {
|| isLiteralPropertyName();
}

function nextTokenCanFollowDefaultKeyword(): boolean {
/**
* For export default assignments like
* "export default foo"
* "export default async"
* no modifiers should be parsed.
*/
function nextTokenCanFollowDefaultModifier(): boolean {
nextToken();
Copy link
Contributor Author

Choose a reason for hiding this comment

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

nextTokenCanFollowDefaultKeyword doesn't really seem like an accurate name considering it's looking at multiple? Same with nextTokenCanFollowModifier. Would something like declarationCanFollowExportDefault be more appropriate?

Copy link

Choose a reason for hiding this comment

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

I think the name is fine since it doesn't take a declaration as input, it just goes to the next token and looks at its kind and maybe does some minimal lookahead.

return token() === SyntaxKind.ClassKeyword || token() === SyntaxKind.FunctionKeyword ||
token() === SyntaxKind.InterfaceKeyword ||
(token() === SyntaxKind.AbstractKeyword && lookAhead(nextTokenIsClassKeywordOnSameLine)) ||
(token() === SyntaxKind.AsyncKeyword && lookAhead(nextTokenIsFunctionKeywordOnSameLine));
switch (token()) {
case SyntaxKind.ClassKeyword:
case SyntaxKind.FunctionKeyword:
case SyntaxKind.EnumKeyword:
case SyntaxKind.NamespaceKeyword:
case SyntaxKind.ModuleKeyword:
case SyntaxKind.ConstKeyword:
return true;

// The following keywords aren't reserved keywords and could be identifiers.
// We need to look at the next token to make sure these should be parsed as a modifier keyword,
// and not as an identifier. If they are followed by an identifier or a keyword on the same line,
// the current statement is not an export default assignment.
// See https://github.com/Microsoft/TypeScript/blob/master/doc/spec.md#221-reserved-words
case SyntaxKind.InterfaceKeyword:
case SyntaxKind.AbstractKeyword:
case SyntaxKind.DeclareKeyword:
case SyntaxKind.AsyncKeyword:
case SyntaxKind.TypeKeyword:
return lookAhead(nextTokenIsIdentifierOrKeywordOnSameLine);
Copy link
Contributor Author

Choose a reason for hiding this comment

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

@rbuckton This simplified lookahead seems to be enough; see 5113249#diff-cc2127059a7624c4bd840e11d106cb81

default:
return false;
}
}

// True if positioned at the start of a list element
Expand Down Expand Up @@ -4081,14 +4106,14 @@ namespace ts {
let expression: MemberExpression;
if (token() === SyntaxKind.ImportKeyword) {
if (lookAhead(nextTokenIsOpenParenOrLessThan)) {
// We don't want to eagerly consume all import keyword as import call expression so we look ahead to find "("
// For example:
// var foo3 = require("subfolder
// import * as foo1 from "module-from-node
// We want this import to be a statement rather than import call expression
sourceFile.flags |= NodeFlags.PossiblyContainsDynamicImport;
expression = parseTokenNode<PrimaryExpression>();
}
// We don't want to eagerly consume all import keyword as import call expression so we look a head to find "("
Copy link
Member

Choose a reason for hiding this comment

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

The indentation here is wrong.

Copy link
Member

Choose a reason for hiding this comment

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

ahead is the correct spelling.

// For example:
// var foo3 = require("subfolder
// import * as foo1 from "module-from-node
// We want this import to be a statement rather than import call expression
sourceFile.flags |= NodeFlags.PossiblyContainsDynamicImport;
expression = parseTokenNode<PrimaryExpression>();
}
else if (lookAhead(nextTokenIsDot)) {
// This is an 'import.*' metaproperty (i.e. 'import.meta')
const fullStart = scanner.getStartPos();
Expand All @@ -4101,7 +4126,7 @@ namespace ts {

sourceFile.flags |= NodeFlags.PossiblyContainsImportMeta;
}
else {
else {
Copy link
Member

Choose a reason for hiding this comment

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

The indentation here is wrong.

expression = parseMemberExpressionOrHigher();
}
}
Expand Down Expand Up @@ -5118,11 +5143,6 @@ namespace ts {
return tokenIsIdentifierOrKeyword(token()) && !scanner.hasPrecedingLineBreak();
}

function nextTokenIsClassKeywordOnSameLine() {
nextToken();
return token() === SyntaxKind.ClassKeyword && !scanner.hasPrecedingLineBreak();
}

function nextTokenIsFunctionKeywordOnSameLine() {
nextToken();
return token() === SyntaxKind.FunctionKeyword && !scanner.hasPrecedingLineBreak();
Expand Down Expand Up @@ -6233,7 +6253,7 @@ namespace ts {
sourceFile.externalModuleIndicator =
forEach(sourceFile.statements, isAnExternalModuleIndicatorNode) ||
getImportMetaIfNecessary(sourceFile);
}
}
Copy link
Member

Choose a reason for hiding this comment

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

The indentation here is wrong.


function isAnExternalModuleIndicatorNode(node: Node) {
return hasModifier(node, ModifierFlags.Export)
Expand All @@ -6243,17 +6263,17 @@ namespace ts {
|| node.kind === SyntaxKind.ExportDeclaration
? node
: undefined;
}
}
Copy link
Member

Choose a reason for hiding this comment

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

The indentation here is wrong.


function getImportMetaIfNecessary(sourceFile: SourceFile) {
return sourceFile.flags & NodeFlags.PossiblyContainsImportMeta ?
walkTreeForExternalModuleIndicators(sourceFile) :
undefined;
}
}
Copy link
Member

Choose a reason for hiding this comment

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

The indentation here is wrong.


function walkTreeForExternalModuleIndicators(node: Node): Node | undefined {
return isImportMeta(node) ? node : forEachChild(node, walkTreeForExternalModuleIndicators);
}
}
Copy link
Member

Choose a reason for hiding this comment

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

The indentation here is wrong.


function isImportMeta(node: Node): boolean {
return isMetaProperty(node) && node.keywordToken === SyntaxKind.ImportKeyword && node.name.escapedText === "meta";
Expand Down Expand Up @@ -6468,7 +6488,7 @@ namespace ts {
break;
}
nextJSDocToken();
}
}
Copy link
Member

Choose a reason for hiding this comment

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

The indentation here is wrong.

removeLeadingNewlines(comments);
removeTrailingWhitespace(comments);
return createJSDocComment();
Expand Down
Loading