Skip to content

Commit 7d132fc

Browse files
authored
Merge pull request #11 from oslabs-beta/check-is-client
check-is-client
2 parents 5cdcfb0 + c1c1623 commit 7d132fc

File tree

2 files changed

+59
-122
lines changed

2 files changed

+59
-122
lines changed

extension.js

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,8 @@ function activate(context) {
2121
const fileArray = await vscode.window.showOpenDialog({ canSelectFolders: false, canSelectFiles: true, canSelectMany: false });
2222
const tree = new Parser(fileArray[0].path);
2323
tree.parse();
24-
console.log('tree instance', tree);
24+
const data = tree.getTree();
25+
console.log('Data sent back: ', data)
2526
});
2627

2728
context.subscriptions.push(disposable, result);

src/parser.js

Lines changed: 57 additions & 121 deletions
Original file line numberDiff line numberDiff line change
@@ -2,118 +2,27 @@ const fs = require('fs')
22
const path = require('path')
33
const babel = require('@babel/parser');
44
const { getNonce } = require('./getNonce.js');
5-
// const { Tree } = require('./treeTemplates/tree.js')
6-
7-
// Parser2 is our old code
8-
// class Parser2 {
9-
// constructor() {
10-
// this.ast = undefined;
11-
// this.entryFile = undefined;
12-
// this.arrList = [];
13-
// this.parentDirectoryPath = undefined;
14-
// }
15-
16-
// async grabFile(file) {
17-
// try {
18-
// if (!file) {
19-
// console.error('Invalid file parameter. Cannot process undefined.');
20-
// return;
21-
// }
22-
23-
// if (typeof file !== 'string') {
24-
// file = path.resolve(file[0].fsPath);
25-
// }
26-
27-
// const fileContent = fs.readFileSync(file, 'utf-8');
28-
// this.ast = babel.parse(fileContent, {
29-
// sourceType: 'module',
30-
// tokens: true,
31-
// plugins: ['jsx', 'typescript'],
32-
// });
33-
// await this.traverseAST(this.ast);
34-
// console.log('Result of arrList: ', this.arrList);
35-
// } catch (error) {
36-
// console.error(`Error processing file: ${error}`)
37-
// }
38-
// }
39-
40-
// // traverse the ast nodes, passing in node
41-
// // input: ast node (object)
42-
// // output ?
43-
// traverseAST(node) {
44-
// if (node.type === 'ImportDeclaration') {
45-
// // extract file name path
46-
// const elementName = node.source.value;
47-
// if (elementName.startsWith('./') || elementName.startsWith('../')) {
48-
// console.log('file path:', elementName)
49-
// console.log('node import: ', node);
50-
// // now with this list, we can call a func to determine if node is a client component?
51-
// this.arrList.push(elementName);
52-
// return this.grabFile(elementName);
53-
// };
54-
55-
// const result = getImports(node.program.body);
56-
// console.log(result, 'result');
57-
58-
// /* this code only works for JSXElement types: has prop of .children, but not the one above
59-
// if (node.children) {
60-
// // if node children exist, then recursively call the child nodes with this func
61-
// for (const child of node.children) {
62-
// await traverseAST(child);
63-
// }
64-
// }*/
65-
// } else {
66-
// // recursively iterate through the other non-jsx types if the jsx node children doesnt exist
67-
// for (const key in node) {
68-
// if (node[key] && typeof node[key] === 'object' && key !== 'tokens') {
69-
// this.traverseAST(node[key]);
70-
// }
71-
// }
72-
// }
73-
// }
745

