@nozbe/zacs
Advanced tools
Comparing version 0.10.0-1 to 1.0.0-0
@@ -7,4 +7,8 @@ # Changelog | ||
- You can now declare multiple unconditional styles by passing an array of stylesets/class names | ||
- You can now add extra styles to a ZACS component via `zacs:style={{ attr: value }}` syntax | ||
- Strip ZACS declarations (`const Foo = zacs.foo(...)`) from output code by default. Pass `keepDeclarations: true` in Babel config to revert to previous behavior | ||
- Strip ZACS declarations (`const Foo = zacs.foo(...)`) and ZACS import from output code by default. Pass `keepDeclarations: true` in Babel config to revert to previous behavior. | ||
- Fix buggy behavior on `zacs.createXXX` components with both `zacs:inherit` and `zacs:style` props whitelisted | ||
- Improved validation and error messages | ||
- Improved Flow typing (likely to reveal new errors) | ||
@@ -11,0 +15,0 @@ ## 0.9.3 (2019-09-27) |
{ | ||
"name": "@nozbe/zacs", | ||
"version": "0.10.0-1", | ||
"version": "1.0.0-0", | ||
"description": "Zero Abstraction Cost Styling (for React DOM and React Native)", | ||
@@ -10,2 +10,3 @@ "main": "src/index.js", | ||
"eslint": "eslint ./src -c ./.eslintrc.yml --cache --cache-location ./.cache/.eslintcache", | ||
"playground": "chokidar \"src/babel/**/*.js\" --initial -c \"node src/babel/__playground__/index.js\"", | ||
"prettier": "prettier-eslint --config ./.prettierrc --write \"./src/**/*.js\"", | ||
@@ -54,2 +55,5 @@ "ci:check": "concurrently 'npm run test' 'npm run eslint' 'npm run flow' --kill-others-on-fail" | ||
"babel-eslint": "^10.0.3", | ||
"chalk": "^4.1.0", | ||
"chokidar-cli": "^2.1.0", | ||
"cli-highlight": "^2.1.4", | ||
"concurrently": "^4.1.2", | ||
@@ -56,0 +60,0 @@ "eslint": "^6.3.0", |
@@ -232,2 +232,30 @@ <p align="center"> | ||
### Multiple unconditional styles | ||
```js | ||
import styles from './styles' | ||
const TitleText = zacs.text([styles.text, styles.titleText]) | ||
const rendered = <TitleText /> | ||
``` | ||
<details> | ||
<summary>See compiled output</summary> | ||
**Web:** | ||
```js | ||
const rendered = <span className={styles.text + ' ' + styles.titleText} /> | ||
``` | ||
**React Native:** | ||
```js | ||
import { Text } from 'react-native' | ||
const rendered = <Text style={[styles.text, styles.titleText]} /> | ||
``` | ||
</details> | ||
### Styling custom components | ||
@@ -278,4 +306,2 @@ | ||
**TODO:** I don't love the `zacs:inherit` name — if you have a better suggestion, let us know! | ||
<details> | ||
@@ -282,0 +308,0 @@ <summary>See compiled output</summary> |
/* eslint-disable no-use-before-define */ | ||
// Note: Why is this one big file? Because that makes it possible to work with it using https://astexplorer.net :) | ||
Object.defineProperty(exports, '__esModule', { | ||
value: true, | ||
}) | ||
exports.__esModule = true | ||
/* | ||
TERMINOLOGY: | ||
zacs.{view,text,styled}() -- styled declaration | ||
zacs.create{View,Text,Styled}() -- styled component | ||
styles.foo -- predefined styleset | ||
{ foo: 'bar' } -- literal styleset | ||
zacs.view( | ||
styles.foo, -- unconditional styleset (uncond styleset) | ||
{ bar: styles.bar }, -- conditional styleset spec (cond styleset) | ||
{ width: 'width' } -- literal style spec | ||
) | ||
*/ | ||
function getPlatform(state) { | ||
@@ -58,7 +74,7 @@ // return 'web' | ||
function withoutStylingProps(t, attributes, conditionalStyles, addedStyles) { | ||
function withoutStylingProps(t, attributes, condStyles, literalStyleSpec) { | ||
const stylingProps = [] | ||
if (conditionalStyles && conditionalStyles.properties) { | ||
conditionalStyles.properties.forEach(property => { | ||
if (condStyles && condStyles.properties) { | ||
condStyles.properties.forEach(property => { | ||
stylingProps.push(property.key.name) | ||
@@ -68,4 +84,4 @@ }) | ||
if (addedStyles && addedStyles.properties) { | ||
addedStyles.properties.forEach(property => { | ||
if (literalStyleSpec && literalStyleSpec.properties) { | ||
literalStyleSpec.properties.forEach(property => { | ||
stylingProps.push(property.key.name) | ||
@@ -142,12 +158,16 @@ }) | ||
function getStyles(t, mainStyle, conditionalStyles, addedStylesDef, jsxAttributes, passedProps) { | ||
const styles = [] | ||
const addedStyles = [] | ||
function getStyles(t, uncondStyles, condStyles, literalStyleSpec, jsxAttributes, passedProps) { | ||
const stylesets = [] | ||
const literalStyles = [] | ||
if (mainStyle && !t.isNullLiteral(mainStyle)) { | ||
styles.push([mainStyle]) | ||
if (uncondStyles && !t.isNullLiteral(uncondStyles)) { | ||
if (t.isArrayExpression(uncondStyles)) { | ||
stylesets.push(...uncondStyles.elements.map(styleset => [styleset])) | ||
} else { | ||
stylesets.push([uncondStyles]) | ||
} | ||
} | ||
if (conditionalStyles && !t.isNullLiteral(conditionalStyles)) { | ||
conditionalStyles.properties.forEach(property => { | ||
if (condStyles && !t.isNullLiteral(condStyles)) { | ||
condStyles.properties.forEach(property => { | ||
const style = property.value | ||
@@ -165,10 +185,10 @@ const propName = property.key.name | ||
if (flag === true) { | ||
styles.push([style]) | ||
stylesets.push([style]) | ||
} else if (flag === false) { | ||
// we know for a fact the style won't be used -- so ignore it | ||
} else { | ||
styles.push([style, flag]) | ||
stylesets.push([style, flag]) | ||
} | ||
} else { | ||
styles.push([style, t.memberExpression(t.identifier('props'), t.identifier(propName))]) | ||
stylesets.push([style, t.memberExpression(t.identifier('props'), t.identifier(propName))]) | ||
} | ||
@@ -178,4 +198,4 @@ }) | ||
if (addedStylesDef && !t.isNullLiteral(addedStylesDef)) { | ||
addedStylesDef.properties.forEach(property => { | ||
if (literalStyleSpec && !t.isNullLiteral(literalStyleSpec)) { | ||
literalStyleSpec.properties.forEach(property => { | ||
const styleAttr = property.value.value | ||
@@ -191,5 +211,5 @@ const propName = property.key.name | ||
const styleValue = jsxAttrValue(t, attr) | ||
addedStyles.push([styleAttr, styleValue]) | ||
literalStyles.push([styleAttr, styleValue]) | ||
} else { | ||
addedStyles.push([ | ||
literalStyles.push([ | ||
styleAttr, | ||
@@ -203,3 +223,3 @@ t.memberExpression(t.identifier('props'), t.identifier(propName)), | ||
// TODO: Validate zacs:style value | ||
// TODO: If the value is a simple object, we could merge them into addedStyles. OTOH, maybe another | ||
// TODO: If the value is a simple object, we could merge them into literalStyles. OTOH, maybe another | ||
// optimizer Babel plugin can do it further down the line? | ||
@@ -221,4 +241,4 @@ const zacsStyleAttribute = jsxAttributes && findNamespacedAttr(t, jsxAttributes, 'style') | ||
return [ | ||
styles, | ||
addedStyles.length ? objectExpressionFromPairs(t, addedStyles) : null, | ||
stylesets, | ||
literalStyles.length ? objectExpressionFromPairs(t, literalStyles) : null, | ||
inheritedProps, | ||
@@ -266,2 +286,7 @@ zacsStyle, | ||
// prevent Object.assign(props.__zacs_style, ...), because if it's not an object, it will crash | ||
if (!styles && zacsStyle && inheritedProps && !t.isObjectExpression(zacsStyle)) { | ||
allStyles.unshift(t.objectExpression([])) | ||
} | ||
// Object.assign({styles:'values'}, props.style) | ||
@@ -305,9 +330,9 @@ // TODO: Maybe we can use spread operator and babel will transpile it into ES5 if necessary? | ||
function nativeStyleAttributes(t, [styleDefs, addedStyles, inheritedProps, zacsStyle]) { | ||
const styles = styleDefs.map(([styleName, condition]) => | ||
function nativeStyleAttributes(t, [stylesets, literalStyleset, inheritedProps, zacsStyle]) { | ||
const styles = stylesets.map(([styleName, condition]) => | ||
condition ? t.logicalExpression('&&', condition, styleName) : styleName, | ||
) | ||
if (addedStyles) { | ||
styles.push(addedStyles) | ||
if (literalStyleset) { | ||
styles.push(literalStyleset) | ||
} | ||
@@ -343,9 +368,16 @@ | ||
platform, | ||
mainStyle, | ||
conditionalStyles, | ||
addedStyles, | ||
uncondStyles, | ||
condStyles, | ||
literalStyleSpec, | ||
jsxAttributes, | ||
passedProps = [], | ||
) { | ||
const styles = getStyles(t, mainStyle, conditionalStyles, addedStyles, jsxAttributes, passedProps) | ||
const styles = getStyles( | ||
t, | ||
uncondStyles, | ||
condStyles, | ||
literalStyleSpec, | ||
jsxAttributes, | ||
passedProps, | ||
) | ||
switch (platform) { | ||
@@ -357,3 +389,3 @@ case 'web': | ||
default: | ||
throw new Error('Unknown platform') | ||
throw new Error('Unknown platform passed to ZACS config') | ||
} | ||
@@ -451,3 +483,3 @@ } | ||
) | ||
const [mainStyle, conditionalStyles, addedStyles, passedPropsExpr] = | ||
const [uncondStyles, condStyles, literalStyleSpec, passedPropsExpr] = | ||
zacsMethod === 'styled' ? init.arguments.slice(1) : init.arguments | ||
@@ -482,3 +514,3 @@ | ||
jsxAttributes.unshift( | ||
...styleAttributes(t, platform, mainStyle, conditionalStyles, addedStyles, null, passedProps), | ||
...styleAttributes(t, platform, uncondStyles, condStyles, literalStyleSpec, null, passedProps), | ||
) | ||
@@ -542,3 +574,3 @@ | ||
throw path.buildCodeFrameError( | ||
`It's not allowed to export zacs declarations -- but you can export zacs components (use zacs.createView/createText/createStyled)`, | ||
`It's not allowed to export zacs declarations. You can export zacs components (use zacs.createView/createText/createStyled), however they behave a little differently -- please check documentation for more information.`, | ||
) | ||
@@ -549,2 +581,3 @@ } | ||
if (zacsMethod === 'styled') { | ||
const componentToStyle = init.arguments[0] | ||
if ( | ||
@@ -554,9 +587,9 @@ !( | ||
// TODO: Validate platform specifier keys | ||
(t.isIdentifier(init.arguments[0]) || | ||
t.isStringLiteral(init.arguments[0]) || | ||
t.isObjectExpression(init.arguments[0])) | ||
(t.isIdentifier(componentToStyle) || | ||
t.isStringLiteral(componentToStyle) || | ||
t.isObjectExpression(componentToStyle)) | ||
) | ||
) { | ||
throw path.buildCodeFrameError( | ||
'zacs.styled() requires an argument - a `Component`, a `{ web: Component, native: Component }` specifier, or a `"builtin"`', | ||
'zacs.styled() requires an argument - a `Component`, a `{ web: Component, native: Component }` specifier, or a `"builtin"` (e.g. `"div"` on web)', | ||
) | ||
@@ -566,3 +599,3 @@ } | ||
const [, conditionalStyles, addedStyles] = | ||
const [, condStyles, literalStyleSpec] = | ||
zacsMethod === 'styled' || zacsMethod === 'createStyled' | ||
@@ -572,6 +605,3 @@ ? init.arguments.slice(1) | ||
if ( | ||
conditionalStyles && | ||
!(t.isObjectExpression(conditionalStyles) || t.isNullLiteral(conditionalStyles)) | ||
) { | ||
if (condStyles && !(t.isObjectExpression(condStyles) || t.isNullLiteral(condStyles))) { | ||
throw path.buildCodeFrameError( | ||
@@ -584,5 +614,8 @@ 'Conditional styles (second argument to ZACS) should be an object expression', | ||
if (addedStyles && !(t.isObjectExpression(addedStyles) || t.isNullLiteral(addedStyles))) { | ||
if ( | ||
literalStyleSpec && | ||
!(t.isObjectExpression(literalStyleSpec) || t.isNullLiteral(literalStyleSpec)) | ||
) { | ||
throw path.buildCodeFrameError( | ||
'Added styles (second argument to ZACS) should be an object expression', | ||
'Literal styles (third argument to ZACS) should be an object expression', | ||
) | ||
@@ -609,2 +642,17 @@ | ||
function validateZacsImport(t, path) { | ||
const { node } = path | ||
if ( | ||
!( | ||
node.specifiers.length === 1 && | ||
t.isImportDefaultSpecifier(node.specifiers[0]) && | ||
node.specifiers[0].local.name === 'zacs' | ||
) | ||
) { | ||
throw path.buildCodeFrameError( | ||
'ZACS import must say exactly `import zacs from \'@nozbe/zacs\'`. Other forms such as `import { view, text }`, `require`, `import * as zacs` are not allowed.', | ||
) | ||
} | ||
} | ||
function transformZacsAttributesOnNonZacsElement(t, platform, path) { | ||
@@ -708,3 +756,3 @@ // this is called on a JSXElement that doesn't (directly) reference a zacs declaration | ||
) | ||
const [mainStyle, conditionalStyles, addedStyles] = | ||
const [uncondStyles, condStyles, literalStyleSpec] = | ||
zacsMethod === 'styled' ? init.arguments.slice(1) : init.arguments | ||
@@ -725,7 +773,7 @@ const originalAttributes = openingElement.attributes | ||
openingElement.attributes, | ||
conditionalStyles, | ||
addedStyles, | ||
condStyles, | ||
literalStyleSpec, | ||
) | ||
// replace definition | ||
// replace component | ||
if (platform === 'web') { | ||
@@ -754,5 +802,5 @@ renameJSX(node, elementName) | ||
platform, | ||
mainStyle, | ||
conditionalStyles, | ||
addedStyles, | ||
uncondStyles, | ||
condStyles, | ||
literalStyleSpec, | ||
originalAttributes, | ||
@@ -782,2 +830,19 @@ ), | ||
}, | ||
ImportDeclaration(path, state) { | ||
const { node } = path | ||
if ( | ||
!node.source || | ||
// Make it work even if someone makes a fork of zacs | ||
!(node.source.value === 'zacs' || node.source.value.endsWith('/zacs')) | ||
) { | ||
return | ||
} | ||
validateZacsImport(t, path) | ||
if (!state.opts.keepDeclarations) { | ||
path.remove() | ||
} | ||
}, | ||
}, | ||
@@ -784,0 +849,0 @@ } |
@@ -10,3 +10,3 @@ /* eslint-disable */ | ||
methodName + | ||
'method called directly (not transpiled). Your Babel file is probably misconfigured or you have a syntax error. See https://github.com/Nozbe/zacs#troubleshooting for more info', | ||
'method was called directly (was not transpiled into zero-abstraction-cost form). This is an error. Most likely, you have a syntax error, your Babel configuration file is misconfigured, or you incorrectly use ZACS declarations as component objects. In fact, unless you pass { keepDeclarations: true } to ZACS Babel config, you should never see this file in the compiled app. See https://github.com/Nozbe/zacs#troubleshooting for more information.', | ||
) | ||
@@ -13,0 +13,0 @@ } |
Sorry, the diff of this file is not supported yet
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
66855
1011
531
19