babel-plugin-estrela
Advanced tools
Comparing version 0.3.1 to 0.10.0
@@ -1,4 +0,6 @@ | ||
import * as t from '@babel/types'; | ||
import { Options } from './options'; | ||
export type { Options } from './options'; | ||
import { transformComponent } from './transforms/component.transform'; | ||
import { transformJSX } from './transforms/jsx.transform'; | ||
import { transformStyles } from './transforms/styles.transform'; | ||
import { Options } from './types'; | ||
export * from './types'; | ||
declare const _default: (api: object, options: Options | null | undefined, dirname: string) => { | ||
@@ -8,5 +10,12 @@ name: string; | ||
visitor: { | ||
Program(this: import("@babel/core").PluginPass, path: import("@babel/traverse").NodePath<t.Program>): void; | ||
Program: { | ||
enter(path: import("@babel/traverse").NodePath<import("@babel/types").Program>): void; | ||
exit(path: import("@babel/traverse").NodePath<import("@babel/types").Program>): void; | ||
}; | ||
Function: typeof transformComponent; | ||
JSXElement: typeof transformJSX; | ||
JSXFragment: typeof transformJSX; | ||
TaggedTemplateExpression: typeof transformStyles; | ||
}; | ||
}; | ||
export default _default; |
@@ -112,3 +112,3 @@ var __create = Object.create; | ||
}); | ||
exports.default = annotateAsPure3; | ||
exports.default = annotateAsPure2; | ||
var _t = require("@babel/types"); | ||
@@ -122,3 +122,3 @@ var { | ||
}) => !!leadingComments && leadingComments.some((comment) => /[@#]__PURE__/.test(comment.value)); | ||
function annotateAsPure3(pathOrNode) { | ||
function annotateAsPure2(pathOrNode) { | ||
const node = pathOrNode["node"] || pathOrNode; | ||
@@ -140,384 +140,529 @@ if (isPureAnnotated(node)) { | ||
var import_helper_plugin_utils = __toESM(require_lib()); | ||
var t4 = __toESM(require("@babel/types")); | ||
// src/functional.transform.ts | ||
// src/transforms/component.transform.ts | ||
var import_helper_annotate_as_pure = __toESM(require_lib2()); | ||
var t = __toESM(require("@babel/types")); | ||
function functional_transform_default(options) { | ||
const { enableGetStateFunction = true, getStateWithDolarSuffix = true } = options; | ||
const visitor2 = { | ||
CallExpression(path, state) { | ||
var _a, _b; | ||
if (enableGetStateFunction && path.get("callee").isIdentifier({ name: "getState" })) { | ||
const param = path.node.arguments[0]; | ||
let object = null; | ||
let prop = null; | ||
if (t.isIdentifier(param)) { | ||
const scopeNode = path.scope.getBindingIdentifier(param.name); | ||
if (state.states.includes(scopeNode)) { | ||
object = t.identifier("_$"); | ||
prop = param.name; | ||
} | ||
if (state.props.includes(scopeNode)) { | ||
object = state.propsParam; | ||
prop = param.name; | ||
} | ||
} else if (t.isMemberExpression(param) || t.isOptionalMemberExpression(param)) { | ||
const member = param; | ||
if (t.isIdentifier(member.object)) { | ||
const scopeNode = path.scope.getBindingIdentifier(member.object.name); | ||
if (state.propsParam === scopeNode) { | ||
object = state.propsParam; | ||
prop = (_b = (_a = member.property) == null ? void 0 : _a.name) != null ? _b : "0"; | ||
} | ||
} | ||
// src/shared/utils.ts | ||
function getOptions(path) { | ||
return path.hub.file.metadata.config; | ||
} | ||
// src/transforms/component.transform.ts | ||
function transformComponent(path) { | ||
var _a, _b, _c; | ||
const { stateProxy } = path.state; | ||
const states = []; | ||
const body = path.get("body"); | ||
let returnValue = null; | ||
let funcName; | ||
if (t.isCallExpression(path.parent) && t.isIdentifier(path.parent.callee, { name: "styled" })) { | ||
funcName = "Styled"; | ||
} else if (t.isVariableDeclarator(path.parent)) { | ||
funcName = t.isIdentifier(path.parent.id) ? path.parent.id.name : ""; | ||
} else { | ||
funcName = t.isFunctionDeclaration(path.node) ? (_b = (_a = path.node.id) == null ? void 0 : _a.name) != null ? _b : "" : ""; | ||
} | ||
if (!funcName[0] || funcName[0] !== funcName[0].toUpperCase() || !body.isBlockStatement()) { | ||
return; | ||
} | ||
for (const node of body.node.body) { | ||
if (t.isReturnStatement(node)) { | ||
returnValue = (_c = node.argument) != null ? _c : null; | ||
} | ||
} | ||
if (t.isJSXElement(returnValue) || t.isJSXFragment(returnValue)) { | ||
const param = path.node.params[0]; | ||
const props = []; | ||
let propsParam = t.identifier("_props"); | ||
if (t.isIdentifier(param)) { | ||
propsParam = param; | ||
} | ||
if (t.isObjectPattern(param)) { | ||
for (const prop of param.properties) { | ||
if (t.isObjectProperty(prop) && t.isIdentifier(prop.value)) { | ||
props.push(prop.value); | ||
} | ||
if (object && prop) { | ||
path.skip(); | ||
path.node.arguments = [object, t.stringLiteral(prop)]; | ||
if (t.isRestElement(prop) && t.isIdentifier(prop.argument)) { | ||
propsParam = prop.argument; | ||
} | ||
} | ||
}, | ||
Identifier(path, state) { | ||
const updateNode = (node, isDolar) => { | ||
const scopeNode = path.scope.getBindingIdentifier(node.name); | ||
if (scopeNode === node || path.parentPath.isMemberExpression({ property: node })) { | ||
return false; | ||
} | ||
const getObject = (obj) => { | ||
return isDolar ? t.memberExpression(obj, t.identifier("$")) : obj; | ||
}; | ||
} | ||
path.node.params[0] = propsParam; | ||
body.traverse(visitor, { propsParam, props, states }); | ||
if (states.length > 0) { | ||
const callExp = t.callExpression(stateProxy, []); | ||
const declarator = t.variableDeclarator(t.identifier("_$"), callExp); | ||
const declaration = t.variableDeclaration("const", [declarator]); | ||
body.unshiftContainer("body", declaration); | ||
(0, import_helper_annotate_as_pure.default)(callExp); | ||
} | ||
} | ||
} | ||
var visitor = { | ||
CallExpression(path, state) { | ||
var _a, _b; | ||
const { enableGetStateFunction } = getOptions(path); | ||
if (enableGetStateFunction && path.get("callee").isIdentifier({ name: "getState" })) { | ||
const param = path.node.arguments[0]; | ||
let object = null; | ||
let prop = null; | ||
if (t.isIdentifier(param)) { | ||
const scopeNode = path.scope.getBindingIdentifier(param.name); | ||
if (state.states.includes(scopeNode)) { | ||
path.skip(); | ||
path.replaceWith(t.memberExpression(getObject(t.identifier("_$")), node)); | ||
return true; | ||
object = t.identifier("_$"); | ||
prop = param.name; | ||
} | ||
if (state.props.includes(scopeNode)) { | ||
path.skip(); | ||
path.replaceWith(t.memberExpression(getObject(t.cloneNode(state.propsParam)), node)); | ||
return true; | ||
object = state.propsParam; | ||
prop = param.name; | ||
} | ||
}; | ||
if (getStateWithDolarSuffix && /[^$]\$$/.test(path.node.name)) { | ||
const node = t.identifier(path.node.name.replace(/\$$/, "")); | ||
if (updateNode(node, true)) { | ||
return; | ||
} | ||
if (path.parentPath.isMemberExpression({ property: path.node }) || path.parentPath.isOptionalMemberExpression({ property: path.node })) { | ||
const object = path.parentPath.node.object; | ||
if (t.isIdentifier(object)) { | ||
const scopeNode = path.scope.getBindingIdentifier(object.name); | ||
if (state.propsParam === scopeNode) { | ||
path.parentPath.replaceWith(t.memberExpression(t.memberExpression(object, t.identifier("$")), node)); | ||
return; | ||
} | ||
} else if (t.isMemberExpression(param) || t.isOptionalMemberExpression(param)) { | ||
const member = param; | ||
if (t.isIdentifier(member.object)) { | ||
const scopeNode = path.scope.getBindingIdentifier(member.object.name); | ||
if (state.propsParam === scopeNode) { | ||
object = state.propsParam; | ||
prop = (_b = (_a = member.property) == null ? void 0 : _a.name) != null ? _b : "0"; | ||
} | ||
} | ||
} | ||
updateNode(path.node); | ||
}, | ||
JSXExpressionContainer(path, state) { | ||
const expPath = path.get("expression"); | ||
if (expPath.isFunction() || expPath.isCallExpression() && expPath.get("callee").isIdentifier({ name: "getState" })) { | ||
path.traverse(visitor2, state); | ||
if (object && prop) { | ||
path.node.arguments = [object, t.stringLiteral(prop)]; | ||
path.skip(); | ||
return; | ||
} | ||
const hasStates = () => { | ||
const node = path.node; | ||
let hasState = false; | ||
path.traverse({ | ||
Function(path2) { | ||
if (path2.parent === node) { | ||
path2.skip(); | ||
} | ||
}, | ||
Identifier(path2) { | ||
const scopeNode = path2.scope.getBindingIdentifier(path2.node.name); | ||
if (state.states.includes(scopeNode) || state.props.includes(scopeNode) || t.isNodesEquivalent(state.propsParam, path2.node)) { | ||
hasState = true; | ||
path2.stop(); | ||
} | ||
} | ||
}); | ||
return hasState; | ||
} | ||
}, | ||
Identifier(path, state) { | ||
const { getStateWithDolarSuffix } = getOptions(path); | ||
const updateNode = (node, isDolar) => { | ||
const scopeNode = path.scope.getBindingIdentifier(node.name); | ||
if (scopeNode === node || path.parentPath.isMemberExpression({ property: node })) { | ||
return false; | ||
} | ||
const getObject = (obj) => { | ||
return isDolar ? t.memberExpression(obj, t.identifier("$")) : obj; | ||
}; | ||
if (expPath.isExpression() && hasStates()) { | ||
expPath.replaceWith(t.arrowFunctionExpression([], expPath.node)); | ||
if (state.states.includes(scopeNode)) { | ||
path.skip(); | ||
path.replaceWith(t.memberExpression(getObject(t.identifier("_$")), node)); | ||
return true; | ||
} | ||
}, | ||
VariableDeclaration(path, state) { | ||
if (path.node.kind === "let") { | ||
const nodes = path.node.declarations.reduce((acc, declaration) => { | ||
if (t.isIdentifier(declaration.id)) { | ||
state.states.push(declaration.id); | ||
if (declaration.init) { | ||
const left = t.memberExpression(t.identifier("_$"), declaration.id); | ||
const assignment = t.assignmentExpression("=", left, declaration.init); | ||
acc.push(assignment); | ||
} | ||
} | ||
return acc; | ||
}, []); | ||
path.replaceWithMultiple(nodes); | ||
if (state.props.includes(scopeNode)) { | ||
path.skip(); | ||
path.replaceWith(t.memberExpression(getObject(t.cloneNode(state.propsParam)), node)); | ||
return true; | ||
} | ||
} | ||
}; | ||
return { | ||
Function(path) { | ||
var _a, _b, _c; | ||
let funcName; | ||
let returnValue = null; | ||
if (t.isVariableDeclarator(path.parent)) { | ||
funcName = t.isIdentifier(path.parent.id) ? path.parent.id.name : ""; | ||
} else { | ||
funcName = t.isFunctionDeclaration(path.node) ? (_b = (_a = path.node.id) == null ? void 0 : _a.name) != null ? _b : "" : ""; | ||
} | ||
if (!funcName[0] || funcName[0] !== funcName[0].toUpperCase()) { | ||
}; | ||
if (getStateWithDolarSuffix && /[^$]\$$/.test(path.node.name)) { | ||
const node = t.identifier(path.node.name.replace(/\$$/, "")); | ||
if (updateNode(node, true)) { | ||
return; | ||
} | ||
if (t.isBlockStatement(path.node.body)) { | ||
for (const node of path.node.body.body) { | ||
if (t.isReturnStatement(node)) { | ||
returnValue = (_c = node.argument) != null ? _c : null; | ||
if (path.parentPath.isMemberExpression({ property: path.node }) || path.parentPath.isOptionalMemberExpression({ property: path.node })) { | ||
const object = path.parentPath.node.object; | ||
if (t.isIdentifier(object)) { | ||
const scopeNode = path.scope.getBindingIdentifier(object.name); | ||
if (state.propsParam === scopeNode) { | ||
path.parentPath.replaceWith(t.memberExpression(t.memberExpression(object, t.identifier("$")), node)); | ||
return; | ||
} | ||
} | ||
} else { | ||
returnValue = path.node.body; | ||
} | ||
if (t.isJSXElement(returnValue) || t.isJSXFragment(returnValue)) { | ||
const param = path.node.params[0]; | ||
const props = []; | ||
let propsParam = t.identifier("_props"); | ||
const body = path.get("body"); | ||
if (body.isBlockStatement()) { | ||
const callExp = t.callExpression(t.identifier("_$$"), []); | ||
const declaration = t.variableDeclarator(t.identifier("_$"), callExp); | ||
body.unshiftContainer("body", t.variableDeclaration("const", [declaration])); | ||
(0, import_helper_annotate_as_pure.default)(callExp); | ||
} | ||
if (t.isIdentifier(param)) { | ||
propsParam = param; | ||
} | ||
if (t.isObjectPattern(param)) { | ||
for (const prop of param.properties) { | ||
if (t.isObjectProperty(prop) && t.isIdentifier(prop.value)) { | ||
props.push(prop.value); | ||
} | ||
if (t.isRestElement(prop) && t.isIdentifier(prop.argument)) { | ||
propsParam = prop.argument; | ||
} | ||
} | ||
updateNode(path.node); | ||
}, | ||
VariableDeclaration(path, state) { | ||
if (path.node.kind === "let") { | ||
const nodes = path.node.declarations.reduce((acc, declaration) => { | ||
if (t.isIdentifier(declaration.id)) { | ||
state.states.push(declaration.id); | ||
if (declaration.init) { | ||
const left = t.memberExpression(t.identifier("_$"), declaration.id); | ||
const assignment = t.assignmentExpression("=", left, declaration.init); | ||
acc.push(assignment); | ||
} | ||
} | ||
path.skip(); | ||
path.node.params[0] = propsParam; | ||
body.traverse(visitor2, { propsParam, props, states: [] }); | ||
} | ||
return acc; | ||
}, []); | ||
path.replaceWithMultiple(nodes); | ||
} | ||
} | ||
}; | ||
// src/transforms/jsx.transform.ts | ||
var t2 = __toESM(require("@babel/types")); | ||
// src/shared/tags.ts | ||
var selfClosingTags = [ | ||
"area", | ||
"base", | ||
"br", | ||
"col", | ||
"embed", | ||
"hr", | ||
"img", | ||
"input", | ||
"link", | ||
"meta", | ||
"param", | ||
"source", | ||
"track", | ||
"wbr" | ||
]; | ||
// src/transforms/jsx.transform.ts | ||
function transformJSX(path) { | ||
const result = { | ||
index: 0, | ||
isLastChild: false, | ||
parentIndex: 0, | ||
props: {}, | ||
template: "" | ||
}; | ||
transformElement(path, result, true); | ||
path.replaceWith(createVirtualNode(path, result)); | ||
} | ||
// src/jsx.transform.ts | ||
var import_helper_annotate_as_pure2 = __toESM(require_lib2()); | ||
var t2 = __toESM(require("@babel/types")); | ||
function visitor() { | ||
return { | ||
JSXAttribute(path) { | ||
if (t2.isJSXElement(path.node.value)) { | ||
path.node.value = t2.jsxExpressionContainer(path.node.value); | ||
function createVirtualNode(path, result) { | ||
var _a, _b; | ||
const state = path.state; | ||
let tmpl; | ||
if (path.isJSXElement() && isComponent(getTagName(path.node))) { | ||
tmpl = t2.identifier(getTagName(path.node)); | ||
} else { | ||
tmpl = path.scope.generateUidIdentifier("_tmpl"); | ||
const template = t2.callExpression(state.template, [ | ||
t2.stringLiteral(result.template) | ||
]); | ||
const declarator = t2.variableDeclarator(tmpl, template); | ||
state.tmplDeclaration.declarations.push(declarator); | ||
} | ||
const args = [tmpl, createProps(result.props)]; | ||
const key = (_b = result.props.key) != null ? _b : (_a = result.props[0]) == null ? void 0 : _a.key; | ||
if (key) { | ||
args.push(key); | ||
} | ||
return t2.callExpression(state.h, args); | ||
} | ||
function createProps(props) { | ||
const result = []; | ||
for (const prop in props) { | ||
let value = props[prop]; | ||
if (prop === "key") { | ||
continue; | ||
} | ||
if (Array.isArray(value)) { | ||
value = t2.arrayExpression(value); | ||
} | ||
if (typeof value === "object" && !t2.isNode(value)) { | ||
value = createProps(props[prop]); | ||
} | ||
if (typeof value !== "object") { | ||
if (typeof value === "string") { | ||
value = t2.stringLiteral(value); | ||
} | ||
}, | ||
JSXElement: { | ||
exit(path) { | ||
const callExpr = buildJSXElementCall(path); | ||
path.replaceWith(t2.inherits(callExpr, path.node)); | ||
if (typeof value === "number") { | ||
value = t2.numericLiteral(value); | ||
} | ||
}, | ||
JSXFragment: { | ||
exit(path) { | ||
const callExpr = buildJSXFragmentCall(path); | ||
if (callExpr) { | ||
path.replaceWith(t2.inherits(callExpr, path.node)); | ||
if (typeof value === "boolean") { | ||
value = t2.booleanLiteral(value); | ||
} | ||
if (typeof value === null) { | ||
value = t2.nullLiteral(); | ||
} | ||
if (typeof value === "undefined") { | ||
value = t2.identifier("undefined"); | ||
} | ||
} | ||
result.push(t2.objectProperty(t2.stringLiteral(prop), value)); | ||
} | ||
return t2.objectExpression(result); | ||
} | ||
function getAttrProps(path) { | ||
const props = {}; | ||
path.get("openingElement").get("attributes").forEach((attribute) => { | ||
if (attribute.isJSXAttribute()) { | ||
const name = getAttrName(attribute.node); | ||
const value = attribute.get("value"); | ||
if (!value.node) { | ||
props[name] = true; | ||
} else if (value.isStringLiteral()) { | ||
props[name] = value.node.value; | ||
} else { | ||
if (value.isJSXExpressionContainer()) { | ||
const expression = value.get("expression"); | ||
if (expression.isStringLiteral()) { | ||
props[name] = expression.node.value; | ||
} else if (expression.isNumericLiteral()) { | ||
props[name] = expression.node.value; | ||
} else if (expression.isJSXElement() || expression.isJSXFragment()) { | ||
transformJSX(expression); | ||
props[name] = expression.node; | ||
} else if (expression.isExpression()) { | ||
if (/^key|ref|on:.+|bind.*$/.test(name)) { | ||
props[name] = expression.node; | ||
} else { | ||
props[name] = t2.arrowFunctionExpression([], expression.node); | ||
} | ||
} | ||
} else if (value.isJSXElement() || value.isJSXFragment()) { | ||
transformJSX(value); | ||
props[name] = value.node; | ||
} | ||
} | ||
} else { | ||
throw new Error("Unsupported attribute type"); | ||
} | ||
}; | ||
}); | ||
return props; | ||
} | ||
function accumulateAttribute(array, attribute) { | ||
var _a; | ||
if (t2.isJSXSpreadAttribute(attribute.node)) { | ||
const arg = attribute.node.argument; | ||
if (t2.isObjectExpression(arg)) { | ||
array.push(...arg.properties); | ||
function transformElement(path, result, isRoot) { | ||
if (path.isJSXElement()) { | ||
const tagName = getTagName(path.node); | ||
const tagIsComponent = isComponent(tagName); | ||
const isSelfClosing = !tagIsComponent && selfClosingTags.includes(tagName); | ||
const props = getAttrProps(path); | ||
if (tagIsComponent) { | ||
if (isRoot) { | ||
result.props = props; | ||
const children = getChildren(path); | ||
if (children.length) { | ||
const childrenGetter = t2.arrowFunctionExpression([], children.length === 1 ? children[0] : t2.arrayExpression(children)); | ||
result.props.children = childrenGetter; | ||
} | ||
} else { | ||
transformJSX(path); | ||
replaceChild(path.node, result); | ||
} | ||
} else { | ||
array.push(t2.spreadElement(arg)); | ||
result.template += `<${tagName}`; | ||
if (tagName === "slot") { | ||
result.props[result.index] = props; | ||
} else { | ||
handleAttributes(props, result); | ||
} | ||
result.template += isSelfClosing ? "/>" : ">"; | ||
if (!isSelfClosing) { | ||
transformChildren(path, result); | ||
result.template += `</${tagName}>`; | ||
} | ||
} | ||
return array; | ||
} | ||
const value = convertAttributeValue(attribute.node.name.name !== "key" ? attribute.node.value || t2.booleanLiteral(true) : attribute.node.value); | ||
if (attribute.node.name.name === "key" && value === null) { | ||
throw attribute.buildCodeFrameError('Please provide an explicit key value. Using "key" as a shorthand for "key={true}" is not allowed.'); | ||
} | ||
if (t2.isStringLiteral(value) && !t2.isJSXExpressionContainer(attribute.node.value)) { | ||
value.value = value.value.replace(/\n\s+/g, " "); | ||
(_a = value.extra) == null ? true : delete _a.raw; | ||
} | ||
if (t2.isJSXNamespacedName(attribute.node.name)) { | ||
attribute.node.name = t2.stringLiteral(attribute.node.name.namespace.name + ":" + attribute.node.name.name.name); | ||
} else if (t2.isValidIdentifier(attribute.node.name.name, false)) { | ||
attribute.node.name.type = "Identifier"; | ||
} else { | ||
attribute.node.name = t2.stringLiteral(attribute.node.name.name); | ||
result.index--; | ||
transformChildren(path, result); | ||
} | ||
array.push(t2.inherits(t2.objectProperty(attribute.node.name, value), attribute.node)); | ||
return array; | ||
} | ||
function buildChildrenProperty(children) { | ||
let childrenNode; | ||
if (children.length === 1) { | ||
childrenNode = children[0]; | ||
} else if (children.length > 1) { | ||
childrenNode = t2.arrayExpression(children); | ||
} else { | ||
return void 0; | ||
} | ||
return t2.objectProperty(t2.identifier("children"), childrenNode); | ||
function getChildren(path) { | ||
return path.get("children").filter(isValidChild).map((child) => { | ||
if (child.isJSXElement() || child.isJSXFragment()) { | ||
transformJSX(child); | ||
} else if (child.isJSXExpressionContainer()) { | ||
child.replaceWith(child.get("expression")); | ||
} else if (child.isJSXText()) { | ||
child.replaceWith(t2.stringLiteral(child.node.value)); | ||
} else { | ||
throw new Error("Unsupported JSX child"); | ||
} | ||
return child.node; | ||
}); | ||
} | ||
function buildJSXElementCall(path) { | ||
const openingPath = path.get("openingElement"); | ||
const args = [getTag(openingPath)]; | ||
const attribsArray = []; | ||
const extracted = /* @__PURE__ */ Object.create(null); | ||
for (const attr of openingPath.get("attributes")) { | ||
if (attr.isJSXAttribute() && t2.isJSXIdentifier(attr.node.name)) { | ||
const { name } = attr.node.name; | ||
switch (name) { | ||
case "__source": | ||
case "__self": | ||
if (extracted[name]) | ||
throw sourceSelfError(path, name); | ||
case "key": { | ||
const keyValue = convertAttributeValue(attr.node.value); | ||
if (keyValue === null) { | ||
throw attr.buildCodeFrameError('Please provide an explicit key value. Using "key" as a shorthand for "key={true}" is not allowed.'); | ||
} | ||
extracted[name] = keyValue; | ||
break; | ||
} | ||
default: | ||
attribsArray.push(attr); | ||
function handleAttributes(props, result) { | ||
let klass = ""; | ||
let style = ""; | ||
for (const prop in props) { | ||
const value = props[prop]; | ||
if (prop === "slot") { | ||
continue; | ||
} | ||
if (prop === "class" && typeof value === "string") { | ||
klass += ` ${value}`; | ||
delete props[prop]; | ||
continue; | ||
} | ||
if (/^class:/.test(prop)) { | ||
if (value === true) { | ||
const name = prop.replace(/^class:/, ""); | ||
klass += ` ${name}`; | ||
delete props[prop]; | ||
continue; | ||
} | ||
} else { | ||
attribsArray.push(attr); | ||
if (value === false) { | ||
delete props[prop]; | ||
continue; | ||
} | ||
} | ||
if (prop === "style" && typeof value === "string") { | ||
style += `${value}${value.at(-1) === ";" ? "" : ";"}`; | ||
delete props[prop]; | ||
continue; | ||
} | ||
if (/^style:/.test(prop)) { | ||
if (typeof value === "string" || typeof value === "number") { | ||
const name = prop.replace(/^style:/, ""); | ||
style += `${name}:${value};`; | ||
delete props[prop]; | ||
continue; | ||
} | ||
} | ||
if (value === true) { | ||
result.template += ` ${prop}`; | ||
delete props[prop]; | ||
} | ||
if (value === false) { | ||
delete props[prop]; | ||
} | ||
if (typeof value === "string" || typeof value === "number") { | ||
result.template += ` ${prop}="${value}"`; | ||
delete props[prop]; | ||
} | ||
} | ||
const children = t2.react.buildChildren(path.node); | ||
let attribs; | ||
if (attribsArray.length || children.length) { | ||
attribs = buildJSXOpeningElementAttributes(attribsArray, children); | ||
} else { | ||
attribs = t2.objectExpression([]); | ||
if (Object.keys(props).length > 0) { | ||
result.props[result.index] = props; | ||
} | ||
args.push(attribs); | ||
if (extracted.key !== void 0) { | ||
args.push(extracted.key); | ||
klass = klass.trim(); | ||
style = style.trim(); | ||
if (klass) { | ||
result.template += ` class="${klass}"`; | ||
} | ||
return call("_jsx", args); | ||
if (style) { | ||
result.template += ` style="${style}"`; | ||
} | ||
} | ||
function buildJSXFragmentCall(path) { | ||
const args = [t2.nullLiteral()]; | ||
const children = t2.react.buildChildren(path.node); | ||
args.push(t2.objectExpression(children.length > 0 ? [ | ||
buildChildrenProperty(children) | ||
].filter((x) => x) : [])); | ||
return call("_jsx", args); | ||
function transformChildren(path, result) { | ||
const parentIndex = result.index; | ||
path.get("children").filter(isValidChild).forEach((child, i, arr) => { | ||
result.parentIndex = parentIndex; | ||
result.isLastChild = i === arr.length - 1; | ||
transformChild(child, result); | ||
}); | ||
} | ||
function buildJSXOpeningElementAttributes(attribs, children) { | ||
const props = attribs.reduce(accumulateAttribute, []); | ||
if ((children == null ? void 0 : children.length) > 0) { | ||
const childrenProp = buildChildrenProperty(children); | ||
if (childrenProp) { | ||
props.push(childrenProp); | ||
function transformChild(child, result) { | ||
result.index++; | ||
if (child.isJSXElement() || child.isJSXFragment()) { | ||
transformElement(child, result); | ||
} else if (child.isJSXExpressionContainer()) { | ||
const expression = child.get("expression"); | ||
if (expression.isStringLiteral() || expression.isNumericLiteral()) { | ||
result.template += expression.node.value; | ||
} else if (expression.isExpression()) { | ||
replaceChild(expression.node, result); | ||
} else { | ||
throw new Error("Unsupported JSX child"); | ||
} | ||
} else if (child.isJSXText()) { | ||
result.template += child.node.value; | ||
} else { | ||
throw new Error("Unsupported JSX child"); | ||
} | ||
return t2.objectExpression(props); | ||
} | ||
function call(name, args) { | ||
const node = t2.callExpression(t2.identifier(name), args); | ||
(0, import_helper_annotate_as_pure2.default)(node); | ||
return node; | ||
} | ||
function convertAttributeValue(node) { | ||
if (t2.isJSXExpressionContainer(node)) { | ||
return node.expression; | ||
function replaceChild(node, result) { | ||
var _a, _b, _c, _d, _e; | ||
if (result.isLastChild) { | ||
result.index--; | ||
} else { | ||
return node; | ||
result.template += "<!>"; | ||
} | ||
(_c = (_a = result.props)[_b = result.parentIndex]) != null ? _c : _a[_b] = {}; | ||
(_e = (_d = result.props[result.parentIndex]).children) != null ? _e : _d.children = []; | ||
result.props[result.parentIndex].children.push(t2.arrayExpression([ | ||
t2.arrowFunctionExpression([], node), | ||
result.isLastChild ? t2.nullLiteral() : t2.identifier(String(result.index)) | ||
])); | ||
} | ||
function convertJSXIdentifier(node, parent) { | ||
if (t2.isJSXIdentifier(node)) { | ||
if (node.name === "this" && t2.isReferenced(node, parent)) { | ||
return t2.thisExpression(); | ||
} else if (t2.isValidIdentifier(node.name, false)) { | ||
node.type = "Identifier"; | ||
} else { | ||
return t2.stringLiteral(node.name); | ||
} | ||
} else if (t2.isJSXMemberExpression(node)) { | ||
return t2.memberExpression(convertJSXIdentifier(node.object, node), convertJSXIdentifier(node.property, node)); | ||
} else if (t2.isJSXNamespacedName(node)) { | ||
return t2.stringLiteral(`${node.namespace.name}:${node.name.name}`); | ||
function getAttrName(attribute) { | ||
if (t2.isJSXIdentifier(attribute.name)) { | ||
return attribute.name.name; | ||
} | ||
return node; | ||
if (t2.isJSXNamespacedName(attribute.name)) { | ||
return `${attribute.name.namespace.name}:${attribute.name.name.name}`; | ||
} | ||
throw new Error("Unsupported attribute type"); | ||
} | ||
function getTag(openingPath) { | ||
const tagExpr = convertJSXIdentifier(openingPath.node.name, openingPath.node); | ||
let tagName; | ||
if (t2.isIdentifier(tagExpr)) { | ||
tagName = tagExpr.name; | ||
} else if (t2.isLiteral(tagExpr)) { | ||
tagName = tagExpr.value; | ||
function getTagName(node) { | ||
const jsxName = node.openingElement.name; | ||
return jsxElementNameToString(jsxName); | ||
} | ||
function isComponent(tagName) { | ||
return tagName[0] && tagName[0].toLowerCase() !== tagName[0] || tagName.includes(".") || /[^a-zA-Z]/.test(tagName[0]); | ||
} | ||
function isValidChild(path) { | ||
const regex = /^\s*$/; | ||
if (path.isStringLiteral() || path.isJSXText()) { | ||
return !regex.test(path.node.value); | ||
} | ||
if (t2.react.isCompatTag(tagName)) { | ||
return t2.stringLiteral(tagName); | ||
} else { | ||
return tagExpr; | ||
return true; | ||
} | ||
function jsxElementNameToString(node) { | ||
if (t2.isJSXMemberExpression(node)) { | ||
return `${jsxElementNameToString(node.object)}.${node.property.name}`; | ||
} | ||
if (t2.isJSXIdentifier(node) || t2.isIdentifier(node)) { | ||
return node.name; | ||
} | ||
return `${node.namespace.name}:${node.name.name}`; | ||
} | ||
function sourceSelfError(path, name) { | ||
return path.buildCodeFrameError(`Duplicate ${name} prop found.`); | ||
} | ||
var jsx_transform_default = visitor; | ||
// src/styled.transform.ts | ||
// src/transforms/program.transform.ts | ||
var t3 = __toESM(require("@babel/types")); | ||
var import_autoprefixer = __toESM(require("autoprefixer")); | ||
var import_postcss = __toESM(require("postcss")); | ||
var import_postcss_nested = __toESM(require("postcss-nested")); | ||
var import_postcss_prefix_selector = __toESM(require("postcss-prefix-selector")); | ||
function styled_transform_default() { | ||
function transformProgram(options) { | ||
return { | ||
TaggedTemplateExpression(path) { | ||
const tagPath = path.get("tag"); | ||
const isStyledComponent = tagPath.isCallExpression() && tagPath.get("callee").isIdentifier({ name: "styled" }); | ||
if (isStyledComponent) { | ||
const css = path.get("quasi").get("quasis").map((quasi) => { | ||
var _a; | ||
return (_a = quasi.node.value.cooked) != null ? _a : quasi.node.value.raw; | ||
}).join(`/*$$*/`); | ||
const styleId = generateStyleId(); | ||
const processedQuasis = processCss(css, styleId).split(`/*$$*/`).map((str) => t3.templateElement({ raw: str, cooked: str })); | ||
path.node.quasi.quasis = [ | ||
t3.templateElement({ raw: "", cooked: "" }), | ||
...processedQuasis | ||
]; | ||
path.node.quasi.expressions = [ | ||
t3.stringLiteral(styleId), | ||
...path.node.quasi.expressions | ||
]; | ||
path.skip(); | ||
enter(path) { | ||
path.state = { | ||
h: path.scope.generateUidIdentifier("h"), | ||
stateProxy: path.scope.generateUidIdentifier("$$"), | ||
tmplDeclaration: t3.variableDeclaration("const", []), | ||
template: path.scope.generateUidIdentifier("template") | ||
}; | ||
path.hub.file.metadata.config = options; | ||
}, | ||
exit(path) { | ||
const state = path.state; | ||
const imports = {}; | ||
if (state.tmplDeclaration.declarations.length > 0) { | ||
const index = path.node.body.reduce((index2, node, i) => t3.isImportDeclaration(node) || t3.isExportDeclaration(node) ? i : index2, 0); | ||
path.node.body.splice(index + 1, 0, state.tmplDeclaration); | ||
imports.template = state.template.name; | ||
} | ||
if (path.scope.hasBinding(state.h.name)) { | ||
imports.h = state.h.name; | ||
} | ||
if (path.scope.hasBinding(state.stateProxy.name)) { | ||
imports.$$ = state.stateProxy.name; | ||
} | ||
if (Object.keys(imports).length > 0) { | ||
path.node.body.unshift(createImport(imports, "estrela/internal")); | ||
} | ||
} | ||
}; | ||
} | ||
function createImport(props, from) { | ||
const imports = Object.entries(props).map(([prop, alias]) => { | ||
const local = t3.identifier(alias); | ||
const imported = t3.identifier(prop); | ||
return t3.importSpecifier(local, imported); | ||
}); | ||
const importSource = t3.stringLiteral(from); | ||
return t3.importDeclaration(imports, importSource); | ||
} | ||
// src/transforms/styles.transform.ts | ||
var t4 = __toESM(require("@babel/types")); | ||
var import_autoprefixer = __toESM(require("autoprefixer")); | ||
var import_postcss = __toESM(require("postcss")); | ||
var import_postcss_nested = __toESM(require("postcss-nested")); | ||
var import_postcss_prefix_selector = __toESM(require("postcss-prefix-selector")); | ||
function transformStyles(path) { | ||
const tag = path.get("tag"); | ||
const isStyledComponent = tag.isCallExpression() && tag.get("callee").isIdentifier({ name: "styled" }); | ||
if (isStyledComponent) { | ||
const idArg = tag.get("arguments")[1]; | ||
const styleId = (idArg == null ? void 0 : idArg.isStringLiteral()) ? idArg.node.value : generateStyleId(); | ||
tag.node.arguments[1] = t4.stringLiteral(styleId); | ||
const css = path.get("quasi").get("quasis").map((quasi) => { | ||
var _a; | ||
return (_a = quasi.node.value.cooked) != null ? _a : quasi.node.value.raw; | ||
}).join(`/*$$*/`); | ||
path.node.quasi.quasis = processCss(css, styleId).split(`/*$$*/`).map((str) => t4.templateElement({ raw: str, cooked: str })); | ||
} | ||
} | ||
function generateStyleId() { | ||
@@ -538,3 +683,7 @@ return Math.random().toString(36).slice(2, 7); | ||
var src_default = (0, import_helper_plugin_utils.declare)((api, options) => { | ||
const { autoDeclareStates = true } = options; | ||
const { | ||
autoDeclareStates = true, | ||
enableGetStateFunction = true, | ||
getStateWithDolarSuffix = true | ||
} = options != null ? options : {}; | ||
return { | ||
@@ -546,27 +695,15 @@ name: "babel-plugin-estrela", | ||
visitor: { | ||
Program(path) { | ||
const imports = createImport({ | ||
h: "_jsx", | ||
$$: "_$$" | ||
}, "estrela/internal"); | ||
path.unshiftContainer("body", imports); | ||
if (autoDeclareStates) { | ||
path.traverse(functional_transform_default(options)); | ||
} | ||
path.traverse(jsx_transform_default()); | ||
path.traverse(styled_transform_default()); | ||
} | ||
Program: transformProgram({ | ||
autoDeclareStates, | ||
enableGetStateFunction, | ||
getStateWithDolarSuffix | ||
}), | ||
Function: transformComponent, | ||
JSXElement: transformJSX, | ||
JSXFragment: transformJSX, | ||
TaggedTemplateExpression: transformStyles | ||
} | ||
}; | ||
}); | ||
function createImport(props, from) { | ||
const imports = Object.entries(props).map(([prop, alias]) => { | ||
const local = t4.identifier(alias); | ||
const imported = t4.identifier(prop); | ||
return t4.importSpecifier(local, imported); | ||
}); | ||
const importSource = t4.stringLiteral(from); | ||
return t4.importDeclaration(imports, importSource); | ||
} | ||
// Annotate the CommonJS export names for ESM import in node: | ||
0 && (module.exports = {}); |
{ | ||
"name": "babel-plugin-estrela", | ||
"description": "A babel plugin to process estrela jsx/tsx files", | ||
"description": "A babel plugin to preprocess estrela jsx/tsx files", | ||
"author": "Eduardo Rosostolato", | ||
"version": "0.3.1", | ||
"version": "0.10.0", | ||
"license": "MIT", | ||
@@ -7,0 +7,0 @@ "main": "dist/index.js", |
@@ -22,22 +22,32 @@ # babel-plugin-estrela | ||
onDestroy(() => clearInterval(interval)); | ||
// return JSX element. | ||
return <div>Count is { count * 2 }</div> | ||
} | ||
return <div>Count is {count * 2}</div>; | ||
}; | ||
``` | ||
It will convert the code above to: | ||
It will transpile the code above to: | ||
```js | ||
import { h as _jsx, $$ as _$$ } from 'estrela/internal'; | ||
import { state, onDestroy } from 'estrela'; | ||
import { template as _template, h as _h, $$ as _$$ } from "estrela/internal"; | ||
import { render } from "estrela"; | ||
import { onDestroy } from "estrela"; | ||
const App = () => { | ||
const _tmpl = _template("<div>Count is </div>"); | ||
const App = _props => { | ||
const _$ = /*#__PURE__*/_$$(); | ||
_$.count = 0; | ||
_$.count = 0 | ||
_$.$.count.subscribe(console.log); | ||
setInterval(() => _$.count++, 1000); | ||
const interval = setInterval(() => _$.count++, 1e3); | ||
onDestroy(() => clearInterval(interval)); | ||
return /*#__PURE__*/h('div', null, 'Count is ', [() => _$.count * 2]); | ||
} | ||
return _h(_tmpl, { | ||
"0": { | ||
"children": [[() => _$.count * 2, null]] | ||
} | ||
}); | ||
}; | ||
``` | ||
@@ -44,0 +54,0 @@ |
import { declare } from '@babel/helper-plugin-utils'; | ||
import * as t from '@babel/types'; | ||
import functionalTransform from './functional.transform'; | ||
import jsxTransform from './jsx.transform'; | ||
import { Options } from './options'; | ||
import styledTransform from './styled.transform'; | ||
import { transformComponent } from './transforms/component.transform'; | ||
import { transformJSX } from './transforms/jsx.transform'; | ||
import { transformProgram } from './transforms/program.transform'; | ||
import { transformStyles } from './transforms/styles.transform'; | ||
import { Options } from './types'; | ||
export * from './types'; | ||
export type { Options } from './options'; | ||
export default declare((api, options: Options) => { | ||
const { autoDeclareStates = true } = options; | ||
const { | ||
autoDeclareStates = true, | ||
enableGetStateFunction = true, | ||
getStateWithDolarSuffix = true, | ||
} = options ?? {}; | ||
return { | ||
@@ -19,29 +21,13 @@ name: 'babel-plugin-estrela', | ||
visitor: { | ||
Program(path) { | ||
const imports = createImport( | ||
{ | ||
h: '_jsx', | ||
$$: '_$$', | ||
}, | ||
'estrela/internal' | ||
); | ||
path.unshiftContainer('body', imports); | ||
if (autoDeclareStates) { | ||
path.traverse(functionalTransform(options)); | ||
} | ||
path.traverse(jsxTransform()); | ||
path.traverse(styledTransform()); | ||
}, | ||
Program: transformProgram({ | ||
autoDeclareStates, | ||
enableGetStateFunction, | ||
getStateWithDolarSuffix, | ||
}), | ||
Function: transformComponent, | ||
JSXElement: transformJSX, | ||
JSXFragment: transformJSX, | ||
TaggedTemplateExpression: transformStyles, | ||
}, | ||
}; | ||
}); | ||
function createImport(props: Record<string, string>, from: string) { | ||
const imports = Object.entries(props).map(([prop, alias]) => { | ||
const local = t.identifier(alias); | ||
const imported = t.identifier(prop); | ||
return t.importSpecifier(local, imported); | ||
}); | ||
const importSource = t.stringLiteral(from); | ||
return t.importDeclaration(imports, importSource); | ||
} |
@@ -13,3 +13,6 @@ { | ||
"src" | ||
] | ||
], | ||
"exclude": [ | ||
"node_modules" | ||
], | ||
} |
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
Major refactor
Supply chain riskPackage has recently undergone a major refactor. It may be unstable or indicate significant internal changes. Use caution when updating to versions that include significant changes.
Found 1 instance in 1 package
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
53136
22
1509
71
1