756
// // function to determine server or client component (can look for 'use client' and 'hooks')
767
// // input: ast node (object)
778
// // output: boolean
78-
// checkForClientString(node) {
79-
// if (node.type === 'Directive') {
80-
// console.log('node', node);
81-
// // access the value property of the Directive node
82-
// console.log('Directive Value:', node.value);
83-
// // check if the node.value is a 'DirectiveLiteral' node
84-
// if (node.value && node.value.type === 'DirectiveLiteral') {
85-
// // check the value to see if it is 'use client'
86-
// if (typeof node.value.value === 'string' && node.value.value.trim() === 'use client') {
87-
// // access the value property of the 'DirectiveLiteral' node
88-
// console.log('DirectiveLiteral Value:', node.value.value);
89-
// // might need to do something else here to make it known as client type
90-
// return true;
91-
// }
92-
// }
93-
// }
94-
// return false;
95-
// }
96-
97-
// // function to determine if file uses react hooks (startswith 'use')
98-
// // input: ast node (object)
99-
// // output: boolean
100-
// checkReactHooks(node) {
101-
// // for just the mvp, look up for the FIRST client component and make every child as a client component
102-
// // function to determine if component uses react hooks (checks if its BEING CALLED IN COMPONENT)
103-
// if (node.type === 'CallExpression') {
104-
// console.log('nodeCall', node)
105-
// if (node.callee && node.callee.name) {
106-
// // to be more specific, we might want to consider declaring an array of hooks and write logic to iterate and checks if the name includes any of the elements, then return true
107-
// if (node.callee.name.startsWith('use')) {
108-
// // if the node.type is CallExpression (dealing with function or method call) (callee is prop on callexpression - an identifier), return true
109-
// console.log('node.callee', node.callee);
110-
// console.log('Node with Hook', node.callee.name);
111-
// return true;
112-
// }
9+
// checkForClientString(node) {
10+
// if (node.type === 'Directive') {
11+
// console.log('node', node);
12+
// // access the value property of the Directive node
13+
// console.log('Directive Value:', node.value);
14+
// // check if the node.value is a 'DirectiveLiteral' node
15+
// if (node.value && node.value.type === 'DirectiveLiteral') {
16+
// // check the value to see if it is 'use client'
17+
// if (typeof node.value.value === 'string' && node.value.value.trim() === 'use client') {
18+
// // access the value property of the 'DirectiveLiteral' node
19+
// console.log('DirectiveLiteral Value:', node.value.value);
20+
// // might need to do something else here to make it known as client type
21+
// return true;
11322
// }
11423
// }
115-
// return false;
11624
// }
25+
// return false;
11726
// }
11827

