From a7af4b41841b8860dde4dcd34b9bd92d04074321 Mon Sep 17 00:00:00 2001 From: Joshua Pomazal Date: Sun, 14 Aug 2016 16:29:24 -0600 Subject: [PATCH 1/9] Moved to using _reactInternalInstance to calculate sibling position, not public API but works and avoids having to map --- examples/README.md | 20 +++++------------- src/client.js | 3 +-- src/wrap.js | 52 +++++++++++++++++++--------------------------- 3 files changed, 27 insertions(+), 48 deletions(-) diff --git a/examples/README.md b/examples/README.md index 2b50f3f..2bf33a4 100644 --- a/examples/README.md +++ b/examples/README.md @@ -30,7 +30,7 @@ You can use the wrapped style components instead of those out of the `react-nati ### Create your stylesheet You create your CSS styles normally, however remember that React Native only supports a small subset of style properties that can be mapped to CSS. See [http://facebook.github.io/react-native/docs/view.html#style](http://facebook.github.io/react-native/docs/view.html#style) for more information. -Also, only a small subset of selectors are supported. Components (including `*`), classes, child (`>`) and `:not(.className)` are supported and `:first-child`, `:nth-child(n)` and `:last-child` are supported only if the components are immediately wrapped with the *map* function. +Also, only a small subset of selectors are supported. Components (including `*`), classes, child (`>`), `:not(.className)`,`:first-child`, `:nth-child(n)` and `:last-child` are supported. There is currently **no** support for siblings (`+`,`~`), props (`[name="value"]`) or the myriad of pseudo-class selectors. Also, styles for text must be in a selector that terminates in a text component (compound selectors are okay, e.g. `text.someClass:first-child`). This is because text styles are filtered out of all other selectors when the CSS is converted. @@ -51,7 +51,6 @@ root { //This will take precedence over the text {...} rule font-size: 22px; color: $myAwesomeColor; - //If you wrap components in the map method, you can use child-position pseudo selectors. &:last-child { font-weight: bold; } @@ -120,7 +119,7 @@ register(require('./styles/default.js'), require('./styles/ios_theme.js')) //MyComponent.js import React,{Component} from 'react' //use react-native-css as a drop-in replacement for react-native -import {View,Text,TouchableOpacity,wrap,map} from 'react-native-css' +import {View,Text,TouchableOpacity,wrap} from 'react-native-css' class MyComponent extends Component { render() { @@ -168,7 +167,6 @@ class MyComponent extends Component { export default MyComponent -//Enable :last-child,:first-child and :nth-child(n) selectors using map([Component]) class MyComponent extends Component { state = { @@ -181,14 +179,9 @@ class MyComponent extends Component { //Here we can do a short circuit for the 'active' class and let the library do the ugly work. return ( - {//map will automatically add keys (index) if not set. This is fine for static arrays, however set keys for dynamic children to prevent state issues. - map([ - This is some text that will be automatically styled with text element selector (.my-component text:first-child {'{...}'})., - This is some text that will be automatically styled with text element selector (.my-component text:nth-child(2) {'{...}'})., - This is some text that will be automatically styled with text element selector (.my-component text:last-child {'{...}'}). - ]) - //just wrap an Array#map() call to get functionality on a dynamic set, e.g. map(someArray.map(item=>)) - } + This is some text that will be automatically styled with text element selector (.my-component text:first-child {'{...}'}). + This is some text that will be automatically styled with text element selector (.my-component text:nth-child(2) {'{...}'}). + This is some text that will be automatically styled with text element selector (.my-component text:last-child {'{...}'}). this.setState({someActiveFlag:true})}> @@ -232,9 +225,6 @@ Rules are ordered by how well they match the path of a component and ordered fro ### className The *className* property is slightly different than how it implemented in standard React. Standard React only excepts a string as the value, while this library will accept either a string or an array of strings or string arrays. If you use this library in a web environment, className is passed unaltered which means you can only pass a string. If you use it in web environments, wrap the className arrays with the `normalizeClassNames` functions which will convert the array into a string. -### :first-child, :last-child, :nth-child(n) -Because we use standardized Context to calculate a component's position in the tree, it is impossible to infer a component's position amongst its siblings. To overcome this we have the `map` function to clone each child aware of its position amongst its siblings in the passed array. Without using `map` these pseudo classes will not work. - ### Running in the browser If you plan on using the fantastic *react-native-web* in order to run your React Native app in the browser, you should know that this library becomes a no op meaning any library specific features can't be used. This is a performance consideration as it is much less expensive to defer to the browsers native CSS operations. diff --git a/src/client.js b/src/client.js index 4d3b316..dea9ed4 100644 --- a/src/client.js +++ b/src/client.js @@ -1,6 +1,6 @@ import ReactNative from "react-native"; import css,{register,unregister} from "./css"; -import wrap,{map} from "./wrap"; +import wrap from "./wrap"; //These are all the components in react native to wrap with the styled proxy component const componentsToWrap = ["ActivityIndicator", @@ -76,6 +76,5 @@ StyledComponents.unregister = unregister; StyledComponents.register = register; StyledComponents.css = css; StyledComponents.wrap = wrap; -StyledComponents.map = map; module.exports = StyledComponents; diff --git a/src/wrap.js b/src/wrap.js index 6ef06e0..d367aa9 100644 --- a/src/wrap.js +++ b/src/wrap.js @@ -28,31 +28,17 @@ function normalizeClassNames(classNames) { return classes; } -/** - * This takes an array of components and adds position properties for leveraging :first-child, :last-child, - * :nth-child(1...n) selectors. Without these properties, those selectors will fail. - * Passing a single component will result in first,last and nth(1) all succeeding. - * A key is automatically added (just the index) if omitted, which allows easily adding this to static children. - * @param {[Component]|Component} componentOrComponentArray - * @return {*} - */ -function map(componentOrComponentArray) { - componentOrComponentArray = (componentOrComponentArray instanceof Array ? componentOrComponentArray : [componentOrComponentArray]); - if (isWebApp) { - //css pseudo already supported in the browser - return componentOrComponentArray; +function indexOf(instance, children) { + let index = 0 + for (let key in children) { + if (children[key]._mountOrder === instance._reactInternalInstance._mountOrder) { + return index; + } + index++ } - return componentOrComponentArray.map((component, i)=> { - return React.cloneElement(component, { - firstChild: i === 0, - lastChild: i === componentOrComponentArray.length - 1, - nthChild: i + 1, - key: i - }); - }); + return -1 } - const pathCache = {}; /** @@ -119,8 +105,8 @@ export default function wrap(name, WrappedComponent) { this._root && this._root.setNativeProps(nativeProps); } - shouldComponentUpdate() { - return false; + shouldComponentUpdate(nextProps) { + return shallowCompare(this, nextProps); } /** @@ -128,14 +114,21 @@ export default function wrap(name, WrappedComponent) { * @param props */ createPath(props) { + let index = -1, + count = 1 + if (this._reactInternalInstance) { + let children = this._reactInternalInstance._hostParent._renderedChildren + count = Object.keys(children).length + index = indexOf(this, children) + } let element = { e: name.toLowerCase(), c: normalizeClassNames(props.className), //Maybe in the future, but is it worth the performance hit? // p: props, - i: props.nthChild || -1, - f: props.firstChild || false, - l: props.lastChild || false + i: index, + f: index === 0, + l: count === index + 1 }; let key = `${this.context.cssPathKey || ""}>${element.e}.${element.c.join(".")}:${element.i || ""}:${element.f || ""}:${element.l || ""}`; if (this.cssPathKey !== key) { @@ -148,8 +141,7 @@ export default function wrap(name, WrappedComponent) { } render() { - let props = {ref: ref=>this._root = ref, style: this.styles}; - return ; + return this._root = ref} style={[this.styles,this.props.style]}/>; } }; @@ -164,5 +156,3 @@ export default function wrap(name, WrappedComponent) { StyledComponent.contextTypes = {cssPath: React.PropTypes.array, cssPathKey: React.PropTypes.string}; return StyledComponent; } - -export {map}; From 7979ad931499f179cbc478966ddb18a933c8d829 Mon Sep 17 00:00:00 2001 From: Joshua Pomazal Date: Sun, 14 Aug 2016 16:32:57 -0600 Subject: [PATCH 2/9] Only pass prop.style if exisits --- src/wrap.js | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/wrap.js b/src/wrap.js index d367aa9..1e54462 100644 --- a/src/wrap.js +++ b/src/wrap.js @@ -116,7 +116,7 @@ export default function wrap(name, WrappedComponent) { createPath(props) { let index = -1, count = 1 - if (this._reactInternalInstance) { + if (this._reactInternalInstance && this._reactInternalInstance._hostParent) { let children = this._reactInternalInstance._hostParent._renderedChildren count = Object.keys(children).length index = indexOf(this, children) @@ -141,7 +141,8 @@ export default function wrap(name, WrappedComponent) { } render() { - return this._root = ref} style={[this.styles,this.props.style]}/>; + return this._root = ref} + style={this.props.style?[this.styles,this.props.style]:this.styles}/>; } }; From c93010c7803ba0a92f4df7d825422cb0d406a1d7 Mon Sep 17 00:00:00 2001 From: Joshua Pomazal Date: Sun, 14 Aug 2016 21:10:41 -0600 Subject: [PATCH 3/9] Added --silent to circle.yml to fix CI error peer dependency --- circle.yml | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/circle.yml b/circle.yml index 40c3f4c..cbee8b3 100644 --- a/circle.yml +++ b/circle.yml @@ -1,7 +1,6 @@ dependencies: pre: - - npm install - - npm install -g babel + - npm install --silent test: override: From 9402b35e64cb626ae7dd31dac1f460b4be2f8271 Mon Sep 17 00:00:00 2001 From: Joshua Pomazal Date: Sun, 14 Aug 2016 21:42:29 -0600 Subject: [PATCH 4/9] Check if npm update fixes peer depencies fail --- circle.yml | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/circle.yml b/circle.yml index cbee8b3..ba3155d 100644 --- a/circle.yml +++ b/circle.yml @@ -1,6 +1,7 @@ dependencies: pre: - - npm install --silent + - npm install -g npm + - npm install test: override: From 059faaec5e4d672d3b46c1d34a4f821551ebdedf Mon Sep 17 00:00:00 2001 From: Joshua Pomazal Date: Sun, 14 Aug 2016 21:54:38 -0600 Subject: [PATCH 5/9] Specify node version in circle-ci --- circle.yml | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/circle.yml b/circle.yml index ba3155d..27a1a85 100644 --- a/circle.yml +++ b/circle.yml @@ -1,3 +1,7 @@ +machine: + node: + version: 6.1.0 + dependencies: pre: - npm install -g npm From 21d46fbd84a00144faf5961f3715143e14246eaf Mon Sep 17 00:00:00 2001 From: Joshua Pomazal Date: Sun, 14 Aug 2016 21:57:54 -0600 Subject: [PATCH 6/9] Specify node version in circle-ci --- circle.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/circle.yml b/circle.yml index 27a1a85..9e3f894 100644 --- a/circle.yml +++ b/circle.yml @@ -6,6 +6,7 @@ dependencies: pre: - npm install -g npm - npm install + - npm rebuild test: override: From 624180e2f47b393d892b1c2435bf2f74ab4c175f Mon Sep 17 00:00:00 2001 From: Joshua Pomazal Date: Thu, 26 Jan 2017 09:59:09 -0700 Subject: [PATCH 7/9] Added babel transform Synchronized parse Watcher for babel --- __tests__/fixtures/style-inherit.js | 2 +- __tests__/fixtures/style-test-literal.js | 2 +- __tests__/rnc.test.js | 26 ++- __tests__/wrap.test.js | 5 +- babel.js | 2 + bin/react-native-css | 4 +- build/babelTransform.js | 64 ++++++ build/client.js | 80 +++++++ build/css.js | 256 +++++++++++++++++++++++ build/index.js | 62 +++--- build/inheritance.js | 115 ++++++++++ build/utils.js | 5 +- build/watcher.js | 69 ++++++ build/wrap.js | 159 ++++++++++++++ examples/README.md | 4 +- package.json | 4 +- src/babelTransform.js | 49 +++++ src/index.js | 68 +++--- src/utils.js | 7 +- src/watcher.js | 66 ++++++ src/wrap.js | 14 +- 21 files changed, 972 insertions(+), 91 deletions(-) create mode 100644 babel.js create mode 100644 build/babelTransform.js create mode 100644 build/client.js create mode 100644 build/css.js create mode 100644 build/inheritance.js create mode 100644 build/watcher.js create mode 100644 build/wrap.js create mode 100644 src/babelTransform.js create mode 100644 src/watcher.js diff --git a/__tests__/fixtures/style-inherit.js b/__tests__/fixtures/style-inherit.js index 9f181e7..4d58fd3 100644 --- a/__tests__/fixtures/style-inherit.js +++ b/__tests__/fixtures/style-inherit.js @@ -1 +1 @@ -module.exports = {".test view text.hello&inherited":{"fontSize":14},".test view text.hello":{"color":"black"},".test view.direct text.hello:not(.direct)&inherited":{"fontSize":14},".test view.direct>text.hello:not(.direct)":{"color":"blue"},".test view.direct>text.hello":{"fontSize":16},"text&inherited":{"fontSize":14},"text":{"color":"red"},"scrollview":{"padding":10},"*":{"margin":5}} \ No newline at end of file +module.exports = require('react-native').StyleSheet.create({".test view text.hello&inherited":{"fontSize":14},".test view text.hello":{"color":"black"},".test view.direct text.hello:not(.direct)&inherited":{"fontSize":14},".test view.direct>text.hello:not(.direct)":{"color":"blue"},".test view.direct>text.hello":{"fontSize":16},"text&inherited":{"fontSize":14},"text":{"color":"red"},"scrollview":{"padding":10},"*":{"margin":5}}); \ No newline at end of file diff --git a/__tests__/fixtures/style-test-literal.js b/__tests__/fixtures/style-test-literal.js index 6e7fcb7..ac5e5ac 100644 --- a/__tests__/fixtures/style-test-literal.js +++ b/__tests__/fixtures/style-test-literal.js @@ -1 +1 @@ -module.exports = {"maincontainer":{"flex":1,"justifyContent":"center","alignItems":"center","backgroundColor":"#F5FCFF"},"rootcontainer":{"flex":1,"fontSize":18}} \ No newline at end of file +module.exports = require('react-native').StyleSheet.create({"maincontainer":{"flex":1,"justifyContent":"center","alignItems":"center","backgroundColor":"#F5FCFF"},"rootcontainer":{"flex":1,"fontSize":18}}); \ No newline at end of file diff --git a/__tests__/rnc.test.js b/__tests__/rnc.test.js index a52a985..8c7e079 100644 --- a/__tests__/rnc.test.js +++ b/__tests__/rnc.test.js @@ -7,12 +7,12 @@ global.Promise = require.requireActual('promise'); describe('React Native CSS Command-line', ()=> { it("should parse CSS", async ()=> { - let data = await css.parse('./__tests__/fixtures/style.css', './__tests__/fixtures/style.js', false, true); + let data = await css.parse({input: './__tests__/fixtures/style.css'}); expect(data).toEqual({main: {backgroundColor: '#000'}}); }); it("shoud parse SCSS", async ()=> { - let data = await css.parse('./__tests__/fixtures/style.scss', './__tests__/fixtures/stylescss.js', false, false); + let data = await css.parse({input: './__tests__/fixtures/style.scss'}); expect(data).toEqual({ description: { flex: 102, @@ -33,7 +33,7 @@ describe('React Native CSS Command-line', ()=> { }); it("Parse SCSS error", async ()=> { - let data = await css.parse('./__tests__/fixtures/style-20.scss', './__tests__/fixtures/stylescss-20.js', false, false); + let data = await css.parse({input: './__tests__/fixtures/style-20.scss'}); expect(data).toEqual({ maincontainer: { flex: 1, @@ -46,7 +46,7 @@ describe('React Native CSS Command-line', ()=> { }); it("Parse SCSS error", async ()=> { - let data = await css.parse('./__tests__/fixtures/style-dorthwein.scss', './__tests__/fixtures/stylescss-dorthwein.js', false, false); + let data = await css.parse({input: './__tests__/fixtures/style-dorthwein.scss'}); expect(data).toEqual({ center: { alignItems: 'center', @@ -58,12 +58,12 @@ describe('React Native CSS Command-line', ()=> { }); it("Parse CSS and ignore unsupported property", async ()=> { - let data = await css.parse('./__tests__/fixtures/style-unsupported.scss', './__tests__/fixtures/style-unsupported.js', false, false); + let data = await css.parse({input: './__tests__/fixtures/style-unsupported.scss'}); expect(data).toEqual({container: {background: 'white'}}); }); it("Parse CSS and turn properties into numbers", async ()=> { - let data = await css.parse('./__tests__/fixtures/style-number.scss', './__tests__/fixtures/style-number.js', false, false); + let data = await css.parse({input: './__tests__/fixtures/style-number.scss'}); expect(data).toEqual({ text: { fontSize: 12, @@ -73,7 +73,7 @@ describe('React Native CSS Command-line', ()=> { }); it("Regression test for issue #26", async ()=> { - let data = await css.parse('./__tests__/fixtures/style-test.css', './__tests__/fixtures/style-test.js', false, false); + let data = await css.parse({input: './__tests__/fixtures/style-test.css'}); expect(data).toEqual({ "row": { "top": 50, @@ -91,14 +91,18 @@ describe('React Native CSS Command-line', ()=> { }); it("Argument --literal generates a javascript literal object", async ()=> { - let data = await css.parse('./__tests__/fixtures/style-20.scss', './__tests__/fixtures/style-test-literal.js', false, true); + await css.parse({ + input: './__tests__/fixtures/style-20.scss', + output: './__tests__/fixtures/style-test-literal.js', //actually tests the output + objectLiteral: true + }); var styles = require('./fixtures/style-test-literal.js'); expect(styles.maincontainer.backgroundColor).toEqual("#F5FCFF"); }); it("Parse CSS and expand shorthand properties", async ()=> { - let data = await css.parse('./__tests__/fixtures/style-expand.css', './__tests__/fixtures/style-expand.js', false, false); + let data = await css.parse({input: './__tests__/fixtures/style-expand.css'}); expect(data).toEqual({ "container": { "paddingTop": 10, @@ -142,8 +146,8 @@ describe('React Native CSS Command-line', ()=> { }); }); - it("Test inheritance CSS", async ()=> { - let data = await css.parse('./__tests__/fixtures/style-inherit.css', './__tests__/fixtures/style-inherit.js', false, true, true); + it("Test inheritance CSS", ()=> { + let data = css.parse({input: './__tests__/fixtures/style-inherit.css', useInheritance: true}); register(data); expect(matchRules([{e: 'view', c: ['test']}, {e: 'view', c: ['test']}, { diff --git a/__tests__/wrap.test.js b/__tests__/wrap.test.js index a3724ba..206f664 100644 --- a/__tests__/wrap.test.js +++ b/__tests__/wrap.test.js @@ -14,7 +14,10 @@ global.Promise = require.requireActual('promise'); describe('wrap', ()=> { it('should pass all properties to wrapped component and add styles to the view', async ()=> { //create the style object - let styles = await css.parse('./__tests__/fixtures/style-inherit.css', null, false, true, true); + let styles = css.parse({ + input: './__tests__/fixtures/style-inherit.css', + useInheritance: true + }); //register the global sheet register(styles); diff --git a/babel.js b/babel.js new file mode 100644 index 0000000..f0408b9 --- /dev/null +++ b/babel.js @@ -0,0 +1,2 @@ +require('babel-polyfill') +module.exports = require('./build/babelTransform').default; diff --git a/bin/react-native-css b/bin/react-native-css index 49cb41c..bcd24d4 100755 --- a/bin/react-native-css +++ b/bin/react-native-css @@ -21,7 +21,7 @@ if(inputOutput) { var input = path.resolve(process.cwd(), inputArgs); var output = path.resolve(process.cwd(), outputArgs); - css.parse(input, output, prettyPrint, literalObject, useInheritance); + css.parse({input:input, output:output, prettyPrint:prettyPrint, literalObject:literalObject, useInheritance:useInheritance}); if(watch) { console.log('Watching for changes on:'.green, inputArgs); @@ -31,7 +31,7 @@ if(inputOutput) { ignored: /[\/\\]\./, persistent: true }).on('change', function(path) { console.log('File', inputArgs, 'has been changed'.green); - css.parse(input, output, prettyPrint, literalObject, useInheritance); + css.parse({input:input, output:output, prettyPrint:prettyPrint, literalObject:literalObject, useInheritance:useInheritance}); }); } }else if(help ) { diff --git a/build/babelTransform.js b/build/babelTransform.js new file mode 100644 index 0000000..313e1c9 --- /dev/null +++ b/build/babelTransform.js @@ -0,0 +1,64 @@ +Object.defineProperty(exports,"__esModule",{value:true});exports.default= + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +function(_ref){var t=_ref.types; +return{ +visitor:{ +ImportDeclaration:function ImportDeclaration(transformPath,_ref2){var file=_ref2.file; +var resolvePath=transformPath.node.source.value; +if(resolvePath.startsWith('css!')){ +resolvePath=resolvePath.substr(4); +var name=resolvePath.replace(/\.\.\/|\.\//g,'').replace(/\//g,'_').split('.')[0]; +var absolutePath=_path2.default.resolve(resolvePath), +relativePath=_path2.default.dirname(_path2.default.relative('./',file.opts.filename))+'/_css/'+name+'.js'; +startTransform(absolutePath,_path2.default.resolve('./_css/'+name+'.js')); + +var expression=t.callExpression(t.memberExpression(t.callExpression(t.identifier('require'),[t.stringLiteral('react-native-css')]),t.identifier('register')),[t.callExpression(t.identifier('require'),[t.stringLiteral(relativePath)])]); +transformPath.replaceWith(expression); +} +}}}; + + +};var _index=require('./index');var _index2=_interopRequireDefault(_index);var _fs=require('fs');var _fs2=_interopRequireDefault(_fs);var _path=require('path');var _path2=_interopRequireDefault(_path);var _child_process=require('child_process');function _interopRequireDefault(obj){return obj&&obj.__esModule?obj:{default:obj};}var css=new _index2.default();function runWatcher(input,output){console.log('starting watcher');var ls=(0,_child_process.spawn)('node',['./watcher.js',input,output],{detached:true,cwd:__dirname});ls.stdout.on('data',function(data){console.log('stdout: '+data);});ls.stderr.on('data',function(data){console.log('stderr: '+data);});ls.on('close',function(code){console.log('child process exited with code '+code);});}function startTransform(input,output){var transformOnly=arguments.length<=2||arguments[2]===undefined?false:arguments[2];console.log('transform');try{_fs2.default.mkdirSync('./_css');}catch(e){}try{css.parse({input:input,output:output,useInheritance:true});}catch(e){console.log('hi');}if(process.env.BABEL_ENV==='development'&&!transformOnly){runWatcher(input,output);}return true;}// \ No newline at end of file diff --git a/build/client.js b/build/client.js new file mode 100644 index 0000000..cb5b43c --- /dev/null +++ b/build/client.js @@ -0,0 +1,80 @@ +var _reactNative=require("react-native");var _reactNative2=_interopRequireDefault(_reactNative); +var _css=require("./css");var _css2=_interopRequireDefault(_css); +var _wrap=require("./wrap");var _wrap2=_interopRequireDefault(_wrap);function _interopRequireDefault(obj){return obj&&obj.__esModule?obj:{default:obj};} + +//These are all the components in react native to wrap with the styled proxy component +var componentsToWrap=["ActivityIndicator", +"ActivityIndicatorIOS", +"ART", +"DatePickerIOS", +"DrawerLayoutAndroid", +"Image", +"ImageEditor", +"ImageStore", +"KeyboardAvoidingView", +"ListView", +"MapView", +"Modal", +"Navigator", +"NavigatorIOS", +"Picker", +"PickerIOS", +"ProgressBarAndroid", +"ProgressViewIOS", +"ScrollView", +"SegmentedControlIOS", +"Slider", +"SliderIOS", +"SnapshotViewIOS", +"Switch", +"RecyclerViewBackedScrollView", +"RefreshControl", +"StatusBar", +"SwipeableListView", +"SwitchAndroid", +"SwitchIOS", +"TabBarIOS", +"Text", +"TextInput", +"ToastAndroid", +"ToolbarAndroid", +"Touchable", +"TouchableHighlight", +"TouchableNativeFeedback", +"TouchableOpacity", +"TouchableWithoutFeedback", +"View", +"ViewPagerAndroid", +"WebView"]; + + +var StyledComponents={}; +var wrappedComponents={}; +//wrap all the UI components in React Native +componentsToWrap.forEach(function(name){ +Object.defineProperty(StyledComponents,name,{ +get:function get(){ +return wrappedComponents[name]||(wrappedComponents[name]=(0,_wrap2.default)(name,_reactNative2.default[name])); +}}); + +}); + +//Proxy all other APIs, etc. so it can be a drop-in replacement for import {...} from 'react-native. All properties +// use a getter so APIs are only initialized when explicitly imported +Object.keys(_reactNative2.default).forEach(function(name){ +if(componentsToWrap.indexOf(name)===-1){ +Object.defineProperty(StyledComponents,name,{ +get:function get(){ +return _reactNative2.default[name]; +}}); + +} +}); + +//Add in all the utility functions +StyledComponents.unregister=_css.unregister; +StyledComponents.register=_css.register; +StyledComponents.css=_css2.default; +StyledComponents.wrap=_wrap2.default; + +module.exports=StyledComponents; \ No newline at end of file diff --git a/build/css.js b/build/css.js new file mode 100644 index 0000000..e30b898 --- /dev/null +++ b/build/css.js @@ -0,0 +1,256 @@ +Object.defineProperty(exports,"__esModule",{value:true});exports.matchingRules=exports.unregister=exports.register=undefined;var _extends=Object.assign||function(target){for(var i=1;itext.hello text +Object.keys(styles).forEach(function(key,o){ +if(key.endsWith("&inherited")){ +return; +} +var path=[]; +key.replace(/\s*>\s*/g,">").replace(/\s+/g," ").split(" ").forEach(function(part){ +var immediates=part.split(">"); +immediates.forEach(function(immediate,i){ +var pseudo=immediate.split(":"), +normal=pseudo.shift(), +classes=normal.split(".").filter(function(a){return a;}), +element=normal.charAt(0)==="."?"*":classes.shift(); +//prep pseudo functions +pseudo=pseudo.map(function(pseudo){return pseudos.find(function(test){return test.regex.exec(pseudo);});}).filter(function(test){return test;}).map(function(test){ +var matches=test.regex.exec(pseudo); +if(matches){ +return test.match.bind(null,matches); +} +}).filter(function(ps){return ps;}); + +path.push({ +e:element, +c:classes, +ps:pseudo.length&&pseudo, +i:immediates.length>0&&i!=immediates.length-1}); + +}); +}); +path=path.reverse(); +var first=path.shift(); +//Calculate specificity +var specificity=0; +//add +10 for classes +specificity+=first.c.length*10; +if(path.length>0&&path.i){ +specificity+=5; +} +if(first.e!=="*"){ +specificity++; +} + +path.forEach(function(a){ +specificity+=a.c.length; +}); + +if(!rules[first.e]){ +rules[first.e]=[]; +} +rules[first.e].push(_extends({},first,{a:path,s:specificity,o:o,k:key})); +}); +return rules; +} + +/** + * Test to see if the target matches the matching element. Compares element type and classes. The target must have + * all the classes in matching, but can have more. + * @param target + * @param matching + * @return {boolean} + */ +function elementMatches(target,matching){ +if(target.e!==matching.e&&matching.e!=="*"){ +return false; +} +if(matching.c&&!matching.c.every(function(className){return target.c&&target.c.indexOf(className)>-1;})){ +return false; +} +return!matching.ps||matching.ps.every(function(match){return match(target);}); +} + +/** + * Finds all the rules that match the passed path, sorted by specificity asc. + * @param {[{e:String,c:[String],p:{},i:Number,f:Boolean,l:Boolean}]} path + * @return {[String]} Array of references to StyleSheets + */ +function matchingRules(path,key){ +try{ +if(!key){ +key=JSON.stringify(path); +} +if(styleCache[key]){ +return styleCache[key]; +} + +path=path.slice(0).reverse(); +if(!path[0]){ +return styleCache[key]=null; +} +var element=path[0].e; + + +//find matching +var starting=(ruleKeys[element]||[]).concat(ruleKeys["*"]||[]); +var matchingStyles=starting.filter(function(rule){ +if(!elementMatches(path[0],rule)){ +return; +} +var index=1,lastRelative=-1; +return rule.a.every(function(ancestor,i){ +main:for(;index-1){ +//push the path up to the next matching +index++; +var ancestors=rule.a.slice(lastRelative,i-1), +directAncestor=ancestor.shift(); +for(;index-1){ +sheets.splice(index,1); +} +}); +flattenSheets(); +}exports.default= + +css;exports. +register=register;exports.unregister=unregister;exports.matchingRules=matchingRules; \ No newline at end of file diff --git a/build/index.js b/build/index.js index b8312ce..024d2a2 100644 --- a/build/index.js +++ b/build/index.js @@ -3,33 +3,31 @@ var _toCamelCase=require('to-camel-case');var _toCamelCase2=_interopRequireDefau var _utils=require('./utils');var _utils2=_interopRequireDefault(_utils); var _inheritance=require('./inheritance');var _inheritance2=_interopRequireDefault(_inheritance);function _interopRequireDefault(obj){return obj&&obj.__esModule?obj:{default:obj};}function _classCallCheck(instance,Constructor){if(!(instance instanceof Constructor)){throw new TypeError("Cannot call a class as a function");}}var -ReactNativeCss=function(){function ReactNativeCss(){_classCallCheck(this,ReactNativeCss);}_createClass(ReactNativeCss,[{key:'parse',value:function parse( - -input){var output=arguments.length<=1||arguments[1]===undefined?'./style.js':arguments[1];var prettyPrint=arguments.length<=2||arguments[2]===undefined?false:arguments[2];var _this=this;var literalObject=arguments.length<=3||arguments[3]===undefined?false:arguments[3];var useInheritance=arguments.length<=4||arguments[4]===undefined?false:arguments[4];var _require$renderSync,css,styleSheet;return regeneratorRuntime.async(function parse$(_context){while(1){switch(_context.prev=_context.next){case 0:if(! -_utils2.default.contains(input,/scss/)){_context.next=8;break;}_require$renderSync= +ReactNativeCss=function(){function ReactNativeCss(){_classCallCheck(this,ReactNativeCss);}_createClass(ReactNativeCss,[{key:'parse',value:function parse(_ref) +{var input=_ref.input;var _ref$output=_ref.output;var output=_ref$output===undefined?'./style.js':_ref$output;var _ref$prettyPrint=_ref.prettyPrint;var prettyPrint=_ref$prettyPrint===undefined?false:_ref$prettyPrint;var _ref$literalObject=_ref.literalObject;var literalObject=_ref$literalObject===undefined?false:_ref$literalObject;var _ref$useInheritance=_ref.useInheritance;var useInheritance=_ref$useInheritance===undefined?false:_ref$useInheritance; +console.log(input,output); +var data=void 0; +if(_utils2.default.contains(input,/scss/)){var _require$renderSync= require('node-sass').renderSync({ file:input, -outputStyle:'compressed'});css=_require$renderSync.css; - - -styleSheet=this.toJSS(css.toString(),useInheritance); -if(output) -_utils2.default.outputReactFriendlyStyle(styleSheet,output,prettyPrint,literalObject);return _context.abrupt('return', -styleSheet);case 8:_context.next=10;return regeneratorRuntime.awrap( +outputStyle:'compressed'});var css=_require$renderSync.css; - -new Promise(function(resolve,reject){return _utils2.default.readFile(input,function(err,data){ -if(err){ -return reject(err); +data=css.toString(); +}else{ +console.log('awaiting promise'); +data=_utils2.default.readFile(input); +console.log('promise done'); } -var styleSheet=_this.toJSS(data,useInheritance); -if(output) -_utils2.default.outputReactFriendlyStyle(styleSheet,output,prettyPrint,literalObject); -resolve(styleSheet); -});}));case 10:return _context.abrupt('return',_context.sent);case 11:case'end':return _context.stop();}}},null,this);}},{key:'toJSS',value:function toJSS( +console.log('stylesheet',data.length); +var styleSheet=this.toJSS(data,useInheritance); +if(output){ +_utils2.default.outputReactFriendlyStyle(styleSheet,output,prettyPrint,literalObject); +} +return styleSheet; +}},{key:'toJSS',value:function toJSS( stylesheetString){var useInheritance=arguments.length<=1||arguments[1]===undefined?false:arguments[1]; var directions=['top','right','bottom','left']; @@ -65,7 +63,8 @@ var directionMaps={ -//Convert the shorthand property to the individual directions, handles edge cases, i.e. border-width and border-radius +//Convert the shorthand property to the individual directions, handles edge cases, i.e. border-width and +// border-radius function directionToPropertyName(property,direction){ var names=property.split('-'); names.splice(1,0,directionMaps[property]?directionMaps[property][direction]:direction); @@ -73,15 +72,18 @@ return(0,_toCamelCase2.default)(names.join('-')); } // CSS properties that are not supported by React Native -// The list of supported properties is at https://facebook.github.io/react-native/docs/style.html#supported-properties +// The list of supported properties is at +// https://facebook.github.io/react-native/docs/style.html#supported-properties var unsupported=['display'];var _ParseCSS= (0,_cssParse2.default)(_utils2.default.clean(stylesheetString));var stylesheet=_ParseCSS.stylesheet; var JSONResult={}; -for(var _iterator=stylesheet.rules,_isArray=Array.isArray(_iterator),_i=0,_iterator=_isArray?_iterator:_iterator[typeof Symbol==='function'?Symbol.iterator:'@@iterator']();;){var _ref;if(_isArray){if(_i>=_iterator.length)break;_ref=_iterator[_i++];}else{_i=_iterator.next();if(_i.done)break;_ref=_i.value;}var rule=_ref; -if(rule.type!=='rule')continue;var _loop=function _loop(_selector){ +for(var _iterator=stylesheet.rules,_isArray=Array.isArray(_iterator),_i=0,_iterator=_isArray?_iterator:_iterator[typeof Symbol==='function'?Symbol.iterator:'@@iterator']();;){var _ref2;if(_isArray){if(_i>=_iterator.length)break;_ref2=_iterator[_i++];}else{_i=_iterator.next();if(_i.done)break;_ref2=_i.value;}var rule=_ref2; +if(rule.type!=='rule'){ +continue; +}var _loop=function _loop(_selector){ if(!useInheritance){ @@ -92,7 +94,9 @@ var styles=JSONResult[_selector]=JSONResult[_selector]||{};var _loop2=function _ declaration){ -if(declaration.type!=='declaration')return'continue'; +if(declaration.type!=='declaration'){ +return'continue'; +} var value=declaration.value; var property=declaration.property; @@ -118,7 +122,9 @@ return'continue'; } } -if(_utils2.default.arrayContains(property,unsupported))return'continue'; +if(_utils2.default.arrayContains(property,unsupported)){ +return'continue'; +} if(_utils2.default.arrayContains(property,numberize)){ styles[(0,_toCamelCase2.default)(property)]=parseFloat(value.replace(/px|\s*/g,'')); @@ -173,8 +179,8 @@ declaration.value=parseFloat(declaration.value); } styles[(0,_toCamelCase2.default)(property)]=declaration.value; -}};for(var _iterator3=rule.declarations,_isArray3=Array.isArray(_iterator3),_i3=0,_iterator3=_isArray3?_iterator3:_iterator3[typeof Symbol==='function'?Symbol.iterator:'@@iterator']();;){var _ref3;if(_isArray3){if(_i3>=_iterator3.length)break;_ref3=_iterator3[_i3++];}else{_i3=_iterator3.next();if(_i3.done)break;_ref3=_i3.value;}var declaration=_ref3;var _ret2=_loop2(declaration);if(_ret2==='continue')continue; -}selector=_selector;};for(var _iterator2=rule.selectors,_isArray2=Array.isArray(_iterator2),_i2=0,_iterator2=_isArray2?_iterator2:_iterator2[typeof Symbol==='function'?Symbol.iterator:'@@iterator']();;){var _ref2;if(_isArray2){if(_i2>=_iterator2.length)break;_ref2=_iterator2[_i2++];}else{_i2=_iterator2.next();if(_i2.done)break;_ref2=_i2.value;}var selector=_ref2;var values;var length;_loop(selector); +}};for(var _iterator3=rule.declarations,_isArray3=Array.isArray(_iterator3),_i3=0,_iterator3=_isArray3?_iterator3:_iterator3[typeof Symbol==='function'?Symbol.iterator:'@@iterator']();;){var _ref4;if(_isArray3){if(_i3>=_iterator3.length)break;_ref4=_iterator3[_i3++];}else{_i3=_iterator3.next();if(_i3.done)break;_ref4=_i3.value;}var declaration=_ref4;var _ret2=_loop2(declaration);if(_ret2==='continue')continue; +}selector=_selector;};for(var _iterator2=rule.selectors,_isArray2=Array.isArray(_iterator2),_i2=0,_iterator2=_isArray2?_iterator2:_iterator2[typeof Symbol==='function'?Symbol.iterator:'@@iterator']();;){var _ref3;if(_isArray2){if(_i2>=_iterator2.length)break;_ref3=_iterator2[_i2++];}else{_i2=_iterator2.next();if(_i2.done)break;_ref3=_i2.value;}var selector=_ref3;var values;var length;_loop(selector); } } return useInheritance?(0,_inheritance2.default)(JSONResult):JSONResult; diff --git a/build/inheritance.js b/build/inheritance.js new file mode 100644 index 0000000..95ffccc --- /dev/null +++ b/build/inheritance.js @@ -0,0 +1,115 @@ +Object.defineProperty(exports,"__esModule",{value:true});exports.numeric=undefined;var _extends=Object.assign||function(target){for(var i=1;i\s*/g,">").replace(/\s+/g," ");select.split(" ").forEach(function(part){part.split(">").forEach(function(key,i){target=target[key]||(target[key]={});target._isDirectDescendent=i>0;});});_extends(target,styles[selector]);});};for(var _iterator=Object.keys(styles),_isArray=Array.isArray(_iterator),_i=0,_iterator=_isArray?_iterator:_iterator[typeof Symbol==="function"?Symbol.iterator:"@@iterator"]();;){var _ref;if(_isArray){if(_i>=_iterator.length)break;_ref=_iterator[_i++];}else{_i=_iterator.next();if(_i.done)break;_ref=_i.value;}var selector=_ref;_loop(selector);}return tree;}function getInheritedProperties(state,node,rootState){state=_extends({},state);textProps.forEach(function(prop){if(node&&node[prop]){var value=node[prop];if(numericProps.indexOf(prop)>-1&&typeof value==="string"){var numericValue=parseFloat(value.replace(/[^0-9\.]/g,"")),isRelativeToRoot=value.indexOf("rem")>-1,isRelative=value.indexOf("em")>-1;if(isRelativeToRoot){numericValue*=rootState[prop];}else if(isRelative){numericValue*=state[prop]||0;}node[prop]=~~numericValue;}state[prop]=node[prop];}});return state;}function cleanInherits(inherited,node){var keys=Object.keys(node),clean={};Object.keys(inherited).forEach(function(key){if(keys.indexOf(key)===-1){clean[key]=inherited[key];}});return clean;}function inherit(node,state,rootState){if(!state){rootState=_extends({},defaultRootState,node.root);rootState=state=getInheritedProperties(rootState,node.root,rootState);}var cleanNode={};for(var _iterator2=Object.keys(node),_isArray2=Array.isArray(_iterator2),_i2=0,_iterator2=_isArray2?_iterator2:_iterator2[typeof Symbol==="function"?Symbol.iterator:"@@iterator"]();;){var _ref2;if(_isArray2){if(_i2>=_iterator2.length)break;_ref2=_iterator2[_i2++];}else{_i2=_iterator2.next();if(_i2.done)break;_ref2=_i2.value;}var key=_ref2;var childNode=node[key];var textPsuedo=key.toLowerCase().indexOf(":text")>-1;key=key.replace(/:text/i,"");if((key.toLowerCase().indexOf("text")===0||textPsuedo)&&typeof childNode==="object"){cleanNode[key+"&inherited"]=cleanInherits(getInheritedProperties(state,childNode,rootState),childNode);cleanNode[key]=childNode;}else if(childNode&&typeof childNode==="object"){cleanNode[key]=inherit(childNode,getInheritedProperties(state,childNode,rootState),rootState);}else if(textProps.indexOf(key)===-1){cleanNode[key]=childNode;}}return cleanNode;}function flatten(tree){var flatObject=arguments.length<=1||arguments[1]===undefined?{}:arguments[1];var name=arguments.length<=2||arguments[2]===undefined?"":arguments[2];var flatStyle={};for(var _iterator3=Object.keys(tree),_isArray3=Array.isArray(_iterator3),_i3=0,_iterator3=_isArray3?_iterator3:_iterator3[typeof Symbol==="function"?Symbol.iterator:"@@iterator"]();;){var _ref3;if(_isArray3){if(_i3>=_iterator3.length)break;_ref3=_iterator3[_i3++];}else{_i3=_iterator3.next();if(_i3.done)break;_ref3=_i3.value;}var key=_ref3;var value=tree[key],isDirectDescendent=value._isDirectDescendent;if(typeof value==="object"){flatten(value,flatObject,name+(isDirectDescendent?">":" ")+key);}else if(key!=="_isDirectDescendent"){flatStyle[key]=value;}}if(Object.keys(flatStyle).length>0){flatObject[name.trim()]=flatStyle;}return flatObject;}function inheritance(styles){return flatten(inherit(expandStyles(styles)));}exports.numeric=numeric; \ No newline at end of file diff --git a/build/utils.js b/build/utils.js index 6681a6f..3d96d28 100644 --- a/build/utils.js +++ b/build/utils.js @@ -19,7 +19,7 @@ return string.replace(/\r?\n|\r/g,""); }},{key:"readFile",value:function readFile( file,cb){ -_fs2.default.readFile(file,"utf8",cb); +return _fs2.default.readFileSync(file,"utf8"); }},{key:"outputReactFriendlyStyle",value:function outputReactFriendlyStyle( style,outputFile,prettyPrint,literalObject){ @@ -28,8 +28,9 @@ var jsonOutput=JSON.stringify(style,null,indentation); var output="module.exports = "; output+=literalObject?""+jsonOutput:"require('react-native').StyleSheet.create("+jsonOutput+");"; // Write to file -if(outputFile) +if(outputFile){ _fs2.default.writeFileSync(outputFile,output); +} return output; }},{key:"contains",value:function contains( diff --git a/build/watcher.js b/build/watcher.js new file mode 100644 index 0000000..3bad637 --- /dev/null +++ b/build/watcher.js @@ -0,0 +1,69 @@ +require('babel-polyfill'); +var _index=require('./index');var _index2=_interopRequireDefault(_index); +var _fs=require('fs');var _fs2=_interopRequireDefault(_fs); +var _request=require('request');var _request2=_interopRequireDefault(_request); +var _chokidar=require('chokidar');var _chokidar2=_interopRequireDefault(_chokidar);function _interopRequireDefault(obj){return obj&&obj.__esModule?obj:{default:obj};} + +console.log('started watcher'); + +var css=new _index2.default(); + +/** + * Checks if the packager is running, if not there really isn't point of keeping this thing alive. + * @return {Promise} + */ +function isServerRunning(){return regeneratorRuntime.async(function isServerRunning$(_context){while(1){switch(_context.prev=_context.next){case 0:return _context.abrupt('return', +new Promise(function(resolve){return(0,_request2.default)('http://localhost:8081/status',function(err,response,body){ +resolve(body==="packager-status:running"); +});}));case 1:case'end':return _context.stop();}}},null,this);} + + +function run(){var _this=this;var input,output,name,pidFile, + + + + + + + + + + + +check,watcher;return regeneratorRuntime.async(function run$(_context3){while(1){switch(_context3.prev=_context3.next){case 0:check=function check(){ +try{ +return process.kill(~~_fs2.default.readFileSync(pidFile,'utf8'),0); +} +catch(e){ +return e.code==='EPERM'; +} +};input=process.argv[2],output=process.argv[3];if(!(!input||!output)){_context3.next=4;break;}return _context3.abrupt('return');case 4://check pid +name=input.replace(/[^0-9a-z]/g,''),pidFile='/tmp/rnc_watch_'+name+'.pid';if(! +check()){_context3.next=7;break;}return _context3.abrupt('return', +process.exit(0));case 7: + + +_fs2.default.writeFileSync(pidFile,process.pid,'utf8'); + +//check if server is up +_context3.next=10;return regeneratorRuntime.awrap(isServerRunning());case 10:if(_context3.sent){_context3.next=13;break;} +console.warn('Server isn\'t running.');return _context3.abrupt('return', +process.exit(0));case 13: + + +watcher=_chokidar2.default.watch(input,{ +ignored:/[\/\\]\./,persistent:true}). +on('change',function(){ +css.parse({input:input,output:output,useInheritance:true}); +}); + +setInterval(function _callee(){return regeneratorRuntime.async(function _callee$(_context2){while(1){switch(_context2.prev=_context2.next){case 0:_context2.next=2;return regeneratorRuntime.awrap( +isServerRunning());case 2:if(_context2.sent){_context2.next=4;break;} +process.exit(0);case 4:case'end':return _context2.stop();}}},null,_this);}, + +10000);case 15:case'end':return _context3.stop();}}},null,this);} + + + + +run(); \ No newline at end of file diff --git a/build/wrap.js b/build/wrap.js new file mode 100644 index 0000000..fff6167 --- /dev/null +++ b/build/wrap.js @@ -0,0 +1,159 @@ +Object.defineProperty(exports,"__esModule",{value:true});var _extends=Object.assign||function(target){for(var i=1;i"+element.e+"."+element.c.join(".")+":"+(element.i||"")+":"+(element.f||"")+":"+(element.l||""); +if(this.cssPathKey!==key){ +//Create the path or use the cache. Since this can be called thousands of times, the cache reduces the more +// expensive array generations and reduces memory consumption by resusing arrays. Because of this it is +// important that the arrays are treated as immutable. +this.cssPath=pathCache[key]||(pathCache[key]=(this.context.cssPath||[{e:"root"}]).concat([element])); +this.cssPathKey=key; +} +}},{key:"render",value:function render() + +{var _this2=this; +return _react2.default.createElement(WrappedComponent,_extends({},this.props,{ref:function ref(_ref){return _this2._root=_ref;}, +style:this.props.style?[this.styles,this.props.style]:this.styles})); +}},{key:"pathKey",get:function get(){if(!this.cssPathKey||this.context.cssPathKey!==this._lastPathKey){this.createPath(this.props);this._lastPathKey=this.context.cssPathKey;}return this.cssPathKey;}},{key:"styles",get:function get(){if(!this.style||this._lastKey!==this.pathKey){if(!this.cssPath){this.createPath(this.props);}var style=(0,_css.matchingRules)(this.cssPath,this.cssPathKey);this.style=this.props.style?(0,_css2.default)(style,this.props.style):style;this._lastKey=this.pathKey;}return this.style;}}]);return StyledComponent;}(_react.Component); + + +(0,_hoistNonReactStatics2.default)(StyledComponent,WrappedComponent); +StyledComponent.WrappedComponent=WrappedComponent; +StyledComponent.displayName=name; +StyledComponent.childContextTypes={ +cssPath:_react2.default.PropTypes.array, +cssPathKey:_react2.default.PropTypes.string}; + +//Make sure we are getting our parent's path +StyledComponent.contextTypes={cssPath:_react2.default.PropTypes.array,cssPathKey:_react2.default.PropTypes.string}; +return StyledComponent; +} \ No newline at end of file diff --git a/examples/README.md b/examples/README.md index 2bf33a4..f00253f 100644 --- a/examples/README.md +++ b/examples/README.md @@ -56,8 +56,8 @@ root { } } -//You need to either style a text component or specify that a selector is a // text element using the :text pseudo selector. -.someTextClass:text { +//You need to explicitly style a text component for text properties to apply +text.someTextClass { font-size:20px; } diff --git a/package.json b/package.json index 2171e91..1d29d49 100644 --- a/package.json +++ b/package.json @@ -12,17 +12,19 @@ "author": "Sabeur Thabti", "license": "MIT", "dependencies": { + "babel-polyfill": "^6.13.0", "chokidar": "^1.4.1", "colors": "^1.1.2", "css-parse": "^2.0.0", "cssauron": "^1.4.0", + "debug": "^2.2.0", "hoist-non-react-statics": "^1.2.0", "minimist": "^1.1.1", "node-sass": "^3.3.2", "npm-web-api": "^0.1.1", "react": "~15.2.1", "react-native": "^0.31.0", - "request": "^2.67.0", + "request": "^2.74.0", "to-camel-case": "~1.0.0" }, "devDependencies": { diff --git a/src/babelTransform.js b/src/babelTransform.js new file mode 100644 index 0000000..77369d7 --- /dev/null +++ b/src/babelTransform.js @@ -0,0 +1,49 @@ +import RNC from './index'; +import fs from 'fs'; +import path from 'path'; +import {spawn} from 'child_process'; +const css = new RNC(); + +function runWatcher(input, output) { + spawn('node', ['./watcher.js', input, output], {detached: true, cwd: __dirname}); +} + +function startTransform(input, output) { + try { + fs.mkdirSync('./_css'); + } catch (e) { + } + + css.parse({ + input, + output, + useInheritance: true + }); + + if (process.env.BABEL_ENV === 'development') { + runWatcher(input, output); + } + return true; +} + +// + +export default function ({ types: t }) { + return { + visitor: { + ImportDeclaration (transformPath, {file}) { + let resolvePath = transformPath.node.source.value; + if (resolvePath.startsWith('css!')) { + resolvePath = resolvePath.substr(4); + let name = resolvePath.replace(/\.\.\/|\.\//g, '').replace(/\//g, '_').split('.')[0]; + let absolutePath = path.resolve(resolvePath), + relativePath = `${path.dirname(path.relative('./', file.opts.filename))}/_css/${name}.js`; + startTransform(absolutePath, path.resolve(`./_css/${name}.js`)); + + let expression = (t.callExpression(t.memberExpression(t.callExpression(t.identifier('require'), [t.stringLiteral('react-native-css')]), t.identifier('register')), [t.callExpression(t.identifier('require'), [t.stringLiteral(relativePath)])])); + transformPath.replaceWith(expression); + } + } + } + }; +} diff --git a/src/index.js b/src/index.js index 984a4e5..b5bd884 100644 --- a/src/index.js +++ b/src/index.js @@ -5,36 +5,32 @@ import inheritance,{numeric} from './inheritance'; export default class ReactNativeCss { - async parse(input, output = './style.js', prettyPrint = false, literalObject = false, useInheritance = false) { - if(utils.contains(input, /scss/)) { - + parse({input, output, prettyPrint = false, literalObject = false, useInheritance = false}) { + if (!input) { + throw new Error('An input file is required.'); + } + let data; + if (utils.contains(input, /scss/)) { let {css} = require('node-sass').renderSync({ file: input, outputStyle: 'compressed' }); - - let styleSheet = this.toJSS(css.toString(),useInheritance); - if(output) - utils.outputReactFriendlyStyle(styleSheet, output, prettyPrint, literalObject); - return styleSheet; - + data = css.toString(); } else { - return await new Promise((resolve,reject)=>utils.readFile(input, (err, data) => { - if (err) { - return reject(err); - } - let styleSheet = this.toJSS(data,useInheritance); - if(output) - utils.outputReactFriendlyStyle(styleSheet, output, prettyPrint, literalObject); - resolve(styleSheet); - })); + data = utils.readFile(input); } + + let styleSheet = this.toJSS(data, useInheritance); + if (output) { + utils.outputReactFriendlyStyle(styleSheet, output, prettyPrint, literalObject); + } + return styleSheet; } - toJSS(stylesheetString,useInheritance=false) { + toJSS(stylesheetString, useInheritance = false) { const directions = ['top', 'right', 'bottom', 'left']; const changeArr = ['margin', 'padding', 'border-width', 'border-radius']; - const numberize = utils.filterArray(['width', 'height', 'font-size', 'line-height'].concat(directions),useInheritance?numeric:[]); + const numberize = utils.filterArray(['width', 'height', 'font-size', 'line-height'].concat(directions), useInheritance ? numeric : []); //special properties and shorthands that need to be broken down separately const specialProperties = {}; ['border', 'border-top', 'border-right', 'border-bottom', 'border-left'].forEach(name=> { @@ -57,23 +53,25 @@ export default class ReactNativeCss { //map of properties that when expanded use different directions than the default Top,Right,Bottom,Left. const directionMaps = { - 'border-radius':{ - 'Top':'top-left', - 'Right':'top-right', - 'Bottom':'bottom-right', + 'border-radius': { + 'Top': 'top-left', + 'Right': 'top-right', + 'Bottom': 'bottom-right', 'Left': 'bottom-left' } }; - //Convert the shorthand property to the individual directions, handles edge cases, i.e. border-width and border-radius - function directionToPropertyName(property,direction){ + //Convert the shorthand property to the individual directions, handles edge cases, i.e. border-width and + // border-radius + function directionToPropertyName(property, direction) { let names = property.split('-'); - names.splice(1,0,directionMaps[property]?directionMaps[property][direction]:direction); + names.splice(1, 0, directionMaps[property] ? directionMaps[property][direction] : direction); return toCamelCase(names.join('-')); } // CSS properties that are not supported by React Native - // The list of supported properties is at https://facebook.github.io/react-native/docs/style.html#supported-properties + // The list of supported properties is at + // https://facebook.github.io/react-native/docs/style.html#supported-properties const unsupported = ['display']; let {stylesheet} = ParseCSS(utils.clean(stylesheetString)); @@ -81,7 +79,9 @@ export default class ReactNativeCss { let JSONResult = {}; for (let rule of stylesheet.rules) { - if (rule.type !== 'rule') continue; + if (rule.type !== 'rule') { + continue; + } for (let selector of rule.selectors) { if (!useInheritance) { @@ -92,14 +92,16 @@ export default class ReactNativeCss { for (let declaration of rule.declarations) { - if (declaration.type !== 'declaration') continue; + if (declaration.type !== 'declaration') { + continue; + } let value = declaration.value; let property = declaration.property; if (specialProperties[property]) { let special = specialProperties[property], - matches = special.regex.exec(value); + matches = special.regex.exec(value); if (matches) { if (typeof special.map === 'function') { special.map(matches, styles, rule.declarations); @@ -118,7 +120,9 @@ export default class ReactNativeCss { } } - if (utils.arrayContains(property, unsupported)) continue; + if (utils.arrayContains(property, unsupported)) { + continue; + } if (utils.arrayContains(property, numberize)) { styles[toCamelCase(property)] = parseFloat(value.replace(/px|\s*/g, '')); diff --git a/src/utils.js b/src/utils.js index b0665aa..8784929 100644 --- a/src/utils.js +++ b/src/utils.js @@ -18,8 +18,8 @@ export default class Utils { return string.replace(/\r?\n|\r/g, ""); } - static readFile(file, cb) { - fs.readFile(file, "utf8", cb); + static readFile(file) { + return fs.readFileSync(file, "utf8"); } static outputReactFriendlyStyle(style, outputFile, prettyPrint, literalObject) { @@ -28,8 +28,9 @@ export default class Utils { var output = "module.exports = "; output += (literalObject) ? `${jsonOutput}` : `require('react-native').StyleSheet.create(${jsonOutput});`; // Write to file - if(outputFile) + if (outputFile) { fs.writeFileSync(outputFile, output); + } return output; } diff --git a/src/watcher.js b/src/watcher.js new file mode 100644 index 0000000..8f90d43 --- /dev/null +++ b/src/watcher.js @@ -0,0 +1,66 @@ +import 'babel-polyfill'; +import RNC from './index'; +import fs from 'fs'; +import request from 'request'; +import chokidar from 'chokidar'; + +const css = new RNC(); + +/** + * Checks if the packager is running, if not there really isn't point of keeping this thing alive. + * @return {Promise} + */ +function isServerRunning() { + return new Promise(resolve=>request('http://localhost:8081/status', (err, response, body)=> { + resolve(body === "packager-status:running"); + })); +} + +async function run() { + let input = process.argv[2], + output = process.argv[3]; + + if (!input || !output) { + return; + } + + //check pid + let name = input.replace(/[^0-9a-z]/g, ''), + pidFile = `/tmp/rnc_watch_${name}.pid`; + + function check() { + try { + return process.kill(~~fs.readFileSync(pidFile, 'utf8'), 0); + } + catch (e) { + return e.code === 'EPERM'; + } + } + + if (check()) { + return process.exit(0); + } + + fs.writeFileSync(pidFile, process.pid, 'utf8'); + + //check if server is up + if (!(await isServerRunning())) { + return process.exit(0); + } + + chokidar.watch(input, { + ignored: /[\/\\]\./, persistent: true + }).on('change', function () { + css.parse({input, output, useInheritance: true}); + }); + + setInterval(async ()=> { + if (!(await isServerRunning())) { + process.exit(0); + } + }, 10000); + +} + + +run(); diff --git a/src/wrap.js b/src/wrap.js index 1e54462..e82080b 100644 --- a/src/wrap.js +++ b/src/wrap.js @@ -29,14 +29,14 @@ function normalizeClassNames(classNames) { } function indexOf(instance, children) { - let index = 0 + let index = 0; for (let key in children) { if (children[key]._mountOrder === instance._reactInternalInstance._mountOrder) { return index; } - index++ + index++; } - return -1 + return -1; } const pathCache = {}; @@ -115,11 +115,11 @@ export default function wrap(name, WrappedComponent) { */ createPath(props) { let index = -1, - count = 1 + count = 1; if (this._reactInternalInstance && this._reactInternalInstance._hostParent) { - let children = this._reactInternalInstance._hostParent._renderedChildren - count = Object.keys(children).length - index = indexOf(this, children) + let children = this._reactInternalInstance._hostParent._renderedChildren; + count = Object.keys(children).length; + index = indexOf(this, children); } let element = { e: name.toLowerCase(), From bb9165d21a66166d5a295ca9bb9bdc1793416a6f Mon Sep 17 00:00:00 2001 From: Joshua Pomazal Date: Thu, 23 Feb 2017 11:34:19 -0700 Subject: [PATCH 8/9] Updated versions Fix babel file include --- __tests__/__snapshots__/wrap.test.js.snap | 7 +- build/babelTransform.js | 21 +--- build/client.js | 10 +- build/css.js | 146 +++++++++++----------- build/index.js | 30 +++-- build/inheritance.js | 11 +- build/utils.js | 11 +- build/watcher.js | 45 ++++--- build/wrap.js | 50 ++++---- package.json | 15 +-- src/babelTransform.js | 13 +- src/index.js | 2 +- src/utils.js | 3 +- 13 files changed, 171 insertions(+), 193 deletions(-) diff --git a/__tests__/__snapshots__/wrap.test.js.snap b/__tests__/__snapshots__/wrap.test.js.snap index e98c5ab..46e539e 100644 --- a/__tests__/__snapshots__/wrap.test.js.snap +++ b/__tests__/__snapshots__/wrap.test.js.snap @@ -1,3 +1,5 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + exports[`wrap should pass all properties to wrapped component and add styles to the view 1`] = ` + } +/> `; diff --git a/build/babelTransform.js b/build/babelTransform.js index 313e1c9..4452218 100644 --- a/build/babelTransform.js +++ b/build/babelTransform.js @@ -19,21 +19,6 @@ Object.defineProperty(exports,"__esModule",{value:true});exports.default= - - - - - - - - - - - - - - - @@ -52,8 +37,8 @@ if(resolvePath.startsWith('css!')){ resolvePath=resolvePath.substr(4); var name=resolvePath.replace(/\.\.\/|\.\//g,'').replace(/\//g,'_').split('.')[0]; var absolutePath=_path2.default.resolve(resolvePath), -relativePath=_path2.default.dirname(_path2.default.relative('./',file.opts.filename))+'/_css/'+name+'.js'; -startTransform(absolutePath,_path2.default.resolve('./_css/'+name+'.js')); +relativePath=_path2.default.dirname(absolutePath)+'/_transformed/'+name+'.js'; +startTransform(absolutePath,_path2.default.resolve(relativePath)); var expression=t.callExpression(t.memberExpression(t.callExpression(t.identifier('require'),[t.stringLiteral('react-native-css')]),t.identifier('register')),[t.callExpression(t.identifier('require'),[t.stringLiteral(relativePath)])]); transformPath.replaceWith(expression); @@ -61,4 +46,4 @@ transformPath.replaceWith(expression); }}}; -};var _index=require('./index');var _index2=_interopRequireDefault(_index);var _fs=require('fs');var _fs2=_interopRequireDefault(_fs);var _path=require('path');var _path2=_interopRequireDefault(_path);var _child_process=require('child_process');function _interopRequireDefault(obj){return obj&&obj.__esModule?obj:{default:obj};}var css=new _index2.default();function runWatcher(input,output){console.log('starting watcher');var ls=(0,_child_process.spawn)('node',['./watcher.js',input,output],{detached:true,cwd:__dirname});ls.stdout.on('data',function(data){console.log('stdout: '+data);});ls.stderr.on('data',function(data){console.log('stderr: '+data);});ls.on('close',function(code){console.log('child process exited with code '+code);});}function startTransform(input,output){var transformOnly=arguments.length<=2||arguments[2]===undefined?false:arguments[2];console.log('transform');try{_fs2.default.mkdirSync('./_css');}catch(e){}try{css.parse({input:input,output:output,useInheritance:true});}catch(e){console.log('hi');}if(process.env.BABEL_ENV==='development'&&!transformOnly){runWatcher(input,output);}return true;}// \ No newline at end of file +};var _index=require('./index');var _index2=_interopRequireDefault(_index);var _fs=require('fs');var _fs2=_interopRequireDefault(_fs);var _path=require('path');var _path2=_interopRequireDefault(_path);var _child_process=require('child_process');function _interopRequireDefault(obj){return obj&&obj.__esModule?obj:{default:obj};}var css=new _index2.default();function runWatcher(input,output){(0,_child_process.spawn)('node',['./watcher.js',input,output],{detached:true,cwd:__dirname});}function startTransform(input,output){try{_fs2.default.mkdirSync('./_css');}catch(e){}css.parse({input:input,output:output,useInheritance:true});if(process.env.BABEL_ENV==='development'){runWatcher(input,output);}return true;} \ No newline at end of file diff --git a/build/client.js b/build/client.js index cb5b43c..48960c5 100644 --- a/build/client.js +++ b/build/client.js @@ -2,7 +2,7 @@ var _reactNative=require("react-native");var _reactNative2=_interopRequireDefaul var _css=require("./css");var _css2=_interopRequireDefault(_css); var _wrap=require("./wrap");var _wrap2=_interopRequireDefault(_wrap);function _interopRequireDefault(obj){return obj&&obj.__esModule?obj:{default:obj};} -//These are all the components in react native to wrap with the styled proxy component + var componentsToWrap=["ActivityIndicator", "ActivityIndicatorIOS", "ART", @@ -50,7 +50,7 @@ var componentsToWrap=["ActivityIndicator", var StyledComponents={}; var wrappedComponents={}; -//wrap all the UI components in React Native + componentsToWrap.forEach(function(name){ Object.defineProperty(StyledComponents,name,{ get:function get(){ @@ -59,8 +59,8 @@ return wrappedComponents[name]||(wrappedComponents[name]=(0,_wrap2.default)(name }); -//Proxy all other APIs, etc. so it can be a drop-in replacement for import {...} from 'react-native. All properties -// use a getter so APIs are only initialized when explicitly imported + + Object.keys(_reactNative2.default).forEach(function(name){ if(componentsToWrap.indexOf(name)===-1){ Object.defineProperty(StyledComponents,name,{ @@ -71,7 +71,7 @@ return _reactNative2.default[name]; } }); -//Add in all the utility functions + StyledComponents.unregister=_css.unregister; StyledComponents.register=_css.register; StyledComponents.css=_css2.default; diff --git a/build/css.js b/build/css.js index e30b898..bb4478d 100644 --- a/build/css.js +++ b/build/css.js @@ -1,39 +1,39 @@ Object.defineProperty(exports,"__esModule",{value:true});exports.matchingRules=exports.unregister=exports.register=undefined;var _extends=Object.assign||function(target){for(var i=1;itext.hello text + Object.keys(styles).forEach(function(key,o){ if(key.endsWith("&inherited")){ return; @@ -75,7 +75,7 @@ var pseudo=immediate.split(":"), normal=pseudo.shift(), classes=normal.split(".").filter(function(a){return a;}), element=normal.charAt(0)==="."?"*":classes.shift(); -//prep pseudo functions + pseudo=pseudo.map(function(pseudo){return pseudos.find(function(test){return test.regex.exec(pseudo);});}).filter(function(test){return test;}).map(function(test){ var matches=test.regex.exec(pseudo); if(matches){ @@ -93,9 +93,9 @@ i:immediates.length>0&&i!=immediates.length-1}); }); path=path.reverse(); var first=path.shift(); -//Calculate specificity + var specificity=0; -//add +10 for classes + specificity+=first.c.length*10; if(path.length>0&&path.i){ specificity+=5; @@ -116,13 +116,13 @@ rules[first.e].push(_extends({},first,{a:path,s:specificity,o:o,k:key})); return rules; } -/** - * Test to see if the target matches the matching element. Compares element type and classes. The target must have - * all the classes in matching, but can have more. - * @param target - * @param matching - * @return {boolean} - */ + + + + + + + function elementMatches(target,matching){ if(target.e!==matching.e&&matching.e!=="*"){ return false; @@ -133,11 +133,11 @@ return false; return!matching.ps||matching.ps.every(function(match){return match(target);}); } -/** - * Finds all the rules that match the passed path, sorted by specificity asc. - * @param {[{e:String,c:[String],p:{},i:Number,f:Boolean,l:Boolean}]} path - * @return {[String]} Array of references to StyleSheets - */ + + + + + function matchingRules(path,key){ try{ if(!key){ @@ -154,7 +154,7 @@ return styleCache[key]=null; var element=path[0].e; -//find matching + var starting=(ruleKeys[element]||[]).concat(ruleKeys["*"]||[]); var matchingStyles=starting.filter(function(rule){ if(!elementMatches(path[0],rule)){ @@ -171,7 +171,7 @@ index++; return true; }else if(ancestor.i){ if(lastRelative>-1){ -//push the path up to the next matching + index++; var ancestors=rule.a.slice(lastRelative,i-1), directAncestor=ancestor.shift(); @@ -189,8 +189,8 @@ return false; } }); }); -//sort styles by specificity and then by order in style sheet so that the highest specificity is the last rule, -// overwriting any previous rules + + matchingStyles.sort(function(rule1,rule2){return rule1.s-rule2.s||rule1.o-rule2.o;}); styleCache[key]=_reactNative.StyleSheet.flatten(matchingStyles.map(function(rule){return styles[rule.k+"&inherited"];}).concat(matchingStyles.map(function(rule){return styles[rule.k];})).filter(function(rule){return rule;})); return styleCache[key]; @@ -199,11 +199,11 @@ return[]; } } -/** - * Converts the passed arguments to a style array which can be set into the style prop in a React component - * @param {[[]|String,{]} args - * @return {*} - */ + + + + + function css(){for(var _len=arguments.length,args=Array(_len),_key=0;_key<_len;_key++){args[_key]=arguments[_key];} var first=args[0]; return args.map(function(arg){ @@ -220,28 +220,28 @@ return arg; }); } -/** - * Flattens all the registerd sheets into the style object and updates ruleKeys with the declarations - */ + + + function flattenSheets(){ styles=Object.assign.apply(null,[{}].concat(sheets)); ruleKeys=createRules(styles); } -/** - * Registers the passed style sheets to be used against the CSS method and wrapped StyledComponents - * @param {...StyleSheet} stylesheets - */ + + + + function register(){ sheets.push.apply(sheets,arguments); flattenSheets(); } -/** - * Removes the stylesheets from the style component. This is useful for changing themes while using the application or - * activating style settings for things like accessibility. - * @param stylesheets - */ + + + + + function unregister(){for(var _len2=arguments.length,stylesheets=Array(_len2),_key2=0;_key2<_len2;_key2++){stylesheets[_key2]=arguments[_key2];} stylesheets.forEach(function(sheet){ var index=sheets.indexOf(sheet); diff --git a/build/index.js b/build/index.js index 024d2a2..8dd24d7 100644 --- a/build/index.js +++ b/build/index.js @@ -5,23 +5,21 @@ var _inheritance=require('./inheritance');var _inheritance2=_interopRequireDefau ReactNativeCss=function(){function ReactNativeCss(){_classCallCheck(this,ReactNativeCss);}_createClass(ReactNativeCss,[{key:'parse',value:function parse(_ref) -{var input=_ref.input;var _ref$output=_ref.output;var output=_ref$output===undefined?'./style.js':_ref$output;var _ref$prettyPrint=_ref.prettyPrint;var prettyPrint=_ref$prettyPrint===undefined?false:_ref$prettyPrint;var _ref$literalObject=_ref.literalObject;var literalObject=_ref$literalObject===undefined?false:_ref$literalObject;var _ref$useInheritance=_ref.useInheritance;var useInheritance=_ref$useInheritance===undefined?false:_ref$useInheritance; -console.log(input,output); +{var input=_ref.input,output=_ref.output,_ref$prettyPrint=_ref.prettyPrint,prettyPrint=_ref$prettyPrint===undefined?false:_ref$prettyPrint,_ref$literalObject=_ref.literalObject,literalObject=_ref$literalObject===undefined?false:_ref$literalObject,_ref$useInheritance=_ref.useInheritance,useInheritance=_ref$useInheritance===undefined?false:_ref$useInheritance; +if(!input){ +throw new Error('An input file is required.'); +} var data=void 0; if(_utils2.default.contains(input,/scss/)){var _require$renderSync= require('node-sass').renderSync({ file:input, -outputStyle:'compressed'});var css=_require$renderSync.css; +outputStyle:'compressed'}),css=_require$renderSync.css; data=css.toString(); }else{ -console.log('awaiting promise'); data=_utils2.default.readFile(input); -console.log('promise done'); } -console.log('stylesheet',data.length); - var styleSheet=this.toJSS(data,useInheritance); if(output){ _utils2.default.outputReactFriendlyStyle(styleSheet,output,prettyPrint,literalObject); @@ -29,11 +27,11 @@ _utils2.default.outputReactFriendlyStyle(styleSheet,output,prettyPrint,literalOb return styleSheet; }},{key:'toJSS',value:function toJSS( -stylesheetString){var useInheritance=arguments.length<=1||arguments[1]===undefined?false:arguments[1]; +stylesheetString){var useInheritance=arguments.length>1&&arguments[1]!==undefined?arguments[1]:false; var directions=['top','right','bottom','left']; var changeArr=['margin','padding','border-width','border-radius']; var numberize=_utils2.default.filterArray(['width','height','font-size','line-height'].concat(directions),useInheritance?_inheritance.numeric:[]); -//special properties and shorthands that need to be broken down separately + var specialProperties={}; ['border','border-top','border-right','border-bottom','border-left'].forEach(function(name){ specialProperties[name]={ @@ -53,7 +51,7 @@ numberize.push(prop+'-'+dir); }); }); -//map of properties that when expanded use different directions than the default Top,Right,Bottom,Left. + var directionMaps={ 'border-radius':{ 'Top':'top-left', @@ -63,20 +61,20 @@ var directionMaps={ -//Convert the shorthand property to the individual directions, handles edge cases, i.e. border-width and -// border-radius + + function directionToPropertyName(property,direction){ var names=property.split('-'); names.splice(1,0,directionMaps[property]?directionMaps[property][direction]:direction); return(0,_toCamelCase2.default)(names.join('-')); } -// CSS properties that are not supported by React Native -// The list of supported properties is at -// https://facebook.github.io/react-native/docs/style.html#supported-properties + + + var unsupported=['display'];var _ParseCSS= -(0,_cssParse2.default)(_utils2.default.clean(stylesheetString));var stylesheet=_ParseCSS.stylesheet; +(0,_cssParse2.default)(_utils2.default.clean(stylesheetString)),stylesheet=_ParseCSS.stylesheet; var JSONResult={}; diff --git a/build/inheritance.js b/build/inheritance.js index 95ffccc..93a9445 100644 --- a/build/inheritance.js +++ b/build/inheritance.js @@ -106,10 +106,7 @@ Object.defineProperty(exports,"__esModule",{value:true});exports.numeric=undefin -inheritance;var _toCamelCase=require("to-camel-case");var _toCamelCase2=_interopRequireDefault(_toCamelCase);function _interopRequireDefault(obj){return obj&&obj.__esModule?obj:{default:obj};}//Text properties are typically inherited in normal CSS, and it is a pain in React Native to have to re-define a lot of -// properties. Additionally it would be nice to use relative units, such as em and rem for sizing text as that is -// typical in modern web applications. -var textProps=["fontSize","color","lineHeight","fontFamily","fontStyle","fontWeight","textAlign","textDecorationLine","textShadowColor","textShadowOffset","textShadowRadius","textAlignVertical","letterSpacing","textDecorationColor","textDecorationStyle","writingDirection"];var numeric=["font-size","line-height","letter-spacing","text-shadow-radius"],numericProps=numeric.map(_toCamelCase2.default);var defaultRootState={fontSize:14};/** - * This will expand styles into a tree that can be traversed for inheritance. - * @param styles - */function expandStyles(styles){var tree={};var _loop=function _loop(selector){selector.split(",").forEach(function(select){var target=tree;select=select.replace(/\s*>\s*/g,">").replace(/\s+/g," ");select.split(" ").forEach(function(part){part.split(">").forEach(function(key,i){target=target[key]||(target[key]={});target._isDirectDescendent=i>0;});});_extends(target,styles[selector]);});};for(var _iterator=Object.keys(styles),_isArray=Array.isArray(_iterator),_i=0,_iterator=_isArray?_iterator:_iterator[typeof Symbol==="function"?Symbol.iterator:"@@iterator"]();;){var _ref;if(_isArray){if(_i>=_iterator.length)break;_ref=_iterator[_i++];}else{_i=_iterator.next();if(_i.done)break;_ref=_i.value;}var selector=_ref;_loop(selector);}return tree;}function getInheritedProperties(state,node,rootState){state=_extends({},state);textProps.forEach(function(prop){if(node&&node[prop]){var value=node[prop];if(numericProps.indexOf(prop)>-1&&typeof value==="string"){var numericValue=parseFloat(value.replace(/[^0-9\.]/g,"")),isRelativeToRoot=value.indexOf("rem")>-1,isRelative=value.indexOf("em")>-1;if(isRelativeToRoot){numericValue*=rootState[prop];}else if(isRelative){numericValue*=state[prop]||0;}node[prop]=~~numericValue;}state[prop]=node[prop];}});return state;}function cleanInherits(inherited,node){var keys=Object.keys(node),clean={};Object.keys(inherited).forEach(function(key){if(keys.indexOf(key)===-1){clean[key]=inherited[key];}});return clean;}function inherit(node,state,rootState){if(!state){rootState=_extends({},defaultRootState,node.root);rootState=state=getInheritedProperties(rootState,node.root,rootState);}var cleanNode={};for(var _iterator2=Object.keys(node),_isArray2=Array.isArray(_iterator2),_i2=0,_iterator2=_isArray2?_iterator2:_iterator2[typeof Symbol==="function"?Symbol.iterator:"@@iterator"]();;){var _ref2;if(_isArray2){if(_i2>=_iterator2.length)break;_ref2=_iterator2[_i2++];}else{_i2=_iterator2.next();if(_i2.done)break;_ref2=_i2.value;}var key=_ref2;var childNode=node[key];var textPsuedo=key.toLowerCase().indexOf(":text")>-1;key=key.replace(/:text/i,"");if((key.toLowerCase().indexOf("text")===0||textPsuedo)&&typeof childNode==="object"){cleanNode[key+"&inherited"]=cleanInherits(getInheritedProperties(state,childNode,rootState),childNode);cleanNode[key]=childNode;}else if(childNode&&typeof childNode==="object"){cleanNode[key]=inherit(childNode,getInheritedProperties(state,childNode,rootState),rootState);}else if(textProps.indexOf(key)===-1){cleanNode[key]=childNode;}}return cleanNode;}function flatten(tree){var flatObject=arguments.length<=1||arguments[1]===undefined?{}:arguments[1];var name=arguments.length<=2||arguments[2]===undefined?"":arguments[2];var flatStyle={};for(var _iterator3=Object.keys(tree),_isArray3=Array.isArray(_iterator3),_i3=0,_iterator3=_isArray3?_iterator3:_iterator3[typeof Symbol==="function"?Symbol.iterator:"@@iterator"]();;){var _ref3;if(_isArray3){if(_i3>=_iterator3.length)break;_ref3=_iterator3[_i3++];}else{_i3=_iterator3.next();if(_i3.done)break;_ref3=_i3.value;}var key=_ref3;var value=tree[key],isDirectDescendent=value._isDirectDescendent;if(typeof value==="object"){flatten(value,flatObject,name+(isDirectDescendent?">":" ")+key);}else if(key!=="_isDirectDescendent"){flatStyle[key]=value;}}if(Object.keys(flatStyle).length>0){flatObject[name.trim()]=flatStyle;}return flatObject;}function inheritance(styles){return flatten(inherit(expandStyles(styles)));}exports.numeric=numeric; \ No newline at end of file +inheritance;var _toCamelCase=require("to-camel-case");var _toCamelCase2=_interopRequireDefault(_toCamelCase);function _interopRequireDefault(obj){return obj&&obj.__esModule?obj:{default:obj};}var textProps=["fontSize","color","lineHeight","fontFamily","fontStyle","fontWeight","textAlign","textDecorationLine","textShadowColor","textShadowOffset","textShadowRadius","textAlignVertical","letterSpacing","textDecorationColor","textDecorationStyle","writingDirection"];var numeric=["font-size","line-height","letter-spacing","text-shadow-radius"],numericProps=numeric.map(_toCamelCase2.default);var defaultRootState={fontSize:14};function expandStyles(styles){var tree={};var _loop=function _loop(selector){selector.split(",").forEach(function(select){var target=tree;select=select.replace(/\s*>\s*/g,">").replace(/\s+/g," ");select.split(" ").forEach(function(part){part.split(">").forEach(function(key,i){target=target[key]||(target[key]={});target._isDirectDescendent=i>0;});});_extends(target,styles[selector]);});};for(var _iterator=Object.keys(styles),_isArray=Array.isArray(_iterator),_i=0,_iterator=_isArray?_iterator:_iterator[typeof Symbol==="function"?Symbol.iterator:"@@iterator"]();;){var _ref;if(_isArray){if(_i>=_iterator.length)break;_ref=_iterator[_i++];}else{_i=_iterator.next();if(_i.done)break;_ref=_i.value;}var selector=_ref;_loop(selector);}return tree;}function getInheritedProperties(state,node,rootState){state=_extends({},state);textProps.forEach(function(prop){if(node&&node[prop]){var value=node[prop];if(numericProps.indexOf(prop)>-1&&typeof value==="string"){var numericValue=parseFloat(value.replace(/[^0-9\.]/g,"")),isRelativeToRoot=value.indexOf("rem")>-1,isRelative=value.indexOf("em")>-1;if(isRelativeToRoot){numericValue*=rootState[prop];}else if(isRelative){numericValue*=state[prop]||0;}node[prop]=~~numericValue;}state[prop]=node[prop];}});return state;}function cleanInherits(inherited,node){var keys=Object.keys(node),clean={};Object.keys(inherited).forEach(function(key){if(keys.indexOf(key)===-1){clean[key]=inherited[key];}});return clean;}function inherit(node,state,rootState){if(!state){rootState=_extends({},defaultRootState,node.root);rootState=state=getInheritedProperties(rootState,node.root,rootState);}var cleanNode={};for(var _iterator2=Object.keys(node),_isArray2=Array.isArray(_iterator2),_i2=0,_iterator2=_isArray2?_iterator2:_iterator2[typeof Symbol==="function"?Symbol.iterator:"@@iterator"]();;){var _ref2;if(_isArray2){if(_i2>=_iterator2.length)break;_ref2=_iterator2[_i2++];}else{_i2=_iterator2.next();if(_i2.done)break;_ref2=_i2.value;}var key=_ref2;var childNode=node[key];var textPsuedo=key.toLowerCase().indexOf(":text")>-1;key=key.replace(/:text/i,"");if((key.toLowerCase().indexOf("text")===0||textPsuedo)&&typeof childNode==="object"){cleanNode[key+"&inherited"]=cleanInherits(getInheritedProperties(state,childNode,rootState),childNode);cleanNode[key]=childNode;}else if(childNode&&typeof childNode==="object"){cleanNode[key]=inherit(childNode,getInheritedProperties(state,childNode,rootState),rootState);}else if(textProps.indexOf(key)===-1){cleanNode[key]=childNode;}}return cleanNode;}function flatten(tree){var flatObject=arguments.length>1&&arguments[1]!==undefined?arguments[1]:{};var name=arguments.length>2&&arguments[2]!==undefined?arguments[2]:"";var flatStyle={};for(var _iterator3=Object.keys(tree),_isArray3=Array.isArray(_iterator3),_i3=0,_iterator3=_isArray3?_iterator3:_iterator3[typeof Symbol==="function"?Symbol.iterator:"@@iterator"]();;){var _ref3;if(_isArray3){if(_i3>=_iterator3.length)break;_ref3=_iterator3[_i3++];}else{_i3=_iterator3.next();if(_i3.done)break;_ref3=_i3.value;}var key=_ref3;var value=tree[key],isDirectDescendent=value._isDirectDescendent;if(typeof value==="object"){flatten(value,flatObject,name+(isDirectDescendent?">":" ")+key);}else if(key!=="_isDirectDescendent"){flatStyle[key]=value;}}if(Object.keys(flatStyle).length>0){flatObject[name.trim()]=flatStyle;}return flatObject;}function inheritance(styles){ +return flatten(inherit(expandStyles(styles))); +}exports. +numeric=numeric; \ No newline at end of file diff --git a/build/utils.js b/build/utils.js index 3d96d28..6eddb37 100644 --- a/build/utils.js +++ b/build/utils.js @@ -1,4 +1,4 @@ -Object.defineProperty(exports,"__esModule",{value:true});var _createClass=function(){function defineProperties(target,props){for(var i=0;i"+element.e+"."+element.c.join(".")+":"+(element.i||"")+":"+(element.f||"")+":"+(element.l||""); if(this.cssPathKey!==key){ -//Create the path or use the cache. Since this can be called thousands of times, the cache reduces the more -// expensive array generations and reduces memory consumption by resusing arrays. Because of this it is -// important that the arrays are treated as immutable. + + + this.cssPath=pathCache[key]||(pathCache[key]=(this.context.cssPath||[{e:"root"}]).concat([element])); this.cssPathKey=key; } @@ -153,7 +153,7 @@ StyledComponent.childContextTypes={ cssPath:_react2.default.PropTypes.array, cssPathKey:_react2.default.PropTypes.string}; -//Make sure we are getting our parent's path + StyledComponent.contextTypes={cssPath:_react2.default.PropTypes.array,cssPathKey:_react2.default.PropTypes.string}; return StyledComponent; } \ No newline at end of file diff --git a/package.json b/package.json index 1d29d49..c7474c2 100644 --- a/package.json +++ b/package.json @@ -18,25 +18,26 @@ "css-parse": "^2.0.0", "cssauron": "^1.4.0", "debug": "^2.2.0", + "fs-extra": "^2.0.0", "hoist-non-react-statics": "^1.2.0", "minimist": "^1.1.1", - "node-sass": "^3.3.2", + "node-sass": "^4.5.0", "npm-web-api": "^0.1.1", - "react": "~15.2.1", - "react-native": "^0.31.0", + "react": "~15.4.2", + "react-native": "^0.41.2", "request": "^2.74.0", "to-camel-case": "~1.0.0" }, "devDependencies": { "babel-cli": "^6.11.0", - "babel-eslint": "^6.1.2", - "babel-jest": "^14.1.0", + "babel-eslint": "^7.1.1", + "babel-jest": "^19.0.0", "babel-polyfill": "^6.13.0", "babel-preset-react-native": "^1.9.0", "eslint": "^3.3.0", "eslint-plugin-react": "^6.0.0", - "jest-cli": "^14.1.0", - "jest-react-native": "^14.1.2", + "jest-cli": "^19.0.2", + "jest-react-native": "^18.0.0", "react-test-renderer": "^15.3.0" }, "jest": { diff --git a/src/babelTransform.js b/src/babelTransform.js index 77369d7..691f5e0 100644 --- a/src/babelTransform.js +++ b/src/babelTransform.js @@ -1,5 +1,4 @@ import RNC from './index'; -import fs from 'fs'; import path from 'path'; import {spawn} from 'child_process'; const css = new RNC(); @@ -9,10 +8,6 @@ function runWatcher(input, output) { } function startTransform(input, output) { - try { - fs.mkdirSync('./_css'); - } catch (e) { - } css.parse({ input, @@ -31,16 +26,16 @@ function startTransform(input, output) { export default function ({ types: t }) { return { visitor: { - ImportDeclaration (transformPath, {file}) { + ImportDeclaration (transformPath) { let resolvePath = transformPath.node.source.value; if (resolvePath.startsWith('css!')) { resolvePath = resolvePath.substr(4); let name = resolvePath.replace(/\.\.\/|\.\//g, '').replace(/\//g, '_').split('.')[0]; let absolutePath = path.resolve(resolvePath), - relativePath = `${path.dirname(path.relative('./', file.opts.filename))}/_css/${name}.js`; - startTransform(absolutePath, path.resolve(`./_css/${name}.js`)); + relativePath = `${path.dirname(absolutePath)}/_transformed/${name}.js`; + startTransform(absolutePath, path.resolve(relativePath)); - let expression = (t.callExpression(t.memberExpression(t.callExpression(t.identifier('require'), [t.stringLiteral('react-native-css')]), t.identifier('register')), [t.callExpression(t.identifier('require'), [t.stringLiteral(relativePath)])])); + let expression = (t.callExpression(t.memberExpression(t.callExpression(t.identifier('require'), [t.stringLiteral('react-native-css')]), t.identifier('register')), [t.callExpression(t.identifier('require'), [path.resolve(relativePath)])])); transformPath.replaceWith(expression); } } diff --git a/src/index.js b/src/index.js index b5bd884..2cfd937 100644 --- a/src/index.js +++ b/src/index.js @@ -101,7 +101,7 @@ export default class ReactNativeCss { if (specialProperties[property]) { let special = specialProperties[property], - matches = special.regex.exec(value); + matches = special.regex.exec(value); if (matches) { if (typeof special.map === 'function') { special.map(matches, styles, rule.declarations); diff --git a/src/utils.js b/src/utils.js index 8784929..c93c05a 100644 --- a/src/utils.js +++ b/src/utils.js @@ -1,4 +1,4 @@ -import fs from "fs"; +import fs from "fs-extra"; export default class Utils { static arrayContains(value, arr) { @@ -29,6 +29,7 @@ export default class Utils { output += (literalObject) ? `${jsonOutput}` : `require('react-native').StyleSheet.create(${jsonOutput});`; // Write to file if (outputFile) { + fs.ensureFileSync(outputFile); fs.writeFileSync(outputFile, output); } return output; From 9e2648d5e85a07878682fd3b16da02ade8a19b61 Mon Sep 17 00:00:00 2001 From: Joshua Pomazal Date: Wed, 1 Mar 2017 15:19:45 -0700 Subject: [PATCH 9/9] Fix bable transform relative path --- build/babelTransform.js | 11 +++-------- src/babelTransform.js | 6 +++--- 2 files changed, 6 insertions(+), 11 deletions(-) diff --git a/build/babelTransform.js b/build/babelTransform.js index 4452218..6c995ac 100644 --- a/build/babelTransform.js +++ b/build/babelTransform.js @@ -17,11 +17,6 @@ Object.defineProperty(exports,"__esModule",{value:true});exports.default= - - - - - @@ -36,14 +31,14 @@ var resolvePath=transformPath.node.source.value; if(resolvePath.startsWith('css!')){ resolvePath=resolvePath.substr(4); var name=resolvePath.replace(/\.\.\/|\.\//g,'').replace(/\//g,'_').split('.')[0]; -var absolutePath=_path2.default.resolve(resolvePath), +var absolutePath=_path2.default.resolve(_path2.default.dirname(file.opts.filename),resolvePath), relativePath=_path2.default.dirname(absolutePath)+'/_transformed/'+name+'.js'; startTransform(absolutePath,_path2.default.resolve(relativePath)); -var expression=t.callExpression(t.memberExpression(t.callExpression(t.identifier('require'),[t.stringLiteral('react-native-css')]),t.identifier('register')),[t.callExpression(t.identifier('require'),[t.stringLiteral(relativePath)])]); +var expression=t.callExpression(t.memberExpression(t.callExpression(t.identifier('require'),[t.stringLiteral('react-native-css')]),t.identifier('register')),[t.callExpression(t.identifier('require'),[_path2.default.resolve(relativePath)])]); transformPath.replaceWith(expression); } }}}; -};var _index=require('./index');var _index2=_interopRequireDefault(_index);var _fs=require('fs');var _fs2=_interopRequireDefault(_fs);var _path=require('path');var _path2=_interopRequireDefault(_path);var _child_process=require('child_process');function _interopRequireDefault(obj){return obj&&obj.__esModule?obj:{default:obj};}var css=new _index2.default();function runWatcher(input,output){(0,_child_process.spawn)('node',['./watcher.js',input,output],{detached:true,cwd:__dirname});}function startTransform(input,output){try{_fs2.default.mkdirSync('./_css');}catch(e){}css.parse({input:input,output:output,useInheritance:true});if(process.env.BABEL_ENV==='development'){runWatcher(input,output);}return true;} \ No newline at end of file +};var _index=require('./index');var _index2=_interopRequireDefault(_index);var _path=require('path');var _path2=_interopRequireDefault(_path);var _child_process=require('child_process');function _interopRequireDefault(obj){return obj&&obj.__esModule?obj:{default:obj};}var css=new _index2.default();function runWatcher(input,output){(0,_child_process.spawn)('node',['./watcher.js',input,output],{detached:true,cwd:__dirname});}function startTransform(input,output){css.parse({input:input,output:output,useInheritance:true});if(process.env.BABEL_ENV==='development'){runWatcher(input,output);}return true;} \ No newline at end of file diff --git a/src/babelTransform.js b/src/babelTransform.js index 691f5e0..ea99e9a 100644 --- a/src/babelTransform.js +++ b/src/babelTransform.js @@ -26,13 +26,13 @@ function startTransform(input, output) { export default function ({ types: t }) { return { visitor: { - ImportDeclaration (transformPath) { + ImportDeclaration (transformPath, {file}) { let resolvePath = transformPath.node.source.value; if (resolvePath.startsWith('css!')) { resolvePath = resolvePath.substr(4); let name = resolvePath.replace(/\.\.\/|\.\//g, '').replace(/\//g, '_').split('.')[0]; - let absolutePath = path.resolve(resolvePath), - relativePath = `${path.dirname(absolutePath)}/_transformed/${name}.js`; + let absolutePath = path.resolve(path.dirname(file.opts.filename), resolvePath), + relativePath = `${path.dirname(absolutePath)}/_transformed/${name}.js`; startTransform(absolutePath, path.resolve(relativePath)); let expression = (t.callExpression(t.memberExpression(t.callExpression(t.identifier('require'), [t.stringLiteral('react-native-css')]), t.identifier('register')), [t.callExpression(t.identifier('require'), [path.resolve(relativePath)])]));