Skip to content

Commit

Permalink
feat: JS parser
Browse files Browse the repository at this point in the history
  • Loading branch information
Gabriel Musat authored and gabotechs committed Dec 24, 2022
1 parent a7e6d76 commit 5b7c66d
Show file tree
Hide file tree
Showing 5 changed files with 169 additions and 141 deletions.
4 changes: 1 addition & 3 deletions internal/js/grammar/.export_test/export.js
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,6 @@ export const name1 = 1, name2 = 2/*, … */; // also var, let
export function functionName() { /* … */ }
export class ClassName { /* … */ }
export function* generatorFunctionName() { /* … */ }
export const { name1, name2: bar } = o;
export const [ name1, name2 ] = array;

const nameN = 0
const variable1 = 0
Expand All @@ -30,4 +28,4 @@ export * from "module-name";
export * as name1 from "module-name";
export { name1, /* …, */ nameN } from "module-name";
export { import1 as name1, import2 as name2, /* …, */ nameN } from "module-name";
export { default, /* …, */ } from "module-name";
export { default } from "module-name";
44 changes: 5 additions & 39 deletions internal/js/grammar/export.go
Original file line number Diff line number Diff line change
@@ -1,59 +1,25 @@
//nolint:govet
package grammar

import (
"github.com/alecthomas/participle/v2/lexer"
)

type ExportDeconstruction struct {
Names []string `"{" ((Ident "as" @Ident) | @Ident) ("," ((Ident "as" @Ident) | @Ident))* "}"`
}

type DeclarationExport struct {
Default bool `"export" ("let"|"const"|"var"|"function"|"class")`
Name string `@Ident`
Name string `"export" "async"? ("let"|"const"|"var"|"function"|"class") ALL? @Ident`
}

type ListExport struct {
ExportDeconstruction *ExportDeconstruction `"export" @@`
}

type DefaultExport struct {
Default bool `"export" "default"`
Default bool `"export" @"default"`
}

type ProxyExport struct {
ExportDeconstruction *ExportDeconstruction `"export" @@`
ExportDeconstruction *ExportDeconstruction `"export" (@@`
ExportAll bool ` | (@ALL`
ExportAllAlias string ` ("as" @Ident)?)) `
From string `"from" @String`
}

