Skip to content

Commit

Permalink
Fix module import/export identifier parsing (#217)
Browse files Browse the repository at this point in the history
  • Loading branch information
lahma authored Dec 20, 2021
1 parent 9f20853 commit 68f28de
Show file tree
Hide file tree
Showing 8 changed files with 57 additions and 31 deletions.
8 changes: 6 additions & 2 deletions src/Esprima/Ast/ExportAllDeclaration.cs
Original file line number Diff line number Diff line change
Expand Up @@ -5,13 +5,17 @@ namespace Esprima.Ast
public sealed class ExportAllDeclaration : ExportDeclaration
{
public readonly Literal Source;
public readonly Identifier? Exported;

/// <summary>
/// Identifier | StringLiteral
/// </summary>
public readonly Expression? Exported;

public ExportAllDeclaration(Literal source) : this(source, null)
{
}

public ExportAllDeclaration(Literal source, Identifier? exported) : base(Nodes.ExportAllDeclaration)
public ExportAllDeclaration(Literal source, Expression? exported) : base(Nodes.ExportAllDeclaration)
{
Source = source;
Exported = exported;
Expand Down
13 changes: 10 additions & 3 deletions src/Esprima/Ast/ExportSpecifier.cs
Original file line number Diff line number Diff line change
Expand Up @@ -4,10 +4,17 @@ namespace Esprima.Ast
{
public sealed class ExportSpecifier : Statement
{
public readonly Identifier Exported;
public readonly Identifier Local;
/// <summary>
/// Identifier | StringLiteral
/// </summary>
public readonly Expression Exported;

public ExportSpecifier(Identifier local, Identifier exported) : base(Nodes.ExportSpecifier)
/// <summary>
/// Identifier | StringLiteral
/// </summary>
public readonly Expression Local;

public ExportSpecifier(Expression local, Expression exported) : base(Nodes.ExportSpecifier)
{
Exported = exported;
Local = local;
Expand Down
11 changes: 7 additions & 4 deletions src/Esprima/Ast/ImportDeclarationSpecifier.cs
Original file line number Diff line number Diff line change
Expand Up @@ -2,11 +2,14 @@
{
public abstract class ImportDeclarationSpecifier : Declaration
{
protected ImportDeclarationSpecifier(Nodes type) : base(type)
/// <summary>
/// Identifier | StringLiteral
/// </summary>
public readonly Expression Local;

protected ImportDeclarationSpecifier(Expression local, Nodes type) : base(type)
{
Local = local;
}

public Identifier Local => LocalId;
protected abstract Identifier LocalId { get; }
}
}
6 changes: 1 addition & 5 deletions src/Esprima/Ast/ImportDefaultSpecifier.cs
Original file line number Diff line number Diff line change
Expand Up @@ -4,12 +4,8 @@ namespace Esprima.Ast
{
public sealed class ImportDefaultSpecifier : ImportDeclarationSpecifier
{
public new readonly Identifier Local;
protected override Identifier LocalId => Local;

public ImportDefaultSpecifier(Identifier local) : base(Nodes.ImportDefaultSpecifier)
public ImportDefaultSpecifier(Identifier local) : base(local, Nodes.ImportDefaultSpecifier)
{
Local = local;
}

public override NodeCollection ChildNodes => new(Local);
Expand Down
6 changes: 1 addition & 5 deletions src/Esprima/Ast/ImportNamespaceSpecifier.cs
Original file line number Diff line number Diff line change
Expand Up @@ -4,12 +4,8 @@ namespace Esprima.Ast
{
public sealed class ImportNamespaceSpecifier : ImportDeclarationSpecifier
{
public new readonly Identifier Local;
protected override Identifier LocalId => Local;

public ImportNamespaceSpecifier(Identifier local) : base(Nodes.ImportNamespaceSpecifier)
public ImportNamespaceSpecifier(Identifier local) : base(local, Nodes.ImportNamespaceSpecifier)
{
Local = local;
}

public override NodeCollection ChildNodes => new(Local);
Expand Down
11 changes: 5 additions & 6 deletions src/Esprima/Ast/ImportSpecifier.cs
Original file line number Diff line number Diff line change
Expand Up @@ -4,14 +4,13 @@ namespace Esprima.Ast
{
public sealed class ImportSpecifier : ImportDeclarationSpecifier
{
public new readonly Identifier Local;
protected override Identifier LocalId => Local;
/// <summary>
/// Identifier | StringLiteral
/// </summary>
public readonly Expression Imported;

public readonly Identifier Imported;

public ImportSpecifier(Identifier local, Identifier imported) : base(Nodes.ImportSpecifier)
public ImportSpecifier(Expression local, Expression imported) : base(local, Nodes.ImportSpecifier)
{
Local = local;
Imported = imported;
}

Expand Down
23 changes: 17 additions & 6 deletions src/Esprima/JavascriptParser.cs
Original file line number Diff line number Diff line change
Expand Up @@ -4672,7 +4672,8 @@ private ImportSpecifier ParseImportSpecifier()
{
var node = CreateNode();

Identifier local, imported;
Expression local;
Expression imported;

if (_lookahead.Type == TokenType.Identifier)
{
Expand All @@ -4686,7 +4687,10 @@ private ImportSpecifier ParseImportSpecifier()
}
else
{
imported = ParseIdentifierName();
imported = this._lookahead.Type == TokenType.StringLiteral
? ParseModuleSpecifier()
: ParseIdentifierName();

local = imported;
if (MatchContextualKeyword("as"))
{
Expand Down Expand Up @@ -4824,12 +4828,17 @@ private ExportSpecifier ParseExportSpecifier()
{
var node = CreateNode();

var local = ParseIdentifierName();
Expression local = this._lookahead.Type == TokenType.StringLiteral
? ParseModuleSpecifier()
: ParseIdentifierName();

var exported = local;
if (MatchContextualKeyword("as"))
{
NextToken();
exported = ParseIdentifierName();
exported = this._lookahead.Type == TokenType.StringLiteral
? ParseModuleSpecifier()
: ParseIdentifierName();
}

return Finalize(node, new ExportSpecifier(local, exported));
Expand Down Expand Up @@ -4898,11 +4907,13 @@ private ExportDeclaration ParseExportDeclaration()
NextToken();

//export * as ns from 'foo'
Identifier? exported = null;
Expression? exported = null;
if (MatchContextualKeyword("as"))
{
NextToken();
exported = ParseIdentifierName();
exported = this._lookahead.Type == TokenType.StringLiteral
? ParseModuleSpecifier()
: ParseIdentifierName();
}

if (!MatchContextualKeyword("from"))
Expand Down
10 changes: 10 additions & 0 deletions test/Esprima.Tests/ParserTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -104,6 +104,16 @@ public void ShouldParseNumericLiterals(object expected, string source)
Assert.Equal(expected, literal.NumericValue);
}

[Theory]
[InlineData("export { Mercury as \"\" } from \"./export-expname_FIXTURE.js\";")]
[InlineData("export * as \"All\" from \"./export-expname_FIXTURE.js\";")]
[InlineData("export { \"\" as Ami } from \"./export-expname_FIXTURE.js\"")]
[InlineData("import { \"\" as Ami } from \"./export-expname_FIXTURE.js\";")]
public void ShouldParseModuleImportExportWithStringIdentifiers(string source)
{
new JavaScriptParser(source).ParseModule();
}

[Fact]
public void ShouldParseClassInheritance()
{
Expand Down

0 comments on commit 68f28de

Please sign in to comment.