Skip to content
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

Ast/parser #2

Merged
merged 4 commits into from Feb 18, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
48 changes: 48 additions & 0 deletions nexus/src/parser/App.jsx
@@ -0,0 +1,48 @@
// Example React component for testing parser logic.

import React from 'react';
import { Link } from 'react-router-dom';
import Chatrooms from './components/Chatrooms.jsx';
import Chatroom from './components/Chatroom.jsx';
import styles from './styles.css';
import Children from "./Children.jex";

const URL = "http://google.com";
const NUMBER = 10;
function FUNC() {
console.log('func');
}
const FUNCTWO = () => {
return 2;
}

export async function getStaticProps() {
const allPostsData = 'function';
return {
props: {
allPostsData
}
}
}

const App = (props) => {
const socket = io();

return (
<div>
{/* <button onClick={useRouter}></button> */}
{/* <Chatroom name={'Brian'} otherProp={500}/> */}
{/* <Chatrooms name={'Mike'} otherProp={600}/> */}
<Link href="/dogs"><a>click me for dogs</a></Link>
<Children name={'Brian'} otherProp={500}/>
<Children name={'David'} otherProp={300}/>
<Children name={'Nico'} otherProp={100}/>
<Children name={'Alex'} otherProp={400}/>
<Children name={'Mike'} otherProp={600}/>
<Comp />
<button>HTML Button</button>
</div>
)
};

export default {App, URL};
12 changes: 12 additions & 0 deletions nexus/src/parser/Children.jsx
@@ -0,0 +1,12 @@
import React from 'react';
import Nested from './Nested.jsx';

const Children = (props) => {
return (
<div>
<Nested />
</div>
)
};

export default Children;
12 changes: 12 additions & 0 deletions nexus/src/parser/Nested.jsx
@@ -0,0 +1,12 @@
import React from 'react';

const Nested = (props) => {
return (
<div>
<h1></h1>
<p></p>
</div>
)
};

export default Nested;
136 changes: 136 additions & 0 deletions nexus/src/parser/parser.js
@@ -0,0 +1,136 @@
var PARSER = require('acorn').Parser;
var jsx = require('acorn-jsx');
var JSXPARSER = PARSER.extend(jsx());
var fs = require('fs');
var path = require('path');
// CONSTANTS
var JSXTEXT = 'JSXText';
var JSXELEMENT = 'JSXElement';
var JSXEXPRESSIONCONTAINER = 'JSXExpressionContainer';
var ComponentNode = /** @class */ (function () {
function ComponentNode(name, props, children) {
this.name = name;
this.children = children;
this.props = props;
}
return ComponentNode;
}());
// Parser
function getTree(filePath) {
var source = fs.readFileSync(path.resolve(__dirname, filePath));
var parsed = JSXPARSER.parse(source, { sourceType: "module" });
var programBody = parsed.body; // get body of Program Node(i.e. source code entry)
return programBody;
}
function getImportNodes(programBody) {
var importNodes = programBody.filter(function (node) { return node.type === 'ImportDeclaration'; });
return importNodes;
}
function getVariableNodes(programBody) {
// const variables that hold different nodes depending on type
var variableNodes = programBody.filter(function (node) { return node.type === 'VariableDeclaration'; });
return variableNodes;
}
function getOtherNodes(programBody) {
var otherNodes = programBody.filter(function (node) { return node.type !== 'ImportDeclaration'; });
return otherNodes;
}
function getExportDefaultNodes(otherNodes) {
var exportDefaultNode = otherNodes.filter(function (node) {
node.type === 'ExportDefaultDeclaration';
})[0];
return exportDefaultNode;
}
function getChildrenNodes(variableNodes) {
var childrenNodes = variableNodes[variableNodes.length - 1].declarations[0].init.body.body[1].argument.children;
return childrenNodes;
}
function getJsxNodes(childrenNodes) {
var jsxNodes = childrenNodes.filter(function (node) { return node.type === JSXELEMENT; });
return jsxNodes;
}
function getChildrenComponents(jsxNodes) {
var components = [];
for (var _i = 0, jsxNodes_1 = jsxNodes; _i < jsxNodes_1.length; _i++) {
var node = jsxNodes_1[_i];
var firstChar = node.openingElement.name.name[0]; // actual name label (i.e. 'Chatroom', 'Component')
var componentName = node.openingElement.name.name;
if (firstChar === firstChar.toUpperCase()) {
var props = getProps(node);
// check componentName against importNodes
// if name matches import node name, take filepath
// recursively invoke parsing algo on file
// const children = main(filePath)
// const componentNode = new ComponentNode(componentName, props, children);
var componentNode = new ComponentNode(componentName, props, node.children);
components.push(componentNode);
}
}
return components;
}
function getPropValue(node) {
if (Object.keys(node).includes('expression')) {
return node.expression.value; // look into the value (node) and find the expression
}
else {
return node.value; // return the value
}
}
function getProps(node) {
var propObj = {};
for (var _i = 0, _a = node.openingElement.attributes; _i < _a.length; _i++) {
var prop = _a[_i];
var name_1 = prop.name.name;
propObj[name_1] = getPropValue(prop.value);
// console.log('propObj', propObj);
}
// console.log(propArr);
return propObj;
}
function main(filePath) {
var tree = getTree(filePath);
var importNodes = getImportNodes(tree);
var variableNodes = getVariableNodes(tree);
var childrenNodes = getChildrenNodes(variableNodes);
var jsxNodes = getJsxNodes(childrenNodes);
var result = getChildrenComponents(jsxNodes);
console.log(result);
return result;
}
main('./App.jsx');
// Node {
// type: 'JSXElement',
// start: 815,
// end: 857,
// openingElement: Node {
// type: 'JSXOpeningElement',
// start: 815,
// end: 857,
// attributes: [ [Node], [Node] ],
// name: Node {
// type: 'JSXIdentifier',
// start: 816,
// end: 825,
// name: 'Chatrooms'
// },
// selfClosing: true
// },
// closingElement: null,
// children: []
// }
// some logic that will help filter out nodes that we actually need
// use a switch statement to avoid too many if conditional statements
// <Chatroom name={'Brian'} otherProp={500}/>
// value: Node {
// type: 'Literal',
// start: 650,
// end: 657,
// value: '/dogs',
// raw: '"/dogs"'
// }
// value: Node {
// type: 'JSXExpressionContainer',
// start: 775,
// end: 784,
// expression: [Node]
// }
175 changes: 175 additions & 0 deletions nexus/src/parser/parser.ts
@@ -0,0 +1,175 @@
const PARSER = require('acorn').Parser;
const jsx = require('acorn-jsx');
const JSXPARSER = PARSER.extend(jsx());
const fs = require('fs');
const path = require('path');

