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 5b7c66d commit a8a48d3
Show file tree
Hide file tree
Showing 48 changed files with 574 additions and 249 deletions.
3 changes: 2 additions & 1 deletion cmd/root.go
Original file line number Diff line number Diff line change
Expand Up @@ -33,14 +33,15 @@ var Root = &cobra.Command{
`,
Args: cobra.ExactArgs(1),
RunE: func(cmd *cobra.Command, args []string) error {
ctx := cmd.Context()
entrypoint := args[0]

if endsWith(entrypoint, js.Extensions) {
parser, err := js.MakeJsParser(entrypoint)
if err != nil {
return err
}
content, err := graph.RenderGraph[js.Data](entrypoint, parser)
_, content, err := graph.RenderGraph[js.Data](ctx, entrypoint, parser)
fmt.Print(content)
return err
} else {
Expand Down
39 changes: 21 additions & 18 deletions internal/graph/graph.go
Original file line number Diff line number Diff line change
Expand Up @@ -11,32 +11,33 @@ import (
type NodeParser[T any] interface {
Display(node *node.Node[T], root *node.Node[T]) string
Entrypoint(entrypoint string) (*node.Node[T], error)
Deps(node *node.Node[T]) ([]*node.Node[T], error)
Deps(ctx context.Context, node *node.Node[T]) (context.Context, []*node.Node[T], error)
}

func computeDeps[T any](
ctx context.Context,
root *node.Node[T],
parser NodeParser[T],
seen map[string]*node.Node[T],
) error {
) (context.Context, error) {
if _, ok := seen[root.Id]; ok {
return nil
return ctx, nil
} else {
seen[root.Id] = root
}

deps, err := parser.Deps(root)
ctx, deps, err := parser.Deps(ctx, root)
if err != nil {
return err
return ctx, err
}
for _, dep := range deps {
root.AddChild(dep)
err = computeDeps(dep, parser, seen)
ctx, err = computeDeps(ctx, dep, parser, seen)
if err != nil {
return err
return ctx, err
}
}
return nil
return ctx, nil
}

type graphNode[T any] struct {
Expand Down Expand Up @@ -69,10 +70,11 @@ func sortNodes[T any](root *node.Node[T]) []graphNode[T] {
const indent = 2

func renderGraph[T any](
ctx context.Context,
parser NodeParser[T],
root *node.Node[T],
nodes []graphNode[T],
) (string, error) {
) (context.Context, string, error) {
b := board.MakeBoard()

lastLevel := -1
Expand All @@ -99,7 +101,7 @@ func renderGraph[T any](
i,
)
if err != nil {
return "", err
return ctx, "", err
}
}

Expand All @@ -108,26 +110,27 @@ func renderGraph[T any](
child, _ := n.node.Children.Get(childId)
err := b.AddConnector(n.node.Id, child.Id)
if err != nil {
return "", err
return ctx, "", err
}
}
}

return b.Render()
rendered, err := b.Render()
return ctx, rendered, err
}

func RenderGraph[T any](
ctx context.Context,
entrypoint string,
parser NodeParser[T],
) (string, error) {
) (context.Context, string, error) {
root, err := parser.Entrypoint(entrypoint)
if err != nil {
return "", err
return ctx, "", err
}
err = computeDeps(root, parser, map[string]*node.Node[T]{})
ctx, err = computeDeps(ctx, root, parser, map[string]*node.Node[T]{})
if err != nil {
return "", err
return ctx, "", err
}
nodes := sortNodes(root)
return renderGraph(parser, root, nodes)
return renderGraph(ctx, parser, root, nodes)
}
9 changes: 6 additions & 3 deletions internal/graph/graph_test.go
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
package graph

import (
"context"
"fmt"
"os"
"path"
Expand Down Expand Up @@ -34,13 +35,13 @@ func (t *TestGraph) Entrypoint(id string) (*node.Node[[]int], error) {
return node.MakeNode(id, children), nil
}

func (t *TestGraph) Deps(n *node.Node[[]int]) ([]*node.Node[[]int], error) {
func (t *TestGraph) Deps(ctx context.Context, n *node.Node[[]int]) (context.Context, []*node.Node[[]int], error) {
result := make([]*node.Node[[]int], 0)
for _, child := range n.Data {
c, _ := t.Entrypoint(strconv.Itoa(child))
result = append(result, c)
}
return result, nil
return ctx, result, nil
}

func (t *TestGraph) Display(n *node.Node[[]int], _ *node.Node[[]int]) string {
Expand Down Expand Up @@ -95,7 +96,9 @@ func TestMakeGraph(t *testing.T) {
Spec: tt.Spec,
}

result, err := RenderGraph[[]int]("0", &testParser)
ctx := context.Background()

_, result, err := RenderGraph[[]int](ctx, "0", &testParser)
a.NoError(err)
print(result)

Expand Down
1 change: 1 addition & 0 deletions internal/js/.exports_test/package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
{}
1 change: 1 addition & 0 deletions internal/js/.exports_test/src/index.js
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
export * from "./utils"
3 changes: 3 additions & 0 deletions internal/js/.exports_test/src/utils/index.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
export * from "./math"
export * from "./sort"
export { default as UnSorter } from "./unsort"
5 changes: 5 additions & 0 deletions internal/js/.exports_test/src/utils/math/abs.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
export async function abs(a) {
return Math.abs(a)
}

export default abs
5 changes: 5 additions & 0 deletions internal/js/.exports_test/src/utils/math/equals.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
const equals = (a, b) => {
return a === b
}

export { equals }
3 changes: 3 additions & 0 deletions internal/js/.exports_test/src/utils/math/index.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
export * from "./sum"
export { equals } from "./equals"
export * as abs from "./abs"
3 changes: 3 additions & 0 deletions internal/js/.exports_test/src/utils/math/sum.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
export function sum (a, b) {
return a + b
}
3 changes: 3 additions & 0 deletions internal/js/.exports_test/src/utils/sort.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
export class Sorter {

}
3 changes: 3 additions & 0 deletions internal/js/.exports_test/src/utils/unsort.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
export default class UnSorter {

}
1 change: 1 addition & 0 deletions internal/js/.exports_test/tsconfig.json
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
{}
File renamed without changes.
File renamed without changes.
1 change: 1 addition & 0 deletions internal/js/.imports_test/1/a/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
import './a'
File renamed without changes.
1 change: 1 addition & 0 deletions internal/js/.imports_test/1/b/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
import './b'
3 changes: 3 additions & 0 deletions internal/js/.imports_test/1/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
import './1'
import './a'
import './b'
Empty file.
1 change: 1 addition & 0 deletions internal/js/.imports_test/2/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
import './2'
4 changes: 4 additions & 0 deletions internal/js/.imports_test/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
import { a, b } from './2/2'
import * as something from './2/index'
import './1/a/a'
import('./1/a')
2 changes: 1 addition & 1 deletion internal/js/.parser_test/with-imports-imported.js
Original file line number Diff line number Diff line change
@@ -1 +1 @@
console.log("hello world!")
export const a = 1
4 changes: 3 additions & 1 deletion internal/js/.parser_test/with-imports-imported/imported.js
Original file line number Diff line number Diff line change
@@ -1 +1,3 @@
console.log("hello world!")
export function foo () {

}
Original file line number Diff line number Diff line change
@@ -1 +1 @@
console.log("hello world!")
export const b = 2
2 changes: 2 additions & 0 deletions internal/js/.parser_test/with-imports-index-imported/one.js
Original file line number Diff line number Diff line change
@@ -1 +1,3 @@
import './index'

export const b = 2
2 changes: 2 additions & 0 deletions internal/js/.parser_test/with-imports-index-imported/other.js
Original file line number Diff line number Diff line change
@@ -1 +1,3 @@
import './'

export class A {}
Original file line number Diff line number Diff line change
@@ -1 +1 @@
console.log("hello world!")
export default () => console.log("hello world!")
File renamed without changes.
Empty file.
Empty file.
Empty file.
3 changes: 0 additions & 3 deletions internal/js/.resolve_test/test_1/src/index.ts

This file was deleted.

File renamed without changes.
96 changes: 96 additions & 0 deletions internal/js/exports.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,96 @@
package js

import (
"context"
"fmt"
"path"

"dep-tree/internal/js/grammar"
"dep-tree/internal/utils"
)

type ExportsCacheKey string

func (p *Parser) parseExports(
ctx context.Context,
filePath string,
) (context.Context, map[string]string, error) {
cacheKey := ExportsCacheKey(filePath)
if cached, ok := ctx.Value(cacheKey).(map[string]string); ok {
return ctx, cached, nil
} else {
ctx, result, err := p.uncachedParseExports(ctx, filePath)
if err != nil {
return ctx, nil, err
}
ctx = context.WithValue(ctx, cacheKey, result)
return ctx, result, err
}
}

func (p *Parser) uncachedParseExports(
ctx context.Context,
filePath string,
) (context.Context, map[string]string, error) {
ctx, jsFile, err := grammar.Parse(ctx, filePath)
if err != nil {
return ctx, nil, err
}
exported := make(map[string]string)
for _, stmt := range jsFile.Statements {
switch {
case stmt == nil:
continue
case stmt.DeclarationExport != nil:
exported[stmt.DeclarationExport.Name] = filePath
case stmt.ListExport != nil:
if stmt.ListExport.ExportDeconstruction != nil {
for _, name := range stmt.ListExport.ExportDeconstruction.Names {
exportedName := name.Alias
if exportedName == "" {
exportedName = name.Original
}
exported[exportedName] = filePath
}
}
case stmt.DefaultExport != nil:
if stmt.DefaultExport.Default {
exported["default"] = filePath
}
case stmt.ProxyExport != nil:
var exportFrom string
ctx, exportFrom, err = p.ResolvePath(ctx, stmt.ProxyExport.From, path.Dir(filePath))
if err != nil {
return ctx, nil, err
}
var proxyExports map[string]string
ctx, proxyExports, err = p.parseExports(ctx, exportFrom)
if err != nil {
return ctx, nil, err
}
if stmt.ProxyExport.ExportAll {
if stmt.ProxyExport.ExportAllAlias != "" {
exported[stmt.ProxyExport.ExportAllAlias] = filePath
} else {
exported = utils.Merge(exported, proxyExports)
}
} else if stmt.ProxyExport.ExportDeconstruction != nil {
for _, name := range stmt.ProxyExport.ExportDeconstruction.Names {
alias := name.Alias
original := name.Original
if alias == "" {
alias = original
}
if proxyPath, ok := proxyExports[original]; ok {
exported[alias] = proxyPath
} else {
return ctx, nil, fmt.Errorf("cannot import \"%s\" from %s", original, exportFrom)
}
}
}
default:
continue
}
}
return ctx, exported, nil
}
45 changes: 45 additions & 0 deletions internal/js/exports_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
package js

import (
"context"
"os"
"path"
"testing"

"github.com/stretchr/testify/require"
)

const exportsTestFolder = ".exports_test"

func TestParser_parseExports(t *testing.T) {
cwd, _ := os.Getwd()

tests := []struct {
Name string
File string
Expected map[string]string
}{
{
Name: "test",
File: path.Join(exportsTestFolder, "src", "index.js"),
Expected: map[string]string{
"Sorter": path.Join(cwd, exportsTestFolder, "src", "utils", "sort.js"),
"UnSorter": path.Join(cwd, exportsTestFolder, "src", "utils", "unsort.js"),
"equals": path.Join(cwd, exportsTestFolder, "src", "utils", "math", "equals.js"),
"abs": path.Join(cwd, exportsTestFolder, "src", "utils", "math", "index.js"),
"sum": path.Join(cwd, exportsTestFolder, "src", "utils", "math", "sum.js"),
},
},
}

for _, tt := range tests {
t.Run(tt.Name, func(t *testing.T) {
a := require.New(t)
parser, err := MakeJsParser(tt.File)
a.NoError(err)
_, exports, err := parser.parseExports(context.Background(), tt.File)
a.NoError(err)
a.Equal(tt.Expected, exports)
})
}
}
7 changes: 6 additions & 1 deletion internal/js/grammar/export.go
Original file line number Diff line number Diff line change
@@ -1,8 +1,13 @@
//nolint:govet
package grammar

type AliasedName struct {
Original string `@Ident`
Alias string `("as" @Ident)?`
}

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

type DeclarationExport struct {
Expand Down
Loading

0 comments on commit a8a48d3

Please sign in to comment.