fix: update rts logic to use updated shared AST logic (#16849)

* update rts logic to use updated shared AST logic

* Make changes to naming conventions

* Add test cases for RTS and rename ast functions

* Add running jest test to RTS workflow

* Install dependencies and then trigger jest tests in workflow

* Close server connection after test ends

* Remove logs

* Improve jest test descriptions
This commit is contained in:
Ayangade Adeoluwa 2022-09-28 18:28:18 +01:00 committed by GitHub
parent 9d32752abb
commit 610509506e
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
15 changed files with 2541 additions and 182 deletions

View File

@ -119,6 +119,16 @@ jobs:
echo ::set-output name=version::$next_version-SNAPSHOT
echo ::set-output name=tag::$(echo ${GITHUB_REF:11})
# Install all the dependencies
- name: Install dependencies
if: steps.run_result.outputs.run_result != 'success'
run: yarn install --frozen-lockfile
# Run the Jest tests only if the workflow has been invoked in a PR
- name: Run the jest tests
if: steps.run_result.outputs.run_result != 'success'
run: yarn run test:unit
- name: Build
if: steps.run_result.outputs.run_result != 'success'
run: |

View File

@ -7,7 +7,7 @@ import {
getDynamicBindings,
extraLibrariesNames,
} from "utils/DynamicBindingUtils";
import { extractInfoFromCode } from "@shared/ast";
import { extractIdentifierInfoFromCode } from "@shared/ast";
import { convertPathToString, isWidget } from "../evaluationUtils";
import { DataTreeWidget } from "entities/DataTree/dataTreeFactory";
import {
@ -31,7 +31,7 @@ export const extractInfoFromBinding = (
script: string,
allPaths: Record<string, true>,
): { validReferences: string[]; invalidReferences: string[] } => {
const { references } = extractInfoFromCode(
const { references } = extractIdentifierInfoFromCode(
script,
self.evaluationVersion,
invalidEntityIdentifiers,

23
app/rts/jest.config.js Normal file
View File

@ -0,0 +1,23 @@
module.exports = {
roots: ["<rootDir>/src"],
transform: {
"^.+\\.(png|js|ts|tsx)$": "ts-jest",
},
testTimeout: 9000,
testRegex: "(/__tests__/.*|(\\.|/)(test|spec))\\.(tsx|ts|js)?$",
moduleFileExtensions: ["ts", "tsx", "js", "jsx", "json", "node", "css"],
moduleDirectories: ["node_modules", "src", "test"],
moduleNameMapper: {
"@constants/(.*)": ["<rootDir>/src/constants/$1"],
"@services/(.*)": ["<rootDir>/src/services/$1"],
"@middlewares/(.*)": ["<rootDir>/src/middlewares/$1"],
"@controllers/(.*)": ["<rootDir>/src/controllers/$1"],
"@rules/(.*)": ["<rootDir>/src/middlewares/rules/$1"],
"@utils/(.*)": ["<rootDir>/src/utils/$1"],
},
globals: {
"ts-jest": {
isolatedModules: true,
},
},
};

View File

@ -12,17 +12,22 @@
},
"devDependencies": {
"@types/express": "^4.17.11",
"@types/jest": "^29.0.3",
"@types/mongodb": "^3.6.10",
"axios": "^0.21.2",
"express": "^4.17.1",
"jest": "^29.0.3",
"loglevel": "^1.7.1",
"mongodb": "^3.6.4",
"socket.io": "^4.5.1",
"socket.io-adapter": "^2.3.2",
"source-map-support": "^0.5.19",
"ts-jest": "^29.0.2",
"typescript": "^4.2.3"
},
"scripts": {
"test:unit": "export APPSMITH_API_BASE_URL=http APPSMITH_MONGODB_URI=mongodb && $(npm bin)/jest -b --colors --no-cache --silent --coverage --collectCoverage=true --coverageDirectory='../../' --coverageReporters='json-summary'",
"test:jest": "export APPSMITH_API_BASE_URL=http APPSMITH_MONGODB_URI=mongodb && $(npm bin)/jest --watch ",
"preinstall": "CURRENT_SCOPE=rts node ../shared/build-shared-dep.js",
"build": "./build.sh",
"postinstall": "CURRENT_SCOPE=rts node ../shared/install-dependencies.js",
@ -31,6 +36,7 @@
"dependencies": {
"express-validator": "^6.14.2",
"http-status-codes": "^2.2.0",
"supertest": "^6.2.4",
"tsc-alias": "^1.7.0"
}
}

View File

@ -18,11 +18,11 @@ export default class AstController extends BaseController {
super();
}
async getDependentIdentifiers(req: Request, res: Response) {
async getIdentifierDataFromScript(req: Request, res: Response) {
try {
// By default the application eval version is set to be 2
const { script, evalVersion = 2 }: ScriptToIdentifiersType = req.body;
const data = await AstService.getIdentifiersFromScript(
const data = await AstService.extractIdentifierDataFromScript(
script,
evalVersion
);
@ -37,7 +37,7 @@ export default class AstController extends BaseController {
}
}
async getMultipleDependentIdentifiers(req: Request, res: Response) {
async getIdentifierDataFromMultipleScripts(req: Request, res: Response) {
try {
// By default the application eval version is set to be 2
const { scripts, evalVersion = 2 }: MultipleScriptToIdentifiersType =
@ -46,7 +46,10 @@ export default class AstController extends BaseController {
Promise.all(
scripts.map(
async (script) =>
await AstService.getIdentifiersFromScript(script, evalVersion)
await AstService.extractIdentifierDataFromScript(
script,
evalVersion
)
)
).then((data) => {
return super.sendResponse(res, data);

View File

@ -1,6 +1,7 @@
import { Response } from "express";
import { ValidationError } from "express-validator";
import { StatusCodes } from "http-status-codes";
import { IdentifierInfo } from "@shared/ast";
type ErrorData = {
error: string | string[];
@ -16,7 +17,7 @@ type ErrorBag = {
type ResponseData = {
success: boolean;
message?: string;
data: unknown; //setting unknown for now, to be modified later.
data: IdentifierInfo;
};
export default class BaseController {

View File

@ -8,17 +8,17 @@ const astController = new AstController();
const validator = new Validator();
router.post(
"/single-script-identifiers",
"/single-script-data",
AstRules.getScriptValidator(),
validator.validateRequest,
astController.getDependentIdentifiers
astController.getIdentifierDataFromScript
);
router.post(
"/multiple-script-identifiers",
"/multiple-script-data",
AstRules.getMultipleScriptValidator(),
validator.validateRequest,
astController.getMultipleDependentIdentifiers
astController.getIdentifierDataFromMultipleScripts
);
export default router;

View File

@ -10,7 +10,7 @@ import { initializeSockets } from "./sockets";
import ast_routes from "./routes/ast_routes";
const RTS_BASE_PATH = "/rts";
const RTS_BASE_API_PATH = "/rts-api/v1";
export const RTS_BASE_API_PATH = "/rts-api/v1";
// Setting the logLevel for all log messages
const logLevel: LogLevelDesc = (process.env.APPSMITH_LOG_LEVEL ||
@ -36,32 +36,30 @@ if (API_BASE_URL == null || API_BASE_URL === "") {
const PORT = process.env.PORT || 8091;
main();
//Disable x-powered-by header to prevent information disclosure
const app = express();
app.disable("x-powered-by");
const server = new http.Server(app);
const io = new Server(server, {
path: RTS_BASE_PATH,
});
function main() {
const app = express();
//Disable x-powered-by header to prevent information disclosure
app.disable("x-powered-by");
const server = new http.Server(app);
const io = new Server(server, {
path: RTS_BASE_PATH,
});
// Initializing Sockets
initializeSockets(io);
// Initializing Sockets
initializeSockets(io);
// parse incoming json requests
app.use(express.json({ limit: "5mb" }));
// Initializing Routes
app.use(express.static(path.join(__dirname, "static")));
app.get("/", (_, res) => {
res.redirect("/index.html");
});
// parse incoming json requests
app.use(express.json({ limit: "5mb" }));
// Initializing Routes
app.use(express.static(path.join(__dirname, "static")));
app.get("/", (_, res) => {
res.redirect("/index.html");
});
app.use(`${RTS_BASE_API_PATH}/ast`, ast_routes);
app.use(`${RTS_BASE_API_PATH}/ast`, ast_routes);
// Run the server
server.listen(PORT, () => {
log.info(`RTS version ${buildVersion} running at http://localhost:${PORT}`);
});
// Run the server
server.listen(PORT, () => {
log.info(`RTS version ${buildVersion} running at http://localhost:${PORT}`);
});
}
export default server;

View File

@ -1,18 +1,20 @@
import { extractInfoFromCode } from "@shared/ast";
import { extractIdentifierInfoFromCode } from "@shared/ast";
export default class AstService {
static async getIdentifiersFromScript(
static async extractIdentifierDataFromScript(
script,
evalVersion
evalVersion,
invalidIdentifiers = {}
): Promise<any> {
return new Promise((resolve, reject) => {
try {
const extractions = extractInfoFromCode(
const identifierInfo = extractIdentifierInfoFromCode(
script,
evalVersion
evalVersion,
invalidIdentifiers
);
resolve(extractions);
resolve(identifierInfo);
} catch (err) {
reject(err);
}

View File

@ -0,0 +1,67 @@
import app, { RTS_BASE_API_PATH } from "../server";
import supertest from "supertest";
const singleScript = {
script:
"(function abc() { let Api2 = { }; return Api2.data ? str.data + Api1.data : [] })()",
};
const multipleScripts = {
scripts: [
"(function abc() { return Api1.data })() ",
"(function abc() { let str = ''; return str ? Api1.data : [] })()",
],
};
afterAll((done) => {
app.close();
done();
});
describe("AST tests", () => {
it("Checks to see if single script is parsed correctly using the API", async () => {
const expectedResponse = {
references: ["str.data", "Api1.data"],
functionalParams: [],
variables: ["Api2"],
};
await supertest(app)
.post(`${RTS_BASE_API_PATH}/ast/single-script-data`, {
JSON: true,
})
.send(singleScript)
.expect(200)
.then((response) => {
expect(response.body.success).toEqual(true);
expect(response.body.data).toEqual(expectedResponse);
});
});
it("Checks to see if multiple scripts are parsed correctly using the API", async () => {
const expectedResponse = [
{
references: ["Api1.data"],
functionalParams: [],
variables: [],
},
{
references: ["Api1.data"],
functionalParams: [],
variables: ["str"],
},
];
await supertest(app)
.post(`${RTS_BASE_API_PATH}/ast/multiple-script-data`, {
JSON: true,
})
.send(multipleScripts)
.expect(200)
.then((response) => {
expect(response.body.success).toEqual(true);
expect(response.body.data.length).toBeGreaterThan(1);
expect(response.body.data).toEqual(expectedResponse);
});
});
});

View File

@ -14,7 +14,7 @@
"@middlewares/*": ["./src/middlewares/*"],
"@controllers/*": ["./src/controllers/*"],
"@rules/*": ["./src/middlewares/rules/*"],
"@utils/*": ["./src/utils/*"],
"@utils/*": ["./src/utils/*"]
}
},
"lib": ["es2015"]

File diff suppressed because it is too large Load Diff

View File

@ -8,21 +8,27 @@ import {
isPropertyNode,
isPropertyAFunctionNode,
getAST,
extractInfoFromCode,
extractIdentifierInfoFromCode,
extractInvalidTopLevelMemberExpressionsFromCode,
getFunctionalParamsFromNode,
isTypeOfFunction,
MemberExpressionData,
} from './src';
IdentifierInfo,
} from "./src";
// constants
import { ECMA_VERSION, SourceType, NodeTypes } from './src/constants';
import { ECMA_VERSION, SourceType, NodeTypes } from "./src/constants";
// JSObjects
import { parseJSObjectWithAST } from './src/jsObject';
import { parseJSObjectWithAST } from "./src/jsObject";
// types or intefaces should be exported with type keyword, while enums can be exported like normal functions
export type { ObjectExpression, PropertyNode, MemberExpressionData };
export type {
ObjectExpression,
PropertyNode,
MemberExpressionData,
IdentifierInfo,
};
export {
isIdentifierNode,
@ -32,7 +38,7 @@ export {
isPropertyNode,
isPropertyAFunctionNode,
getAST,
extractInfoFromCode,
extractIdentifierInfoFromCode,
extractInvalidTopLevelMemberExpressionsFromCode,
getFunctionalParamsFromNode,
isTypeOfFunction,

View File

@ -1,85 +1,85 @@
import { extractInfoFromCode } from '../src/index';
import { parseJSObjectWithAST } from '../src/jsObject';
import { extractIdentifierInfoFromCode } from "../src/index";
import { parseJSObjectWithAST } from "../src/jsObject";
describe('getAllIdentifiers', () => {
it('works properly', () => {
describe("getAllIdentifiers", () => {
it("works properly", () => {
const cases: { script: string; expectedResults: string[] }[] = [
{
// Entity reference
script: 'DirectTableReference',
expectedResults: ['DirectTableReference'],
script: "DirectTableReference",
expectedResults: ["DirectTableReference"],
},
{
// One level nesting
script: 'TableDataReference.data',
expectedResults: ['TableDataReference.data'],
script: "TableDataReference.data",
expectedResults: ["TableDataReference.data"],
},
{
// Deep nesting
script: 'TableDataDetailsReference.data.details',
expectedResults: ['TableDataDetailsReference.data.details'],
script: "TableDataDetailsReference.data.details",
expectedResults: ["TableDataDetailsReference.data.details"],
},
{
// Deep nesting
script: 'TableDataDetailsMoreReference.data.details.more',
expectedResults: ['TableDataDetailsMoreReference.data.details.more'],
script: "TableDataDetailsMoreReference.data.details.more",
expectedResults: ["TableDataDetailsMoreReference.data.details.more"],
},
{
// Deep optional chaining
script: 'TableDataOptionalReference.data?.details.more',
expectedResults: ['TableDataOptionalReference.data'],
script: "TableDataOptionalReference.data?.details.more",
expectedResults: ["TableDataOptionalReference.data"],
},
{
// Deep optional chaining with logical operator
script:
'TableDataOptionalWithLogical.data?.details.more || FallbackTableData.data',
"TableDataOptionalWithLogical.data?.details.more || FallbackTableData.data",
expectedResults: [
'TableDataOptionalWithLogical.data',
'FallbackTableData.data',
"TableDataOptionalWithLogical.data",
"FallbackTableData.data",
],
},
{
// null coalescing
script: 'TableDataOptionalWithLogical.data ?? FallbackTableData.data',
script: "TableDataOptionalWithLogical.data ?? FallbackTableData.data",
expectedResults: [
'TableDataOptionalWithLogical.data',
'FallbackTableData.data',
"TableDataOptionalWithLogical.data",
"FallbackTableData.data",
],
},
{
// Basic map function
script: 'Table5.data.map(c => ({ name: c.name }))',
expectedResults: ['Table5.data.map'],
script: "Table5.data.map(c => ({ name: c.name }))",
expectedResults: ["Table5.data.map"],
},
{
// Literal property search
script: "Table6['data']",
expectedResults: ['Table6'],
expectedResults: ["Table6"],
},
{
// Deep literal property search
script: "TableDataOptionalReference['data'].details",
expectedResults: ['TableDataOptionalReference'],
expectedResults: ["TableDataOptionalReference"],
},
{
// Array index search
script: 'array[8]',
expectedResults: ['array[8]'],
script: "array[8]",
expectedResults: ["array[8]"],
},
{
// Deep array index search
script: 'Table7.data[4]',
expectedResults: ['Table7.data[4]'],
script: "Table7.data[4]",
expectedResults: ["Table7.data[4]"],
},
{
// Deep array index search
script: 'Table7.data[4].value',
expectedResults: ['Table7.data[4].value'],
script: "Table7.data[4].value",
expectedResults: ["Table7.data[4].value"],
},
{
// string literal and array index search
script: "Table['data'][9]",
expectedResults: ['Table'],
expectedResults: ["Table"],
},
{
// array index and string literal search
@ -88,29 +88,29 @@ describe('getAllIdentifiers', () => {
},
{
// Index identifier search
script: 'Table8.data[row][name]',
expectedResults: ['Table8.data', 'row'],
script: "Table8.data[row][name]",
expectedResults: ["Table8.data", "row"],
},
{
// Index identifier search with global
script: 'Table9.data[appsmith.store.row]',
expectedResults: ['Table9.data', 'appsmith.store.row'],
script: "Table9.data[appsmith.store.row]",
expectedResults: ["Table9.data", "appsmith.store.row"],
},
{
// Index literal with further nested lookups
script: 'Table10.data[row].name',
expectedResults: ['Table10.data', 'row'],
script: "Table10.data[row].name",
expectedResults: ["Table10.data", "row"],
},
{
// IIFE and if conditions
script:
'(function(){ if(Table11.isVisible) { return Api1.data } else { return Api2.data } })()',
expectedResults: ['Table11.isVisible', 'Api1.data', 'Api2.data'],
"(function(){ if(Table11.isVisible) { return Api1.data } else { return Api2.data } })()",
expectedResults: ["Table11.isVisible", "Api1.data", "Api2.data"],
},
{
// Functions and arguments
script: 'JSObject1.run(Api1.data, Api2.data)',
expectedResults: ['JSObject1.run', 'Api1.data', 'Api2.data'],
script: "JSObject1.run(Api1.data, Api2.data)",
expectedResults: ["JSObject1.run", "Api1.data", "Api2.data"],
},
{
// IIFE - without braces
@ -124,7 +124,7 @@ describe('getAllIdentifiers', () => {
return obj[index]
}()`,
expectedResults: ['Input1.text'],
expectedResults: ["Input1.text"],
},
{
// IIFE
@ -138,7 +138,7 @@ describe('getAllIdentifiers', () => {
return obj[index]
})()`,
expectedResults: ['Input2.text'],
expectedResults: ["Input2.text"],
},
{
// arrow IIFE - without braces - will fail
@ -166,19 +166,19 @@ describe('getAllIdentifiers', () => {
return obj[index]
})()`,
expectedResults: ['Input4.text'],
expectedResults: ["Input4.text"],
},
{
// Direct object access
script: `{ "a": 123 }[Input5.text]`,
expectedResults: ['Input5.text'],
expectedResults: ["Input5.text"],
},
{
// Function declaration and default arguments
script: `function run(apiData = Api1.data) {
return apiData;
}`,
expectedResults: ['Api1.data'],
expectedResults: ["Api1.data"],
},
{
// Function declaration with arguments
@ -197,7 +197,7 @@ describe('getAllIdentifiers', () => {
row = row += 1;
}
}`,
expectedResults: ['Table12.data'],
expectedResults: ["Table12.data"],
},
{
// function with variables
@ -209,17 +209,17 @@ describe('getAllIdentifiers', () => {
row = row += 1;
}
}`,
expectedResults: ['Table13.data'],
expectedResults: ["Table13.data"],
},
{
// expression with arithmetic operations
script: `Table14.data + 15`,
expectedResults: ['Table14.data'],
expectedResults: ["Table14.data"],
},
{
// expression with logical operations
script: `Table15.data || [{}]`,
expectedResults: ['Table15.data'],
expectedResults: ["Table15.data"],
},
// JavaScript built in classes should not be valid identifiers
{
@ -229,7 +229,7 @@ describe('getAllIdentifiers', () => {
const randomNumber = Math.random();
return Promise.all([firstApiRun, secondApiRun])
}()`,
expectedResults: ['Api1.run', 'Api2.run'],
expectedResults: ["Api1.run", "Api2.run"],
},
// Global dependencies should not be valid identifiers
{
@ -251,7 +251,7 @@ describe('getAllIdentifiers', () => {
console.log(joinedName)
return Api2.name
}()`,
expectedResults: ['Api2.name'],
expectedResults: ["Api2.name"],
},
// identifiers and member expressions derived from params should not be valid identifiers
{
@ -274,19 +274,19 @@ describe('getAllIdentifiers', () => {
script: `function(){
return appsmith.user
}()`,
expectedResults: ['appsmith.user'],
expectedResults: ["appsmith.user"],
},
];
cases.forEach((perCase) => {
const { references } = extractInfoFromCode(perCase.script, 2);
const { references } = extractIdentifierInfoFromCode(perCase.script, 2);
expect(references).toStrictEqual(perCase.expectedResults);
});
});
});
describe('parseJSObjectWithAST', () => {
it('parse js object', () => {
describe("parseJSObjectWithAST", () => {
it("parse js object", () => {
const body = `{
myVar1: [],
myVar2: {},
@ -299,25 +299,25 @@ describe('parseJSObjectWithAST', () => {
}`;
const parsedObject = [
{
key: 'myVar1',
value: '[]',
type: 'ArrayExpression',
key: "myVar1",
value: "[]",
type: "ArrayExpression",
},
{
key: 'myVar2',
value: '{}',
type: 'ObjectExpression',
key: "myVar2",
value: "{}",
type: "ObjectExpression",
},
{
key: 'myFun1',
value: '() => {}',
type: 'ArrowFunctionExpression',
key: "myFun1",
value: "() => {}",
type: "ArrowFunctionExpression",
arguments: [],
},
{
key: 'myFun2',
value: 'async () => {}',
type: 'ArrowFunctionExpression',
key: "myFun2",
value: "async () => {}",
type: "ArrowFunctionExpression",
arguments: [],
},
];
@ -325,7 +325,7 @@ describe('parseJSObjectWithAST', () => {
expect(resultParsedObject).toStrictEqual(parsedObject);
});
it('parse js object with literal', () => {
it("parse js object with literal", () => {
const body = `{
myVar1: [],
myVar2: {
@ -340,25 +340,25 @@ describe('parseJSObjectWithAST', () => {
}`;
const parsedObject = [
{
key: 'myVar1',
value: '[]',
type: 'ArrayExpression',
key: "myVar1",
value: "[]",
type: "ArrayExpression",
},
{
key: 'myVar2',
key: "myVar2",
value: '{\n "a": "app"\n}',
type: 'ObjectExpression',
type: "ObjectExpression",
},
{
key: 'myFun1',
value: '() => {}',
type: 'ArrowFunctionExpression',
key: "myFun1",
value: "() => {}",
type: "ArrowFunctionExpression",
arguments: [],
},
{
key: 'myFun2',
value: 'async () => {}',
type: 'ArrowFunctionExpression',
key: "myFun2",
value: "async () => {}",
type: "ArrowFunctionExpression",
arguments: [],
},
];
@ -366,7 +366,7 @@ describe('parseJSObjectWithAST', () => {
expect(resultParsedObject).toStrictEqual(parsedObject);
});
it('parse js object with variable declaration inside function', () => {
it("parse js object with variable declaration inside function", () => {
const body = `{
myFun1: () => {
const a = {
@ -382,7 +382,7 @@ describe('parseJSObjectWithAST', () => {
}`;
const parsedObject = [
{
key: 'myFun1',
key: "myFun1",
value: `() => {
const a = {
conditions: [],
@ -391,13 +391,13 @@ describe('parseJSObjectWithAST', () => {
testFunc2: function () {}
};
}`,
type: 'ArrowFunctionExpression',
type: "ArrowFunctionExpression",
arguments: [],
},
{
key: 'myFun2',
value: 'async () => {}',
type: 'ArrowFunctionExpression',
key: "myFun2",
value: "async () => {}",
type: "ArrowFunctionExpression",
arguments: [],
},
];
@ -405,7 +405,7 @@ describe('parseJSObjectWithAST', () => {
expect(resultParsedObject).toStrictEqual(parsedObject);
});
it('parse js object with params of all types', () => {
it("parse js object with params of all types", () => {
const body = `{
myFun2: async (a,b = Array(1,2,3),c = "", d = [], e = this.myVar1, f = {}, g = function(){}, h = Object.assign({}), i = String(), j = storeValue()) => {
//use async-await or promises
@ -414,49 +414,49 @@ describe('parseJSObjectWithAST', () => {
const parsedObject = [
{
key: 'myFun2',
key: "myFun2",
value:
'async (a, b = Array(1, 2, 3), c = "", d = [], e = this.myVar1, f = {}, g = function () {}, h = Object.assign({}), i = String(), j = storeValue()) => {}',
type: 'ArrowFunctionExpression',
type: "ArrowFunctionExpression",
arguments: [
{
paramName: 'a',
paramName: "a",
defaultValue: undefined,
},
{
paramName: 'b',
paramName: "b",
defaultValue: undefined,
},
{
paramName: 'c',
paramName: "c",
defaultValue: undefined,
},
{
paramName: 'd',
paramName: "d",
defaultValue: undefined,
},
{
paramName: 'e',
paramName: "e",
defaultValue: undefined,
},
{
paramName: 'f',
paramName: "f",
defaultValue: undefined,
},
{
paramName: 'g',
paramName: "g",
defaultValue: undefined,
},
{
paramName: 'h',
paramName: "h",
defaultValue: undefined,
},
{
paramName: 'i',
paramName: "i",
defaultValue: undefined,
},
{
paramName: 'j',
paramName: "j",
defaultValue: undefined,
},
],

View File

@ -1,8 +1,8 @@
import { parse, Node, SourceLocation, Options } from 'acorn';
import { ancestor, simple } from 'acorn-walk';
import { ECMA_VERSION, NodeTypes } from './constants/ast';
import { has, isFinite, isString, memoize, toPath } from 'lodash';
import { isTrueObject, sanitizeScript } from './utils';
import { parse, Node, SourceLocation, Options } from "acorn";
import { ancestor, simple } from "acorn-walk";
import { ECMA_VERSION, NodeTypes } from "./constants/ast";
import { has, isFinite, isString, memoize, toPath } from "lodash";
import { isTrueObject, sanitizeScript } from "./utils";
/*
* Valuable links:
@ -90,7 +90,7 @@ export interface PropertyNode extends Node {
type: NodeTypes.Property;
key: LiteralNode | IdentifierNode;
value: Node;
kind: 'init' | 'get' | 'set';
kind: "init" | "get" | "set";
}
// Node with location details
@ -98,7 +98,7 @@ type NodeWithLocation<NodeType> = NodeType & {
loc: SourceLocation;
};
type AstOptions = Omit<Options, 'ecmaVersion'>;
type AstOptions = Omit<Options, "ecmaVersion">;
/* We need these functions to typescript casts the nodes with the correct types */
export const isIdentifierNode = (node: Node): node is IdentifierNode => {
@ -196,23 +196,23 @@ export const getAST = memoize((code: string, options?: AstOptions) =>
* @param code: The piece of script where references need to be extracted from
*/
interface ExtractInfoFromCode {
export interface IdentifierInfo {
references: string[];
functionalParams: string[];
variables: string[];
}
export const extractInfoFromCode = (
export const extractIdentifierInfoFromCode = (
code: string,
evaluationVersion: number,
invalidIdentifiers?: Record<string, unknown>
): ExtractInfoFromCode => {
): IdentifierInfo => {
// List of all references found
const references = new Set<string>();
// List of variables declared within the script. All identifiers and member expressions derived from declared variables will be removed
const variableDeclarations = new Set<string>();
// List of functional params declared within the script. All identifiers and member expressions derived from functional params will be removed
let functionalParams = new Set<string>();
let ast: Node = { end: 0, start: 0, type: '' };
let ast: Node = { end: 0, start: 0, type: "" };
try {
const sanitizedScript = sanitizeScript(code, evaluationVersion);
/* wrapCode - Wrapping code in a function, since all code/script get wrapped with a function during evaluation.
@ -377,7 +377,7 @@ export const getFunctionalParamsFromNode = (
const constructFinalMemberExpIdentifier = (
node: MemberExpressionNode,
child = ''
child = ""
): string => {
const propertyAccessor = getPropertyAccessor(node.property);
if (isIdentifierNode(node.object)) {
@ -438,7 +438,7 @@ export const extractInvalidTopLevelMemberExpressionsFromCode = (
const invalidTopLevelMemberExpressions = new Set<MemberExpressionData>();
const variableDeclarations = new Set<string>();
let functionalParams = new Set<string>();
let ast: Node = { end: 0, start: 0, type: '' };
let ast: Node = { end: 0, start: 0, type: "" };
try {
const sanitizedScript = sanitizeScript(code, evaluationVersion);
const wrappedCode = wrapCode(sanitizedScript);