babel-plugin-transform-react-pug
Advanced tools
Comparing version 3.1.8 to 3.2.0
@@ -38,3 +38,3 @@ 'use strict'; | ||
var Context = function () { | ||
function Context(definesScope, key, parent, file, path) { | ||
function Context(definesScope, key, parent, file, path, interpolations) { | ||
(0, _classCallCheck3.default)(this, Context); | ||
@@ -52,2 +52,3 @@ this._variables = new _map2.default(); | ||
this.path = path; | ||
this._interpolations = interpolations; | ||
} | ||
@@ -98,8 +99,11 @@ | ||
var variable = this._variables.get(name); | ||
if (variable) { | ||
return variable; | ||
} | ||
if (this._parent) { | ||
return this._parent.getVariable(name); | ||
} | ||
// TODO: maybe actually verify existance/non-const in parent scope? | ||
@@ -114,3 +118,5 @@ return null; | ||
} | ||
var oldVariable = this._variables.get(name); | ||
if (oldVariable) { | ||
@@ -123,2 +129,3 @@ if (oldVariable.kind !== 'var' || kind !== 'var') { | ||
} | ||
var variable = { | ||
@@ -128,2 +135,3 @@ kind: kind, | ||
}; | ||
this.variablesToDeclare.push(variable.id); | ||
@@ -143,6 +151,27 @@ this._variables.set(name, variable); | ||
} | ||
/** | ||
* Check whether interpolations exist for the context, if not, | ||
* recursively check the parent context for the interpolation. | ||
* @param { String } reference - The interpolation reference | ||
* @returns { ?Expression } The interpolation or nothing. | ||
*/ | ||
}, { | ||
key: 'getInterpolationByRef', | ||
value: function getInterpolationByRef(reference) { | ||
var interpolation = null; | ||
if (this._interpolations && (interpolation = this._interpolations.get(reference))) { | ||
return interpolation; | ||
} else if (this._parent) { | ||
return this._parent.getInterpolationByRef(reference); | ||
} | ||
return this.getInterpolationByRef(reference); | ||
} | ||
}], [{ | ||
key: 'create', | ||
value: function create(file, path) { | ||
return new Context(true, new _blockKey.BaseKey(), null, file, path); | ||
value: function create(file, path, interpolations) { | ||
return new Context(true, new _blockKey.BaseKey(), null, file, path, interpolations); | ||
} | ||
@@ -149,0 +178,0 @@ }]); |
@@ -10,3 +10,5 @@ 'use strict'; | ||
(0, _babelTypes.setBabelTypes)(t); | ||
function isReactPugReference(node) { | ||
@@ -16,16 +18,37 @@ // TODO: do this better | ||
} | ||
return { | ||
visitor: { | ||
TaggedTemplateExpression: function TaggedTemplateExpression(path) { | ||
if (isReactPugReference(path.node.tag) && path.node.quasi.expressions.length === 0 && path.node.quasi.quasis.length === 1) { | ||
var src = path.node.quasi.quasis[0].value.raw; | ||
var minIndent = src.split('\n').reduce(function (minIndent, line) { | ||
var node = path.node; | ||
var _node$quasi = node.quasi, | ||
quasis = _node$quasi.quasis, | ||
expressions = _node$quasi.expressions; | ||
if (isReactPugReference(node.tag) && quasis.length >= 1) { | ||
var template = void 0, | ||
interpolationRef = void 0; | ||
if (expressions.length) { | ||
var interpolatedTpl = (0, _interpolation.getInterpolatedTemplate)(quasis, expressions); | ||
template = interpolatedTpl.template; | ||
interpolationRef = interpolatedTpl.interpolationRef; | ||
} else { | ||
template = quasis[0].value.raw; | ||
} | ||
var src = template.split('\n'); | ||
var minIndent = src.reduce(function (minIndent, line) { | ||
return line.trim().length ? Math.min(/^ */.exec(line)[0].length, minIndent) : minIndent; | ||
}, Infinity); | ||
src = src.split('\n').map(function (line) { | ||
src = src.map(function (line) { | ||
return line.substr(minIndent); | ||
}).join('\n'); | ||
var ast = (0, _parsePug2.default)(src); | ||
var context = _context2.default.create(this.file, path); | ||
var context = _context2.default.create(this.file, path, interpolationRef); | ||
var transformed = ast.nodes.map(function (node) { | ||
@@ -35,6 +58,7 @@ return (0, _visitors.visitExpression)(node, context); | ||
var expression = transformed.length === 1 ? transformed[0] : t.arrayExpression(transformed); | ||
if (context.variablesToDeclare.length) {} | ||
context.variablesToDeclare.forEach(function (id) { | ||
path.scope.push({ kind: 'let', id: id }); | ||
}); | ||
path.replaceWith(expression); | ||
@@ -59,4 +83,6 @@ } | ||
var _interpolation = require('./utils/interpolation'); | ||
var _babelTypes = require('./babel-types'); | ||
function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } |
@@ -20,18 +20,45 @@ 'use strict'; | ||
var _interpolation = require('./interpolation'); | ||
function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } | ||
function parseExpression(src, context) { | ||
if ((0, _interpolation.getInterpolationRefs)(src)) { | ||
var matched = src.split(_interpolation.INTERPOLATION_REFERENCE_REGEX); | ||
var isInterpolation = matched.every(function (text) { | ||
return text === ''; | ||
}); | ||
if (!isInterpolation) { | ||
var errMsg = matched.length === 1 ? 'Interpolation does not exist' : 'Only an interpolation can be specified. You may want to remove ' + matched.join(' ') + '.'; | ||
throw context.error('INVALID_EXPRESSION', errMsg); | ||
} | ||
var interpolation = context.getInterpolationByRef(src); | ||
if (interpolation == null) { | ||
throw context.error('INVALID_EXPRESSION', 'Interpolation does not exist for ' + src); | ||
} | ||
return interpolation; | ||
} | ||
var val = (0, _parse2.default)('x = (' + src + ');', context); | ||
if (val.length !== 1) { | ||
var err = context.error('INVALID_EXPRESSION', 'There was an error parsing the expression "' + src + '".'); | ||
var err = context.error('INVALID_EXPRESSION', 'There was an error parsing the expression ' + src + '.'); | ||
throw err; | ||
} | ||
var expressionStatement = _babelTypes2.default.asExpressionStatement(val[0]); | ||
var assignmentExpression = expressionStatement && _babelTypes2.default.asAssignmentExpression(expressionStatement.expression); | ||
if (!assignmentExpression) { | ||
var _err = context.error('INVALID_EXPRESSION', 'There was an error parsing the expression "' + src + '".'); | ||
var _err = context.error('INVALID_EXPRESSION', 'There was an error parsing the expression ' + src + '.'); | ||
throw _err; | ||
} | ||
(0, _addLocToAst2.default)(assignmentExpression); | ||
return assignmentExpression.right; | ||
} |
@@ -17,4 +17,14 @@ 'use strict'; | ||
var _interpolation = require('../utils/interpolation'); | ||
function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } | ||
/** | ||
* Get children nodes from the node, passing the node's | ||
* context to the children and generating JSX values. | ||
* @param {Object} node - The node | ||
* @param {Context} context - The context to apply to the children | ||
* nodes | ||
* @returns {Array<JSXValue>} | ||
*/ | ||
function getChildren(node, context) { | ||
@@ -28,2 +38,11 @@ return context.noKey(function (childContext) { | ||
/** | ||
* Iterate through the node's attributes and convert | ||
* them into JSX attributes. | ||
* @param {Object} node - The node | ||
* @param {Context} context - The context | ||
* @returns {Array<JSXAttribute|JSXSpreadAttribute>} | ||
*/ | ||
function getAttributes(node, context) { | ||
@@ -36,5 +55,7 @@ var classes = []; | ||
if (/\.\.\./.test(name) && val === true) { | ||
return _babelTypes2.default.jSXSpreadAttribute((0, _parseExpression2.default)(name.substr(3), context)); | ||
} | ||
switch (name) { | ||
@@ -51,6 +72,13 @@ case 'for': | ||
} | ||
var expr = (0, _parseExpression2.default)(val === true ? 'true' : val, context); | ||
if (!mustEscape && (!_babelTypes2.default.isStringLiteral(expr) || /(\<\>\&)/.test(val))) { | ||
throw new Error('Unescaped attributes are not supported in react-pug'); | ||
} | ||
if (expr == null) { | ||
return null; | ||
} | ||
if (name === 'className') { | ||
@@ -60,6 +88,9 @@ classes.push(expr); | ||
} | ||
var jsxValue = _babelTypes2.default.asStringLiteral(expr) || _babelTypes2.default.asJSXElement(expr) || _babelTypes2.default.jSXExpressionContainer(expr); | ||
if (/\.\.\./.test(name)) { | ||
throw new Error('spread attributes must not have a value'); | ||
} | ||
return _babelTypes2.default.jSXAttribute(_babelTypes2.default.jSXIdentifier(name), jsxValue); | ||
@@ -78,25 +109,100 @@ }).filter(Boolean); | ||
function build(node, context) { | ||
var name = _babelTypes2.default.jSXIdentifier(node.name); | ||
/** | ||
* Retrieve attributes and children of the passed node. | ||
* @param {Object} node - The node | ||
* @param {Context} context - The context | ||
* @returns {Object} Contains the attributes and children | ||
* of the node. | ||
*/ | ||
function getAttributesAndChildren(node, context) { | ||
var children = getChildren(node, context); | ||
if (node.attributeBlocks.length) { | ||
throw new Error('Attribute blocks are not yet supported in react-pug'); | ||
} | ||
var attrs = getAttributes(node, context); | ||
context.key.handleAttributes(attrs); | ||
var open = _babelTypes2.default.jSXOpeningElement(name, attrs, // Array<JSXAttribute | JSXSpreadAttribute> | ||
children.length === 0); | ||
return { attrs: attrs, children: children }; | ||
} | ||
var close = children.length === 0 ? null : _babelTypes2.default.jSXClosingElement(name); | ||
/** | ||
* Generate a JSX element. | ||
* @param { string } name - The name of the JSX element | ||
* @param { Array<JSXAttribute|JSXSpreadAttribute> } attrs - | ||
* The attributes for the JSX element | ||
* @param { Array<JSXValue> } children - The children for | ||
* the JSX element | ||
* @returns { JSXElement } The JSX element. | ||
*/ | ||
function buildJSXElement(name, attrs, children) { | ||
var tagName = _babelTypes2.default.jSXIdentifier(name); | ||
var noChildren = children.length === 0; | ||
return _babelTypes2.default.jSXElement(open, close, children, children.length === 0); | ||
var open = _babelTypes2.default.jSXOpeningElement(tagName, attrs, // Array<JSXAttribute | JSXSpreadAttribute> | ||
noChildren); | ||
var close = noChildren ? null : _babelTypes2.default.jSXClosingElement(tagName); | ||
return _babelTypes2.default.jSXElement(open, close, children, noChildren); | ||
} | ||
/** | ||
* Check whether an interpolation exists, if so, check whether | ||
* the interpolation is a react component and return either | ||
* the component as a JSX element or the interpolation. | ||
* @param {string} name - The interpolation reference | ||
* @param {Context} context - The current context to retrieve | ||
* the interpolation from | ||
* @param {Array<JSXValue>} children - Whether the element has | ||
* attributes or children | ||
* @returns {?Object} The context's interpolation or a JSX element. | ||
*/ | ||
function getInterpolationByContext(name, context, attrs, children) { | ||
if (!(0, _interpolation.getInterpolationRefs)(name)) { | ||
return null; | ||
} | ||
var interpolation = context.getInterpolationByRef(name); | ||
var isReactComponent = _babelTypes2.default.isIdentifier(interpolation) && interpolation.name.charAt(0) === interpolation.name.charAt(0).toUpperCase(); | ||
if (attrs.length || children.length) { | ||
if (isReactComponent) { | ||
return buildJSXElement(interpolation.name, attrs, children); | ||
} else { | ||
throw context.error('INVALID_EXPRESSION', 'Only components can have children and attributes'); | ||
} | ||
} | ||
return interpolation; | ||
} | ||
var TagVisitor = { | ||
jsx: function jsx(node, context) { | ||
return build(node, context); | ||
var _getAttributesAndChil = getAttributesAndChildren(node, context), | ||
attrs = _getAttributesAndChil.attrs, | ||
children = _getAttributesAndChil.children; | ||
var interpolation = getInterpolationByContext(node.name, context, attrs, children); | ||
if (interpolation != null) { | ||
return _babelTypes2.default.asJSXElement(interpolation) || _babelTypes2.default.jSXExpressionContainer(interpolation); | ||
} | ||
return buildJSXElement(node.name, attrs, children); | ||
}, | ||
expression: function expression(node, context) { | ||
return build(node, context); | ||
var _getAttributesAndChil2 = getAttributesAndChildren(node, context), | ||
attrs = _getAttributesAndChil2.attrs, | ||
children = _getAttributesAndChil2.children; | ||
var interpolation = getInterpolationByContext(node.name, context, attrs, children); | ||
if (interpolation != null) { | ||
return interpolation; | ||
} | ||
return buildJSXElement(node.name, attrs, children); | ||
} | ||
@@ -103,0 +209,0 @@ }; |
@@ -11,10 +11,66 @@ 'use strict'; | ||
var _interpolation = require('../utils/interpolation'); | ||
function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } | ||
/** | ||
* Find interpolation references in the text | ||
* and interweave the text with interpolations. | ||
* @param {string} value - The value to interpolate | ||
* @param {Array<string>} refs - The array of references | ||
* @param {Context} context - The context of the expression. | ||
* @returns {Expression} The interpolation or an array containing | ||
* text and interpolations. | ||
*/ | ||
function buildInterpolation(value, refs, context) { | ||
var splitText = value.split(_interpolation.INTERPOLATION_REFERENCE_REGEX); | ||
if (refs.length === 1 && splitText.every(function (text) { | ||
return text === ''; | ||
})) { | ||
var ref = context.getInterpolationByRef(refs[0]); | ||
return ref || _babelTypes2.default.nullLiteral(); | ||
} | ||
var textArr = splitText.reduce(function (arr, value, index) { | ||
var valueArr = value ? [_babelTypes2.default.stringLiteral(value)] : []; | ||
var interpolation = refs[index] ? context.getInterpolationByRef(refs[index]) : null; | ||
if (interpolation) { | ||
valueArr.push(interpolation); | ||
} | ||
return arr.concat(valueArr); | ||
}, []); | ||
return _babelTypes2.default.callExpression(_babelTypes2.default.memberExpression(_babelTypes2.default.arrayExpression(textArr), _babelTypes2.default.identifier('join')), [_babelTypes2.default.stringLiteral('')]); | ||
} | ||
var TextVisitor = { | ||
jsx: function jsx(node, context) { | ||
return _babelTypes2.default.jSXText(node.val); | ||
jsx: function jsx(_ref, context) { | ||
var val = _ref.val; | ||
var refs = (0, _interpolation.getInterpolationRefs)(val); | ||
if (refs) { | ||
var expr = buildInterpolation(val, refs, context); | ||
return _babelTypes2.default.jSXExpressionContainer(expr); | ||
} | ||
return _babelTypes2.default.jSXText(val); | ||
}, | ||
expression: function expression(node, context) { | ||
return _babelTypes2.default.stringLiteral(node.val); | ||
expression: function expression(_ref2, context) { | ||
var val = _ref2.val; | ||
var refs = (0, _interpolation.getInterpolationRefs)(val); | ||
if (refs) { | ||
return buildInterpolation(val, refs, context); | ||
} | ||
return _babelTypes2.default.stringLiteral(val); | ||
} | ||
@@ -21,0 +77,0 @@ }; |
{ | ||
"name": "babel-plugin-transform-react-pug", | ||
"version": "3.1.8", | ||
"version": "3.2.0", | ||
"description": "A plugin for transpiling pug templates to jsx", | ||
@@ -39,5 +39,17 @@ "main": "lib/index.js", | ||
"lint": "eslint src", | ||
"test": "jest ./src/**/*.test.js --coverage", | ||
"watch": "jest ./src/**/*.test.js --coverage --watch" | ||
"test": "jest --coverage", | ||
"watch": "jest --coverage --watch" | ||
}, | ||
"jest": { | ||
"testEnvironment": "node", | ||
"testRegex": "src/__tests__/.*(\\.test.js)$", | ||
"collectCoverageFrom": [ | ||
"src/**/*.js", | ||
"!src/__tests__/*.js", | ||
"!src/babel-types.js" | ||
], | ||
"snapshotSerializers": [ | ||
"./scripts/filename-serializer.js" | ||
] | ||
}, | ||
"repository": { | ||
@@ -44,0 +56,0 @@ "type": "git", |
@@ -109,1 +109,19 @@ # babel-plugin-transform-react-pug | ||
``` | ||
### Example 4 - Using interpolation in your Pug Component | ||
If you'd prefer to use interpolation, you can! This is possible by using `${ }` within your template. | ||
This lets you benefit from syntax highlighting and linting! | ||
```js | ||
import React from 'react'; | ||
const List = (props) => { | ||
return pug` | ||
ul.list(className=${ props.modifier }) | ||
${ props.items.map((item, index) => pug`li.list__item(key=${ index }) ${ item }` ) } | ||
` | ||
} | ||
``` | ||
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
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
236424
39
4986
127