Skip to content

Commit e8f27f4

Browse files
authored
Merge pull request #9 from oslabs-beta/AL/client-conditionals
Al/client conditionals
2 parents 7ab1f38 + 4d35b74 commit e8f27f4

File tree

4 files changed

+165
-14
lines changed

4 files changed

+165
-14
lines changed

extension.js

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
const vscode = require('vscode');
2-
const {createPanel} = require('./src/panel');
2+
const { createPanel } = require('./src/panel');
3+
const { grabFile } = require('./src/parser');
34

45
// This method is called when your extension is activated
56
// Your extension is activated the very first time the command is executed
@@ -16,6 +17,10 @@ function activate(context) {
1617
createPanel(context);
1718
});
1819

20+
vscode.commands.registerCommand('myExtension.pickFile', () => {
21+
grabFile();
22+
});
23+
1924
context.subscriptions.push(disposable, result);
2025
}
2126

package-lock.json

Lines changed: 12 additions & 11 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

package.json

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,11 @@
2323
"command": "myExtension.showPanel",
2424
"title": "Show Panel",
2525
"category": "React Labyrinth"
26+
},
27+
{
28+
"command": "myExtension.pickFile",
29+
"title": "Pick File",
30+
"category": "React Labyrinth"
2631
}
2732
],
2833
"viewsContainers": {
@@ -49,7 +54,7 @@
4954
"viewsWelcome": [
5055
{
5156
"view": "metrics-file",
52-
"contents": "View tree to see where improvements can be made!\n[View Tree](command:myExtension.showPanel)\n"
57+
"contents": "View tree to see where improvements can be made!\n[View Tree](command:myExtension.pickFile)\n"
5358
}
5459
]
5560
},
@@ -64,14 +69,15 @@
6469
"@types/node": "18.x",
6570
"@types/vscode": "^1.84.0",
6671
"@vscode/test-electron": "^2.3.6",
67-
"eslint": "^8.52.0",
72+
"eslint": "^8.54.0",
6873
"glob": "^10.3.10",
6974
"mocha": "^10.2.0",
7075
"typescript": "^5.2.2",
7176
"webpack-cli": "^5.1.4"
7277
},
7378
"dependencies": {
7479
"@babel/core": "^7.23.3",
80+
"@babel/parser": "^7.23.4",
7581
"@babel/preset-env": "^7.23.3",
7682
"@babel/preset-react": "^7.23.3",
7783
"babel": "^6.23.0",

src/parser.js

Lines changed: 139 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,139 @@
1+
const vscode = require('vscode');
2+
const fs = require('fs');
3+
const path = require('path');
4+
const babel = require('@babel/parser');
5+
6+
let ast;
7+
async function grabFile() {
8+
try {
9+
const file = await vscode.window.showOpenDialog({ canSelectFolders: true, canSelectFiles: true, canSelectMany: true });
10+
console.log('file path', file[0].path);
11+
const name = path.basename(file[0].path);
12+
console.log('name of file', name);
13+
ast = await babel.parse(
14+
fs.readFileSync(path.resolve(file[0].path), 'utf-8'),
15+
{
16+
sourceType: 'module',
17+
tokens: true,
18+
plugins: ['jsx', 'typescript'],
19+
});
20+
// console.log('ast', ast);
21+
const result = await traverseAST(ast);
22+
console.log('res', result);
23+
// const tokens = ast.tokens;
24+
// console.log('tokens are:', tokens);
25+
} catch (error) {
26+
console.error(`Error processing file: ${error}`)
27+
}
28+
}
29+
30+
// create a set to reduce redundancy in console logs during recursive call
31+
const processedNodes = new Set();
32+
33+
// traverse the ast nodes, passing in node
34+
async function traverseAST(node) {
35+
// identify which are jsx elements type and then extract info about them (like the component name) and store it in var
36+
if (node.type === 'ImportDeclaration' && !processedNodes.has(node)) {
37+
processedNodes.add(node);
38+
// console.log('JSX Node', node);
39+
40+
// im guessing that jsx elements will never contain the use client or hook declaration, so i wouldnt need to call the functions here
41+
42+
// property on node to obtain component name (could be tag or component name)
43+
const elementName = node.source.value;
44+
if(elementName.startsWith('./') || elementName.startsWith('../')) console.log('file path:', elementName);
45+
46+
47+
if (node.children) {
48+
// if node children exist, then recursively call the child nodes with this func
49+
for (const child of node.children) {
50+
await traverseAST(child);
51+
}
52+
}
53+
} else if (!processedNodes.has(node)) {
54+
processedNodes.add(node);
55+
56+
// call the function to determine if it is a client component and store it in var
57+
// const isClientComp = await checkForClientString(node);
58+
// const isReactHook = await checkReactHooks(node);
59+
60+
// recursively iterate through the other non-jsx types if the jsx node children doesnt exist
61+
for (const key in node) {
62+
if (node[key] && typeof node[key] === 'object' && key !== 'tokens') {
63+
await traverseAST(node[key]);
64+
}
65+
}
66+
}
67+
return processedNodes;
68+
}
69+
70+
// function to determine server or client component (can look for 'use client' and 'hooks')
71+
72+
// also might want to consider functionality for child components of the current node to be classifed as client component (except for server clients rendered tree)
73+
74+
function checkForClientString(node) {
75+
if (node.type === 'Directive') {
76+
console.log('node', node);
77+
78+
// access the value property of the Directive node
79+
console.log('Directive Value:', node.value);
80+
81+
// check if the node.value is a 'DirectiveLiteral' node
82+
if (node.value && node.value.type === 'DirectiveLiteral') {
83+
84+
// check the value to see if it is 'use client'
85+
if (typeof node.value.value === 'string' && node.value.value.trim() === 'use client') {
86+
// access the value property of the 'DirectiveLiteral' node
87+
console.log('DirectiveLiteral Value:', node.value.value);
88+
89+
// might need to do something else here to make it known as client type
90+
console.log(`this node above has 'use client': `, true);
91+
return true;
92+
}
93+
}
94+
}
95+
return false;
96+
}
97+
98+
function checkReactHooks(node) {
99+
// for just the mvp, look up for the FIRST client component and make every child as a client component
100+
101+
// function to determine if component uses react hooks (this only checks if its BEING CALLED IN COMPONENT, not IMPORTED)
102+
// console.log('node', node);
103+
if (node.type === 'CallExpression') {
104+
console.log('nodeCall', node)
105+
if (node.callee && node.callee.name) {
106+
if (node.callee.name.startsWith('use')) {
107+
// if the node.type is CallExpression (dealing with function or method call) (callee is prop on callexpression - an identifier), return true
108+
console.log('node.callee', node.callee);
109+
console.log('Node with Hook', node.callee.name);
110+
console.log(`this node above uses hooks: `, true);
111+
return true;
112+
}
113+
}
114+
}
115+
116+
// function to determine if hooks are being IMPORTED
117+
// if (node.type === 'ImportDeclaration') {
118+
// console.log('node import', node);
119+
// if (node.specifiers) {
120+
// // filter through the array to see which ones uses hooks
121+
// const clientNodes = node.specifiers.filter((nodeImport) => {
122+
// return nodeImport.type === 'ImportSpecifier' && nodeImport.imported.name.startsWith('use');
123+
// });
124+
// // mapped over to console log the name of hook
125+
// clientNodes.map((nodeImport) => console.log('Names of Hooks', nodeImport.imported.name));
126+
// console.log(clientNodes);
127+
// // we'll wanna change this to use it somehow
128+
// return clientNodes;
129+
// }
130+
return false;
131+
}
132+
133+
// function to determine if the client component imports server components or call server hooks/utils, if it does, then return 'is not valid client comp'
134+
135+
// function to determine if the component is server
136+
137+
// render component tree using react flow, passing in node and recursvely call on child nodes
138+
139+
module.exports = { grabFile };

0 commit comments

Comments
 (0)