Socket
Socket
Sign inDemoInstall

degenerator

Package Overview
Dependencies
Maintainers
1
Versions
28
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

degenerator - npm Package Compare versions

Comparing version 4.0.4 to 5.0.0

dist/compile.d.ts

199

./dist/index.js
"use strict";
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
if (k2 === undefined) k2 = k;
var desc = Object.getOwnPropertyDescriptor(m, k);
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
desc = { enumerable: true, get: function() { return m[k]; } };
}
Object.defineProperty(o, k2, desc);
}) : (function(o, m, k, k2) {
if (k2 === undefined) k2 = k;
o[k2] = m[k];
}));
var __exportStar = (this && this.__exportStar) || function(m, exports) {
for (var p in m) if (p !== "default" && !Object.prototype.hasOwnProperty.call(exports, p)) __createBinding(exports, m, p);
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.compile = exports.degenerator = void 0;
const util_1 = require("util");
const escodegen_1 = require("escodegen");
const esprima_1 = require("esprima");
const ast_types_1 = require("ast-types");
const vm2_1 = require("vm2");
/**
* Compiles sync JavaScript code into JavaScript with async Functions.
*
* @param {String} code JavaScript string to convert
* @param {Array} names Array of function names to add `await` operators to
* @return {String} Converted JavaScript string with async/await injected
* @api public
*/
function degenerator(code, _names) {
if (!Array.isArray(_names)) {
throw new TypeError('an array of async function "names" is required');
}
// Duplicate the `names` array since it's rude to augment the user args
const names = _names.slice(0);
const ast = (0, esprima_1.parseScript)(code);
// First pass is to find the `function` nodes and turn them into async or
// generator functions only if their body includes `CallExpressions` to
// function in `names`. We also add the names of the functions to the `names`
// array. We'll iterate several time, as every iteration might add new items
// to the `names` array, until no new names were added in the iteration.
let lastNamesLength = 0;
do {
lastNamesLength = names.length;
(0, ast_types_1.visit)(ast, {
visitVariableDeclaration(path) {
if (path.node.declarations) {
for (let i = 0; i < path.node.declarations.length; i++) {
const declaration = path.node.declarations[i];
if (ast_types_1.namedTypes.VariableDeclarator.check(declaration) &&
ast_types_1.namedTypes.Identifier.check(declaration.init) &&
ast_types_1.namedTypes.Identifier.check(declaration.id) &&
checkName(declaration.init.name, names) &&
!checkName(declaration.id.name, names)) {
names.push(declaration.id.name);
}
}
}
return false;
},
visitAssignmentExpression(path) {
if (ast_types_1.namedTypes.Identifier.check(path.node.left) &&
ast_types_1.namedTypes.Identifier.check(path.node.right) &&
checkName(path.node.right.name, names) &&
!checkName(path.node.left.name, names)) {
names.push(path.node.left.name);
}
return false;
},
visitFunction(path) {
if (path.node.id) {
let shouldDegenerate = false;
(0, ast_types_1.visit)(path.node, {
visitCallExpression(path) {
if (checkNames(path.node, names)) {
shouldDegenerate = true;
}
return false;
},
});
if (!shouldDegenerate) {
return false;
}
// Got a "function" expression/statement,
// convert it into an async function
path.node.async = true;
// Add function name to `names` array
if (!checkName(path.node.id.name, names)) {
names.push(path.node.id.name);
}
}
this.traverse(path);
},
});
} while (lastNamesLength !== names.length);
// Second pass is for adding `await` statements to any function
// invocations that match the given `names` array.
(0, ast_types_1.visit)(ast, {
visitCallExpression(path) {
if (checkNames(path.node, names)) {
// A "function invocation" expression,
// we need to inject an `AwaitExpression`
const delegate = false;
const { name, parent: { node: pNode }, } = path;
const expr = ast_types_1.builders.awaitExpression(path.node, delegate);
if (ast_types_1.namedTypes.CallExpression.check(pNode)) {
pNode.arguments[name] = expr;
}
else {
pNode[name] = expr;
}
}
this.traverse(path);
},
});
return (0, escodegen_1.generate)(ast);
}
exports.degenerator = degenerator;
function compile(code, returnName, names, options = {}) {
const compiled = degenerator(code, names);
const vm = new vm2_1.VM(options);
const script = new vm2_1.VMScript(`${compiled};${returnName}`, {
filename: options.filename,
});
const fn = vm.run(script);
if (typeof fn !== 'function') {
throw new Error(`Expected a "function" to be returned for \`${returnName}\`, but got "${typeof fn}"`);
}
const r = function (...args) {
try {
const p = fn.apply(this, args);
if (typeof p?.then === 'function') {
return p;
}
return Promise.resolve(p);
}
catch (err) {
return Promise.reject(err);
}
};
Object.defineProperty(r, 'toString', {
value: fn.toString.bind(fn),
enumerable: false,
});
return r;
}
exports.compile = compile;
/**
* Returns `true` if `node` has a matching name to one of the entries in the
* `names` array.
*
* @param {types.Node} node
* @param {Array} names Array of function names to return true for
* @return {Boolean}
* @api private
*/
function checkNames({ callee }, names) {
let name;
if (ast_types_1.namedTypes.Identifier.check(callee)) {
name = callee.name;
}
else if (ast_types_1.namedTypes.MemberExpression.check(callee)) {
if (ast_types_1.namedTypes.Identifier.check(callee.object) &&
ast_types_1.namedTypes.Identifier.check(callee.property)) {
name = `${callee.object.name}.${callee.property.name}`;
}
else {
return false;
}
}
else if (ast_types_1.namedTypes.FunctionExpression.check(callee)) {
if (callee.id) {
name = callee.id.name;
}
else {
return false;
}
}
else {
throw new Error(`Don't know how to get name for: ${callee.type}`);
}
return checkName(name, names);
}
function checkName(name, names) {
// now that we have the `name`, check if any entries match in the `names` array
for (let i = 0; i < names.length; i++) {
const n = names[i];
if (util_1.types.isRegExp(n)) {
if (n.test(name)) {
return true;
}
}
else if (name === n) {
return true;
}
}
return false;
}
__exportStar(require("./degenerator"), exports);
__exportStar(require("./compile"), exports);
//# sourceMappingURL=index.js.map

@@ -1,18 +0,3 @@

/// <reference types="node" />
import { Context, RunningScriptOptions } from 'vm';
/**
* Compiles sync JavaScript code into JavaScript with async Functions.
*
* @param {String} code JavaScript string to convert
* @param {Array} names Array of function names to add `await` operators to
* @return {String} Converted JavaScript string with async/await injected
* @api public
*/
export declare function degenerator(code: string, _names: DegeneratorNames): string;
export type DegeneratorName = string | RegExp;
export type DegeneratorNames = DegeneratorName[];
export interface CompileOptions extends RunningScriptOptions {
sandbox?: Context;
}
export declare function compile<R = unknown, A extends unknown[] = []>(code: string, returnName: string, names: DegeneratorNames, options?: CompileOptions): (...args: A) => Promise<R>;
export * from './degenerator';
export * from './compile';
//# sourceMappingURL=index.d.ts.map
"use strict";
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
if (k2 === undefined) k2 = k;
var desc = Object.getOwnPropertyDescriptor(m, k);
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
desc = { enumerable: true, get: function() { return m[k]; } };
}
Object.defineProperty(o, k2, desc);
}) : (function(o, m, k, k2) {
if (k2 === undefined) k2 = k;
o[k2] = m[k];
}));
var __exportStar = (this && this.__exportStar) || function(m, exports) {
for (var p in m) if (p !== "default" && !Object.prototype.hasOwnProperty.call(exports, p)) __createBinding(exports, m, p);
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.compile = exports.degenerator = void 0;
const util_1 = require("util");
const escodegen_1 = require("escodegen");
const esprima_1 = require("esprima");
const ast_types_1 = require("ast-types");
const vm2_1 = require("vm2");
/**
* Compiles sync JavaScript code into JavaScript with async Functions.
*
* @param {String} code JavaScript string to convert
* @param {Array} names Array of function names to add `await` operators to
* @return {String} Converted JavaScript string with async/await injected
* @api public
*/
function degenerator(code, _names) {
if (!Array.isArray(_names)) {
throw new TypeError('an array of async function "names" is required');
}
// Duplicate the `names` array since it's rude to augment the user args
const names = _names.slice(0);
const ast = (0, esprima_1.parseScript)(code);
// First pass is to find the `function` nodes and turn them into async or
// generator functions only if their body includes `CallExpressions` to
// function in `names`. We also add the names of the functions to the `names`
// array. We'll iterate several time, as every iteration might add new items
// to the `names` array, until no new names were added in the iteration.
let lastNamesLength = 0;
do {
lastNamesLength = names.length;
(0, ast_types_1.visit)(ast, {
visitVariableDeclaration(path) {
if (path.node.declarations) {
for (let i = 0; i < path.node.declarations.length; i++) {
const declaration = path.node.declarations[i];
if (ast_types_1.namedTypes.VariableDeclarator.check(declaration) &&
ast_types_1.namedTypes.Identifier.check(declaration.init) &&
ast_types_1.namedTypes.Identifier.check(declaration.id) &&
checkName(declaration.init.name, names) &&
!checkName(declaration.id.name, names)) {
names.push(declaration.id.name);
}
}
}
return false;
},
visitAssignmentExpression(path) {
if (ast_types_1.namedTypes.Identifier.check(path.node.left) &&
ast_types_1.namedTypes.Identifier.check(path.node.right) &&
checkName(path.node.right.name, names) &&
!checkName(path.node.left.name, names)) {
names.push(path.node.left.name);
}
return false;
},
visitFunction(path) {
if (path.node.id) {
let shouldDegenerate = false;
(0, ast_types_1.visit)(path.node, {
visitCallExpression(path) {
if (checkNames(path.node, names)) {
shouldDegenerate = true;
}
return false;
},
});
if (!shouldDegenerate) {
return false;
}
// Got a "function" expression/statement,
// convert it into an async function
path.node.async = true;
// Add function name to `names` array
if (!checkName(path.node.id.name, names)) {
names.push(path.node.id.name);
}
}
this.traverse(path);
},
});
} while (lastNamesLength !== names.length);
// Second pass is for adding `await` statements to any function
// invocations that match the given `names` array.
(0, ast_types_1.visit)(ast, {
visitCallExpression(path) {
if (checkNames(path.node, names)) {
// A "function invocation" expression,
// we need to inject an `AwaitExpression`
const delegate = false;
const { name, parent: { node: pNode }, } = path;
const expr = ast_types_1.builders.awaitExpression(path.node, delegate);
if (ast_types_1.namedTypes.CallExpression.check(pNode)) {
pNode.arguments[name] = expr;
}
else {
pNode[name] = expr;
}
}
this.traverse(path);
},
});
return (0, escodegen_1.generate)(ast);
}
exports.degenerator = degenerator;
function compile(code, returnName, names, options = {}) {
const compiled = degenerator(code, names);
const vm = new vm2_1.VM(options);
const script = new vm2_1.VMScript(`${compiled};${returnName}`, {
filename: options.filename,
});
const fn = vm.run(script);
if (typeof fn !== 'function') {
throw new Error(`Expected a "function" to be returned for \`${returnName}\`, but got "${typeof fn}"`);
}
const r = function (...args) {
try {
const p = fn.apply(this, args);
if (typeof p?.then === 'function') {
return p;
}
return Promise.resolve(p);
}
catch (err) {
return Promise.reject(err);
}
};
Object.defineProperty(r, 'toString', {
value: fn.toString.bind(fn),
enumerable: false,
});
return r;
}
exports.compile = compile;
/**
* Returns `true` if `node` has a matching name to one of the entries in the
* `names` array.
*
* @param {types.Node} node
* @param {Array} names Array of function names to return true for
* @return {Boolean}
* @api private
*/
function checkNames({ callee }, names) {
let name;
if (ast_types_1.namedTypes.Identifier.check(callee)) {
name = callee.name;
}
else if (ast_types_1.namedTypes.MemberExpression.check(callee)) {
if (ast_types_1.namedTypes.Identifier.check(callee.object) &&
ast_types_1.namedTypes.Identifier.check(callee.property)) {
name = `${callee.object.name}.${callee.property.name}`;
}
else {
return false;
}
}
else if (ast_types_1.namedTypes.FunctionExpression.check(callee)) {
if (callee.id) {
name = callee.id.name;
}
else {
return false;
}
}
else {
throw new Error(`Don't know how to get name for: ${callee.type}`);
}
return checkName(name, names);
}
function checkName(name, names) {
// now that we have the `name`, check if any entries match in the `names` array
for (let i = 0; i < names.length; i++) {
const n = names[i];
if (util_1.types.isRegExp(n)) {
if (n.test(name)) {
return true;
}
}
else if (name === n) {
return true;
}
}
return false;
}
__exportStar(require("./degenerator"), exports);
__exportStar(require("./compile"), exports);
//# sourceMappingURL=index.js.map
{
"name": "degenerator",
"version": "4.0.4",
"version": "5.0.0",
"description": "Compiles sync functions into async generator functions",

@@ -23,6 +23,6 @@ "main": "./dist/index.js",

"escodegen": "^1.14.3",
"esprima": "^4.0.1",
"vm2": "^3.9.19"
"esprima": "^4.0.1"
},
"devDependencies": {
"@tootallnate/quickjs-emscripten": "^0.23.0",
"@types/escodegen": "^0.0.6",

@@ -29,0 +29,0 @@ "@types/esprima": "^4.0.3",

Sorry, the diff of this file is not supported yet

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