MACHIN
This commit is contained in:
195
33/node_modules/with/src/globals.ts
generated
vendored
Normal file
195
33/node_modules/with/src/globals.ts
generated
vendored
Normal file
@@ -0,0 +1,195 @@
|
||||
import assertNever from 'assert-never';
|
||||
import {ancestor as walk} from 'babel-walk';
|
||||
import * as t from '@babel/types';
|
||||
import isReferenced from './reference';
|
||||
|
||||
const isScope = (node: t.Node) => t.isFunctionParent(node) || t.isProgram(node);
|
||||
const isBlockScope = (node: t.Node) =>
|
||||
t.isBlockStatement(node) || isScope(node);
|
||||
|
||||
const declaresArguments = (node: t.Node) =>
|
||||
t.isFunction(node) && !t.isArrowFunctionExpression(node);
|
||||
|
||||
const declaresThis = declaresArguments;
|
||||
|
||||
const LOCALS_SYMBOL = Symbol('locals');
|
||||
|
||||
const getLocals = (node: t.Node): Set<string> | undefined =>
|
||||
(node as any)[LOCALS_SYMBOL];
|
||||
const declareLocals = (node: t.Node): Set<string> =>
|
||||
((node as any)[LOCALS_SYMBOL] = (node as any)[LOCALS_SYMBOL] || new Set());
|
||||
|
||||
const setLocal = (node: t.Node, name: string) => declareLocals(node).add(name);
|
||||
|
||||
// First pass
|
||||
|
||||
function declareFunction(node: t.Function) {
|
||||
for (const param of node.params) {
|
||||
declarePattern(param, node);
|
||||
}
|
||||
const id = (node as t.FunctionDeclaration).id;
|
||||
if (id) {
|
||||
setLocal(node, id.name);
|
||||
}
|
||||
}
|
||||
|
||||
function declarePattern(node: t.LVal, parent: t.Node) {
|
||||
switch (node.type) {
|
||||
case 'Identifier':
|
||||
setLocal(parent, node.name);
|
||||
break;
|
||||
case 'ObjectPattern':
|
||||
for (const prop of node.properties) {
|
||||
switch (prop.type) {
|
||||
case 'RestElement':
|
||||
declarePattern(prop.argument, parent);
|
||||
break;
|
||||
case 'ObjectProperty':
|
||||
declarePattern(prop.value as t.LVal, parent);
|
||||
break;
|
||||
default:
|
||||
assertNever(prop);
|
||||
break;
|
||||
}
|
||||
}
|
||||
break;
|
||||
case 'ArrayPattern':
|
||||
for (const element of node.elements) {
|
||||
if (element) declarePattern(element, parent);
|
||||
}
|
||||
break;
|
||||
case 'RestElement':
|
||||
declarePattern(node.argument, parent);
|
||||
break;
|
||||
case 'AssignmentPattern':
|
||||
declarePattern(node.left, parent);
|
||||
break;
|
||||
// istanbul ignore next
|
||||
default:
|
||||
throw new Error('Unrecognized pattern type: ' + node.type);
|
||||
}
|
||||
}
|
||||
|
||||
function declareModuleSpecifier(
|
||||
node:
|
||||
| t.ImportSpecifier
|
||||
| t.ImportDefaultSpecifier
|
||||
| t.ImportNamespaceSpecifier,
|
||||
_state: unknown,
|
||||
parents: t.Node[],
|
||||
) {
|
||||
for (let i = parents.length - 2; i >= 0; i--) {
|
||||
if (isScope(parents[i])) {
|
||||
setLocal(parents[i], node.local.name);
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const firstPass = walk({
|
||||
VariableDeclaration(node, _state, parents) {
|
||||
for (let i = parents.length - 2; i >= 0; i--) {
|
||||
if (
|
||||
node.kind === 'var'
|
||||
? t.isFunctionParent(parents[i])
|
||||
: isBlockScope(parents[i])
|
||||
) {
|
||||
for (const declaration of node.declarations) {
|
||||
declarePattern(declaration.id, parents[i]);
|
||||
}
|
||||
return;
|
||||
}
|
||||
}
|
||||
},
|
||||
FunctionDeclaration(node, _state, parents) {
|
||||
if (node.id) {
|
||||
for (let i = parents.length - 2; i >= 0; i--) {
|
||||
if (isScope(parents[i])) {
|
||||
setLocal(parents[i], node.id.name);
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
Function: declareFunction,
|
||||
ClassDeclaration(node, _state, parents) {
|
||||
for (let i = parents.length - 2; i >= 0; i--) {
|
||||
if (isScope(parents[i])) {
|
||||
setLocal(parents[i], node.id.name);
|
||||
return;
|
||||
}
|
||||
}
|
||||
},
|
||||
TryStatement(node) {
|
||||
if (node.handler === null) return;
|
||||
if (node.handler.param === null) return;
|
||||
declarePattern(node.handler.param, node.handler);
|
||||
},
|
||||
ImportDefaultSpecifier: declareModuleSpecifier,
|
||||
ImportSpecifier: declareModuleSpecifier,
|
||||
ImportNamespaceSpecifier: declareModuleSpecifier,
|
||||
});
|
||||
|
||||
// Second pass
|
||||
|
||||
const secondPass = walk<{
|
||||
globals: (t.Identifier | t.ThisExpression)[];
|
||||
}>({
|
||||
Identifier(node, state, parents) {
|
||||
const name = node.name;
|
||||
if (name === 'undefined') return;
|
||||
|
||||
const lastParent = parents[parents.length - 2];
|
||||
if (lastParent) {
|
||||
if (!isReferenced(node, lastParent)) return;
|
||||
|
||||
for (const parent of parents) {
|
||||
if (name === 'arguments' && declaresArguments(parent)) {
|
||||
return;
|
||||
}
|
||||
if (getLocals(parent)?.has(name)) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
state.globals.push(node);
|
||||
},
|
||||
|
||||
ThisExpression(node, state, parents) {
|
||||
for (const parent of parents) {
|
||||
if (declaresThis(parent)) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
state.globals.push(node);
|
||||
},
|
||||
});
|
||||
|
||||
export default function findGlobals(ast: t.Node) {
|
||||
const globals: (t.Identifier | t.ThisExpression)[] = [];
|
||||
|
||||
// istanbul ignore if
|
||||
if (!t.isNode(ast)) {
|
||||
throw new TypeError('Source must be a Babylon AST');
|
||||
}
|
||||
|
||||
firstPass(ast, undefined);
|
||||
secondPass(ast, {globals});
|
||||
|
||||
const groupedGlobals = new Map<string, (t.Identifier | t.ThisExpression)[]>();
|
||||
for (const node of globals) {
|
||||
const name: string = node.type === 'ThisExpression' ? 'this' : node.name;
|
||||
const existing = groupedGlobals.get(name);
|
||||
if (existing) {
|
||||
existing.push(node);
|
||||
} else {
|
||||
groupedGlobals.set(name, [node]);
|
||||
}
|
||||
}
|
||||
|
||||
return [...groupedGlobals]
|
||||
.map(([name, nodes]) => ({name, nodes}))
|
||||
.sort((a, b) => (a.name < b.name ? -1 : 1));
|
||||
}
|
||||
154
33/node_modules/with/src/index.ts
generated
vendored
Normal file
154
33/node_modules/with/src/index.ts
generated
vendored
Normal file
@@ -0,0 +1,154 @@
|
||||
import {parse} from '@babel/parser';
|
||||
import {recursive as walk} from 'babel-walk';
|
||||
import * as t from '@babel/types';
|
||||
import detect from './globals';
|
||||
|
||||
const parseOptions = {
|
||||
allowReturnOutsideFunction: true,
|
||||
allowImportExportEverywhere: true,
|
||||
};
|
||||
|
||||
/**
|
||||
* Mimic `with` as far as possible but at compile time
|
||||
*
|
||||
* @param obj The object part of a with expression
|
||||
* @param src The body of the with expression
|
||||
* @param exclude A list of variable names to explicitly exclude
|
||||
*/
|
||||
export default function addWith(
|
||||
obj: string,
|
||||
src: string,
|
||||
exclude: string[] = [],
|
||||
) {
|
||||
// tslint:disable-next-line: no-parameter-reassignment
|
||||
obj = obj + '';
|
||||
// tslint:disable-next-line: no-parameter-reassignment
|
||||
src = src + '';
|
||||
|
||||
let ast;
|
||||
try {
|
||||
ast = parse(src, parseOptions);
|
||||
} catch (e) {
|
||||
throw Object.assign(
|
||||
new Error('Error parsing body of the with expression'),
|
||||
{
|
||||
component: 'src',
|
||||
babylonError: e,
|
||||
},
|
||||
);
|
||||
}
|
||||
let objAst;
|
||||
try {
|
||||
objAst = parse(obj, parseOptions);
|
||||
} catch (e) {
|
||||
throw Object.assign(
|
||||
new Error('Error parsing object part of the with expression'),
|
||||
{
|
||||
component: 'obj',
|
||||
babylonError: e,
|
||||
},
|
||||
);
|
||||
}
|
||||
const excludeSet = new Set([
|
||||
'undefined',
|
||||
'this',
|
||||
...exclude,
|
||||
...detect(objAst).map((g) => g.name),
|
||||
]);
|
||||
|
||||
const vars = new Set(
|
||||
detect(ast)
|
||||
.map((global) => global.name)
|
||||
.filter((v) => !excludeSet.has(v)),
|
||||
);
|
||||
|
||||
if (vars.size === 0) return src;
|
||||
|
||||
let declareLocal = '';
|
||||
let local = 'locals_for_with';
|
||||
let result = 'result_of_with';
|
||||
if (t.isValidIdentifier(obj)) {
|
||||
local = obj;
|
||||
} else {
|
||||
while (vars.has(local) || excludeSet.has(local)) {
|
||||
local += '_';
|
||||
}
|
||||
declareLocal = `var ${local} = (${obj});`;
|
||||
}
|
||||
while (vars.has(result) || excludeSet.has(result)) {
|
||||
result += '_';
|
||||
}
|
||||
|
||||
const args = [
|
||||
'this',
|
||||
...Array.from(vars).map(
|
||||
(v) =>
|
||||
`${JSON.stringify(v)} in ${local} ?
|
||||
${local}.${v} :
|
||||
typeof ${v} !== 'undefined' ? ${v} : undefined`,
|
||||
),
|
||||
];
|
||||
|
||||
const unwrapped = unwrapReturns(ast, src, result);
|
||||
|
||||
return `;
|
||||
${declareLocal}
|
||||
${unwrapped.before}
|
||||
(function (${Array.from(vars).join(', ')}) {
|
||||
${unwrapped.body}
|
||||
}.call(${args.join(', ')}));
|
||||
${unwrapped.after};`;
|
||||
}
|
||||
|
||||
interface UnwrapReturnsState {
|
||||
hasReturn: boolean;
|
||||
source(node: t.Node): string;
|
||||
replace(node: t.Node, str: string): void;
|
||||
}
|
||||
const unwrapReturnsVisitors = walk<UnwrapReturnsState>({
|
||||
Function(_node, _state, _c) {
|
||||
// returns in these functions are not applicable
|
||||
},
|
||||
|
||||
ReturnStatement(node, state) {
|
||||
state.hasReturn = true;
|
||||
let value = '';
|
||||
if (node.argument) {
|
||||
value = `value: (${state.source(node.argument)})`;
|
||||
}
|
||||
state.replace(node, `return {${value}};`);
|
||||
},
|
||||
});
|
||||
|
||||
/**
|
||||
* Take a self calling function, and unwrap it such that return inside the function
|
||||
* results in return outside the function
|
||||
*
|
||||
* @param src Some JavaScript code representing a self-calling function
|
||||
* @param result A temporary variable to store the result in
|
||||
*/
|
||||
function unwrapReturns(ast: t.Node, src: string, result: string) {
|
||||
const charArray = src.split('');
|
||||
|
||||
const state: UnwrapReturnsState = {
|
||||
hasReturn: false,
|
||||
source(node) {
|
||||
return src.slice(node.start!, node.end!);
|
||||
},
|
||||
replace(node, str) {
|
||||
charArray.fill('', node.start!, node.end!);
|
||||
charArray[node.start!] = str;
|
||||
},
|
||||
};
|
||||
|
||||
unwrapReturnsVisitors(ast, state);
|
||||
|
||||
return {
|
||||
before: state.hasReturn ? `var ${result} = ` : '',
|
||||
body: charArray.join(''),
|
||||
after: state.hasReturn ? `;if (${result}) return ${result}.value` : '',
|
||||
};
|
||||
}
|
||||
|
||||
module.exports = addWith;
|
||||
module.exports.default = addWith;
|
||||
24
33/node_modules/with/src/reference.ts
generated
vendored
Normal file
24
33/node_modules/with/src/reference.ts
generated
vendored
Normal file
@@ -0,0 +1,24 @@
|
||||
import * as t from '@babel/types';
|
||||
|
||||
export default function isReferenced(node: t.Node, parent: t.Node) {
|
||||
switch (parent.type) {
|
||||
// yes: { [NODE]: '' }
|
||||
// yes: { NODE }
|
||||
// no: { NODE: '' }
|
||||
case 'ObjectProperty':
|
||||
return parent.value === node || parent.computed;
|
||||
|
||||
// no: break NODE;
|
||||
// no: continue NODE;
|
||||
case 'BreakStatement':
|
||||
case 'ContinueStatement':
|
||||
return false;
|
||||
|
||||
// yes: left = NODE;
|
||||
// yes: NODE = right;
|
||||
case 'AssignmentExpression':
|
||||
return true;
|
||||
}
|
||||
|
||||
return t.isReferenced(node, parent);
|
||||
}
|
||||
Reference in New Issue
Block a user