UNPKG

@expressive/babel-plugin-core

Version:

Babel transformer preset for React Expression Syntax

1,438 lines (1,421 loc) 61.1 kB
'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 =