@expressive/babel-plugin-core
Version:
Babel transformer preset for React Expression Syntax
1,438 lines (1,421 loc) • 61.1 kB
JavaScript
'use strict';
Object.defineProperty(exports, '__esModule', { value: true });
var types = require('@babel/types');
var crypto = require('crypto');
var path = require('path');
const ensureArray = (a) => Array.isArray(a) ? a : [a];
const Shared = {};
const env = process.env || {
NODE_ENV: "production"
};
function hash(data, length = 3) {
return crypto.createHash("md5")
.update(data)
.digest('hex')
.substring(0, length);
}
function inParenthesis(node) {
const { extra } = node;
return extra ? extra.parenthesized === true : false;
}
function ParseErrors(register) {
const Errors = {};
for (const error in register) {
let message = [];
for (const segment of register[error].split(/\{(?=\d+\})/)) {
const ammend = /(\d+)\}(.*)/.exec(segment);
if (ammend)
message.push(parseInt(ammend[1]), ammend[2]);
else
message.push(segment);
}
Errors[error] = (node, ...args) => {
if ("node" in node)
node = node.node;
let quote = "";
for (const slice of message)
quote += (typeof slice == "string"
? slice : args[slice - 1]);
return Shared.currentFile.buildCodeFrameError(node, quote);
};
}
return Errors;
}
const Error$1 = ParseErrors({
ExpressionUnknown: "Unhandled expressionary statement of type {1}",
NodeUnknown: "Unhandled node of type {1}",
BadInputModifier: "Modifier input of type {1} not supported here!"
});
class TraversableBody {
constructor(context) {
this.sequence = [];
this.context = context.create(this);
}
didEnterOwnScope(path) {
this.context.push();
const traversable = path.get("body").get("body");
for (const item of traversable)
this.parse(item);
}
didExitOwnScope(path) {
this.context.pop(this);
}
handleContentBody(content) {
if (!types.isBlockStatement(content))
content = types.blockStatement([content]);
const body = types.doExpression(content);
body.meta = this;
return body;
}
add(item) {
this.sequence.push(item);
if ("wasAddedTo" in item
&& item.wasAddedTo)
item.wasAddedTo(this);
}
parse(item) {
const content = item.isBlockStatement() ? ensureArray(item.get("body")) : [item];
for (const item of content)
if (item.type in this)
this[item.type](item.node, item);
else {
throw Error$1.NodeUnknown(item, item.type);
}
}
parseNodes(body) {
const content = types.isBlockStatement(body) ? body.body : [body];
for (const item of content) {
if (item.type in this)
this[item.type](item);
else
throw Error$1.NodeUnknown(item, item.type);
}
}
ExpressionStatement(node) {
return this.Expression(node.expression);
}
Expression(node) {
const self = this;
if (node.type in this)
self[node.type](node);
else if (this.ExpressionDefault)
this.ExpressionDefault(node);
else
throw Error$1.ExpressionUnknown(node, node.type);
}
}
const Error$2 = ParseErrors({
ExpressionUnknown: "Unhandled expressionary statement of type {1}",
NodeUnknown: "Unhandled node of type {1}",
BadInputModifier: "Modifier input of type {1} not supported here!",
BadModifierName: "Modifier name cannot start with _ symbol!",
DuplicateModifier: "Duplicate declaration of named modifier!"
});
class AttributeBody extends TraversableBody {
constructor() {
super(...arguments);
this.props = {};
this.style = {};
}
insert(item) {
const { name } = item;
const accumulator = item instanceof Prop
? this.props : this.style;
if (name) {
const existing = accumulator[name];
if (existing)
existing.overridden = true;
accumulator[name] = item;
}
this.add(item);
}
get uid() {
return this.uid =
this.name + "_" + hash(this.context.prefix);
}
set uid(uid) {
Object.defineProperty(this, "uid", { value: uid });
}
LabeledStatement(node, _path, applyTo = this) {
const body = node.body;
const { name } = node.label;
const { context } = this;
if (name[0] == "_")
throw Error$2.BadModifierName(node);
if (context.hasOwnModifier(name))
throw Error$2.DuplicateModifier(node);
const handler = applyTo.context.propertyMod(name);
if (types.isExpressionStatement(body) || handler && (types.isBlockStatement(body) || types.isLabeledStatement(body)))
applyModifier(name, applyTo, body);
else if (types.isBlockStatement(body) || types.isLabeledStatement(body)) {
const mod = new ElementModifier(context, name, body);
applyTo.ElementModifier(mod);
}
else
throw Error$2.BadInputModifier(body, body.type);
}
ExpressionDefault(e) {
throw Error$2.ExpressionUnknown(e, e.type);
}
}
class Attribute {
constructor(name, value) {
if (name)
this.name = name;
if (value !== undefined)
this.value = value;
if (typeof value !== "object" || value === null)
this.invariant = true;
}
}
class Prop extends Attribute {
}
class ExplicitStyle extends Attribute {
constructor(name, value, important = false) {
super(name, flatten(value));
this.important = important;
function flatten(content) {
if (Array.isArray(content)) {
const [callee, ...args] = content;
return `${callee}(${args.join(" ")})`;
}
return content;
}
}
}
const containsLineBreak = (text) => /\n/.test(text);
const Error$3 = ParseErrors({
NoParenChildren: "Children in Parenthesis are not allowed, for direct insertion use an Array literal",
SemicolonRequired: "Due to how parser works, a semicolon is required after the element preceeding escaped children.",
DoExists: "Do Expression was already declared!",
PropUnknown: "There is no property inferred from an {1}",
AssignOnlyIdent: "Prop assignment only works on an identifier to denote name",
PropNameMember: "Member Expressions can't be prop names",
BadProp: "Bad Prop! + only works in conjuction with an Identifier, got {1}",
BadOperator: "Props may only be assigned with `=` or using tagged templates.",
BadUnary: "{1} is not a recognized operator for props",
BadExpression: "Expression must start with an identifier",
BadPrefix: "Improper element prefix",
BadObjectKeyValue: "Object based props must have a value. Got {1}",
PropNotIdentifier: "Prop name must be an Identifier",
NotImplemented: "{1} Not Implemented",
VoidArgsOverzealous: "Pass-Thru (void) elements can only receive styles through `do { }` statement.",
BadShorthandProp: "\"+\" shorthand prop must be an identifier!",
NestOperatorInEffect: "",
UnrecognizedBinary: ""
});
function addElementsFromExpression(subject, parent) {
var attrs = [];
if (types.isSequenceExpression(subject))
[subject, ...attrs] = subject.expressions;
if (isJustAValue(subject))
return applyPassthru(subject, parent, attrs);
else
return parseLayers(subject, parent, attrs);
}
function isJustAValue(subject) {
if (subject.doNotTransform)
return true;
if (!types.isExpression(subject))
return false;
let leftOf;
let target = subject;
while (types.isBinaryExpression(target) || types.isLogicalExpression(target)) {
leftOf = target;
target = target.left;
}
if (types.isUnaryExpression(target, { operator: "void" })) {
if (leftOf)
leftOf.left = target.argument;
else
Object.assign(target, target.argument);
return true;
}
if (types.isStringLiteral(target))
return true;
return false;
}
function applyPassthru(subject, parent, baseAttributes) {
const identifierName = types.isIdentifier(subject) && subject.name;
const styleReference = "$" + identifierName;
if (baseAttributes.length == 0) {
const hasModifiers = parent.context.elementMod(styleReference);
if (identifierName && !hasModifiers) {
parent.adopt(subject);
return subject;
}
}
else {
const hasDoArgument = baseAttributes[0].type === "DoExpression";
if (baseAttributes.length >= 2 || !hasDoArgument)
throw Error$3.VoidArgsOverzealous(subject);
}
const container = new ElementInline(parent.context);
const elementType = types.isStringLiteral(subject) ? "string" : "void";
applyNameImplications(container, elementType);
if (identifierName) {
applyNameImplications(container, styleReference);
container.name = identifierName;
}
container.explicitTagName = "div";
container.adopt(subject);
container.parent = parent;
parent.adopt(container);
parseProps(baseAttributes, container);
return container;
}
function parseLayers(subject, parent, baseAttributes, inSequence) {
const chain = [];
let restAreChildren;
let nestedExpression;
let leftMost;
if (types.isBinaryExpression(subject, { operator: ">" })) {
const { left, right } = subject;
right.doNotTransform = true;
nestedExpression = right;
subject = left;
}
while (types.isBinaryExpression(subject)) {
const { operator } = subject;
const rightHand = subject.right;
const leftHand = subject.left;
switch (operator) {
case ">>>":
if (inSequence)
throw Error$3.NestOperatorInEffect(subject);
restAreChildren = true;
break;
case ">>":
if (inParenthesis(rightHand))
throw Error$3.NoParenChildren(rightHand);
break;
default:
throw Error$3.UnrecognizedBinary(subject);
}
chain.unshift(rightHand);
subject = leftHand;
}
chain.unshift(subject);
for (const segment of chain) {
const child = new ElementInline(parent.context);
parseIdentity(segment, child);
if (!leftMost)
leftMost = child;
parent.adopt(child);
child.parent = parent;
parent = child;
}
if (restAreChildren) {
for (const child of baseAttributes)
parseLayers(child, leftMost, [], true);
baseAttributes = nestedExpression ? [nestedExpression] : [];
}
else if (nestedExpression)
baseAttributes.unshift(nestedExpression);
parseProps(baseAttributes, parent);
return parent;
}
const COMMON_HTML = [
"article", "input",
"h1", "h2", "h3", "h4", "h5", "h6",
"p", "a", "ul", "ol", "li", "blockquote",
"i", "b", "em", "strong", "span",
"hr", "img", "div", "br"
];
function applyPrimaryName(target, name, defaultTag, force) {
if (name == "s")
name = "span";
const isCommonTag = COMMON_HTML.indexOf(name) >= 0;
if (isCommonTag || force || /^[A-Z]/.test(name))
applyNameImplications(target, name, true, "html");
else {
applyNameImplications(target, defaultTag, true, "html");
applyNameImplications(target, name);
}
}
function applyNameImplications(target, name, isHead, prefix) {
const { context } = target;
let modify = context.elementMod(name);
if (isHead) {
let explicit;
if (prefix == "html" || /^[A-Z]/.test(name))
explicit = target.explicitTagName = name;
if (!explicit || !target.name)
target.name = name;
}
else
target.name = name;
while (modify) {
target.modifiers.push(modify);
modify.nTargets += 1;
// for(const mod of parent.modifiers)
// if(mod instanceof ElementModifier)
for (const sub of modify.provides)
target.context.elementMod(sub);
if (infiniteLoopDetected(modify))
break;
modify = modify.next;
}
}
function infiniteLoopDetected(modify) {
if (modify !== modify.next)
return false;
try {
if (~process.execArgv.join().indexOf("inspect-brk"))
console.error(`Still haven't fixed inheritance leak apparently. \n target: ${name}`);
}
catch (err) { }
return true;
}
function parseIdentity(tag, target) {
let prefix;
if (types.isBinaryExpression(tag, { operator: "-" })) {
const left = tag.left;
if (types.isIdentifier(left))
prefix = left.name;
else
throw Error$3.BadPrefix(left);
tag = tag.right;
}
if (types.isUnaryExpression(tag, { operator: "!" })) {
prefix = "html";
tag = tag.argument;
}
tag = unwrapExpression(tag, target);
if (types.isIdentifier(tag))
applyPrimaryName(target, tag.name, "div", prefix === "html");
else if (types.isStringLiteral(tag) || types.isTemplateLiteral(tag)) {
const Text = "span";
applyNameImplications(target, "string");
applyNameImplications(target, Text, true);
target.add(tag);
}
else
throw Error$3.BadExpression(tag);
}
function unwrapExpression(expression, target) {
while (!inParenthesis(expression))
switch (expression.type) {
case "TaggedTemplateExpression": {
let content = expression.quasi;
const hasExpressions = content.expressions.length > 0;
const hasLineBreak = containsLineBreak(content.quasis[0].value.cooked);
if (!hasExpressions && !hasLineBreak) {
const text = content.quasis[0].value.cooked;
content = types.stringLiteral(text);
}
target.add(content);
expression = expression.tag;
break;
}
case "CallExpression": {
const exp = expression;
const args = exp.arguments;
parseProps(args, target);
expression = exp.callee;
break;
}
case "MemberExpression": {
const selector = expression.property;
if (expression.computed !== true && types.isIdentifier(selector))
applyNameImplications(target, selector.name);
else
throw Error$3.SemicolonRequired(selector);
return expression.object;
}
default:
return expression;
}
return expression;
}
function parseProps(props, target) {
if (!props)
return;
for (let node of props) {
if (isJustAValue(node)) {
target.add(node);
continue;
}
switch (node.type) {
case "DoExpression":
node.meta = target;
target.doBlock = node;
break;
case "TemplateLiteral":
case "ArrowFunctionExpression":
target.add(node);
break;
case "ArrayExpression":
{
for (const item of node.elements)
if (types.isExpression(item))
target.add(item);
}
break;
case "TaggedTemplateExpression":
{
const { tag, quasi } = node;
if (tag.type != "Identifier")
throw Error$3.PropNotIdentifier(node);
const value = quasi.expressions.length == 0 ?
types.stringLiteral(quasi.quasis[0].value.raw) :
quasi;
const prop = new Prop(tag.name, value);
target.add(prop);
}
break;
case "AssignmentExpression":
{
const name = node.left;
const value = node.right;
if (!types.isIdentifier(name))
throw Error$3.AssignOnlyIdent(name);
target.add(new Prop(name.name, value));
}
break;
case "Identifier":
{
const prop = new Prop(node.name, node);
target.add(prop);
}
break;
case "SpreadElement":
{
const prop = new Prop(false, node.argument);
target.add(prop);
}
break;
case "ObjectExpression":
{
for (const property of node.properties) {
let insert;
if (types.isObjectProperty(property)) {
const { key, value } = property;
const name = key.value || key.name;
if (!types.isExpression(value))
throw Error$3.BadObjectKeyValue(value, value.type);
insert = new Prop(name, value);
}
else if (types.isSpreadElement(property))
insert = new Prop(false, property.argument);
else if (types.isObjectMethod(property)) {
const func = Object.assign({ id: null, type: "FunctionExpression" }, property);
insert = new Prop(property.key, func);
}
target.add(insert);
}
}
break;
case "UnaryExpression":
{
const value = node.argument;
switch (node.operator) {
case "+":
if (types.isIdentifier(value))
target.add(new Prop(value.name, value));
else
throw Error$3.BadShorthandProp(node);
break;
case "-":
target.add(new Prop("className", value));
break;
case "~":
target.add(new ExplicitStyle(false, value));
break;
}
}
break;
default: {
throw Error$3.PropUnknown(node, node.type);
}
}
}
}
const Error$4 = ParseErrors({
InvalidPropValue: "Can only consume an expression or string literal as value here.",
UnhandledChild: "Can't parse JSX child of type {1}",
JSXMemberExpression: "Member Expression is not supported!",
NonJSXIdentifier: "Cannot handle non-identifier!"
});
function addElementFromJSX(node, parent) {
const shouldApplyToSelf = types.isJSXIdentifier(node.openingElement.name, { name: "this" });
if (!shouldApplyToSelf)
parent = createElement(node, parent);
const queue = [[parent, node]];
for (const [element, node] of queue) {
const { children } = node;
const { attributes } = node.openingElement;
for (const attribute of attributes)
applyAttribute(element, attribute);
for (const node of children) {
switch (node.type) {
case "JSXElement":
{
const child = createElement(node, element);
queue.push([child, node]);
}
break;
case "JSXText":
if (/^\n+ *$/.test(node.value))
continue;
const text = node.value.replace(/\s+/g, " ");
element.add(types.stringLiteral(text));
break;
case "JSXExpressionContainer":
element.add(node.expression);
break;
default:
throw Error$4.UnhandledChild(node, node.type);
}
}
}
}
function createElement(element, parent) {
let target = new ElementInline(parent.context);
const { name } = element.openingElement;
if (types.isJSXMemberExpression(name)) {
applyNameImplications(target, name.property.name, true);
target.explicitTagName = name;
}
else if (!types.isJSXIdentifier(name)) {
throw Error$4.NonJSXIdentifier(name);
}
else if (/^html-.+/.test(name.name)) {
const tag = name.name.slice(5);
applyNameImplications(target, tag, true, "html");
}
else
applyPrimaryName(target, name.name, "div");
parent.adopt(target);
return target;
}
function applyAttribute(parent, attr) {
if (attr.type == "JSXSpreadAttribute") {
const prop = new Prop(false, attr.argument);
parent.add(prop);
return;
}
const name = attr.name;
const propValue = attr.value;
if (propValue === null) {
applyNameImplications(parent, name.name);
return;
}
let value;
switch (propValue.type) {
case "JSXExpressionContainer":
value = propValue.expression;
break;
case "StringLiteral":
value = propValue;
break;
default:
throw Error$4.InvalidPropValue(propValue);
}
parent.add(new Prop(name.name, value));
}
const Error$5 = ParseErrors({
PropNotIdentifier: "Assignment must be identifier name of a prop.",
AssignmentNotEquals: "Only `=` assignment may be used here.",
BadShorthandProp: "\"+\" shorthand prop must be an identifier!",
UnarySpaceRequired: "Unary Expression must include a space between {1} and the value.",
StatementInElement: "Statement insertion not implemented while within elements!",
MinusMinusNotImplemented: "-- is not implemented as an integration statement."
});
class ElementInline extends AttributeBody {
constructor() {
super(...arguments);
this.children = [];
this.modifiers = [];
this.data = {};
}
adopt(child) {
const index = this.children.push(child);
if ("context" in child && child.context instanceof StackFrame)
child.context.resolveFor(index);
this.add(child);
}
ExpressionDefault(node) {
if (inParenthesis(node))
this.adopt(node);
else
addElementsFromExpression(node, this);
}
JSXElement(node) {
addElementFromJSX(node, this);
}
ElementModifier(mod) {
this.context.elementMod(mod);
}
IfStatement(_, path) {
const mod = new ComponentIf(path, this.context);
this.adopt(mod);
path.replaceWith(types.blockStatement(mod.doBlocks));
}
ForInStatement(_, stat) {
this.ForStatement(_, stat);
}
ForOfStatement(_, stat) {
this.ForStatement(_, stat);
}
ForStatement(_, stat) {
const element = new ComponentFor(stat, this.context);
this.adopt(element);
const { doBlock } = element;
if (doBlock)
stat.replaceWith(doBlock);
}
BlockStatement(node, path) {
const blockElement = new ElementInline(this.context);
const block = types.blockStatement(node.body);
const doExp = types.doExpression(block);
applyNameImplications(blockElement, "block");
this.add(blockElement);
blockElement.doBlock = doExp;
doExp.meta = blockElement;
path.replaceWith(types.expressionStatement(doExp));
}
UpdateExpression(node) {
const value = node.argument;
const op = node.operator;
if (node.start !== value.start - 3)
throw Error$5.UnarySpaceRequired(node, op);
if (op !== "++")
throw Error$5.MinusMinusNotImplemented(node);
this.add(new Prop(false, value));
}
UnaryExpression(node) {
const value = node.argument;
const op = node.operator;
switch (op) {
case "delete":
this.ExpressionAsStatement(value);
return;
case "void":
case "!":
this.ExpressionDefault(node);
return;
}
if (node.start !== value.start - 2)
throw Error$5.UnarySpaceRequired(node, op);
switch (op) {
case "+":
if (!types.isIdentifier(value))
throw Error$5.BadShorthandProp(node);
this.add(new Prop(value.name, value));
break;
case "-":
this.add(new Prop("className", value));
break;
case "~":
this.add(new ExplicitStyle(false, value));
break;
}
}
ExpressionAsStatement(node) {
throw Error$5.StatementInElement(node);
}
AssignmentExpression(node) {
if (node.operator !== "=")
throw Error$5.AssignmentNotEquals(node);
let { left, right } = node;
if (!types.isIdentifier(left))
throw Error$5.PropNotIdentifier(left);
let { name } = left;
if (types.isDoExpression(right)) {
const prop = new Prop(name, types.identifier("undefined"));
right.expressive_parent = prop;
this.insert(prop);
}
else
this.insert(new Prop(name, right));
}
}
class ComponentContainer extends ElementInline {
constructor() {
super(...arguments);
this.statements = [];
}
ExpressionAsStatement(node) {
const stat = types.expressionStatement(node);
this.statements.push(stat);
}
VariableDeclaration(node) {
this.statements.push(node);
}
DebuggerStatement(node) {
this.statements.push(node);
}
FunctionDeclaration(node) {
this.statements.push(node);
}
}
class ComponentExpression extends ComponentContainer {
constructor(name, context, path, exec) {
super(context);
this.statements = [];
this.context.currentComponent = this;
if (exec) {
this.exec = exec;
}
this.name = name;
this.explicitTagName = "div";
if (/^[A-Z]/.test(name))
applyNameImplications(this, name);
path.node.meta = this;
this.context.resolveFor(this.name);
}
add(item) {
if (this.forwardTo)
this.forwardTo.add(item);
else
super.add(item);
}
adopt(child) {
if (this.forwardTo) {
this.forwardTo.adopt(child);
return;
}
super.adopt(child);
}
}
const Error$6 = ParseErrors({
ReturnElseNotImplemented: "This is an else condition, returning from here is not implemented.",
IfStatementCannotContinue: "Previous consequent already returned, cannot integrate another clause.",
CantReturnInNestedIf: "Cant return because this if-statement is nested!",
CanOnlyReturnTopLevel: "Cant return here because immediate parent is not the component.",
CanOnlyReturnFromLeadingIf: "Cant return here because it's not the first if consequent in-chain."
});
class ComponentIf {
constructor(path, context, test) {
this.path = path;
this.test = test;
this.forks = [];
this.doBlocks = [];
context = this.context = context.create(this);
context.currentIf = this;
if (!test)
context.entryIf = this;
}
wasAddedTo(parent) {
const { context } = this;
let layer = this.path;
while (true) {
let consequent = layer.get("consequent");
let test;
if (layer.isIfStatement())
test = layer.node.test;
if (consequent.isBlockStatement()) {
const inner = ensureArray(consequent.get("body"));
if (inner.length == 1)
consequent = inner[0];
}
const fork = consequent.isIfStatement()
? new ComponentIf(consequent, context, test)
: new ComponentConsequent(consequent, context, this.forks.length + 1, test);
const index = this.forks.push(fork);
fork.context.resolveFor(index);
if (fork instanceof ComponentConsequent)
fork.index = index;
layer = layer.get("alternate");
const overrideRest = fork.doesReturn || false;
if (overrideRest && layer.node)
throw Error$6.IfStatementCannotContinue(layer);
if (layer.type === "IfStatement")
continue;
const final = new ComponentConsequent(layer, this.context, this.forks.length + 1);
this.forks.push(final);
if (overrideRest) {
final.name = context.currentElement.name;
final.explicitTagName = "div";
const { current } = context.parent;
if (current instanceof ComponentExpression)
current.forwardTo = final;
}
break;
}
const doInsert = [];
for (const fork of this.forks)
if (fork instanceof ComponentConsequent
&& fork.doBlock)
doInsert.push(types.expressionStatement(fork.doBlock));
this.doBlocks = doInsert;
}
}
class ComponentConsequent extends ElementInline {
constructor(path, context, index, test) {
super(context);
this.path = path;
this.context = context;
this.index = index;
this.test = test;
if (!path || !path.node)
return;
this.doBlock = this.handleContentBody(path.node);
if (!this.doBlock) {
this.didExitOwnScope();
const child = this.children[0];
if (child instanceof ElementInline)
this.doBlock = child.doBlock;
}
}
get parentElement() {
return this.context.currentElement;
}
adopt(child) {
let { context } = this;
if (!context.currentIf.hasElementOutput)
do {
if (context.current instanceof ComponentIf)
context.current.hasElementOutput = true;
else
break;
} while (context = context.parent);
super.adopt(child);
}
didExitOwnScope() {
const mod = this.slaveModifier;
const parent = this.context.currentElement;
if (mod) {
// mod.didFinishParsing();
if (mod.sequence.length)
parent.modifiers.push(mod);
else
this.usesClassname = "";
if (mod.applicable.length) {
const uids = mod.applicable.map(x => x.uid).join(" ");
if (this.usesClassname)
this.usesClassname += " " + uids;
else
this.usesClassname = uids;
}
}
}
ReturnStatement(node) {
const arg = node.argument;
const { context } = this;
if (!this.test)
throw Error$6.ReturnElseNotImplemented(node);
if (this.index !== 1)
throw Error$6.CanOnlyReturnFromLeadingIf(node);
if (context.currentIf !== context.entryIf)
throw Error$6.CantReturnInNestedIf(node);
if (!(context.currentElement instanceof ComponentExpression))
throw Error$6.CanOnlyReturnTopLevel(node);
if (arg)
if (types.isDoExpression(arg))
arg.meta = this;
else if (types.isExpression(arg))
this.Expression(arg);
this.doesReturn = true;
}
LabeledStatement(node) {
const mod = this.slaveModifier || this.slaveNewModifier();
super.LabeledStatement(node, null, mod);
}
slaveNewModifier() {
let { context } = this;
const uid = hash(this.context.prefix);
//TODO: Discover helpfulness of customized className.
let selector = specifyOption(this.test) || `opt${this.index}`;
selector += `_${uid}`;
const parent = context.currentElement;
const mod = new ContingentModifier(context, parent, `.${selector}`);
mod.priority = 5;
if (!context.currentIf.hasStyleOutput)
do {
if (context.current instanceof ComponentIf)
context.current.hasStyleOutput = true;
else
break;
} while (context = context.parent);
this.usesClassname = selector;
return this.slaveModifier = mod;
}
}
function specifyOption(test) {
if (!test)
return "else";
let ref = "if_";
if (types.isUnaryExpression(test, { operator: "!" })) {
test = test.argument;
ref = "not_";
}
if (types.isIdentifier(test)) {
const { name } = test;
return ref + name;
}
}
class ComponentFor extends ComponentContainer {
constructor(path, context) {
super(context);
this.path = path;
this.node = path.node;
this.name = this.generateName();
this.doBlock = this.handleContentBody(path.node.body);
}
generateName() {
const { node } = this;
if (types.isForXStatement(node)) {
let { left, right } = this.node;
const name = [];
if (types.isVariableDeclaration(left))
left = left.declarations[0].id;
if (types.isIdentifier(left))
name.push(left.name);
name.push(node.type == "ForInStatement" ? "in" : "of");
if (types.isIdentifier(right))
name.push(right.name);
return name.reduce((acc, w) => acc + w[0].toUpperCase() + w.slice(1), "for");
}
else
return "for";
}
AssignmentExpression(path) {
throw new Error("For block cannot accept Assignments");
}
Prop() {
}
}
const Error$7 = ParseErrors({
NodeUnknown: "Unhandled node of type {1}",
});
function concat(to, from, ...names) {
to = to;
from = from;
for (const name of names)
if (name in from)
to[name] = name in to
? from[name].concat(to[name])
: from[name];
}
class Modifier extends AttributeBody {
constructor() {
super(...arguments);
this.applicable = [];
}
parse(body) {
const content = body.isBlockStatement() ? ensureArray(body.get("body")) : [body];
for (const item of content) {
if (item.type in this)
this[item.type](item.node, item);
else
throw Error$7.NodeUnknown(item, item.type);
}
}
addStyle(name, value) {
this.insert(new ExplicitStyle(name, value));
}
}
class ElementModifier extends Modifier {
constructor(context, name, body) {
super(context);
this.nTargets = 0;
this.provides = [];
this.priority = 1;
this.name = name;
this.context.resolveFor(name);
this.forSelector = [`.${this.uid}`];
this.parseNodes(body);
}
ElementModifier(mod) {
mod.priority = this.priority;
this.provides.push(mod);
this.onlyWithin = mod.onlyWithin;
concat(mod, this, "onGlobalStatus");
}
}
class ContingentModifier extends Modifier {
constructor(context, parent, contingent) {
super(context);
let select;
if (parent instanceof ElementInline)
select = [`.${parent.uid}`];
else {
select = Object.create(parent.forSelector);
if (parent instanceof ContingentModifier)
parent = parent.anchor;
}
if (typeof contingent == "function")
contingent(select);
else if (contingent)
select.push(contingent);
this.anchor = parent;
this.forSelector = select;
}
ElementModifier(mod) {
const { anchor } = this;
mod.onlyWithin = this;
mod.priority = 4;
if (anchor instanceof ElementModifier)
anchor.provides.push(mod);
else
anchor.context.elementMod(mod);
}
didFinishParsing() {
}
}
const { isArray } = Array;
function applyModifier(initial, recipient, input) {
const handler = recipient.context.propertyMod(initial);
const styles = {};
// const props = {} as BunchOf<Attribute>;
let i = 0;
let stack = [
[initial, handler, input]
];
do {
const next = stack[i];
const output = new ModifyDelegate(recipient, ...next);
Object.assign(styles, output.styles);
// Object.assign(props, output.props);
const recycle = output.attrs;
const pending = [];
if (recycle)
for (const named in recycle) {
let input = recycle[named];
if (input == null)
continue;
const useSuper = named === initial;
const handler = recipient.context.findPropertyMod(named, useSuper);
pending.push([named, handler, input]);
}
if (pending.length) {
stack = [...pending, ...stack.slice(i + 1)];
i = 0;
}
else
i++;
} while (i in stack);
for (const name in styles)
recipient.insert(styles[name]);
}
class ModifyDelegate {
constructor(target, name, transform, input) {
this.target = target;
this.name = name;
this.attrs = {};
this.styles = {};
this.props = {};
let important = false;
let args;
if (isArray(input))
args = input;
else {
args = Arguments.Parse(input);
this.body = input;
}
if (args[args.length - 1] == "!important") {
important = true;
args.pop();
}
this.arguments = args;
if (!transform)
transform = propertyModifierDefault;
const output = transform.apply(this, args);
if (!output || this.done)
return;
const { attrs, style } = output;
if (style)
for (const name in style) {
let item = style[name];
this.styles[name] =
new ExplicitStyle(name, item, important);
}
if (attrs)
for (const name in attrs) {
let args = attrs[name];
if (!Array.isArray(args))
args = [args];
if (important)
args.push("!important");
this.attrs[name] = args;
}
}
setContingent(contingent, priority, usingBody) {
const { target } = this;
const mod = new ContingentModifier(this.target.context, this.target, contingent);
mod.priority = priority || this.priority;
mod.parseNodes(usingBody || this.body);
if (target instanceof ElementInline)
target.modifiers.push(mod);
else if (target instanceof ElementModifier)
target.applicable.push(mod);
else if (target instanceof ContingentModifier &&
target.anchor instanceof ElementInline)
target.anchor.modifiers.push(mod);
return mod;
}
}
function propertyModifierDefault() {
const args = this.arguments.map(arg => {
const { value, requires } = arg;
if (value)
return value;
else if (requires)
return requireExpression(requires);
else
return arg;
});
const output = args.length == 1 || typeof args[0] == "object"
? args[0]
: Array.from(args).join(" ");
return {
style: {
[this.name]: output
}
};
}
function requireExpression(from) {
const argument = typeof from == "string"
? types.stringLiteral(from)
: from;
return types.callExpression(types.identifier("require"), [argument]);
}
function use(...args) {
const { target } = this;
for (const item of args) {
if (typeof item !== "string")
continue;
const mod = target.context.elementMod(item);
if (!mod)
continue;
if (target instanceof ElementInline)
target.modifiers.push(mod);
else if (target instanceof Modifier) {
target.applicable.push(mod);
}
}
}
function priority(priority) {
const { target } = this;
if (target instanceof Modifier)
target.priority = priority;
}
function css() {
debugger;
}
function forward(...args) {
let target = this.target;
let parent = target.context.currentComponent;
if (!(target instanceof ElementInline))
throw new Error("Can only forward props to another element");
if (!parent)
throw new Error("No parent component found in hierarchy");
const { exec } = parent;
if (!exec)
throw new Error("Can only apply props from a parent `() => do {}` function!");
const uid = (name) => types.identifier(ensureUID(exec.context.scope, name));
let all = args.indexOf("all") + 1;
const reference = {};
if (all || ~args.indexOf("children")) {
const id = reference["children"] = uid("children");
target.adopt(id);
}
for (const prop of ["className", "style"])
if (all || ~args.indexOf(prop)) {
const id = reference[prop] = uid(prop);
target.insert(new Prop(prop, id));
}
applyToParentProps(parent, reference);
}
function ensureUID(scope, name = "temp") {
let uid;
let i = 0;
name = name
.replace(/^_+/, "")
.replace(/[0-9]+$/g, "");
do {
uid = name;
if (i > 1)
name = name + i;
i++;
} while (scope.hasBinding(uid) ||
scope.hasGlobal(uid) ||
scope.hasReference(uid));
const program = scope.getProgramParent();
program.references[uid] = true;
program.uids[uid] = true;
return uid;
}
function applyToParentProps(parent, assignments) {
const { exec } = parent;
if (!exec)
throw new Error("Can only apply props from a parent `() => do {}` function!");
const { node } = exec;
const properties = Object.entries(assignments).map((e) => types.objectProperty(types.identifier(e[0]), e[1], false, e[1].name == e[0]));
let props = node.params[0];
if (!props)
props = node.params[0] = types.objectPattern(properties);
else if (types.isObjectPattern(props))
props.properties.push(...properties);
else if (types.isIdentifier(props))
parent.statements.unshift(types.variableDeclaration("const", [
types.variableDeclarator(types.objectPattern(properties), props)
]));
}
var builtIn = /*#__PURE__*/Object.freeze({
__proto__: null,
use: use,
priority: priority,
css: css,
forward: forward
});
const { getPrototypeOf, create, assign } = Object;
let debug = false;
try {
debug = process.execArgv.join().indexOf("inspect-brk") >= 0;
}
catch (err) { }
const Error$8 = ParseErrors({
IllegalAtTopLevel: "Cannot apply element styles in top-level of program",
BadModifierName: "Modifier name cannot start with _ symbol!",
DuplicateModifier: "Duplicate declaration of named modifier!"
});
const Program = {
enter({ node }, state) {
if (debug)
try {
console.log(" - ", path.relative(process.cwd(), state.filename));
}
catch (err) { }
let context = state.context = new StackFrame(state).create(this);
context.currentFile = state.file;
Shared.currentFile = state.file;
const filtered = [];
for (const statement of node.body)
if (types.isLabeledStatement(statement)) {
const { name } = statement.label;
const { body } = statement;
if (name[0] == "_")
throw Error$8.BadModifierName(node);
if (this.context.hasOwnModifier(name))
throw Error$8.DuplicateModifier(node);
if (types.isExpressionStatement(body))
throw Error$8.IllegalAtTopLevel(statement);
const mod = new ElementModifier(context, name, body);
context.elementMod(mod);
}
else
filtered.push(statement);
node.body = filtered;
}
};
class StackFrame {
constructor(state) {
this.program = {};
this.styleRoot = {};
this.current = {};
let Stack = this;
const external = [].concat(state.opts.modifiers || []);
const included = assign({}, ...external);
this.stateSingleton = state;
this.prefix = hash(state.filename);
this.options = {};
for (const imports of [builtIn, included]) {
Stack = create(Stack);
const { Helpers, ...Modifiers } = imports;
for (const name in Modifiers)
Stack.propertyMod(name, Modifiers[name]);
}
return Stack;
}
get parent() {
return getPrototypeOf(this);
}
create(node) {
const frame = create(this);
frame.current = node;
if (node instanceof ElementInline)
frame.currentElement = node;
return frame;
}
push() {
this.stateSingleton.context = this;
}
pop(meta, state = this.stateSingleton) {
let { context } = state;
let newContext;
while (true) {
newContext = Object.getPrototypeOf(context);
if (!newContext)
break;
if (context.current === meta)
break;
context = newContext;
}
if (context.current)
state.context = newContext;
else
console.error("StackFrame shouldn't bottom out like this");
}
resolveFor(append) {
this.prefix = this.prefix + " " + append || "";
}
event(ref, set) {
if (set)
this[ref] = set;
else
return this[ref];
}
dispatch(ref, ...args) {
this[ref].apply(null, args);
}
hasOwnPropertyMod(name) {
return this.hasOwnProperty("__" + name);
}
hasOwnModifier(name) {
return this.hasOwnProperty("_" + name);
}
findPropertyMod(named, ignoreOwn = false) {
let context = this;
if (ignoreOwn) {
let found;
do {
found = context.hasOwnPropertyMod(named);
context = context.parent;
} while (!found);
}
return context.propertyMod(named);
}
propertyMod(name, set) {
const ref = "__" + name;
if (set)
this[ref] = set;
else
return this[ref];
}
elementMod(mod) {
if (typeof mod == "string")
return this["_" + mod];
const name = "_" + mod.name;
if (this[name])
mod.next = this[name];
this[name] = mod;
}
}
const DoExpression = {
enter: (path, state) => {
let { meta } = path.node;
if (!meta)
meta = generateEntryElement(path, state.context);
meta.didEnterOwnScope(path);
},
exit: (path) => {
path.node.meta.didExitOwnScope(path);
}
};
function generateEntryElement(path, context) {
let parent = path.parentPath;
let containerFn;
switch (parent.type) {
case "ArrowFunctionExpression":
containerFn = parent;
break;
case "ReturnStatement":
const container = parent.findParent(x => /.*Function.*/.test(x.type));
if (container.type == "ArrowFunctionExpression")
containerFn = container;
break;
}
const name =