Huge News!Announcing our $40M Series B led by Abstract Ventures.Learn More
Socket
Sign inDemoInstall
Socket

flast

Package Overview
Dependencies
Maintainers
2
Versions
24
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

flast - npm Package Compare versions

Comparing version 1.0.1 to 1.1.0

.github/workflows/pull-request.yml

3

package.json
{
"name": "flast",
"version": "1.0.1",
"version": "1.1.0",
"description": "Flatten JS AST",
"main": "src/index.js",
"scripts": {
"lint": "eslint .",
"prepare": "husky install",

@@ -8,0 +9,0 @@ "test": "node tests/allTester.js"

@@ -137,3 +137,3 @@ # flAST - FLat Abstract Syntax Tree

const {generateFlatAST, generateCode} = require('flast');
const ast = generateFlatAST(`console.log('flAST'`);
const ast = generateFlatAST(`console.log('flAST')`);
const reconstructedCode = generateCode(ast[0]); // rebuild from root node

@@ -189,2 +189,8 @@ ```

## Contribution
To contribute to this project see our [contribution guide](CONTRIBUTING.md)
To contribute to this project see our [contribution guide](CONTRIBUTING.md)
## Changes
### v1.1.0
- Added parentKey property.
- Improved ASTNode definition (useful for intellisense).
- Ability to pass options directly to the espree parser.
// noinspection JSUnusedGlobalSymbols
// eslint-disable-next-line no-unused-vars
const {parse, ASTNode} = require('espree');
const {parse, ASTNode:espreeASTNode} = require('espree');
const {generate} = require('escodegen');
const estraverse = require('estraverse');
const eslineScope = require('eslint-scope');
// eslint-disable-next-line no-unused-vars
const {analyze, ScopeManager} = require('eslint-scope');
const ecmaVersion = 2022;
const ecmaVersion = 'latest';
/**
* @param inputCode
* @typedef ASTNode
* @property {number} nodeId
* @property {string} src
* @property {array} childNodes
* @property {?ASTNode} parentNode
* @property {ScopeManager} scope
* @property {?string} parentKey
*/
const ASTNode = espreeASTNode;
/**
* @param {string} inputCode
* @param {object} opts Additional options for espree
* @return {ASTNode} The root of the AST
*/
function parseCode(inputCode) {
return parse(inputCode, {ecmaVersion, comment: true, range: true});
function parseCode(inputCode, opts = {}) {
// noinspection JSValidateTypes
return parse(inputCode, {ecmaVersion, comment: true, range: true, ...opts});
}
/**
* Return the key the child node is assigned in the parent node if applicable; null otherwise.
* @param {ASTNode} parent
* @param {number} targetChildNodeId
* @returns {string|null}
*/
function getParentKey(parent, targetChildNodeId) {
if (parent) {
for (const key of Object.keys(parent)) {
if (parent[key]?.nodeId === targetChildNodeId) return key;
}
}
return null;
}
const generateFlatASTDefaultOptions = {
detailed: true, // If false, include only original node without any further details
includeSrc: true, // If false, do not include node src. Only available when `detailed` option is true
parseOpts: { // Options for the espree parser
sourceType: 'module',
},
};

@@ -30,10 +62,11 @@

function generateFlatAST(inputCode, opts = {}) {
opts = Object.assign(Object.assign({}, generateFlatASTDefaultOptions), opts);
const rootNode = parseCode(inputCode);
opts = { ...generateFlatASTDefaultOptions, ...opts };
const parseOpts = opts.parseOpts || {};
const rootNode = parseCode(inputCode, parseOpts);
let scopeManager;
try {
if (opts.detailed) { // noinspection JSCheckFunctionSignatures
scopeManager = eslineScope.analyze(rootNode, {optimistic: true, ecmaVersion});
scopeManager = analyze(rootNode, {optimistic: true, ecmaVersion});
}
} catch (e) {}
} catch {}
const tree = [];

@@ -44,3 +77,3 @@ let nodeId = 0;

if (opts.detailed) { // noinspection JSCheckFunctionSignatures
currentScope = scopeManager ? scopeManager.acquire(rootNode) : {};
currentScope = scopeManager?.acquire(rootNode) ?? {};
}

@@ -54,2 +87,3 @@ estraverse.traverse(rootNode, {

node.parentNode = parentNode;
node.parentKey = getParentKey(parentNode, node.nodeId);
// Set new scope when entering a function structure

@@ -102,4 +136,3 @@ if (scopeManager && /Function/.test(node.type)) currentScope = scopeManager.acquire(node);

function generateCode(rootNode, opts = {}) {
opts = Object.assign(Object.assign({}, generateCodeDefaultOptions), opts);
return generate(rootNode, opts);
return generate(rootNode, { ...generateCodeDefaultOptions, ...opts });
}

@@ -113,2 +146,2 @@

ASTNode,
};
};

@@ -19,5 +19,5 @@ const assert = require('assert');

};
const expectedNubmerOfNodes = 11;
assert(ast.length === expectedNubmerOfNodes,
`Unexpected number of nodes: Expected ${expectedNubmerOfNodes} but got ${ast.length}`);
const expectedNumberOfNodes = 11;
assert(ast.length === expectedNumberOfNodes,
`Unexpected number of nodes: Expected ${expectedNumberOfNodes} but got ${ast.length}`);
for (const nodeType of Object.keys(expectedBreakdown)) {

@@ -41,7 +41,50 @@ const numberOfNodes = ast.filter(n => n.type === nodeType).length;

function testFlastOptionsDetailed() {
const code = `var a = [1]; a[0];`;
const noDetailsAst = generateFlatAST(code, {detailed: false, includeSrc: true}); // includeSrc will be ignored
const [noDetailsVarDec, noDetailsVarRef] = noDetailsAst.filter(n => n.type === 'Identifier');
assert(!(
noDetailsVarDec.parentNode || noDetailsVarDec.childNodes || noDetailsVarDec.references ||
noDetailsVarRef.declNode || noDetailsVarRef.nodeId || noDetailsVarRef.scope || noDetailsVarRef.src),
`Flat AST generated with details despite 'detailed' option set to false.`);
const detailedAst = generateFlatAST(code, {detailed: true});
const [detailedVarDec, detailedVarRef] = detailedAst.filter(n => n.type === 'Identifier');
assert(
detailedVarDec.parentNode && detailedVarDec.childNodes && detailedVarDec.references &&
detailedVarRef.declNode && detailedVarRef.nodeId && detailedVarRef.scope && detailedVarRef.src,
`Flat AST missing details despite 'detailed' option set to true.`);
const detailedNoSrcAst = generateFlatAST(code, {detailed: true, includeSrc: false});
assert(!detailedNoSrcAst[0].src,
`Flat AST includes details despite 'detailed' option set to true and 'includeSrc' option set to false.`);
}
/**
* Verify the code breakdown generates the expected nodes by checking the properties of the generated ASTNodes.
*/
function testNodesStructureIntegrity() {
const code = `a=3`;
const ast = generateFlatAST(code);
const expectedBreakdown = [
{nodeId: 0, type: 'Program', start: 0, end: 3, src: 'a=3', parentNode: null, parentKey: null},
{nodeId: 1, type: 'ExpressionStatement', start: 0, end: 3, src: 'a=3', parentKey: null},
{nodeId: 2, type: 'AssignmentExpression', start: 0, end: 3, src: 'a=3', operator: '=', parentKey: 'expression'},
{nodeId: 3, type: 'Identifier', start: 0, end: 1, src: 'a', parentKey: 'left'},
{nodeId: 4, type: 'Literal', start: 2, end: 3, src: '3', value: 3, raw: '3', parentKey: 'right'},
];
expectedBreakdown.forEach(node => {
const parsedNode = ast[node.nodeId];
for (const [k, v] of Object.entries(node)) {
assert(parsedNode[k] === v,
`Value in parsed node, ${parsedNode[k]}, does not match expected value: ${v}, for key ${k}`);
}
});
}
const tests = {
'number of nodes': testNumberOfNodes,
'parse and generate': testParseAndGenerate,
'options: detailed': testFlastOptionsDetailed,
'ASTNode structure integrity': testNodesStructureIntegrity,
};
module.exports = tests;

Sorry, the diff of this file is not supported yet

SocketSocket SOC 2 Logo

Product

  • Package Alerts
  • Integrations
  • Docs
  • Pricing
  • FAQ
  • Roadmap
  • Changelog

Packages

npm

Stay in touch

Get open source security insights delivered straight into your inbox.


  • Terms
  • Privacy
  • Security

Made with ⚡️ by Socket Inc