UNPKG

views-morph

Version:
684 lines (588 loc) 17.7 kB
'use strict'; function _interopDefault (ex) { return (ex && (typeof ex === 'object') && 'default' in ex) ? ex['default'] : ex; } var cssProperties = _interopDefault(require('css-properties')); var toCamelCase = _interopDefault(require('to-camel-case')); var slicedToArray = function () { function sliceIterator(arr, i) { var _arr = []; var _n = true; var _d = false; var _e = undefined; try { for (var _i = arr[Symbol.iterator](), _s; !(_n = (_s = _i.next()).done); _n = true) { _arr.push(_s.value); if (i && _arr.length === i) break; } } catch (err) { _d = true; _e = err; } finally { try { if (!_n && _i["return"]) _i["return"](); } finally { if (_d) throw _e; } } return _arr; } return function (arr, i) { if (Array.isArray(arr)) { return arr; } else if (Symbol.iterator in Object(arr)) { return sliceIterator(arr, i); } else { throw new TypeError("Invalid attempt to destructure non-iterable instance"); } }; }(); var toArray = function (arr) { return Array.isArray(arr) ? arr : Array.from(arr); }; var BASIC = /^(CaptureEmail|CaptureFile|CaptureNumber|CapturePhone|CaptureSecure|CaptureText|CaptureTextArea|G|Horizontal|Image|List|Svg|SvgCircle|SvgEllipse|SvgDefs|SvgGroup|SvgLinearGradient|SvgRadialGradient|SvgLine|SvgPath|SvgPolygon|SvgPolyline|SvgRect|SvgSymbol|SvgText|SvgUse|SvgStop|Text|Vertical|FakeProps)$/i; var BLOCK = /^([A-Z][a-zA-Z0-9]*)(\s+([A-Z][a-zA-Z0-9]*))?$/; var BOOL = /^(false|true)$/i; var CAPTURE = /^(CaptureEmail|CaptureFile|CaptureNumber|CapturePhone|CaptureSecure|CaptureText|CaptureTextArea)$/i; var CODE_EXPLICIT = /^{.+}$/; var CODE_IMPLICIT = /props\./; var CODE_DEFAULT = /^props$/; var COMMENT = /^#(.+)$/; var INTERPOLATED_EXPRESSION = /\${.+}/; var FLOAT = /^[0-9]+\.[0-9]+$/; var FONTABLE = /^(CaptureEmail|CaptureNumber|CapturePhone|CaptureSecure|CaptureText|CaptureTextArea|Text)$/; var INT = /^[0-9]+$/; var NOT_GROUP = /^(Image|FakeProps|Text|Proxy|SvgCircle|SvgEllipse|SvgLine|SvgPath|SvgPolygon|SvgPolyline|SvgRect|SvgText|SvgStop)$/i; var PROP = /^([a-z][a-zA-Z0-9]*)(\s+(.+))?$/; var STYLE = new RegExp('^(' + cssProperties.map(toCamelCase).join('|') + '|pointerEvents|clipPath|appRegion|userSelect|hyphens|overflowWrap)$'); var TEMPLATE_LITERAL = /^`.+`$/; var TRUE = /^true$/i; var USER_COMMENT = /^##(.*)$/; var is = function is(thing, line) { return thing.test(line); }; var isBasic = function isBasic(line) { return is(BASIC, line); }; var isBlock = function isBlock(line) { return is(BLOCK, line); }; var isBool = function isBool(line) { return is(BOOL, line); }; var isCapture = function isCapture(line) { return is(CAPTURE, line); }; var isCode = function isCode(line) { return is(CODE_EXPLICIT, line) || is(CODE_IMPLICIT, line) || isCodeDefault(line); }; var isCodeDefault = function isCodeDefault(line) { return is(CODE_DEFAULT, line); }; // TODO var isCodeInvalid = function isCodeInvalid(line) { return getCodeData(line).find(function (l) { return (/\. /.test(l) || // props. x / \./.test(l) || // props . / \[/.test(l) || // props[ /\]/.test(l) ); } // props] ); }; var isComment = function isComment(line) { return is(COMMENT, line); }; var isEmptyText = function isEmptyText(line) { return line === ''; }; var isEnd = function isEnd(line) { return line === ''; }; var isInterpolatedExpression = function isInterpolatedExpression(line) { return is(INTERPOLATED_EXPRESSION, line); }; var isFloat = function isFloat(line) { return is(FLOAT, line); }; var isFontable = function isFontable(line) { return is(FONTABLE, line); }; var isGroup = function isGroup(line) { return !is(NOT_GROUP, line) && !isCapture(line); }; var isList = function isList(line) { return line === 'List'; }; var isInt = function isInt(line) { return is(INT, line); }; var isProp = function isProp(line) { return is(PROP, line); }; var isTemplateLiteral = function isTemplateLiteral(line) { return is(TEMPLATE_LITERAL, line); }; var isStyle = function isStyle(line) { return is(STYLE, line); }; var isTrue = function isTrue(line) { return is(TRUE, line); }; var isUserComment = function isUserComment(line) { return is(USER_COMMENT, line); }; var get = function get(regex, line) { return line.match(regex); }; var getBlock = function getBlock(line) { // eslint-disable-next-line var _get = get(BLOCK, line), _get2 = slicedToArray(_get, 4), _ = _get2[0], is = _get2[1], _1 = _get2[2], block = _get2[3]; return { block: block || is, is: block ? is : null }; }; var getCodeData = function getCodeData(line) { return line.replace(/^{/, '').replace(/}$/, '').split(' ').filter(function (l) { return (/[.[]/.test(l) ); }); }; var getComment = function getComment(line) { try { return get(COMMENT, line).slice(1); } catch (err) { return ''; } }; var getMainFont = function getMainFont(line) { return line ? line.split(',')[0].replace(/['"]/g, '') : ''; }; var getProp = function getProp(line) { // eslint-disable-next-line var _get3 = get(PROP, line), _get4 = slicedToArray(_get3, 4), _ = _get4[0], prop = _get4[1], _1 = _get4[2], _get4$ = _get4[3], value = _get4$ === undefined ? '' : _get4$; if (isCodeDefault(value)) { value = 'props.' + prop; } return [prop, value]; }; var getValue = function getValue(value) { if (isFloat(value)) { return parseFloat(value, 10); } else if (isInt(value)) { return parseInt(value, 10); } else if (isEmptyText(value)) { return ''; } else if (isBool(value)) { return isTrue(value); } else if (isInterpolatedExpression(value)) { return fixBackticks(value); } else { return value; } }; var fixBackticks = function fixBackticks(value) { return isTemplateLiteral(value) ? value : '`' + value + '`'; }; var warn = function warn(message, block) { if (!Array.isArray(block.warnings)) { block.warnings = []; } block.warnings.push(message); }; var SYSTEM_SCOPES = ['active', // TODO disabled should be a prop instead I think 'disabled', 'focus', 'hover', 'placeholder', 'print']; var isSystemScope = function isSystemScope(name) { return SYSTEM_SCOPES.includes(name); }; var getLoc = (function (line, scolumn, ecolumn) { return { start: { line: line, column: scolumn }, end: { line: line, column: ecolumn } }; }); var getMeta = (function (value, line, startLine) { if (!isCode(value)) return []; return getCodeData(value).map(function (value) { return { tag: 'code', value: value }; }); }); var isNumber = { width: true, maxWidth: true, minWidth: true, height: true, maxHeight: true, minHeight: true, margin: true, marginBottom: true, marginLeft: true, marginRight: true, marginTop: true, padding: true, paddingBottom: true, paddingLeft: true, paddingRight: true, paddingTop: true, bottom: true, left: true, right: true, top: true, outline: true, opacity: true, zIndex: true, scale: true, translateX: true, translateY: true, borderBottomLeftRadius: true, borderBottomRightRadius: true, borderBottomWidth: true, borderLeftWidth: true, borderRadius: true, borderRightWidth: true, borderTopLeftRadius: true, borderTopRightRadius: true, borderTopWidth: true, borderWidth: true, fontSize: true, fontWeight: true, letterSpacing: true, lineHeight: true, wordSpacing: true }; var isActionable = function isActionable(item) { return ( // (item.type === 'Horizontal' || // item.type === 'Vertical' || // /^Capture/.test(item.type)) && item.name !== 'onWhen' && /^on[A-Z]/.test(item.name) ); }; var extractPropsAndItems = function extractPropsAndItems(item) { var props = []; var regex = /(props|item)\.([a-zA-Z][a-zA-Z0-9.]*[a-zA-Z0-9]+)(.)?/g; var matches = regex.exec(item.value); while (matches !== null) { // This is necessary to avoid infinite loops with zero-width matches if (matches.index === regex.lastIndex) { regex.lastIndex++; } var isExplicitFunctionCall = matches[3] === '('; props.push({ isItem: matches[1] === 'item', path: matches[2], type: isActionable(item) || isExplicitFunctionCall ? 'function' : isNumber[item.name] ? 'number' : 'string' }); matches = regex.exec(item.value); } return props; }; var isList$1 = function isList(item) { return item.type === 'List'; }; var isListFrom = function isListFrom(item) { return item.name === 'from' && isList$1(item); }; var getPropTypes = (function (list) { var flatProps = {}; var listFromPath = void 0; list.forEach(function (item, index) { extractPropsAndItems(item).forEach(function (propOrItem) { if (isListFrom(item)) { // store the list prop path for later listFromPath = propOrItem.path; } else if (isList$1(item) && propOrItem.path.endsWith('.length')) { // skip array length checks in list return; } if (propOrItem.isItem) { // if we didn't find a list, don't include item props if (!listFromPath) return; propOrItem.path = listFromPath + '.' + propOrItem.path; } flatProps[propOrItem.path] = propOrItem; }); }); var props = {}; Object.keys(flatProps).sort().forEach(function (key) { var prop = flatProps[key]; if (key.includes('.')) { var _key$split = key.split('.'), _key$split2 = toArray(_key$split), main = _key$split2[0], rest = _key$split2.slice(1); var ref = props[main]; if (!ref || typeof ref === 'string') { ref = props[main] = { type: prop.isItem ? 'array' : 'object', shape: {} }; } // TODO support any nesting rest.forEach(function (part) { ref.shape[part] = prop.type; }); } else { props[key] = prop.type; } }); return props; }); var getTags = (function (prop, value) { var tags = {}; if (isCode(value)) tags.code = isCodeInvalid(value) ? 'invalid' : true; if (isStyle(prop)) tags.style = true; return tags; }); var index = (function (rtext) { var skipComments = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : true; // convert crlf to lf var text = rtext.replace(/\r\n/g, '\n'); var fonts = []; var lines = text.split('\n').map(function (line) { return line.trim(); }); var props = []; var stack = []; var views = []; var lastCapture = void 0; var getChildrenProxyMap = function getChildrenProxyMap(block) { var childrenProxyMap = {}; block.children.forEach(function (child, i) { var maybeName = child.is || child.name; var name = maybeName; var next = 1; while (name in childrenProxyMap) { name = '' + maybeName + next; next++; } childrenProxyMap[name] = i; }); return Object.keys(childrenProxyMap).length === 0 ? null : childrenProxyMap; }; var lookForFonts = function lookForFonts(block) { if (block.properties && (isFontable(block.name) || !block.isBasic)) { var fontFamilyProp = block.properties.find(function (p) { return p.name === 'fontFamily'; }); if (fontFamilyProp) { var fontFamily = getMainFont(fontFamilyProp.value); var fontWeightProp = block.properties.find(function (p) { return p.name === 'fontWeight'; }); var fontStyleProp = block.properties.find(function (p) { return p.name === 'fontStyle'; }); var fontWeight = fontWeightProp ? fontWeightProp.value.toString() : '400'; var fontStyle = fontStyleProp ? fontStyleProp.value.toString() : 'normal'; if (!fonts.find(function (font) { return font.family === fontFamily && font.weight === fontWeight && font.style === fontStyle; })) { fonts.push({ id: fontFamily + '-' + fontWeight + (fontStyle === 'italic' ? '-italic' : ''), family: fontFamily, weight: fontWeight, style: fontStyle }); } } } }; var end = function end(block, endLine) { block.loc.end = { line: endLine, column: Math.max(0, lines[endLine].length - 1) }; if (block.isGroup && !block.isBasic) { block.childrenProxyMap = getChildrenProxyMap(block); } if (!block.properties) { block.properties = []; } if (stack.length === 0) { // if we're the last block on the stack, then this is the view! views.push(block); return true; } return false; }; var parseBlock = function parseBlock(line, i) { var _getBlock = getBlock(line), name = _getBlock.block, is$$1 = _getBlock.is; var shouldPushToStack = false; var block = { type: 'Block', name: name, isBasic: isBasic(name), isGroup: false, loc: getLoc(i, 0), properties: [], scopes: [] }; if (is$$1) { block.is = is$$1; if (isCapture(name)) { if (lastCapture) { lastCapture.captureNext = is$$1; } lastCapture = block; } } var last = stack[stack.length - 1]; if (last) { if (last.isGroup) { if (last.isList) { if (block.isBasic) { warn('A basic block can\'t be inside a List.\nPut 1 empty line before', block); shouldPushToStack = true; } else if (last.children.length > 0) { warn('A List can only have one view inside. This block is outside of it.\nPut 1 empty line before.', block); shouldPushToStack = true; } else { last.children.push(block); } } else { last.children.push(block); } } else { // the block is inside a block that isn't a group end(stack.pop(), i); if (views[0].isGroup) { warn(lines[i - 1] === '' ? 'put 1 empty line before' : 'put 2 empty lines before', block); } else { warn('add Vertical at the top', block); } shouldPushToStack = true; } } else if (views.length > 0) { // the block is outside the top level block var newLinesBeforePreviousBlock = 1; while (isEnd(lines[i - newLinesBeforePreviousBlock])) { newLinesBeforePreviousBlock++; } var help = []; if (!views[0].isGroup) { help.push('add Vertical at the top'); } if (newLinesBeforePreviousBlock > 2) { var linesToRemove = newLinesBeforePreviousBlock - 2; help.push('remove ' + linesToRemove + ' empty line' + (linesToRemove > 1 ? 's' : '') + ' before'); } warn(help.join(', '), block); } if (isGroup(name)) { block.isGroup = true; block.isList = isList(name); block.children = []; shouldPushToStack = true; } if (shouldPushToStack || stack.length === 0) { stack.push(block); } parseProps(i, block); lookForFonts(block); }; var parseProps = function parseProps(i, block) { var endOfBlockIndex = i; while (endOfBlockIndex < lines.length - 1 && !isBlock(lines[endOfBlockIndex + 1])) { endOfBlockIndex++; } var properties = []; var scopes = []; var scope = void 0; var inScope = false; for (var j = i; j <= endOfBlockIndex; j++) { var line = lines[j]; var propNode = null; if (isProp(line)) { var _getProp = getProp(line), _getProp2 = slicedToArray(_getProp, 2), name = _getProp2[0], value = _getProp2[1]; var tags = getTags(name, value); if (tags.code) { props.push({ type: block.name, name: name, value: value }); } if (tags.style && tags.code) { block.maybeAnimated = true; } if (name === 'when') { tags.scope = value; inScope = value; scope = { isSystem: isSystemScope(value), value: value, properties: [] }; scopes.push(scope); } propNode = { type: 'Property', loc: getLoc(j, line.indexOf(name), line.length - 1), name: name, tags: tags, meta: getMeta(value, line, j), value: getValue(value) }; } else if (isComment(line) && !skipComments) { var _getComment = getComment(line), _getComment2 = slicedToArray(_getComment, 1), _value = _getComment2[0]; var userComment = isUserComment(line); if (userComment) { _value = getComment(_value); } propNode = { type: 'Property', loc: getLoc(j, 0, line.length - 1), value: _value, tags: { comment: true, userComment: userComment } }; } if (propNode) { block.loc.end = propNode.loc.end; if (inScope) { scope.properties.push(propNode); } else { properties.push(propNode); } } } block.properties = properties; block.scopes = scopes; }; lines.forEach(function (line, i) { if (isBlock(line)) { parseBlock(line, i); } else if (isEnd(line) && stack.length > 0) { end(stack.pop(), i); } }); if (stack.length > 0) { while (!end(stack.pop(), lines.length - 1)) {} } return { fonts: fonts, props: getPropTypes(props), views: views }; }); module.exports = index; //# sourceMappingURL=parse.js.map