11928
class Parser {
@@ -136,7 +45,6 @@ class Parser {
13645
filePath.split(path.win32.sep).slice(1).join(path.posix.sep)
13746
);
13847
}
139-
14048
this.tree = undefined;
14149
// Break down and reasemble given filePath safely for any OS using path?
14250
}
@@ -160,8 +68,8 @@ class Parser {
16068
parentList: [],
16169
props: {},
16270
error: '',
71+
isClientComponent: false,
16372
};
164-
16573
this.tree = root;
16674
this.parser(root);
16775
return this.tree;
@@ -243,7 +151,7 @@ class Parser {
243151

244152
// Recursively builds the React component tree structure starting from root node
245153
parser(componentTree) {
246-
console.log(componentTree);
154+
console.log('componentTree:', componentTree);
247155
// If import is a node module, do not parse any deeper
248156
if (!['\\', '/', '.'].includes(componentTree.importPath[0])) {
249157
componentTree.thirdParty = true;
@@ -287,12 +195,20 @@ class Parser {
287195
// Find imports in the current file, then find child components in the current file
288196
const imports = this.getImports(ast.program.body);
289197

198+
if (this.getCallee(ast.program.body)) {
199+
componentTree.isClientComponent = true;
200+
} else {
201+
componentTree.isClientComponent = false;
202+
}
203+
204+
console.log('componentTree.isClientComponent', componentTree.isClientComponent);
205+
console.log('--------------------------------')
290206
// Get any JSX Children of current file:
291207
if (ast.tokens) {
292208
componentTree.children = this.getJSXChildren(
293209
ast.tokens,
294210
imports,
295-
componentTree
211+
componentTree,
296212
);
297213
}
298214

@@ -328,10 +244,8 @@ class Parser {
328244
// output: object of imoprts
329245
getImports(body) {
330246
const bodyImports = body.filter((item) => item.type === 'ImportDeclaration' || 'VariableDeclaration');
331-
console.log('body imports', bodyImports);
332247

333248
return bodyImports.reduce((accum, curr) => {
334-
// also determine if component is client or server
335249
if (curr.type === 'ImportDeclaration') {
336250
curr.specifiers.forEach(({ local, imported }) => {
337251
accum[local.name] = {
@@ -355,8 +269,6 @@ class Parser {
355269
}
356270

357271
findVarDecImports(ast) {
358-
// also determine if component is client or server
359-
360272
// find import path in variable declaration and return it,
361273
if (ast.hasOwnProperty('callee') && ast.callee.type === 'Import') {
362274
return ast.arguments[0].value;
@@ -373,13 +285,41 @@ class Parser {
373285
return false;
374286
}
375287

288+
// helper function to determine component type (client)
289+
// input: ast.program.body
290+
// output: boolean
291+
getCallee(body) {
292+
// does useStore count as a client component functionality?
293+
const hooksArray = ['useState', 'useContext', 'useRef', 'useImperativeHandle', 'useNavigate', 'useLayoutEffect', 'useInsertionEffect', 'useMemo', 'useCallback', 'useTransition', 'useDeferredValue', 'useEffect', 'useReducer', 'useDispatch', 'useActions', 'useSelector', 'bindActionCreators'];
294+
295+
//! console.log('ast.program.body', body);
296+
const bodyCallee = body.filter((item) => item.type === 'VariableDeclaration');
297+
const calleeArr = bodyCallee[0].declarations[0].init.body.body // gives us an array of callee nodes
298+
299+
console.log('calleArr:', calleeArr);
300+
for (let i = 0; i < calleeArr.length; i++) {
301+
if (calleeArr[i].type === 'VariableDeclaration') {
302+
if (hooksArray.includes(calleeArr[i].declarations[0].init.callee.name) || calleeArr[i].declarations[0].init.callee.name.startsWith('use')) {
303+
return true;
304+
}
305+
}
306+
if (calleeArr[i].type === 'ExpressionStatement') {
307+
if (hooksArray.includes(calleeArr[i].expression.callee.name) || calleeArr[i].expression.callee.name.startsWith('use')) {
308+
return true;
309+
}
310+
}
311+
}
312+
return false;
313+
}
314+
376315
// Finds JSX React Components in current file
377316
getJSXChildren(astTokens, importsObj, parentNode) {
378317
let childNodes = {};
379318
let props = {};
380319
let token;
381320

382321
for (let i = 0; i < astTokens.length; i++) {
322+
383323
// Case for finding JSX tags eg <App .../>
384324
if (
385325
astTokens[i].type.label === 'jsxTagStart' &&
@@ -393,7 +333,7 @@ class Parser {
393333
token,
394334
props,
395335
parentNode,
396-
childNodes
336+
childNodes,
397337
);
398338

399339
// Case for finding components passed in as props e.g. <Route component={App} />
@@ -409,7 +349,7 @@ class Parser {
409349
token,
410350
props,
411351
parentNode,
412-
childNodes
352+
childNodes,
413353
);
414354
}
415355
}
@@ -421,7 +361,7 @@ class Parser {
421361
astToken,
422362
props,
423363
parent,
424-
children
364+
children,
425365
) {
426366
if (children[astToken.value]) {
427367
children[astToken.value].count += 1;
@@ -450,9 +390,9 @@ class Parser {
450390
children: [],
451391
parentList: [parent.filePath].concat(parent.parentList),
452392
error: '',
393+
isClientComponent: false
453394
};
454395
}
455-
456396
return children;
457397
}
458398

@@ -504,8 +444,4 @@ class Parser {
504444
}
505445
}
506446

507-
// 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'
508-
509-
// function to determine if the component is server
510-
511447
module.exports = { Parser };

0 commit comments

Comments
 (0)