Skip to content

Commit

Permalink
Codeclimate refactoring and fixed badge in README.md
Browse files Browse the repository at this point in the history
  • Loading branch information
nktnet committed Oct 12, 2023
1 parent 43b1b5c commit bddf581
Show file tree
Hide file tree
Showing 3 changed files with 57 additions and 54 deletions.
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@
 
[![codecov](https://codecov.io/gh/nktnet1/jewire/branch/main/graph/badge.svg?token=RAC7SKJTGU)](https://codecov.io/gh/nktnet1/jewire)
 
[![Maintainability](https://api.codeclimate.com/v1/badges/3ec8c0ddebe848926277/maintainability)](https://codeclimate.com/github/nktnet1/jewire/maintainability)
[![Maintainability](https://api.codeclimate.com/v1/badges/2cc2478a1b5a2f293149/maintainability)](https://codeclimate.com/github/nktnet1/jewire/maintainability)
 
[![Snyk Security](https://snyk.io/test/github/nktnet1/jewire/badge.svg)](https://snyk.io/test/github/nktnet1/jewire)
 
Expand Down
29 changes: 8 additions & 21 deletions src/clone.ts
Original file line number Diff line number Diff line change
Expand Up @@ -20,23 +20,15 @@ export const isFunction = (functionOrClass: any): boolean => {
* @returns {T} - A deep clone of the input object or array.
*/
const objectClone: CloneFn = <T>(obj: T): T => {
if (obj === null || typeof obj !== 'object') {
if (!obj || typeof obj !== 'object') {
return obj;
}

if (Array.isArray(obj)) {
const cloneArr: any[] = [];
for (let i = 0; i < obj.length; i++) {
cloneArr[i] = objectClone(obj[i]);
}
return cloneArr as any as T;
return obj.map(objectClone) as T;
}

const cloneObj: Record<string, any> = {};
for (const key in obj) {
if (Object.prototype.hasOwnProperty.call(obj, key)) {
cloneObj[key] = objectClone(obj[key]);
}
for (const [key, value] of Object.entries(obj)) {
cloneObj[key] = objectClone(value);
}
return cloneObj as T;
};
Expand Down Expand Up @@ -114,19 +106,14 @@ function decorateClassMethodClone(target: any, clone: CloneFn) {
*/
/* istanbul ignore next */
const classClone = <T>(obj: T, objClone: CloneFn): T => {
if (obj === null || typeof obj !== 'object') {
if (obj ?? typeof obj !== 'object') {
return decorateClassMethodClone(obj as any, objClone);
}
const props = Object.getOwnPropertyDescriptors(obj);
for (const prop in props) {
if (Object.prototype.hasOwnProperty.call(props, prop)) {
props[prop].value = classClone(props[prop].value, objClone);
}
for (const prop of Object.keys(props)) {
props[prop].value = classClone(props[prop].value, objClone);
}
return Object.create(
Object.getPrototypeOf(obj),
props
);
return Object.create(Object.getPrototypeOf(obj), props);
};

/**
Expand Down
80 changes: 48 additions & 32 deletions src/files.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ import fs from 'fs';
import path from 'path';
import { parse } from 'meriyah';
import { VALID_FILE_EXTENSIONS } from './config';
import { ASTProgram, HiddenExportInfo, Symbols } from './types';
import { HiddenExportInfo, Symbols } from './types';
import { Statement } from 'meriyah/dist/src/estree';

/**
Expand Down Expand Up @@ -33,23 +33,21 @@ export const getCallerFilePath = (): string => {
* @throws {Error} If the file is not found
*/
export const findModuleFile = (modulePath: string, basePath: string): string => {
const filePathOriginal = path.join(basePath, modulePath);
let filePath = filePathOriginal;
if (!path.extname(filePathOriginal)) {
const filePath = path.join(basePath, modulePath);
if (fs.existsSync(filePath)) {
return filePath;
}
if (!path.extname(filePath)) {
for (const ext of VALID_FILE_EXTENSIONS) {
filePath = path.join(basePath, `${modulePath}${ext}`);
if (fs.existsSync(filePath)) {
return filePath;
const extFilePath = path.join(basePath, `${modulePath}${ext}`);
if (fs.existsSync(extFilePath)) {
return extFilePath;
}
}
}
if (!fs.existsSync(filePath)) {
const noExt = filePath.replace(/\.[^/.]+$/, '');
throw new Error(
`No such file '${noExt}' with matching extensions [${VALID_FILE_EXTENSIONS}]`
);
}
return filePath;
throw new Error(
`No such file '${filePath}' with matching extensions [${VALID_FILE_EXTENSIONS}]`
);
};

/**
Expand All @@ -60,36 +58,42 @@ export const findModuleFile = (modulePath: string, basePath: string): string =>
* @returns symbols consisting of variables, functions and classes
*/
const retrieveSymbolsFromAst = (node: Statement, symbols: Symbols): void => {
if (node.type === 'FunctionDeclaration' && node.id !== null) {
symbols.functions.push(node.id.name);
} else if (node.type === 'VariableDeclaration') {
node.declarations.forEach((declaration) => {
if (declaration.id.type === 'Identifier') {
symbols.variables.push(declaration.id.name);
switch (node.type) {
case 'VariableDeclaration':
node.declarations.forEach((declaration) => {
if (declaration.id.type === 'Identifier') {
symbols.variables.push(declaration.id.name);
}
});
break;
case 'FunctionDeclaration':
if (node.id !== null) {
symbols.functions.push(node.id.name);
}
break;
case 'ClassDeclaration':
if (node.id !== null) {
symbols.classes.push(node.id.name);
}
});
} else if (node.type === 'ClassDeclaration' && node.id !== null) {
symbols.classes.push(node.id.name);
}
};

/**
* Retrieves the name of all global variables/functions/classes, including
* those not exported in the file
* Parses code from a file and generates an Abstract Syntax Tree (AST)
*
* @param {string} filePath - The path to the JavaScript module file
* @returns {HiddenExportInfo} - Object of string[] for hidden exports
* @throws {Error} If the module file is empty or cannot be parsed.
* @param {string} filePath - The absolute path to the file
* @throws {Error} If the file is empty or contains errors while parsing
* @returns {Object} object with properties:
* - `ast` (ASTProgram): The Abstract Syntax Tree (AST) representing the code
* - `code` (string): The original code read from the file
*/
export const getModuleHiddenExports = (filePath: string): HiddenExportInfo => {
const createAbstractSyntaxTree = (filePath: string) => {
const code = fs.readFileSync(filePath, 'utf-8');
if (code.length === 0) {
throw new Error(`Module '${filePath}' is an empty file`);
}

let ast: ASTProgram;
try {
ast = parse(code);
return { ast: parse(code), code };
} catch (error: any) {
throw new Error(
`>>> Failed to parse code:
Expand All @@ -105,11 +109,23 @@ Please double check the file:
for the error: ${error}`
);
}
};

/**
* Retrieves the name of all global variables/functions/classes, including
* those not exported in the file
*
* @param {string} filePath - The path to the JavaScript module file
* @returns {HiddenExportInfo} - Object of string[] for hidden exports
* @throws {Error} If the module file is empty or cannot be parsed.
*/
export const getModuleHiddenExports = (filePath: string): HiddenExportInfo => {
const symbols = {
variables: [],
functions: [],
classes: [],
};
const { ast, code } = createAbstractSyntaxTree(filePath);
for (const node of ast.body) {
retrieveSymbolsFromAst(node, symbols);
}
Expand Down

0 comments on commit bddf581

Please sign in to comment.