@nozbe/zacs
Advanced tools
Comparing version 0.9.3 to 0.10.0-1
@@ -7,2 +7,5 @@ # Changelog | ||
- 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 | ||
## 0.9.3 (2019-09-27) | ||
@@ -9,0 +12,0 @@ |
{ | ||
"name": "@nozbe/zacs", | ||
"version": "0.9.3", | ||
"version": "0.10.0-1", | ||
"description": "Zero Abstraction Cost Styling (for React DOM and React Native)", | ||
@@ -5,0 +5,0 @@ "main": "src/index.js", |
@@ -25,2 +25,6 @@ <p align="center"> | ||
| <a href="https://youtube.com/watch?v=ryMvNklnDjU"><img src="https://github.com/Nozbe/ZACS/raw/master/assets/youtube-thumbnail.jpg" alt="React Native EU: A Successful Web & React Native Sharing Strategy" width="400" /></a> | | ||
| ---- | | ||
| <p align="center"><a href="https://youtube.com/watch?v=ryMvNklnDjU">📺 <strong>A Successful Web & React Native Sharing Strategy</strong></p> | | ||
**ZACS** turns React components that look like this: | ||
@@ -78,3 +82,4 @@ | ||
+ "platform": "web", // or "native" | ||
+ "production": true // pass `false` to enable debug attributes | ||
+ "production": true, // pass `false` to enable debug attributes | ||
+ "keepDeclarations": false // pass `true` to keep zacs.xxx variable declarations in output | ||
+ }] | ||
@@ -185,3 +190,3 @@ ] | ||
### Adding style attributes | ||
### Adding style attributes (via individual props) | ||
@@ -219,2 +224,13 @@ ```js | ||
### Adding styles directly | ||
```js | ||
const Box = zacs.view(styles.box) | ||
const rendered = <Box zacs:style={{ width: 100, color: '#80EADC' }} /> | ||
``` | ||
This is equivalent to the example above, but instead of predefining list of props that turn into styles, | ||
we pass styles directly. Note that this only works on ZACS components. | ||
### Styling custom components | ||
@@ -305,2 +321,8 @@ | ||
If you're only building for one platform, you can also reference built-ins like this: | ||
```js | ||
const Paragraph = zacs.styled('p', styles.paragraph) // NOTE: No web/native, because this is web-only code | ||
``` | ||
**TODO:** Passing `zacs.text/view` as parameter seems magic and gross. If you have a better idea for this API, let us know! | ||
@@ -342,3 +364,3 @@ | ||
You must declare (in the last argument) all non-zacs props you want to be able to pass into the component you're styling (component props, DOM attributes, `ref`, and `zacs:inherit`). | ||
You must declare (in the last argument) all non-zacs props you want to be able to pass into the component you're styling (component props, DOM attributes, `ref`, and `zacs:inherit`, `zacs:style`). | ||
@@ -412,2 +434,14 @@ <details> | ||
### Style precedence | ||
From least important to most important: | ||
- main style (first argument to `zacs.xxx()`) | ||
- conditional styles (second argument to `zacs.xxx()`) | ||
- styles added via props (third argument to `zacs.xxx()`) | ||
- styles added via `zacs:style` | ||
- styles added via `zacs:inherit` | ||
For example, `width` passed via `zacs:inherit` will override `width` added via props. | ||
## Defining styles | ||
@@ -414,0 +448,0 @@ |
@@ -133,6 +133,6 @@ /* eslint-disable no-use-before-define */ | ||
function findZacsInherited(t, attributes) { | ||
function findNamespacedAttr(t, attributes, attrName) { | ||
return attributes.find( | ||
({ name }) => | ||
t.isJSXNamespacedName(name) && name.namespace.name === 'zacs' && name.name.name === 'inherit', | ||
t.isJSXNamespacedName(name) && name.namespace.name === 'zacs' && name.name.name === attrName, | ||
) | ||
@@ -197,4 +197,14 @@ } | ||
// TODO: Validate zacs:style value | ||
// TODO: If the value is a simple object, we could merge them into addedStyles. OTOH, maybe another | ||
// optimizer Babel plugin can do it further down the line? | ||
const zacsStyleAttribute = jsxAttributes && findNamespacedAttr(t, jsxAttributes, 'style') | ||
const hasZacsStyleAttr = zacsStyleAttribute || passedProps.includes('zacs:style') | ||
const zacsStyle = hasZacsStyleAttr | ||
? (zacsStyleAttribute && zacsStyleAttribute.value.expression) || | ||
t.memberExpression(t.identifier('props'), t.identifier('__zacs_style')) | ||
: null | ||
// TODO: Validate inherited props value | ||
const inheritedPropsAttr = jsxAttributes && findZacsInherited(t, jsxAttributes) | ||
const inheritedPropsAttr = jsxAttributes && findNamespacedAttr(t, jsxAttributes, 'inherit') | ||
const hasInheritedProps = inheritedPropsAttr || passedProps.includes('zacs:inherit') | ||
@@ -209,2 +219,3 @@ const inheritedProps = hasInheritedProps | ||
inheritedProps, | ||
zacsStyle, | ||
] | ||
@@ -238,24 +249,23 @@ } | ||
function webStyleExpr(t, styles, inheritedProps) { | ||
if (inheritedProps) { | ||
const inheritedStyles = t.memberExpression(inheritedProps, t.identifier('style')) | ||
function webStyleExpr(t, styles, inheritedProps, zacsStyle) { | ||
const inheritedStyles = inheritedProps | ||
? t.memberExpression(inheritedProps, t.identifier('style')) | ||
: null | ||
const allStyles = [styles, zacsStyle, inheritedStyles].filter(Boolean) | ||
if (styles) { | ||
// Object.assign({styles:'values'}, props.style) | ||
// TODO: Maybe we can use spread operator and babel will transpile it into ES5 if necessary? | ||
return t.callExpression(t.memberExpression(t.identifier('Object'), t.identifier('assign')), [ | ||
styles, | ||
inheritedStyles, | ||
]) | ||
} | ||
// only inherited styles | ||
return inheritedStyles | ||
if (!allStyles.length) { | ||
return null | ||
} else if (allStyles.length === 1) { | ||
return allStyles[0] | ||
} | ||
// only defined styles | ||
return styles | ||
// Object.assign({styles:'values'}, props.style) | ||
// TODO: Maybe we can use spread operator and babel will transpile it into ES5 if necessary? | ||
return t.callExpression( | ||
t.memberExpression(t.identifier('Object'), t.identifier('assign')), | ||
allStyles, | ||
) | ||
} | ||
function webStyleAttributes(t, [classNames, styles, inheritedProps]) { | ||
function webStyleAttributes(t, [classNames, styles, inheritedProps, zacsStyle]) { | ||
const attributes = [] | ||
@@ -282,3 +292,3 @@ | ||
const stylesValue = webStyleExpr(t, styles, inheritedProps) | ||
const stylesValue = webStyleExpr(t, styles, inheritedProps, zacsStyle) | ||
if (stylesValue) { | ||
@@ -291,3 +301,3 @@ attributes.push(jsxAttr(t, 'style', stylesValue)) | ||
function nativeStyleAttributes(t, [styleDefs, addedStyles, inheritedProps]) { | ||
function nativeStyleAttributes(t, [styleDefs, addedStyles, inheritedProps, zacsStyle]) { | ||
const styles = styleDefs.map(([styleName, condition]) => | ||
@@ -301,2 +311,6 @@ condition ? t.logicalExpression('&&', condition, styleName) : styleName, | ||
if (zacsStyle) { | ||
styles.push(zacsStyle) | ||
} | ||
if (!styles.length && !inheritedProps) { | ||
@@ -450,3 +464,3 @@ return [] | ||
platform === 'web' && htmlElements.has(elementName) ? isAttributeWebSafe(prop) : true | ||
if (prop !== 'zacs:inherit' && prop !== 'ref' && isAttrWebSafe) { | ||
if (prop !== 'zacs:inherit' && prop !== 'zacs:style' && prop !== 'ref' && isAttrWebSafe) { | ||
jsxAttributes.push( | ||
@@ -502,2 +516,8 @@ jsxAttr(t, prop, t.memberExpression(t.identifier('props'), t.identifier(prop))), | ||
// Validate variable name | ||
if (!t.isIdentifier(node.id)) { | ||
throw path.buildCodeFrameError('Expected zacs declaration to be assigned to a simple variable') | ||
} | ||
// Validate declaration | ||
@@ -580,25 +600,41 @@ if ( | ||
// this is called on a JSXElement that doesn't (directly) reference a zacs declaration | ||
// we need to spread zacs:inherit into separate props or it won't work | ||
// we need to spread zacs:inherit and zacs:style into separate props or it won't work | ||
const { node } = path | ||
const { openingElement } = node | ||
const inheritedPropsAttr = findZacsInherited(t, openingElement.attributes) | ||
if (!inheritedPropsAttr) { | ||
const inheritedPropsAttr = findNamespacedAttr(t, openingElement.attributes, 'inherit') | ||
const zacsStyleAttr = findNamespacedAttr(t, openingElement.attributes, 'style') | ||
if (!inheritedPropsAttr && !zacsStyleAttr) { | ||
return | ||
} | ||
const inheritedProps = inheritedPropsAttr.value.expression | ||
const styleAttr = jsxAttr(t, 'style', t.memberExpression(inheritedProps, t.identifier('style'))) | ||
const classNameAttr = jsxAttr( | ||
t, | ||
'className', | ||
t.memberExpression(inheritedProps, t.identifier('className')), | ||
) | ||
const addedAttrs = platform === 'web' ? [styleAttr, classNameAttr] : [styleAttr] | ||
const addedAttrs = [] | ||
if (inheritedPropsAttr) { | ||
const inheritedProps = inheritedPropsAttr.value.expression | ||
const styleAttr = jsxAttr(t, 'style', t.memberExpression(inheritedProps, t.identifier('style'))) | ||
addedAttrs.push(styleAttr) | ||
if (platform === 'web') { | ||
const classNameAttr = jsxAttr( | ||
t, | ||
'className', | ||
t.memberExpression(inheritedProps, t.identifier('className')), | ||
) | ||
addedAttrs.push(classNameAttr) | ||
} | ||
} | ||
if (zacsStyleAttr) { | ||
// rewrite zacs:style to __zacs_style, otherwise React babel plugin will have a problem | ||
addedAttrs.push(jsxAttr(t, '__zacs_style', zacsStyleAttr.value.expression)) | ||
} | ||
openingElement.attributes = openingElement.attributes | ||
.filter(attr => attr !== inheritedPropsAttr) | ||
.filter(attr => attr !== inheritedPropsAttr && attr !== zacsStyleAttr) | ||
.concat(addedAttrs) | ||
} | ||
const componentKey = name => `declaration_${name}` | ||
exports.default = function(babel) { | ||
@@ -623,2 +659,13 @@ const { types: t } = babel | ||
node.init = createZacsComponent(t, state, path) | ||
} else { | ||
const id = node.id.name | ||
const stateKey = componentKey(id) | ||
if (state.get(stateKey)) { | ||
throw path.buildCodeFrameError(`Duplicate ZACS declaration for name: ${id}`) | ||
} | ||
state.set(stateKey, node) | ||
if (!state.opts.keepDeclarations) { | ||
path.remove() | ||
} | ||
} | ||
@@ -630,13 +677,7 @@ }, | ||
const { name } = openingElement.name | ||
const platform = getPlatform(state) | ||
// check if element is referenced (i.e. not built-in element) | ||
const binding = path.scope.getBinding(name) | ||
if (!binding) { | ||
return | ||
} | ||
// check if it's a ZACS element | ||
const elementDeclarator = binding.path.node | ||
const platform = getPlatform(state) | ||
if (!isZacsDeclaration(t, elementDeclarator)) { | ||
const declaration = state.get(componentKey(name)) | ||
if (!declaration) { | ||
transformZacsAttributesOnNonZacsElement(t, platform, path) | ||
@@ -649,3 +690,3 @@ return | ||
// get ZACS element info | ||
const { id, init } = elementDeclarator | ||
const { id, init } = declaration | ||
const originalName = id.name | ||
@@ -652,0 +693,0 @@ const zacsMethod = init.callee.property.name |
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
63386
956
505