added .mjs extension to JavaScript (#3783)

* added .mjs extension to JavaScript

* add missing newline at end of file

* add example from https://github.com/bmeck/composable-ast-walker/blob/master/example/constant_fold.mjs
This commit is contained in:
Bradley Meck
2017-09-07 03:56:36 -05:00
committed by Colin Seymour
parent 6b06e47c67
commit 38bc5fd336
4 changed files with 967 additions and 0 deletions

View File

@@ -2078,6 +2078,7 @@ JavaScript:
- ".jsfl"
- ".jsm"
- ".jss"
- ".mjs"
- ".njs"
- ".pac"
- ".sjs"

View File

@@ -0,0 +1,955 @@
// consumes <stdin> and performs constant folding
// echo '"use strict";"_"[0],1+2;' | node constant_fold.js
import _NodePath from '../NodePath';
const {NodePath} = _NodePath;
import _WalkCombinator from '../WalkCombinator';
const {WalkCombinator} = _WalkCombinator;
const $CONSTEXPR = Symbol.for('$CONSTEXTR');
const $CONSTVALUE = Symbol.for('$CONSTVALUE');
const IS_EMPTY = path => {
return (path.node.type === 'BlockStatement' && path.node.body.length === 0) ||
path.node.type === 'EmptyStatement';
};
const IN_PRAGMA_POS = path => {
if (path.parent && Array.isArray(path.parent.node)) {
const siblings = path.parent.node;
for (let i = 0; i < path.key; i++) {
// preceded by non-pragma
if (
siblings[i].type !== 'ExpressionStatement' ||
!IS_CONSTEXPR(siblings[i].expression) ||
typeof CONSTVALUE(siblings[i].expression) !== 'string'
) {
return false;
}
}
}
return true;
};
const IS_PRAGMA = path => {
if (path.parent && Array.isArray(path.parent.node)) {
const siblings = path.parent.node;
for (let i = 0; i < path.key + 1; i++) {
// preceded by non-pragma
if (
siblings[i].type !== 'ExpressionStatement' ||
!IS_CONSTEXPR(siblings[i].expression) ||
typeof CONSTVALUE(siblings[i].expression) !== 'string'
) {
return false;
}
}
}
return true;
};
// worst case is the completion value
const IS_NOT_COMPLETION = path => {
while (true) {
if (!path.parent) {
return true;
}
if (
Array.isArray(path.parent.node) &&
path.key !== path.parent.node.length - 1
) {
return true;
}
path = path.parent;
while (Array.isArray(path.node)) {
path = path.parent;
}
if (/Function/.test(path.node.type)) {
return true;
} else if (path.node.type === 'Program') {
return false;
}
}
};
const REMOVE_IF_EMPTY = path => {
if (IS_EMPTY(path)) REMOVE(path);
return null;
};
const REPLACE_IF_EMPTY = (path, folded) => {
if (IS_EMPTY(path)) return REPLACE(path, folded);
return path;
};
const REMOVE = path => {
if (Array.isArray(path.parent.node)) {
path.parent.node.splice(path.key, 1);
} else {
path.parent.node[path.key] = null;
}
return null;
};
const REPLACE = (path, folded) => {
const replacement = new NodePath(path.parent, folded, path.key);
path.parent.node[path.key] = folded;
return replacement;
};
// no mutation, this is an atomic value
const NEG_ZERO = Object.freeze({
[$CONSTEXPR]: true,
type: 'UnaryExpression',
operator: '-',
argument: Object.freeze({
[$CONSTEXPR]: true,
type: 'Literal',
value: 0,
}),
});
const INFINITY = Object.freeze({
[$CONSTEXPR]: true,
type: 'BinaryExpression',
operator: '/',
left: Object.freeze({
[$CONSTEXPR]: true,
type: 'Literal',
value: 1,
}),
right: Object.freeze({
[$CONSTEXPR]: true,
type: 'Literal',
value: 0,
}),
});
const NEG_INFINITY = Object.freeze({
[$CONSTEXPR]: true,
type: 'BinaryExpression',
operator: '/',
left: Object.freeze({
[$CONSTEXPR]: true,
type: 'Literal',
value: 1,
}),
right: NEG_ZERO,
});
const EMPTY = Object.freeze({
[$CONSTEXPR]: true,
type: 'EmptyStatement',
});
const NULL = Object.freeze({
[$CONSTEXPR]: true,
type: 'Literal',
value: null,
});
const NAN = Object.freeze({
[$CONSTEXPR]: true,
type: 'BinaryExpression',
operator: '/',
left: Object.freeze({
[$CONSTEXPR]: true,
type: 'Literal',
value: 0,
}),
right: Object.freeze({
[$CONSTEXPR]: true,
type: 'Literal',
value: 0,
}),
});
const UNDEFINED = Object.freeze({
[$CONSTEXPR]: true,
type: 'UnaryExpression',
operator: 'void',
argument: Object.freeze({
[$CONSTEXPR]: true,
type: 'Literal',
value: 0,
}),
});
// ESTree doesn't like negative numeric literals
// this also preserves -0
const IS_UNARY_NEGATIVE = node => {
if (
node.type === 'UnaryExpression' &&
node.operator === '-' &&
typeof node.argument.value === 'number' &&
node.argument.value === node.argument.value &&
node.argument.type === 'Literal'
) {
return true;
}
return false;
};
const IS_CONSTEXPR = node => {
if (typeof node !== 'object' || node === null) {
return false;
}
// DONT CALCULATE THINGS MULTIPLE TIMES!!@!@#
if (node[$CONSTEXPR]) return true;
if (node.type === 'ArrayExpression') {
for (let i = 0; i < node.elements.length; i++) {
const element = node.elements[i];
// hole == null
if (element !== null && !IS_CONSTEXPR(element)) {
return false;
}
}
return true;
}
if (node.type === 'ObjectExpression') {
for (let i = 0; i < node.properties.length; i++) {
const element = node.properties[i];
if (element.kind !== 'init') return false;
if (element.method) return false;
let key;
if (element.computed) {
// be sure {["y"]:1} works
if (!IS_CONSTEXPR(element.key)) {
return false;
}
}
if (!IS_CONSTEXPR(element.value)) return false;
}
return true;
}
if (node.type === 'Literal' || IS_UNDEFINED(node) || IS_NAN(node)) {
return true;
}
if (IS_UNARY_NEGATIVE(node)) {
return true;
}
return false;
};
const IS_NAN = node => {
return node === NAN;
};
const IS_UNDEFINED = node => {
return node === UNDEFINED;
};
const CONSTVALUE = node => {
if (node[$CONSTVALUE]) {
return node[$CONSTVALUE];
}
if (IS_UNDEFINED(node)) return void 0;
if (IS_NAN(node)) return +'_';
if (!IS_CONSTEXPR(node)) throw new Error('Not a CONSTEXPR');
if (node.type === 'ArrayExpression') {
let ret = [];
ret.length = node.elements.length;
for (let i = 0; i < node.elements.length; i++) {
if (node.elements[i] !== null) {
ret[i] = CONSTVALUE(node.elements[i]);
}
}
return ret;
}
if (node.type === 'ObjectExpression') {
let ret = Object.create(null);
for (let i = 0; i < node.properties.length; i++) {
const element = node.properties[i];
let key;
if (element.computed) {
key = `${CONSTVALUE(element.key)}`;
}
else {
key = element.key.name;
}
Object.defineProperty(ret, key, {
// duplicate keys...
configurable: true,
writable: true,
value: CONSTVALUE(element.value),
enumerable: true
});
}
Object.freeze(ret);
return ret;
}
if (IS_UNARY_NEGATIVE(node)) {
return -node.argument.value;
}
if (node.regex !== void 0) {
return new RegExp(node.regex.pattern, node.regex.flags);
}
return node.value;
};
const CONSTEXPRS = new Map();
CONSTEXPRS.set(void 0, UNDEFINED);
CONSTEXPRS.set(+'_', NAN);
CONSTEXPRS.set(null, NULL);
const TO_CONSTEXPR = value => {
if (value === -Infinity) {
return NEG_INFINITY;
}
if (value === Infinity) {
return INFINITY;
}
let is_neg_zero = 1 / value === -Infinity;
if (is_neg_zero) return NEG_ZERO;
if (CONSTEXPRS.has(value)) {
return CONSTEXPRS.get(value);
}
if (typeof value === 'number') {
if (value < 0) {
const CONSTEXPR = Object.freeze({
[$CONSTEXPR]: true,
[$CONSTVALUE]: value,
type: 'UnaryExpression',
operator: '-',
argument: Object.freeze({ type: 'Literal', value: -value }),
});
CONSTEXPRS.set(value, CONSTEXPR);
return CONSTEXPR;
}
}
if (
value === null ||
typeof value === 'number' ||
typeof value === 'boolean' ||
typeof value === 'string'
) {
const CONSTEXPR = Object.freeze({
[$CONSTEXPR]: true,
[$CONSTVALUE]: value,
type: 'Literal',
value,
});
CONSTEXPRS.set(value, CONSTEXPR);
return CONSTEXPR;
}
// have to generate new one every time :-/
if (Array.isArray(value)) {
return Object.freeze({
[$CONSTEXPR]: true,
type: 'ArrayExpression',
elements: Object.freeze(value.map(TO_CONSTEXPR)),
});
}
if (typeof value === 'object' && Object.getPrototypeOf(value) === Object.getPrototypeOf({}) && [...Object.getOwnPropertySymbols(value)].length === 0) {
return Object.freeze({
[$CONSTEXPR]: true,
type: 'ObjectExpression',
properties: Object.freeze(
[...Object.getOwnPropertyKeys(value)].map(key => {
if (!('value' in Object.getOwnProperty(value, key))) {
throw Error('Not a CONSTVALUE (found a setter or getter?)');
}
return {
type: 'Property',
kind: 'init',
method: false,
shorthand: false,
computed: true,
key: {
type: 'Literal',
value: key
},
value: TO_CONSTEXPR(value[key])
}
})),
});
}
throw Error('Not a CONSTVALUE (did you pass a RegExp?)');
};
// THIS DOES NOT HANDLE NODE SPECIFIC CASES LIKE IfStatement
const FOLD_EMPTY = function*(path) {
if (
path &&
path.node &&
path.parent &&
Array.isArray(path.parent.node) &&
IS_EMPTY(path)
) {
REMOVE(path);
return yield;
}
return yield path;
};
// THIS DOES NOT HANDLE NODE SPECIFIC CASES LIKE IfStatement
const FOLD_TEMPLATE = function*(path) {
if (
path &&
path.node &&
path.type === 'TemplateLiteral'
) {
let updated = false;
for (let i = 0; i < path.node.exressions.length; i++) {
if (IS_CONSTEXPR(path.node.expressions[i])) {
//let
}
}
}
return yield path;
};
const FOLD_EXPR_STMT = function*(path) {
// TODO: enforce completion value checking
if (path && path.node && path.node.type === 'ExpressionStatement') {
// merge all the adjacent expression statements into sequences
if (Array.isArray(path.parent.node)) {
// could have nodes after it
const siblings = path.parent.node;
if (!IS_PRAGMA(path)) {
if (path.key < siblings.length - 1) {
const mergeable = [path.node];
for (let needle = path.key + 1; needle < siblings.length; needle++) {
if (siblings[needle].type !== 'ExpressionStatement') {
break;
}
mergeable.push(siblings[needle]);
}
if (mergeable.length > 1) {
siblings.splice(path.key, mergeable.length, {
type: 'ExpressionStatement',
expression: {
type: 'SequenceExpression',
expressions: mergeable.reduce(
(acc, es) => {
if (es.expression.type == 'SequenceExpression') {
return [...acc, ...es.expression.expressions];
} else {
return [...acc, es.expression];
}
},
[]
),
},
});
return path;
}
}
}
}
if (IS_NOT_COMPLETION(path) && IS_CONSTEXPR(path.node.expression)) {
return REPLACE(path, EMPTY);
}
}
return yield path;
};
const FOLD_WHILE = function*(path) {
if (path && path.node) {
if (path.node.type === 'DoWhileStatement') {
console.error('FOLD_DOWHILE');
REPLACE_IF_EMPTY(path.get(['body']), EMPTY);
}
if (path.node.type === 'WhileStatement') {
console.error('FOLD_WHILE');
let { test, consequent, alternate } = path.node;
if (IS_CONSTEXPR(test)) {
test = CONSTVALUE(test);
if (!test) {
return REPLACE(path, EMPTY);
}
}
REPLACE_IF_EMPTY(path.get(['body']), EMPTY);
}
if (path.node.type === 'ForStatement') {
console.error('FOLD_FOR');
REPLACE_IF_EMPTY(path.get(['body']), EMPTY);
let { init, test, update } = path.node;
let updated = false;
if (init && IS_CONSTEXPR(init)) {
updated = true;
REPLACE(path.get(['init']), null);
}
if (test && IS_CONSTEXPR(test)) {
let current = CONSTVALUE(test);
let coerced = Boolean(current);
// remove the test if it is always true
if (coerced === true) {
updated = true;
REPLACE(path.get(['test']), null);
} else if (coerced !== current) {
updated = true;
REPLACE(path.get(['test']), TO_CONSTEXPR(coerced));
}
}
if (update && IS_CONSTEXPR(update)) {
updated = true;
REPLACE(path.get(['update']), null);
}
if (updated) {
return path;
}
}
}
return yield path;
};
const FOLD_IF = function*(path) {
if (path && path.node && path.node.type === 'IfStatement') {
let { test, consequent, alternate } = path.node;
const is_not_completion = IS_NOT_COMPLETION(path);
if (is_not_completion && !alternate) {
if (IS_EMPTY(path.get(['consequent']))) {
console.error('FOLD_IF_EMPTY_CONSEQUENT');
REPLACE(path, {
type: 'ExpressionStatement',
expression: test,
});
return path.parent;
}
}
if (alternate) {
if (alternate.type === consequent.type) {
if (consequent.type === 'ExpressionStatement') {
console.error('FOLD_IF_BOTH_EXPRSTMT');
REPLACE(path, {
type: 'ExpressionStatement', expression:
{
type: 'ConditionalExpression',
test: test,
consequent: consequent.expression,
alternate: alternate.expression,
}});
return path.parent;
}
else if (consequent.type === 'ReturnStatement' ||
consequent.type === 'ThrowStatement') {
console.error('FOLD_IF_BOTH_COMPLETIONS');
REPLACE(path, {
type: 'ExpressionStatement', expression:{
type: consequent.type,
argument: {
type: 'ConditionalExpression',
test: test,
consequent: consequent.argument,
alternate: alternate.argument,
}}
});
return path.parent;
}
}
}
else if (is_not_completion && consequent.type === 'ExpressionStatement') {
console.error('FOLD_IF_NON_COMPLETION_TO_&&');
REPLACE(path, {
type: 'ExpressionStatement',
expression: {
type: 'BinaryExpression',
operator: '&&',
left: test,
right: consequent.expression,
}
});
return path.parent;
}
if (IS_CONSTEXPR(test)) {
test = CONSTVALUE(test);
if (test) {
return REPLACE(path, consequent);
}
if (alternate) {
return REPLACE(path, alternate);
}
return REPLACE(path, EMPTY);
}
consequent = path.get(['consequent']);
let updated;
if (consequent.node !== EMPTY) {
REPLACE_IF_EMPTY(consequent, EMPTY);
if (consequent.parent.node[consequent.key] === EMPTY) {
updated = true;
}
}
if (alternate) {
alternate = path.get(['alternate']);
REMOVE_IF_EMPTY(alternate);
if (path.node.alternate === null) {
updated = true;
}
}
if (updated) {
return path;
}
}
return yield path;
};
const FOLD_SEQUENCE = function*(path) {
if (path && path.node && path.node.type === 'SequenceExpression') {
console.error('FOLD_SEQUENCE');
// never delete the last value
for (let i = 0; i < path.node.expressions.length - 1; i++) {
if (IS_CONSTEXPR(path.node.expressions[i])) {
path.node.expressions.splice(i, 1);
i--;
}
}
if (path.node.expressions.length === 1) {
return REPLACE(path, path.node.expressions[0]);
}
}
return yield path;
};
const FOLD_LOGICAL = function*(path) {
if (path && path.node && path.node.type === 'LogicalExpression') {
console.error('FOLD_LOGICAL');
let { left, right, operator } = path.node;
if (IS_CONSTEXPR(left)) {
left = CONSTVALUE(left);
if (operator === '||') {
if (left) {
return REPLACE(path, TO_CONSTEXPR(left));
}
return REPLACE(path, right);
} else if (operator === '&&') {
if (!left) {
return REPLACE(path, TO_CONSTEXPR(left));
}
return REPLACE(path, right);
}
}
}
return yield path;
};
const FOLD_SWITCH = function*(path) {
if (path && path.node && path.node.type === 'SwitchStatement') {
let { discriminant, cases } = path.node;
// if there are no cases, just become an expression
if (cases.length === 0 && IS_NOT_COMPLETION(path)) {
return REPLACE(path, {
type: 'ExpressionStatement',
expression: discriminant
});
}
// if the discriminant is static
// remove any preceding non-matching static cases
// fold any trailing cases into the matching case
if (cases.length > 1 && IS_CONSTEXPR(discriminant)) {
const discriminant_value = CONSTVALUE(discriminant);
for (var i = 0; i < cases.length; i++) {
const test = cases[i].test;
if (IS_CONSTEXPR(test)) {
let test_value = CONSTVALUE(test);
if (discriminant_value === test_value) {
let new_consequent = cases[i].consequent;
if (i < cases.length - 1) {
for (let fallthrough of cases.slice(i+1)) {
new_consequent.push(...fallthrough.consequent);
}
}
cases[i].consequent = new_consequent;
REPLACE(path.get(['cases']), [cases[i]]);
return path;
}
}
else {
// we had a dynamic case need to bail
break;
}
}
}
}
return yield path;
};
const FOLD_UNREACHABLE = function*(path) {
if (path && path.node && path.parent && Array.isArray(path.parent.node)) {
if (path.node.type === 'ReturnStatement' ||
path.node.type === 'ContinueStatement' ||
path.node.type === 'BreakStatement' ||
path.node.type === 'ThrowStatement') {
const next_key = path.key + 1;
path.parent.node.splice(next_key, path.parent.node.length - next_key);
}
}
return yield path;
}
const FOLD_CONDITIONAL = function*(path) {
if (path && path.node && path.node.type === 'ConditionalExpression') {
console.error('FOLD_CONDITIONAL');
let { test, consequent, alternate } = path.node;
if (IS_CONSTEXPR(test)) {
test = CONSTVALUE(test);
if (test) {
return REPLACE(path, consequent);
}
return REPLACE(path, alternate);
}
}
return yield path;
};
const FOLD_BINARY = function*(path) {
if (
path &&
path.node &&
path.node.type === 'BinaryExpression' &&
!IS_NAN(path.node)
) {
console.error('FOLD_BINARY');
let { left, right, operator } = path.node;
if (operator === '==' || operator === '!=') {
let updated = false;
if (IS_UNDEFINED(left)) {
updated = true;
REPLACE(path.get(['left']), NULL);
}
if (IS_UNDEFINED(right)) {
updated = true;
REPLACE(path.get(['right']), NULL);
}
if (updated) {
return path;
}
}
if (path.node !== INFINITY && path.node !== NEG_INFINITY && IS_CONSTEXPR(left) && IS_CONSTEXPR(right)) {
left = CONSTVALUE(left);
right = CONSTVALUE(right);
let value;
if ((!left || typeof left !== 'object') && (!right || typeof right !== 'object')) {
if (operator === '+') {
value = left + right;
} else if (operator === '-') {
value = left - right;
} else if (operator === '*') {
value = left * right;
} else if (operator === '/') {
value = left / right;
} else if (operator === '%') {
value = left % right;
} else if (operator === '==') {
value = left == right;
} else if (operator === '!=') {
value = left != right;
} else if (operator === '===') {
value = left === right;
} else if (operator === '!==') {
value = left !== right;
} else if (operator === '<') {
value = left < right;
} else if (operator === '<=') {
value = left <= right;
} else if (operator === '>') {
value = left > right;
} else if (operator === '>=') {
value = left >= right;
} else if (operator === '<<') {
value = left << right;
} else if (operator === '>>') {
value = left >> right;
} else if (operator === '>>>') {
value = left >>> right;
} else if (operator === '|') {
value = left | right;
} else if (operator === '&') {
value = left & right;
} else if (operator === '^') {
value = left ^ right;
}
}
else {
if (operator === '==') value = false;
if (operator === '===') value = false;
if (operator === '!=') value = true;
if (operator === '!==') value = true;
if (operator === 'in' && typeof right === 'object' && right) {
value = Boolean(Object.getOwnPropertyDescriptor(right, left));
}
}
if (value !== void 0) {
if (typeof value === 'string' || typeof value === 'boolean' || value === null) {
return REPLACE(path, TO_CONSTEXPR(value));
}
if (typeof value === 'number') {
return REPLACE(path, TO_CONSTEXPR(value));
}
}
}
}
return yield path;
};
const FOLD_UNARY = function*(path) {
if (path && path.node && path.node.type === 'UnaryExpression') {
console.error('FOLD_UNARY');
if (IS_CONSTEXPR(path.node)) {
return yield path;
}
let { argument, operator } = path.node;
if (IS_CONSTEXPR(argument)) {
if (operator === 'void') {
return REPLACE(path, UNDEFINED);
}
let value = CONSTVALUE(argument);
if (operator === '-') {
value = -value;
} else if (operator === '+') {
value = +value;
} else if (operator === '~') {
value = ~value;
} else if (operator === '!') {
value = !value;
} else if (operator === 'typeof') {
value = typeof value;
} else if (operator === 'delete') {
value = true;
}
return REPLACE(path, TO_CONSTEXPR(value));
}
}
return yield path;
};
const FOLD_EVAL = function*(path) {
if (path && path.node && path.node.type === 'CallExpression' &&
path.node.callee.type === 'Identifier' && path.node.callee.name === 'eval') {
console.error('FOLD_EVAL');
if (path.node.arguments.length === 1 && path.node.arguments[0].type === 'Literal') {
let result = esprima.parse(`${
CONSTVALUE(path.node.arguments[0])
}`);
if (result.body.length === 1 && result.body[0].type === 'ExpressionStatement') {
return REPLACE(path, result.body[0].expression);
}
}
}
return yield path;
}
const FOLD_MEMBER = function*(path) {
if (path && path.node && path.node.type === 'MemberExpression') {
console.error('FOLD_MEMBER');
if (path.node.computed && path.node.property.type === 'Literal') {
const current = `${CONSTVALUE(path.node.property)}`;
if (typeof current === 'string' && /^[$_a-z][$_a-z\d]*$/i.test(current)) {
path.node.computed = false;
path.node.property = {
type: 'Identifier',
name: current,
};
return path;
}
}
if (IS_CONSTEXPR(path.node.object)) {
const value = CONSTVALUE(path.node.object);
if (typeof value === 'string' || Array.isArray(value) || (value && typeof value === 'object')) {
let key;
if (IS_CONSTEXPR(path.node.property)) {
key = `${CONSTVALUE(path.node.property)}`;
}
else if (!path.node.computed) {
key = path.node.property.name;
}
if (key !== void 0) {
const desc = Object.getOwnPropertyDescriptor(value, key);
if (desc) {
const folded = value[key];
console.error('FOLDING', JSON.stringify(folded));
if (IN_PRAGMA_POS(path) && typeof folded === 'string') {
if (value.length > 1) {
REPLACE(
path.get(['object']),
TO_CONSTEXPR(value.slice(key, key + 1))
);
REPLACE(path.get(['property']), TO_CONSTEXPR(0));
return path;
}
} else {
return REPLACE(path, TO_CONSTEXPR(value[key]));
}
}
}
}
}
}
return yield path;
};
const $MIN = Symbol();
const MIN_TRUE = Object.freeze({
[$MIN]: true,
type: 'UnaryExpression',
operator: '!',
argument: Object.freeze({
[$MIN]: true,
type: 'Literal',
value: 0
})
});
const MIN_FALSE = Object.freeze({
[$MIN]: true,
type: 'UnaryExpression',
operator: '!',
argument: Object.freeze({
[$MIN]: true,
type: 'Literal',
value: 1
})
});
const MIN_REPLACEMENTS = new Map;
MIN_REPLACEMENTS.set(true, MIN_TRUE);
MIN_REPLACEMENTS.set(false, MIN_FALSE);
const MIN_VALUES = function*(path) {
if (path && path.node && !path.node[$MIN] && IS_CONSTEXPR(path.node)) {
let value = CONSTVALUE(path.node);
if (MIN_REPLACEMENTS.has(value)) {
console.error('MIN_VALUE', value)
return REPLACE(path, MIN_REPLACEMENTS.get(value));
}
}
return yield path;
}
import esprima from 'esprima';
import util from 'util';
import escodegen from 'escodegen';
const optimize = (src) => {
const ROOT = new NodePath(
null,
esprima.parse(
src,
{
// loc: true,
// source: '<stdin>',
}
),
null
);
// all of these are things that could affect completion value positions
const walk_expressions = WalkCombinator.pipe(
...[
WalkCombinator.DEPTH_FIRST,
{
// We never work on Arrays
*inputs(path) {
if (Array.isArray(path)) return;
return yield path;
},
},
{ inputs: FOLD_UNREACHABLE },
{ inputs: FOLD_IF },
{ inputs: FOLD_SWITCH },
{ inputs: FOLD_EXPR_STMT },
{ inputs: FOLD_CONDITIONAL },
{ inputs: FOLD_LOGICAL },
{ inputs: FOLD_BINARY },
{ inputs: FOLD_UNARY },
{ inputs: FOLD_SEQUENCE },
{ inputs: FOLD_MEMBER },
{ inputs: FOLD_EMPTY },
{ inputs: FOLD_WHILE },
{ inputs: FOLD_EVAL },
]
).walk(ROOT);
for (const _ of walk_expressions) {
}
const minify = WalkCombinator.pipe(
...[
WalkCombinator.DEPTH_FIRST,
{
// We never work on Arrays
*inputs(path) {
if (Array.isArray(path)) return;
return yield path;
},
},
{ inputs: MIN_VALUES },
]
).walk(ROOT);
for (const _ of minify) {
}
return ROOT;
}
import mississippi from 'mississippi';
process.stdin.pipe(
mississippi.concat(buff => {
const ROOT = optimize(`${buff}`)
console.error(
'%s',
util.inspect(ROOT.node, {
depth: null,
colors: true,
})
);
const out = escodegen.generate(ROOT.node);
console.log(out);
})
);

View File

@@ -0,0 +1,6 @@
import bar from './module.mjs';
function foo() {
return "I am foo";
}
export {foo};
console.log(bar);

View File

@@ -0,0 +1,5 @@
import {foo} from './entry.mjs';
console.log(foo());
const bar = "I am bar.";
export {bar as default};