var exportLexer = lexer.Rules{
"CommonExport": {
{"ALL", `\*`, nil},
{"Comment", commentRe, nil},
{"Whitespace", `\s+`, nil},
{"Punct", punctuationRe, nil},
},
"DeclarationExport": {
{Name: "String", Pattern: stringRe},
{Name: "Ident", Pattern: identRe, Action: lexer.Pop()},
lexer.Include("CommonExport"),
},
"ListExport": {
{Name: "ClosingBracket", Pattern: `}`, Action: lexer.Pop()},
{Name: "String", Pattern: stringRe},
{Name: "Ident", Pattern: identRe},
lexer.Include("CommonExport"),
},
"DefaultExport": {
{Name: "Default", Pattern: `default`, Action: lexer.Pop()},
{Name: "String", Pattern: stringRe},
{Name: "Ident", Pattern: identRe},
lexer.Include("CommonExport"),
},
"ProxyExport": {
{Name: "String", Pattern: stringRe, Action: lexer.Pop()},
{Name: "Ident", Pattern: identRe},
lexer.Include("CommonExport"),
},
}
205 changes: 150 additions & 55 deletions internal/js/grammar/export_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -13,103 +13,174 @@ func TestExport(t *testing.T) {
tests := []struct {
Name string
ExpectedDeclaration []string
ExpectedProxy []string
ExpectedList []string
ExpectedDefault []bool
ExpectedProxy []*ProxyExport
}{
{
Name: `export let name1, name2;`,
ExpectedDeclaration: []string{"name1"},
},
{
Name: `export const name1 = 1`,
ExpectedDeclaration: []string{},
},
{
Name: `export function functionName() { /* … */ }`,
ExpectedDeclaration: []string{},
},
{
Name: `export class ClassName { /* … */ }`,
ExpectedDeclaration: []string{},
ExpectedDeclaration: []string{"name1"},
},
{
Name: `export function* generatorFunctionName() { /* … */ }`,
ExpectedDeclaration: []string{},
Name: `export function functionName()`,
ExpectedDeclaration: []string{"functionName"},
},
{
Name: `export const { name1, name2: bar } = o;`,
ExpectedDeclaration: []string{},
Name: `export async function functionName()`,
ExpectedDeclaration: []string{"functionName"},
},
{
Name: `export const [ name1, name2 ] = array;`,
ExpectedDeclaration: []string{},
Name: `export class ClassName { /* … */ }`,
ExpectedDeclaration: []string{"ClassName"},
},
{
Name: `export { name1, /* …, */ nameN };`,
ExpectedDeclaration: []string{},
Name: `export function* generatorFunctionName() { /* … */ }`,
ExpectedDeclaration: []string{"generatorFunctionName"},
},
{
Name: `export { variable1 as name1, variable2 as name2, /* …, */ nameN };`,
ExpectedDeclaration: []string{},
Name: `export { name1, nameN };`,
ExpectedList: []string{"name1", "nameN"},
},
{
Name: `export { variable1 as "string name" };`,
ExpectedDeclaration: []string{},
Name: `export { variable1 as name1, variable2 as name2, nameN };`,
ExpectedList: []string{"name1", "name2", "nameN"},
},
{
Name: `export { name1 as default /*, … */ };`,
ExpectedDeclaration: []string{},
Name: `export { name1 as default /*, … */ };`,
ExpectedList: []string{"default"},
},
{
Name: `export default expression;`,
ExpectedDeclaration: []string{},
Name: `export default expression;`,
ExpectedDefault: []bool{true},
},
{
Name: `export default function functionName() { /* … */ }`,
ExpectedDeclaration: []string{},
Name: `export default function functionName() { /* … */ }`,
ExpectedDefault: []bool{true},
},
{
Name: `export default class ClassName { /* … */ }`,
ExpectedDeclaration: []string{},
Name: `export default class ClassName { /* … */ }`,
ExpectedDefault: []bool{true},
},
{
Name: `export default function* generatorFunctionName() { /* … */ }`,
ExpectedDeclaration: []string{},
Name: `export default function* generatorFunctionName() { /* … */ }`,
ExpectedDefault: []bool{true},
},
{
Name: `export default function () { /* … */ }`,
ExpectedDeclaration: []string{},
Name: `export default function () { /* … */ }`,
ExpectedDefault: []bool{true},
},
{
Name: `export default class { /* … */ }`,
ExpectedDeclaration: []string{},
Name: `export default class { /* … */ }`,
ExpectedDefault: []bool{true},
},
{
Name: `export default function* () { /* … */ }`,
ExpectedDeclaration: []string{},
Name: `export default function* () { /* … */ }`,
ExpectedDefault: []bool{true},
},
{
Name: `export * from "module-name";`,
ExpectedDeclaration: []string{},
Name: `export * from "module-name";`,
ExpectedProxy: []*ProxyExport{{
ExportAll: true,
From: "module-name",
}},
},
{
Name: `export * as name1 from "module-name";`,
ExpectedDeclaration: []string{},
Name: `export * as name1 from "module-name";`,
ExpectedProxy: []*ProxyExport{{
ExportAll: true,
ExportAllAlias: "name1",
From: "module-name",
}},
},
{
Name: `export { name1, /* …, */ nameN } from "module-name";`,
ExpectedDeclaration: []string{},
Name: `export { name1, /* …, */ nameN } from "module-name";`,
ExpectedProxy: []*ProxyExport{{
ExportDeconstruction: &ExportDeconstruction{
Names: []string{"name1", "nameN"},
},
From: "module-name",
}},
},
{
Name: `export { import1 as name1, import2 as name2, /* …, */ nameN } from "module-name";`,
ExpectedDeclaration: []string{},
Name: `export { import1 as name1, import2 as name2, /* …, */ nameN } from "module-name";`,
ExpectedProxy: []*ProxyExport{{
ExportDeconstruction: &ExportDeconstruction{
Names: []string{"name1", "name2", "nameN"},
},
From: "module-name",
}},
},
{
Name: `export { default, /* …, */ } from "module-name"; `,
ExpectedDeclaration: []string{},
Name: `export { default } from "module-name"; `,
ExpectedProxy: []*ProxyExport{{
ExportDeconstruction: &ExportDeconstruction{
Names: []string{"default"},
},
From: "module-name",
}},
},
{
Name: "export.js",
ExpectedDeclaration: []string{},
Name: "export.js",
ExpectedDeclaration: []string{
"name1",
"name1",
"functionName",
"ClassName",
"generatorFunctionName",
},
ExpectedList: []string{
"name1",
"nameN",
"name1",
"name2",
"nameN",
"default",
},
ExpectedDefault: []bool{
true,
true,
true,
true,
true,
true,
true,
},

ExpectedProxy: []*ProxyExport{
{
ExportAll: true,
From: "module-name",
},
{
ExportAll: true,
ExportAllAlias: "name1",
From: "module-name",
},
{
ExportDeconstruction: &ExportDeconstruction{
Names: []string{"name1", "nameN"},
},
From: "module-name",
},
{

ExportDeconstruction: &ExportDeconstruction{
Names: []string{"name1", "name2", "nameN"},
},
From: "module-name",
},
{

ExportDeconstruction: &ExportDeconstruction{
Names: []string{"default"},
},
From: "module-name",
},
},
},
}

Expand All @@ -128,16 +199,40 @@ func TestExport(t *testing.T) {
a.NoError(err)

var declarationResults []string
var proxyResults []string
var listResults []string
var defaultResults []bool
var proxyResult []*ProxyExport
for _, stmt := range parsed.Statements {
if stmt.DeclarationExport != nil {
declarationResults = append(declarationResults, stmt.StaticImport.Path)
} else if stmt.DynamicImport != nil {
proxyResults = append(proxyResults, stmt.DynamicImport.Path)
declarationResults = append(declarationResults, stmt.DeclarationExport.Name)
} else if stmt.ListExport != nil {
listResults = append(listResults, stmt.ListExport.ExportDeconstruction.Names...)
} else if stmt.DefaultExport != nil {
defaultResults = append(defaultResults, stmt.DefaultExport.Default)
} else if stmt.ProxyExport != nil {
proxyResult = append(proxyResult, stmt.ProxyExport)
}
}
a.Equal(tt.ExpectedDeclaration, declarationResults)
a.Equal(tt.ExpectedProxy, proxyResults)
a.Equal(tt.ExpectedList, listResults)
a.Equal(tt.ExpectedDefault, defaultResults)

if tt.ExpectedProxy == nil {
a.Equal(tt.ExpectedProxy, proxyResult)
} else {
a.NotEqual(nil, proxyResult)
for i, expectedProxy := range tt.ExpectedProxy {
a.Greater(len(proxyResult), i)
actualProxy := proxyResult[i]
a.Equal(expectedProxy.From, actualProxy.From)
a.Equal(expectedProxy.ExportAll, actualProxy.ExportAll)
a.Equal(expectedProxy.ExportAllAlias, actualProxy.ExportAllAlias)
if expectedProxy.ExportDeconstruction != nil {
a.NotEqual(nil, actualProxy.ExportDeconstruction)
a.Equal(expectedProxy.ExportDeconstruction.Names, actualProxy.ExportDeconstruction.Names)
}
}
}
})
}
}
Loading

0 comments on commit 5b7c66d

Please sign in to comment.