Skip to content
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.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion .eslintrc.json
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@
"no-this-before-super": "warn",
"no-undef": "warn",
"no-unreachable": "warn",
"no-unused-vars": "warn",
"no-unused-vars": "off",
"constructor-super": "warn",
"valid-typeof": "warn"
}
Expand Down
6 changes: 3 additions & 3 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -70,7 +70,7 @@
"scripts": {
"lint": "eslint .",
"pretest": "npm run lint",
"test": "node ./build/src/test/runTest.js",
"test": "npx tsc ; node ./build/src/test/runTest.js",
"dev": "webpack --watch",
"webpack": "webpack"
},
Expand Down
26 changes: 8 additions & 18 deletions src/extension.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ import { Parser } from './parser';
import { Tree } from './types/tree';

let tree: Parser | undefined = undefined;
let panel: vscode.WebviewPanel | undefined = undefined
let panel: vscode.WebviewPanel | undefined = undefined;

// This method is called when your extension is activated
// Your extension is activated the very first time the command is executed
Expand All @@ -14,37 +14,31 @@ function activate(context: vscode.ExtensionContext) {
let columnToShowIn : vscode.ViewColumn | undefined = vscode.window.activeTextEditor
? vscode.window.activeTextEditor.viewColumn
: undefined;


// Command that allows for User to select the root file of their React application.
const pickFile: vscode.Disposable = vscode.commands.registerCommand('myExtension.pickFile', async () => {

// Check if there is an existing webview panel, if so display it.
if(panel) {
panel.reveal(columnToShowIn)
if (panel) {
panel.reveal(columnToShowIn);
}


// Opens window for the User to select the root file of React application
const fileArray: vscode.Uri[] = await vscode.window.showOpenDialog({ canSelectFolders: false, canSelectFiles: true, canSelectMany: false });


// Throw error message if no file was selected
if (!fileArray || fileArray.length === 0) {
vscode.window.showErrorMessage('No file selected');
return;
}


// Create Tree to be inserted into returned HTML
tree = new Parser(fileArray[0].path);
tree.parse();
const data: Tree = tree.getTree();


// Check if panel currently has a webview, if it does dispose of it and create another with updated root file selected.
// Otherwise create a new webview to display root file selected.
if(!panel) {
if (!panel) {
panel = createPanel(context, data, columnToShowIn);
} else {
panel.dispose()
Expand All @@ -54,23 +48,19 @@ function activate(context: vscode.ExtensionContext) {
// Listens for when webview is closed and disposes of webview resources
panel.onDidDispose(
() => {
console.log("Before: ", panel)
panel.dispose()
panel.dispose();
panel = undefined;
columnToShowIn = undefined;
console.log("After: ", panel)
},
null,
context.subscriptions
);
);
});


// Command to show panel if it is hidden
const showPanel: vscode.Disposable = vscode.commands.registerCommand('myExtension.showPanel', () => {
panel.reveal(columnToShowIn)
});

panel.reveal(columnToShowIn);
});

context.subscriptions.push(pickFile, showPanel);
}
Expand Down
20 changes: 9 additions & 11 deletions src/panel.ts
Original file line number Diff line number Diff line change
@@ -1,16 +1,15 @@
import * as vscode from 'vscode';
import { getNonce } from './getNonce';
import { getNonce } from './utils/getNonce';
import { Tree } from './types/tree';

let panel: vscode.WebviewPanel | undefined = undefined
let panel: vscode.WebviewPanel | undefined = undefined;

export function createPanel(context: vscode.ExtensionContext, data: Tree, columnToShowIn: vscode.ViewColumn) {

// utilize method on vscode.window object to create webview
// Utilize method on vscode.window object to create webview
panel = vscode.window.createWebviewPanel(
'reactLabyrinth',
'React Labyrinth',
// create one new tab
// Create one tab
vscode.ViewColumn.One,
{
enableScripts: true,
Expand All @@ -24,10 +23,10 @@ export function createPanel(context: vscode.ExtensionContext, data: Tree, column
// Set URI to be the path to bundle
const bundlePath: vscode.Uri = vscode.Uri.joinPath(context.extensionUri, 'build', 'bundle.js');

// set webview URI to pass into html script
// Set webview URI to pass into html script
const bundleURI: vscode.Uri = panel.webview.asWebviewUri(bundlePath);

// render html of webview here
// Render html of webview here
panel.webview.html = createWebviewHTML(bundleURI, data);

// Sends data to Flow.tsx to be displayed after parsed data is received
Expand All @@ -37,7 +36,6 @@ export function createPanel(context: vscode.ExtensionContext, data: Tree, column
case 'onData':
if (!msg.value) break;
context.workspaceState.update('reactLabyrinth', msg.value);

panel.webview.postMessage(
{
type: 'parsed-data',
Expand All @@ -54,10 +52,10 @@ export function createPanel(context: vscode.ExtensionContext, data: Tree, column
return panel
};

// getNonce generates a new random string each time ext is used to prevent external injection of foreign code into the html
// getNonce generates a new random string to prevent external injection of foreign code into the HTML
const nonce: string = getNonce();

// function to create the HTML page for webview
// Creates the HTML page for webview
function createWebviewHTML(URI: vscode.Uri, initialData: Tree) : string {
return (
`
Expand All @@ -83,5 +81,5 @@ function createWebviewHTML(URI: vscode.Uri, initialData: Tree) : string {
</body>
</html>
`
)
);
}
68 changes: 27 additions & 41 deletions src/parser.ts
Original file line number Diff line number Diff line change
@@ -1,33 +1,11 @@
import * as fs from 'fs';
import * as path from 'path';
import * as babel from '@babel/parser';
import { getNonce } from './getNonce';
import { getNonce } from './utils/getNonce';
import { ImportObj } from './types/ImportObj';
import { Tree } from "./types/tree";
import { File } from '@babel/types';

// // function to determine server or client component (can look for 'use client' and 'hooks')
// // input: ast node (object)
// // output: boolean
// checkForClientString(node) {
// if (node.type === 'Directive') {
// console.log('node', node);
// // access the value property of the Directive node
// console.log('Directive Value:', node.value);
// // check if the node.value is a 'DirectiveLiteral' node
// if (node.value && node.value.type === 'DirectiveLiteral') {
// // check the value to see if it is 'use client'
// if (typeof node.value.value === 'string' && node.value.value.trim() === 'use client') {
// // access the value property of the 'DirectiveLiteral' node
// console.log('DirectiveLiteral Value:', node.value.value);
// // might need to do something else here to make it known as client type
// return true;
// }
// }
// }
// return false;
// }

export class Parser {
entryFile: string;
tree: Tree | undefined;
Expand Down Expand Up @@ -86,7 +64,7 @@ export class Parser {
return this.tree;
}

// Set Sapling Parser with a specific Data Tree (from workspace state)
// Set entryFile property with the result of Parser (from workspace state)
public setTree(tree: Tree) {
this.entryFile = tree.filePath;
this.tree = tree;
Expand Down Expand Up @@ -131,7 +109,7 @@ export class Parser {
return this.tree!;
}

// Traverses the tree and changes expanded property of node whose id matches provided id
// Traverses the tree and changes expanded property of node whose ID matches provided ID
public toggleNode(id: string, expanded: boolean): Tree{
const callback = (node: { id: string; expanded: boolean }) => {
if (node.id === id) {
Expand Down Expand Up @@ -204,14 +182,13 @@ export class Parser {
// Find imports in the current file, then find child components in the current file
const imports = this.getImports(ast.program.body);

if (this.getCallee(ast.program.body)) {
// Set value of isClientComponent property
if (this.getComponentType(ast.program.directives, ast.program.body)) {
componentTree.isClientComponent = true;
} else {
componentTree.isClientComponent = false;
}

// console.log('componentTree.isClientComponent', componentTree.isClientComponent);
// console.log('--------------------------------')
// Get any JSX Children of current file:
if (ast.tokens) {
componentTree.children = this.getJSXChildren(
Expand Down Expand Up @@ -247,10 +224,8 @@ export class Parser {
}

// Extracts Imports from current file
// const Page1 = lazy(() => import('./page1')); -> is parsed as 'ImportDeclaration'
// import Page2 from './page2'; -> is parsed as 'VariableDeclaration'
// input: array of objects: ast.program.body
// output: object of imoprts
// const App1 = lazy(() => import('./App1')); => is parsed as 'ImportDeclaration'
// import App2 from './App2'; => is parsed as 'VariableDeclaration'
private getImports(body: { [key: string]: any }[]): ImportObj {
const bodyImports = body.filter((item) => item.type === 'ImportDeclaration' || 'VariableDeclaration');

Expand Down Expand Up @@ -278,7 +253,7 @@ export class Parser {
}

private findVarDecImports(ast: { [key: string]: any }): string | boolean {
// find import path in variable declaration and return it,
// Find import path in variable declaration and return it,
if (ast.hasOwnProperty('callee') && ast.callee.type === 'Import') {
return ast.arguments[0].value;
}
Expand All @@ -294,20 +269,32 @@ export class Parser {
return false;
}

// helper function to determine component type (client)
// input: ast.program.body
// output: boolean
private getCallee(body: { [key: string]: any }[]): boolean {
const defaultErr = (err,) => {
// Determines server or client component type (looks for use of 'use client' and react/redux state hooks)
private getComponentType(directive: { [key: string]: any }[], body: { [key: string]: any }[]): boolean {
const defaultErr = (err) => {
return {
method: 'Error in getCallee method of Parser:',
log: err,
}
};

console.log('directive: ', directive);
// Initial check for use of directives (ex: 'use client', 'use server', 'use strict')
// Accounts for more than one directive
for (let i = 0; i < directive.length; i++) {
if (directive[i].type === 'Directive') {
if (typeof directive[i].value.value === 'string' && directive[i].value.value.trim() === 'use client') {
return true;
}
}
break;
}

// Second check for use of React/Redux hooks
const bodyCallee = body.filter((item) => item.type === 'VariableDeclaration');
if (bodyCallee.length === 0) return false;

// Helper function
const calleeHelper = (item) => {
const hooksObj = {
useState: 0,
Expand Down Expand Up @@ -420,7 +407,7 @@ export class Parser {
childNodes,
);

// Case for finding components passed in as props e.g. <Route component={App} />
// Case for finding components passed in as props e.g. <Route Component={App} />
} else if (
astTokens[i].type.label === 'jsxName' &&
(astTokens[i].value === 'Component' ||
Expand Down Expand Up @@ -473,7 +460,6 @@ export class Parser {
props: props,
children: [],
parent: parent.id,
// consider adding the id to the parentList array somehow for D3 integration...
parentList: [parent.filePath].concat(parent.parentList),
error: '',
isClientComponent: false
Expand Down Expand Up @@ -501,7 +487,7 @@ export class Parser {

// Checks if current Node is connected to React-Redux Store
private checkForRedux(astTokens: any[], importsObj: ImportObj): boolean {
// Check that react-redux is imported in this file (and we have a connect method or otherwise)
// Check that React-Redux is imported in this file (and we have a connect method or otherwise)
let reduxImported = false;
let connectAlias;
Object.keys(importsObj).forEach((key) => {
Expand Down
5 changes: 1 addition & 4 deletions src/test/runTest.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,18 +4,15 @@ import { runTests } from '@vscode/test-electron';
async function main() {
console.log('made it through the line before try block');
try {
console.log('inside try block');
// The folder containing the Extension Manifest package.json
// Passed to `--extensionDevelopmentPath`
const extensionDevelopmentPath = path.resolve(__dirname, '../../');

console.log('inside try block after first var declare');

// The path to the extension test script
// Passed to --extensionTestsPath
const extensionTestsPath = path.resolve(__dirname, './suite/index');

console.log('inside try block after second var declare');
console.log('inside try block after var declarations');

// Download VS Code, unzip it and run the integration test
await runTests({
Expand Down
3 changes: 0 additions & 3 deletions src/test/suite/extension.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,9 +6,6 @@ import * as vscode from 'vscode'
// we can either use test() or it() -- matter of style for team/project convention

describe('Extension Test Suite', () => {
// beforeEach(() => {
// vscode.window.showInformationMessage('Start all tests.');
// });

it('Sample test', () => {
expect([1, 2, 3].indexOf(5)).toBe(-1);
Expand Down
Loading