// CONSTANTS
const JSXTEXT: string = 'JSXText';
const JSXELEMENT: string = 'JSXElement';
const JSXEXPRESSIONCONTAINER: string = 'JSXExpressionContainer';

// TS Interface
interface Node {
type: string,
start: number,
end: number,
value: any,
raw: string,
declarations: Array<Node>,
declaration: Node,
properties: Array<Node>,
method: boolean,
init: Node,
body: Array<any>|object,
children: Array<Node>,
argument: Node,
openingElement: Node,
name: Node|string,
attributes: Array<Node>,
props: Node,
expression: Node,
}

interface ComponentNode {
name: string,
children: Array<any>,
props: Object
}

class ComponentNode {
constructor(name: string, props: Object, children: Array<any>) {
this.name = name;
this.children = children;
this.props = props;
}
}

// Parser
function getTree(filePath: string){
const source = fs.readFileSync(path.resolve(__dirname, filePath));
const parsed = JSXPARSER.parse(source, {sourceType: "module"});
const programBody: Array<Node> = parsed.body; // get body of Program Node(i.e. source code entry)
return programBody;
}

function getImportNodes(programBody: Array<Node>) {
const importNodes: Array<Node> = programBody.filter((node: Node) => node.type === 'ImportDeclaration');
return importNodes;
}
function getVariableNodes(programBody: Array<Node>) {
// const variables that hold different nodes depending on type
const variableNodes: Array<Node> = programBody.filter((node: Node) => node.type === 'VariableDeclaration');
return variableNodes;
}
function getOtherNodes(programBody: Array<any>) {
const otherNodes: Array<Node> = programBody.filter((node: Node) => node.type !== 'ImportDeclaration');
return otherNodes;
}
function getExportDefaultNodes(otherNodes: Array<any>) {
const exportDefaultNode: Node = otherNodes.filter((node: Node) => {
node.type === 'ExportDefaultDeclaration';
})[0];
return exportDefaultNode;
}

function getChildrenNodes(variableNodes: Array<Node>) {
const childrenNodes = variableNodes[variableNodes.length-1].declarations[0].init.body.body[1].argument.children;
return childrenNodes;
}

function getJsxNodes(childrenNodes: Array<Node>) {
const jsxNodes: Array<Node> = childrenNodes.filter((node: Node) => node.type === JSXELEMENT);
return jsxNodes;
}

function getChildrenComponents(jsxNodes: Array<Node>) {
const components = [];
for (let node of jsxNodes) {
const firstChar = node.openingElement.name.name[0]; // actual name label (i.e. 'Chatroom', 'Component')
const componentName = node.openingElement.name.name;
if (firstChar === firstChar.toUpperCase()) {
const props = getProps(node);
// check componentName against importNodes
// if name matches import node name, take filepath
// recursively invoke parsing algo on file
// const children = main(filePath)

// const componentNode = new ComponentNode(componentName, props, children);
const componentNode = new ComponentNode(componentName, props, node.children);
components.push(componentNode);
}
}
return components;
}

function getPropValue(node: Node) {
if (Object.keys(node).includes('expression')) {
return node.expression.value; // look into the value (node) and find the expression
} else {
return node.value; // return the value
}
}

function getProps(node:Node){
const propObj = {};
for(let prop of node.openingElement.attributes){
const name = prop.name.name;
propObj[name] = getPropValue(prop.value);
// console.log('propObj', propObj);
}
// console.log(propArr);
return propObj;
}

function main(filePath: string) {
const tree = getTree(filePath);
const importNodes = getImportNodes(tree);
const variableNodes = getVariableNodes(tree);
const childrenNodes = getChildrenNodes(variableNodes);
const jsxNodes = getJsxNodes(childrenNodes);
const result = getChildrenComponents(jsxNodes);
console.log(result);
return result;
}

main('./App.jsx');

// Node {
// type: 'JSXElement',
// start: 815,
// end: 857,
// openingElement: Node {
// type: 'JSXOpeningElement',
// start: 815,
// end: 857,
// attributes: [ [Node], [Node] ],
// name: Node {
// type: 'JSXIdentifier',
// start: 816,
// end: 825,
// name: 'Chatrooms'
// },
// selfClosing: true
// },
// closingElement: null,
// children: []
// }
// some logic that will help filter out nodes that we actually need
// use a switch statement to avoid too many if conditional statements
// <Chatroom name={'Brian'} otherProp={500}/>

// value: Node {
// type: 'Literal',
// start: 650,
// end: 657,
// value: '/dogs',
// raw: '"/dogs"'
// }

// value: Node {
// type: 'JSXExpressionContainer',
// start: 775,
// end: 784,
// expression: [Node]
// }