├── examples ├── CsstaNativeDemo │ ├── .watchmanconfig │ ├── android │ │ ├── settings.gradle │ │ ├── app │ │ │ ├── src │ │ │ │ └── main │ │ │ │ │ ├── res │ │ │ │ │ ├── values │ │ │ │ │ │ ├── strings.xml │ │ │ │ │ │ └── styles.xml │ │ │ │ │ ├── mipmap-hdpi │ │ │ │ │ │ └── ic_launcher.png │ │ │ │ │ ├── mipmap-mdpi │ │ │ │ │ │ └── ic_launcher.png │ │ │ │ │ ├── mipmap-xhdpi │ │ │ │ │ │ └── ic_launcher.png │ │ │ │ │ └── mipmap-xxhdpi │ │ │ │ │ │ └── ic_launcher.png │ │ │ │ │ ├── java │ │ │ │ │ └── com │ │ │ │ │ │ └── csstanativedemo │ │ │ │ │ │ ├── MainActivity.java │ │ │ │ │ │ └── MainApplication.java │ │ │ │ │ └── AndroidManifest.xml │ │ │ ├── BUCK │ │ │ └── proguard-rules.pro │ │ ├── gradle │ │ │ └── wrapper │ │ │ │ ├── gradle-wrapper.jar │ │ │ │ └── gradle-wrapper.properties │ │ ├── keystores │ │ │ ├── debug.keystore.properties │ │ │ └── BUCK │ │ ├── build.gradle │ │ ├── gradle.properties │ │ └── gradlew.bat │ ├── .buckconfig │ ├── index.android.js │ ├── index.ios.js │ ├── .babelrc │ ├── .eslintrc.js │ ├── __tests__ │ │ ├── index.ios.js │ │ └── index.android.js │ ├── ios │ │ ├── CsstaNativeDemo │ │ │ ├── AppDelegate.h │ │ │ ├── main.m │ │ │ ├── Images.xcassets │ │ │ │ └── AppIcon.appiconset │ │ │ │ │ └── Contents.json │ │ │ ├── AppDelegate.m │ │ │ └── Info.plist │ │ └── CsstaNativeDemoTests │ │ │ ├── Info.plist │ │ │ └── CsstaNativeDemoTests.m │ ├── README.md │ ├── .gitignore │ ├── demos │ │ ├── Basic.js │ │ ├── ColorFunction.js │ │ ├── Transitions.js │ │ ├── CustomAnimations.js │ │ ├── VariablesProvider.js │ │ ├── StyleOverrides.js │ │ ├── CSSVariables.js │ │ ├── CustomButton.js │ │ └── Animations.js │ ├── package.json │ ├── .flowconfig │ └── App.js └── cssta-demo │ ├── src │ ├── favicon.ico │ ├── index.js │ └── App.js │ ├── upgrade │ ├── src │ │ ├── index.css │ │ ├── index.js │ │ ├── App.test.js │ │ ├── App.css │ │ ├── App.js │ │ └── logo.svg │ ├── public │ │ ├── favicon.ico │ │ └── index.html │ ├── .gitignore │ └── package.json │ ├── .gitignore │ ├── .eslintrc.js │ ├── config-overrides.js │ ├── public │ └── index.html │ ├── package.json │ └── README.md ├── packages ├── benchmark │ ├── .gitignore │ ├── .babelrc │ ├── mocks │ │ └── react-native.js │ ├── .eslintrc.js │ ├── webpack.config.js │ ├── package.json │ └── index.js ├── cssta │ ├── .babelrc │ ├── native.js │ ├── .flowconfig │ ├── src │ │ ├── index.js │ │ ├── native │ │ │ ├── withEnhancers.js │ │ │ ├── util.js │ │ │ ├── createComponent.js │ │ │ ├── cssUtil.js │ │ │ ├── __mocks__ │ │ │ │ └── react-native.js │ │ │ ├── __tests__ │ │ │ │ ├── index.js │ │ │ │ └── selectorTransform.js │ │ │ ├── enhancers │ │ │ │ ├── README.md │ │ │ │ └── animationUtil.js │ │ │ ├── index.js │ │ │ ├── types.js │ │ │ └── selectorTransform.js │ │ ├── web │ │ │ ├── types.js │ │ │ ├── createComponent.js │ │ │ ├── extractRules.js │ │ │ ├── __tests__ │ │ │ │ └── createComponent.js │ │ │ └── index.js │ │ ├── css-transforms │ │ │ ├── colors.js │ │ │ └── variables.js │ │ ├── factories │ │ │ ├── types.js │ │ │ ├── enhancerFactory.js │ │ │ ├── componentFactory.js │ │ │ └── __tests__ │ │ │ │ └── componentFactory.js │ │ └── util │ │ │ ├── index.js │ │ │ ├── resolveVariableDependencies.js │ │ │ └── __tests__ │ │ │ └── resolveVariableDependencies.js │ ├── index.js │ ├── flow-typed │ │ ├── css-color-function.js │ │ └── dependency-graph.js │ ├── .npmignore │ ├── webpack.config.js │ ├── .eslintrc.js │ └── package.json └── babel-plugin-cssta │ ├── fixtures │ ├── no-imports │ │ ├── expected.js │ │ └── actual.js │ ├── empty-rules │ │ ├── actual.js │ │ ├── expected.js │ │ └── expected.css │ ├── native-empty-rules │ │ ├── expected.js │ │ └── actual.js │ ├── default │ │ ├── actual.js │ │ ├── expected.js │ │ └── expected.css │ ├── without-template │ │ ├── actual.js │ │ ├── expected.js │ │ └── expected.css │ ├── native-simple-interpolation │ │ ├── options.json │ │ ├── actual.js │ │ └── expected.js │ ├── native-complex-interpolation │ │ ├── options.json │ │ ├── actual.js │ │ └── expected.js │ ├── native-shorthand-interpolation │ │ ├── options.json │ │ ├── actual.js │ │ └── expected.js │ ├── without-quasi │ │ ├── actual.js │ │ ├── expected.js │ │ └── expected.css │ ├── native-simple-interpolation-suffix │ │ ├── options.json │ │ ├── actual.js │ │ └── expected.js │ ├── import-under-different-name │ │ ├── actual.js │ │ ├── expected.js │ │ └── expected.css │ ├── computed │ │ ├── actual.js │ │ ├── expected.js │ │ └── expected.css │ ├── compose-without-template │ │ ├── actual.js │ │ ├── expected.js │ │ └── expected.css │ ├── boolean-attribute │ │ ├── actual.js │ │ ├── expected.css │ │ └── expected.js │ ├── compose-component │ │ ├── actual.js │ │ ├── expected.js │ │ └── expected.css │ ├── native │ │ ├── actual.js │ │ └── expected.js │ ├── compose-without-quasi │ │ ├── actual.js │ │ ├── expected.js │ │ └── expected.css │ ├── css-variables-single-source-web │ │ ├── options.json │ │ ├── actual.js │ │ ├── expected.js │ │ └── expected.css │ ├── css-variables-single-source-native │ │ ├── options.json │ │ ├── actual.js │ │ └── expected.js │ ├── css-variables-import-native │ │ ├── actual.js │ │ └── expected.js │ ├── native-color-function │ │ ├── actual.js │ │ └── expected.js │ ├── native-shorthand │ │ ├── actual.js │ │ └── expected.js │ ├── css-variables-native │ │ ├── actual.js │ │ └── expected.js │ ├── native-variables-provider │ │ ├── actual.js │ │ └── expected.js │ ├── native-within-closure │ │ ├── actual.js │ │ └── expected.js │ ├── css-variables-single-source-native-interpolated-defaults │ │ ├── options.json │ │ ├── actual.js │ │ └── expected.js │ ├── multiple-calls-in-file │ │ ├── actual.js │ │ ├── expected.css │ │ └── expected.js │ ├── css-variables-export-native │ │ ├── actual.js │ │ └── expected.js │ ├── string-attribute │ │ ├── actual.js │ │ ├── expected.js │ │ └── expected.css │ ├── native-transitions │ │ ├── actual.js │ │ └── expected.js │ ├── native-transitions-variables │ │ ├── actual.js │ │ └── expected.js │ ├── multiple-attributes │ │ ├── actual.js │ │ ├── expected.js │ │ └── expected.css │ ├── native-keyframes-variables │ │ ├── actual.js │ │ └── expected.js │ ├── native-keyframes │ │ ├── actual.js │ │ └── expected.js │ ├── native-keyframes-without-variables │ │ ├── actual.js │ │ └── expected.js │ ├── native-multiple-attributes │ │ ├── actual.js │ │ └── expected.js │ ├── native-interpolation-no-optimisation │ │ ├── expected.js │ │ └── actual.js │ └── native-transitions-shorthand │ │ ├── actual.js │ │ └── expected.js │ ├── .npmignore │ ├── src │ ├── webUtil.js │ ├── platforms │ │ ├── native │ │ │ ├── createArgsStatic.js │ │ │ ├── createUtil.js │ │ │ ├── createArgsVariables.js │ │ │ ├── createKeyframes.js │ │ │ ├── util.js │ │ │ ├── index.js │ │ │ └── simpleInterpolationMap.js │ │ └── web.js │ ├── visitors │ │ ├── program │ │ │ ├── removeUnusedCsstaImports.js │ │ │ ├── singleSourceOfVariables.js │ │ │ └── web.js │ │ ├── importSpecifier │ │ │ └── redirectImports.js │ │ └── csstaCall.js │ ├── index.js │ ├── index.test.js │ ├── transformUtil │ │ └── extractCsstaCallParts.js │ └── optimizations │ │ └── singleSourceOfVariables.js │ ├── .eslintrc.js │ └── package.json ├── docs ├── .gitignore ├── _includes │ └── sidebar-link.html ├── Gemfile ├── external-modules.md ├── _config.yml ├── theming.md ├── web.md ├── _layouts │ └── page.html └── editor-integration.md ├── .vscode └── settings.json ├── lerna.json ├── package.json └── .gitignore /examples/CsstaNativeDemo/.watchmanconfig: -------------------------------------------------------------------------------- 1 | {} -------------------------------------------------------------------------------- /packages/benchmark/.gitignore: -------------------------------------------------------------------------------- 1 | tests-compiled.js 2 | -------------------------------------------------------------------------------- /packages/cssta/.babelrc: -------------------------------------------------------------------------------- 1 | { 2 | "presets": ["es2015"] 3 | } 4 | -------------------------------------------------------------------------------- /packages/cssta/native.js: -------------------------------------------------------------------------------- 1 | module.exports = require('./lib/native'); 2 | -------------------------------------------------------------------------------- /packages/babel-plugin-cssta/fixtures/no-imports/expected.js: -------------------------------------------------------------------------------- 1 | console.log('...'); -------------------------------------------------------------------------------- /docs/.gitignore: -------------------------------------------------------------------------------- 1 | _site 2 | .sass-cache 3 | .asset-cache 4 | .jekyll-metadata 5 | -------------------------------------------------------------------------------- /packages/babel-plugin-cssta/fixtures/no-imports/actual.js: -------------------------------------------------------------------------------- 1 | console.log('...'); 2 | -------------------------------------------------------------------------------- /packages/cssta/.flowconfig: -------------------------------------------------------------------------------- 1 | [ignore] 2 | .*/lib/.* 3 | 4 | [libs] 5 | flow-typed 6 | -------------------------------------------------------------------------------- /packages/cssta/src/index.js: -------------------------------------------------------------------------------- 1 | // @flow 2 | module.exports = require('./web/index'); 3 | -------------------------------------------------------------------------------- /packages/cssta/index.js: -------------------------------------------------------------------------------- 1 | /* eslint-disable */ 2 | module.exports = require('./lib/web'); 3 | -------------------------------------------------------------------------------- /.vscode/settings.json: -------------------------------------------------------------------------------- 1 | // Place your settings in this file to overwrite default and user settings. 2 | {} -------------------------------------------------------------------------------- /examples/CsstaNativeDemo/android/settings.gradle: -------------------------------------------------------------------------------- 1 | rootProject.name = 'CsstaNativeDemo' 2 | 3 | include ':app' 4 | -------------------------------------------------------------------------------- /packages/babel-plugin-cssta/fixtures/empty-rules/actual.js: -------------------------------------------------------------------------------- 1 | import cssta from 'cssta'; 2 | 3 | cssta.div``; 4 | -------------------------------------------------------------------------------- /examples/cssta-demo/src/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/functions/cssta/master/examples/cssta-demo/src/favicon.ico -------------------------------------------------------------------------------- /packages/babel-plugin-cssta/fixtures/native-empty-rules/expected.js: -------------------------------------------------------------------------------- 1 | 2 | import { View } from 'react-native'; 3 | 4 | View; -------------------------------------------------------------------------------- /examples/cssta-demo/upgrade/src/index.css: -------------------------------------------------------------------------------- 1 | body { 2 | margin: 0; 3 | padding: 0; 4 | font-family: sans-serif; 5 | } 6 | -------------------------------------------------------------------------------- /lerna.json: -------------------------------------------------------------------------------- 1 | { 2 | "lerna": "2.0.0-beta.37", 3 | "packages": [ 4 | "packages/*" 5 | ], 6 | "version": "0.7.0" 7 | } 8 | -------------------------------------------------------------------------------- /packages/babel-plugin-cssta/fixtures/default/actual.js: -------------------------------------------------------------------------------- 1 | import cssta from 'cssta'; 2 | 3 | cssta.button` 4 | color: red; 5 | `; 6 | -------------------------------------------------------------------------------- /packages/babel-plugin-cssta/fixtures/without-template/actual.js: -------------------------------------------------------------------------------- 1 | import cssta from 'cssta'; 2 | 3 | cssta.button('color: red;'); 4 | -------------------------------------------------------------------------------- /packages/babel-plugin-cssta/fixtures/native-simple-interpolation/options.json: -------------------------------------------------------------------------------- 1 | { 2 | "optimizations": ["interpolateValuesOnly"] 3 | } 4 | -------------------------------------------------------------------------------- /packages/babel-plugin-cssta/fixtures/native-complex-interpolation/options.json: -------------------------------------------------------------------------------- 1 | { 2 | "optimizations": ["interpolateValuesOnly"] 3 | } 4 | -------------------------------------------------------------------------------- /packages/babel-plugin-cssta/fixtures/native-shorthand-interpolation/options.json: -------------------------------------------------------------------------------- 1 | { 2 | "optimizations": ["interpolateValuesOnly"] 3 | } 4 | -------------------------------------------------------------------------------- /packages/babel-plugin-cssta/fixtures/without-quasi/actual.js: -------------------------------------------------------------------------------- 1 | import cssta from 'cssta'; 2 | 3 | cssta.button(` 4 | color: red; 5 | `); 6 | -------------------------------------------------------------------------------- /packages/benchmark/.babelrc: -------------------------------------------------------------------------------- 1 | { 2 | "presets": [["latest", { "modules": false }], "react"], 3 | "plugins": ["babel-plugin-cssta"] 4 | } 5 | -------------------------------------------------------------------------------- /examples/cssta-demo/upgrade/public/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/functions/cssta/master/examples/cssta-demo/upgrade/public/favicon.ico -------------------------------------------------------------------------------- /packages/babel-plugin-cssta/fixtures/native-simple-interpolation-suffix/options.json: -------------------------------------------------------------------------------- 1 | { 2 | "optimizations": ["interpolateValuesOnly"] 3 | } 4 | -------------------------------------------------------------------------------- /examples/CsstaNativeDemo/android/app/src/main/res/values/strings.xml: -------------------------------------------------------------------------------- 1 | 2 | CsstaNativeDemo 3 | 4 | -------------------------------------------------------------------------------- /packages/babel-plugin-cssta/fixtures/import-under-different-name/actual.js: -------------------------------------------------------------------------------- 1 | import otherName from 'cssta'; 2 | 3 | otherName.button` 4 | color: red; 5 | `; 6 | -------------------------------------------------------------------------------- /packages/babel-plugin-cssta/fixtures/computed/actual.js: -------------------------------------------------------------------------------- 1 | import cssta from 'cssta'; 2 | 3 | const button = 'button'; 4 | cssta[button]` 5 | color: red; 6 | `; 7 | -------------------------------------------------------------------------------- /packages/babel-plugin-cssta/fixtures/native-empty-rules/actual.js: -------------------------------------------------------------------------------- 1 | import cssta from 'cssta/native'; 2 | import { View } from 'react-native'; 3 | 4 | cssta(View)``; 5 | -------------------------------------------------------------------------------- /examples/CsstaNativeDemo/.buckconfig: -------------------------------------------------------------------------------- 1 | 2 | [android] 3 | target = Google Inc.:Google APIs:23 4 | 5 | [maven_repositories] 6 | central = https://repo1.maven.org/maven2 7 | -------------------------------------------------------------------------------- /packages/babel-plugin-cssta/fixtures/compose-without-template/actual.js: -------------------------------------------------------------------------------- 1 | import cssta from 'cssta'; 2 | import Link from 'react-router'; 3 | 4 | cssta(Link)('color: red;'); 5 | -------------------------------------------------------------------------------- /packages/babel-plugin-cssta/fixtures/boolean-attribute/actual.js: -------------------------------------------------------------------------------- 1 | import cssta from 'cssta'; 2 | 3 | cssta.button` 4 | &[@booleanAttribute] { 5 | color: red; 6 | } 7 | `; 8 | -------------------------------------------------------------------------------- /packages/babel-plugin-cssta/fixtures/compose-component/actual.js: -------------------------------------------------------------------------------- 1 | import cssta from 'cssta'; 2 | import Link from 'react-router'; 3 | 4 | cssta(Link)` 5 | color: red; 6 | `; 7 | -------------------------------------------------------------------------------- /packages/babel-plugin-cssta/fixtures/native/actual.js: -------------------------------------------------------------------------------- 1 | import cssta from 'cssta/native'; 2 | import { View } from 'react-native'; 3 | 4 | cssta(View)` 5 | color: red; 6 | `; 7 | -------------------------------------------------------------------------------- /examples/CsstaNativeDemo/android/gradle/wrapper/gradle-wrapper.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/functions/cssta/master/examples/CsstaNativeDemo/android/gradle/wrapper/gradle-wrapper.jar -------------------------------------------------------------------------------- /packages/babel-plugin-cssta/fixtures/compose-without-quasi/actual.js: -------------------------------------------------------------------------------- 1 | import cssta from 'cssta'; 2 | import Link from 'react-router'; 3 | 4 | cssta(Link)(` 5 | color: red; 6 | `); 7 | -------------------------------------------------------------------------------- /examples/CsstaNativeDemo/android/keystores/debug.keystore.properties: -------------------------------------------------------------------------------- 1 | key.store=debug.keystore 2 | key.alias=androiddebugkey 3 | key.store.password=android 4 | key.alias.password=android 5 | -------------------------------------------------------------------------------- /examples/CsstaNativeDemo/index.android.js: -------------------------------------------------------------------------------- 1 | import { AppRegistry } from 'react-native'; 2 | import App from './App'; 3 | 4 | 5 | AppRegistry.registerComponent('CsstaNativeDemo', () => App); 6 | -------------------------------------------------------------------------------- /examples/CsstaNativeDemo/index.ios.js: -------------------------------------------------------------------------------- 1 | import { AppRegistry } from 'react-native'; 2 | import App from './App'; 3 | 4 | 5 | AppRegistry.registerComponent('CsstaNativeDemo', () => App); 6 | -------------------------------------------------------------------------------- /packages/babel-plugin-cssta/fixtures/css-variables-single-source-web/options.json: -------------------------------------------------------------------------------- 1 | { 2 | "optimizations": [ 3 | ["singleSourceOfVariables", { "sourceFilename": "actual.js" }] 4 | ] 5 | } 6 | -------------------------------------------------------------------------------- /packages/babel-plugin-cssta/fixtures/css-variables-single-source-native/options.json: -------------------------------------------------------------------------------- 1 | { 2 | "optimizations": [ 3 | ["singleSourceOfVariables", { "sourceFilename": "actual.js" }] 4 | ] 5 | } 6 | -------------------------------------------------------------------------------- /examples/CsstaNativeDemo/android/app/src/main/res/mipmap-hdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/functions/cssta/master/examples/CsstaNativeDemo/android/app/src/main/res/mipmap-hdpi/ic_launcher.png -------------------------------------------------------------------------------- /examples/CsstaNativeDemo/android/app/src/main/res/mipmap-mdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/functions/cssta/master/examples/CsstaNativeDemo/android/app/src/main/res/mipmap-mdpi/ic_launcher.png -------------------------------------------------------------------------------- /packages/cssta/flow-typed/css-color-function.js: -------------------------------------------------------------------------------- 1 | // @flow 2 | /*:: 3 | declare module 'css-color-function' { 4 | declare module.exports: { 5 | convert: (input: string) => string 6 | } 7 | } 8 | */ 9 | -------------------------------------------------------------------------------- /examples/CsstaNativeDemo/android/app/src/main/res/mipmap-xhdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/functions/cssta/master/examples/CsstaNativeDemo/android/app/src/main/res/mipmap-xhdpi/ic_launcher.png -------------------------------------------------------------------------------- /examples/CsstaNativeDemo/android/app/src/main/res/mipmap-xxhdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/functions/cssta/master/examples/CsstaNativeDemo/android/app/src/main/res/mipmap-xxhdpi/ic_launcher.png -------------------------------------------------------------------------------- /packages/babel-plugin-cssta/fixtures/css-variables-import-native/actual.js: -------------------------------------------------------------------------------- 1 | import cssta from 'cssta/native'; 2 | import { View } from 'react-native'; 3 | 4 | cssta(View)` 5 | color: var(--color); 6 | `; 7 | -------------------------------------------------------------------------------- /packages/babel-plugin-cssta/fixtures/native-color-function/actual.js: -------------------------------------------------------------------------------- 1 | import cssta from 'cssta/native'; 2 | import { View } from 'react-native'; 3 | 4 | cssta(View)` 5 | color: color(red l(+ 25%)); 6 | `; 7 | -------------------------------------------------------------------------------- /examples/CsstaNativeDemo/android/keystores/BUCK: -------------------------------------------------------------------------------- 1 | keystore( 2 | name = 'debug', 3 | store = 'debug.keystore', 4 | properties = 'debug.keystore.properties', 5 | visibility = [ 6 | 'PUBLIC', 7 | ], 8 | ) 9 | -------------------------------------------------------------------------------- /examples/cssta-demo/src/index.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import ReactDOM from 'react-dom'; 3 | import App from './App'; 4 | 5 | ReactDOM.render( 6 | , 7 | document.getElementById('root') 8 | ); 9 | -------------------------------------------------------------------------------- /packages/babel-plugin-cssta/fixtures/native-shorthand/actual.js: -------------------------------------------------------------------------------- 1 | import cssta from 'cssta/native'; 2 | import { View } from 'react-native'; 3 | 4 | cssta(View)` 5 | font: bold italic 12px/18px "Helvetica"; 6 | `; 7 | -------------------------------------------------------------------------------- /packages/babel-plugin-cssta/fixtures/css-variables-native/actual.js: -------------------------------------------------------------------------------- 1 | import cssta from 'cssta/native'; 2 | import { View } from 'react-native'; 3 | 4 | cssta(View)` 5 | --color: red; 6 | color: var(--color); 7 | `; 8 | -------------------------------------------------------------------------------- /docs/_includes/sidebar-link.html: -------------------------------------------------------------------------------- 1 |
  • 2 | 3 | {{ include.title }} 4 | 5 |
  • 6 | -------------------------------------------------------------------------------- /packages/babel-plugin-cssta/fixtures/default/expected.js: -------------------------------------------------------------------------------- 1 | import _createComponent from 'cssta/lib/web/createComponent'; 2 | 3 | 4 | _createComponent('button', null, { 5 | 'defaultClassName': 'A', 6 | 'classNameMap': {} 7 | }); -------------------------------------------------------------------------------- /packages/babel-plugin-cssta/fixtures/empty-rules/expected.js: -------------------------------------------------------------------------------- 1 | import _createComponent from 'cssta/lib/web/createComponent'; 2 | 3 | 4 | _createComponent('div', null, { 5 | 'defaultClassName': null, 6 | 'classNameMap': {} 7 | }); -------------------------------------------------------------------------------- /packages/babel-plugin-cssta/fixtures/empty-rules/expected.css: -------------------------------------------------------------------------------- 1 | /* File generated by babel-plugin-cssta */ 2 | 3 | /* Start /fixtures/empty-rules/actual.js */ 4 | 5 | 6 | /* End /fixtures/empty-rules/actual.js */ 7 | -------------------------------------------------------------------------------- /packages/babel-plugin-cssta/fixtures/without-quasi/expected.js: -------------------------------------------------------------------------------- 1 | import _createComponent from 'cssta/lib/web/createComponent'; 2 | 3 | 4 | _createComponent('button', null, { 5 | 'defaultClassName': 'A', 6 | 'classNameMap': {} 7 | }); -------------------------------------------------------------------------------- /packages/benchmark/mocks/react-native.js: -------------------------------------------------------------------------------- 1 | // @flow 2 | import { createElement } from 'react'; 3 | 4 | export const StyleSheet = { 5 | create: () => ({}), 6 | }; 7 | 8 | export const View = props => createElement('view', props); 9 | -------------------------------------------------------------------------------- /packages/babel-plugin-cssta/fixtures/without-template/expected.js: -------------------------------------------------------------------------------- 1 | import _createComponent from 'cssta/lib/web/createComponent'; 2 | 3 | 4 | _createComponent('button', null, { 5 | 'defaultClassName': 'A', 6 | 'classNameMap': {} 7 | }); -------------------------------------------------------------------------------- /packages/cssta/src/native/withEnhancers.js: -------------------------------------------------------------------------------- 1 | // @flow 2 | const enhancerFactory = require('../factories/enhancerFactory'); 3 | const createComponent = require('./createComponent'); 4 | 5 | module.exports = enhancerFactory(createComponent); 6 | -------------------------------------------------------------------------------- /packages/babel-plugin-cssta/fixtures/native-variables-provider/actual.js: -------------------------------------------------------------------------------- 1 | import cssta, { VariablesProvider } from 'cssta/native'; 2 | import { View } from 'react-native'; 3 | 4 | cssta(View)` 5 | color: red; 6 | `; 7 | 8 | VariablesProvider 9 | -------------------------------------------------------------------------------- /packages/babel-plugin-cssta/fixtures/native-within-closure/actual.js: -------------------------------------------------------------------------------- 1 | import cssta from 'cssta/native'; 2 | import { View } from 'react-native'; 3 | 4 | function test() { 5 | const Component = cssta(View)` 6 | color: red; 7 | `; 8 | } 9 | -------------------------------------------------------------------------------- /packages/cssta/src/web/types.js: -------------------------------------------------------------------------------- 1 | // @flow 2 | /*:: 3 | export type ClassNameMap = { [key:string]: { [key:string]: string } } 4 | 5 | export type Args = { 6 | defaultClassName: ?string, 7 | classNameMap: ClassNameMap, 8 | } 9 | */ 10 | -------------------------------------------------------------------------------- /packages/babel-plugin-cssta/fixtures/import-under-different-name/expected.js: -------------------------------------------------------------------------------- 1 | import _createComponent from 'cssta/lib/web/createComponent'; 2 | 3 | 4 | _createComponent('button', null, { 5 | 'defaultClassName': 'A', 6 | 'classNameMap': {} 7 | }); -------------------------------------------------------------------------------- /packages/babel-plugin-cssta/fixtures/computed/expected.js: -------------------------------------------------------------------------------- 1 | import _createComponent from 'cssta/lib/web/createComponent'; 2 | 3 | 4 | const button = 'button'; 5 | _createComponent(button, null, { 6 | 'defaultClassName': 'A', 7 | 'classNameMap': {} 8 | }); -------------------------------------------------------------------------------- /packages/babel-plugin-cssta/fixtures/css-variables-single-source-native-interpolated-defaults/options.json: -------------------------------------------------------------------------------- 1 | { 2 | "optimizations": [ 3 | ["singleSourceOfVariables", { "sourceFilename": "actual.js" }], 4 | "interpolateValuesOnly" 5 | ] 6 | } 7 | -------------------------------------------------------------------------------- /packages/babel-plugin-cssta/fixtures/multiple-calls-in-file/actual.js: -------------------------------------------------------------------------------- 1 | import cssta from 'cssta'; 2 | 3 | cssta.button` 4 | color: red; 5 | `; 6 | 7 | cssta.span` 8 | color: green; 9 | `; 10 | 11 | cssta.div` 12 | color: blue; 13 | `; 14 | -------------------------------------------------------------------------------- /examples/cssta-demo/upgrade/src/index.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import ReactDOM from 'react-dom'; 3 | import App from './App'; 4 | import './index.css'; 5 | 6 | ReactDOM.render( 7 | , 8 | document.getElementById('root') 9 | ); 10 | -------------------------------------------------------------------------------- /packages/babel-plugin-cssta/fixtures/native-simple-interpolation-suffix/actual.js: -------------------------------------------------------------------------------- 1 | import cssta from 'cssta/native'; 2 | import { View, StyleSheet } from 'react-native'; 3 | 4 | cssta(View)` 5 | border-bottom-width: ${StyleSheet.hairlineWidth}px; 6 | `; 7 | -------------------------------------------------------------------------------- /examples/CsstaNativeDemo/.babelrc: -------------------------------------------------------------------------------- 1 | { 2 | "presets": ["react-native"], 3 | "env": { 4 | "production": { 5 | "plugins": [ 6 | ["babel-plugin-cssta", { "optimizations": ["interpolateValuesOnly"] }] 7 | ] 8 | } 9 | } 10 | } 11 | -------------------------------------------------------------------------------- /packages/babel-plugin-cssta/fixtures/css-variables-export-native/actual.js: -------------------------------------------------------------------------------- 1 | import cssta from 'cssta/native'; 2 | import { View } from 'react-native'; 3 | 4 | cssta(View)` 5 | --color: red; 6 | 7 | &[@blue] { 8 | --color: blue; 9 | } 10 | `; 11 | -------------------------------------------------------------------------------- /packages/babel-plugin-cssta/fixtures/default/expected.css: -------------------------------------------------------------------------------- 1 | /* File generated by babel-plugin-cssta */ 2 | 3 | /* Start /fixtures/default/actual.js */ 4 | 5 | .A { 6 | color: red 7 | } 8 | 9 | /* End /fixtures/default/actual.js */ 10 | -------------------------------------------------------------------------------- /packages/babel-plugin-cssta/fixtures/string-attribute/actual.js: -------------------------------------------------------------------------------- 1 | import cssta from 'cssta'; 2 | 3 | cssta.button` 4 | &[@stringAttribute = "1"] { 5 | color: red; 6 | } 7 | 8 | &[@stringAttribute = "2"] { 9 | color: red; 10 | } 11 | `; 12 | -------------------------------------------------------------------------------- /packages/babel-plugin-cssta/fixtures/compose-component/expected.js: -------------------------------------------------------------------------------- 1 | import _createComponent from 'cssta/lib/web/createComponent'; 2 | 3 | import Link from 'react-router'; 4 | 5 | _createComponent(Link, null, { 6 | 'defaultClassName': 'A', 7 | 'classNameMap': {} 8 | }); -------------------------------------------------------------------------------- /packages/babel-plugin-cssta/fixtures/computed/expected.css: -------------------------------------------------------------------------------- 1 | /* File generated by babel-plugin-cssta */ 2 | 3 | /* Start /fixtures/computed/actual.js */ 4 | 5 | .A { 6 | color: red 7 | } 8 | 9 | /* End /fixtures/computed/actual.js */ 10 | -------------------------------------------------------------------------------- /packages/cssta/src/css-transforms/colors.js: -------------------------------------------------------------------------------- 1 | // @flow 2 | const color = require('css-color-function'); 3 | 4 | const colorFnRe = /color\((?:[^()]+|\([^)]+\))+\)/g; 5 | 6 | module.exports = (value /*: string */) /*: string */ => value.replace(colorFnRe, color.convert); 7 | -------------------------------------------------------------------------------- /examples/cssta-demo/.gitignore: -------------------------------------------------------------------------------- 1 | # See http://help.github.com/ignore-files/ for more about ignoring files. 2 | 3 | # dependencies 4 | node_modules 5 | 6 | # testing 7 | coverage 8 | 9 | # production 10 | build 11 | 12 | # misc 13 | .DS_Store 14 | npm-debug.log 15 | -------------------------------------------------------------------------------- /packages/babel-plugin-cssta/fixtures/compose-without-quasi/expected.js: -------------------------------------------------------------------------------- 1 | import _createComponent from 'cssta/lib/web/createComponent'; 2 | 3 | import Link from 'react-router'; 4 | 5 | _createComponent(Link, null, { 6 | 'defaultClassName': 'A', 7 | 'classNameMap': {} 8 | }); -------------------------------------------------------------------------------- /packages/benchmark/.eslintrc.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | "extends": "airbnb", 3 | "plugins": [ 4 | "react", 5 | "jsx-a11y", 6 | "import" 7 | ], 8 | "rules": { 9 | "react/jsx-filename-extension": [0], 10 | } 11 | }; 12 | -------------------------------------------------------------------------------- /examples/cssta-demo/upgrade/src/App.test.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import ReactDOM from 'react-dom'; 3 | import App from './App'; 4 | 5 | it('renders without crashing', () => { 6 | const div = document.createElement('div'); 7 | ReactDOM.render(, div); 8 | }); 9 | -------------------------------------------------------------------------------- /packages/babel-plugin-cssta/fixtures/compose-without-template/expected.js: -------------------------------------------------------------------------------- 1 | import _createComponent from 'cssta/lib/web/createComponent'; 2 | 3 | import Link from 'react-router'; 4 | 5 | _createComponent(Link, null, { 6 | 'defaultClassName': 'A', 7 | 'classNameMap': {} 8 | }); -------------------------------------------------------------------------------- /packages/babel-plugin-cssta/fixtures/without-template/expected.css: -------------------------------------------------------------------------------- 1 | /* File generated by babel-plugin-cssta */ 2 | 3 | /* Start /fixtures/without-template/actual.js */ 4 | 5 | .A { 6 | color: red 7 | } 8 | /* End /fixtures/without-template/actual.js */ 9 | -------------------------------------------------------------------------------- /packages/babel-plugin-cssta/.npmignore: -------------------------------------------------------------------------------- 1 | # See http://help.github.com/ignore-files/ for more about ignoring files. 2 | 3 | # dependencies 4 | node_modules 5 | 6 | # testing 7 | coverage 8 | 9 | # production 10 | build 11 | 12 | # misc 13 | .DS_Store 14 | npm-debug.log 15 | -------------------------------------------------------------------------------- /packages/babel-plugin-cssta/fixtures/without-quasi/expected.css: -------------------------------------------------------------------------------- 1 | /* File generated by babel-plugin-cssta */ 2 | 3 | /* Start /fixtures/without-quasi/actual.js */ 4 | 5 | .A { 6 | color: red 7 | } 8 | 9 | /* End /fixtures/without-quasi/actual.js */ 10 | -------------------------------------------------------------------------------- /examples/CsstaNativeDemo/android/app/src/main/res/values/styles.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /examples/CsstaNativeDemo/android/gradle/wrapper/gradle-wrapper.properties: -------------------------------------------------------------------------------- 1 | distributionBase=GRADLE_USER_HOME 2 | distributionPath=wrapper/dists 3 | zipStoreBase=GRADLE_USER_HOME 4 | zipStorePath=wrapper/dists 5 | distributionUrl=https\://services.gradle.org/distributions/gradle-2.4-all.zip 6 | -------------------------------------------------------------------------------- /examples/cssta-demo/.eslintrc.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | parser: 'babel-eslint', 3 | extends: 'airbnb', 4 | rules: { 5 | 'no-shadow': [0], 6 | 'react/prop-types': [0], 7 | 'react/jsx-filename-extension': [0], 8 | 'jsx-a11y/label-has-for': [0], 9 | }, 10 | }; 11 | -------------------------------------------------------------------------------- /examples/cssta-demo/upgrade/.gitignore: -------------------------------------------------------------------------------- 1 | # See http://help.github.com/ignore-files/ for more about ignoring files. 2 | 3 | # dependencies 4 | node_modules 5 | 6 | # testing 7 | coverage 8 | 9 | # production 10 | build 11 | 12 | # misc 13 | .DS_Store 14 | .env 15 | npm-debug.log 16 | -------------------------------------------------------------------------------- /packages/babel-plugin-cssta/fixtures/compose-component/expected.css: -------------------------------------------------------------------------------- 1 | /* File generated by babel-plugin-cssta */ 2 | 3 | /* Start /fixtures/compose-component/actual.js */ 4 | 5 | .A { 6 | color: red 7 | } 8 | 9 | /* End /fixtures/compose-component/actual.js */ 10 | -------------------------------------------------------------------------------- /packages/babel-plugin-cssta/fixtures/compose-without-template/expected.css: -------------------------------------------------------------------------------- 1 | /* File generated by babel-plugin-cssta */ 2 | 3 | /* Start /fixtures/compose-without-template/actual.js */ 4 | 5 | .A { 6 | color: red 7 | } 8 | /* End /fixtures/compose-without-template/actual.js */ 9 | -------------------------------------------------------------------------------- /packages/babel-plugin-cssta/fixtures/native-transitions/actual.js: -------------------------------------------------------------------------------- 1 | import cssta from 'cssta/native'; 2 | import { Animated } from 'react-native'; 3 | 4 | cssta(Animated.View)` 5 | color: red; 6 | transition: color 1s linear; 7 | 8 | &[@boolAttr] { 9 | color: blue; 10 | } 11 | `; 12 | -------------------------------------------------------------------------------- /packages/cssta/.npmignore: -------------------------------------------------------------------------------- 1 | # See http://help.github.com/ignore-files/ for more about ignoring files. 2 | 3 | # dependencies 4 | node_modules 5 | 6 | # testing 7 | coverage 8 | 9 | # production 10 | build 11 | 12 | # misc 13 | .DS_Store 14 | npm-debug.log 15 | 16 | # babel 17 | .babelrc 18 | -------------------------------------------------------------------------------- /packages/babel-plugin-cssta/fixtures/boolean-attribute/expected.css: -------------------------------------------------------------------------------- 1 | /* File generated by babel-plugin-cssta */ 2 | 3 | /* Start /fixtures/boolean-attribute/actual.js */ 4 | 5 | 6 | .A.B { 7 | color: red; 8 | } 9 | 10 | /* End /fixtures/boolean-attribute/actual.js */ 11 | -------------------------------------------------------------------------------- /packages/babel-plugin-cssta/fixtures/compose-without-quasi/expected.css: -------------------------------------------------------------------------------- 1 | /* File generated by babel-plugin-cssta */ 2 | 3 | /* Start /fixtures/compose-without-quasi/actual.js */ 4 | 5 | .A { 6 | color: red 7 | } 8 | 9 | /* End /fixtures/compose-without-quasi/actual.js */ 10 | -------------------------------------------------------------------------------- /packages/babel-plugin-cssta/fixtures/css-variables-single-source-web/actual.js: -------------------------------------------------------------------------------- 1 | import cssta from 'cssta'; 2 | 3 | cssta.div` 4 | --large: 100; 5 | --small: 50; 6 | --margin: var(--large) var(--small); 7 | 8 | width: var(--large); 9 | `; 10 | 11 | cssta.div` 12 | width: var(--small); 13 | `; 14 | -------------------------------------------------------------------------------- /examples/CsstaNativeDemo/.eslintrc.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | "extends": "airbnb", 3 | "plugins": [ 4 | "react", 5 | "jsx-a11y", 6 | "import" 7 | ], 8 | "rules": { 9 | "react/jsx-filename-extension": [0], 10 | "react/prop-types": [0] 11 | } 12 | }; 13 | -------------------------------------------------------------------------------- /packages/babel-plugin-cssta/fixtures/boolean-attribute/expected.js: -------------------------------------------------------------------------------- 1 | import _createComponent from 'cssta/lib/web/createComponent'; 2 | 3 | 4 | _createComponent('button', null, { 5 | 'defaultClassName': 'A', 6 | 'classNameMap': { 7 | 'booleanAttribute': { 8 | 'true': 'B' 9 | } 10 | } 11 | }); -------------------------------------------------------------------------------- /packages/babel-plugin-cssta/fixtures/import-under-different-name/expected.css: -------------------------------------------------------------------------------- 1 | /* File generated by babel-plugin-cssta */ 2 | 3 | /* Start /fixtures/import-under-different-name/actual.js */ 4 | 5 | .A { 6 | color: red 7 | } 8 | 9 | /* End /fixtures/import-under-different-name/actual.js */ 10 | -------------------------------------------------------------------------------- /packages/cssta/webpack.config.js: -------------------------------------------------------------------------------- 1 | const path = require('path'); 2 | 3 | module.exports = [{ 4 | entry: 'postcss', 5 | output: { 6 | path: path.join(__dirname, 'vendor'), 7 | filename: 'postcss.js', 8 | libraryTarget: 'commonjs2', 9 | }, 10 | node: { 11 | fs: 'empty', 12 | }, 13 | }]; 14 | -------------------------------------------------------------------------------- /packages/babel-plugin-cssta/fixtures/string-attribute/expected.js: -------------------------------------------------------------------------------- 1 | import _createComponent from 'cssta/lib/web/createComponent'; 2 | 3 | 4 | _createComponent('button', null, { 5 | 'defaultClassName': 'A', 6 | 'classNameMap': { 7 | 'stringAttribute': { 8 | '1': 'B', 9 | '2': 'C' 10 | } 11 | } 12 | }); -------------------------------------------------------------------------------- /packages/babel-plugin-cssta/fixtures/css-variables-single-source-native-interpolated-defaults/actual.js: -------------------------------------------------------------------------------- 1 | import cssta from 'cssta/native'; 2 | import { View } from 'react-native'; 3 | 4 | const color = 'blue'; 5 | 6 | cssta(View)` 7 | --color: red; 8 | `; 9 | 10 | cssta(View)` 11 | color: var(--color, ${color}); 12 | `; 13 | -------------------------------------------------------------------------------- /packages/babel-plugin-cssta/fixtures/native-transitions-variables/actual.js: -------------------------------------------------------------------------------- 1 | import cssta from 'cssta/native'; 2 | import { Animated } from 'react-native'; 3 | 4 | cssta(Animated.View)` 5 | color: var(--primary); 6 | transition: color 1s linear; 7 | 8 | &[@boolAttr] { 9 | color: var(--secondary); 10 | } 11 | `; 12 | -------------------------------------------------------------------------------- /packages/cssta/src/native/util.js: -------------------------------------------------------------------------------- 1 | // @flow 2 | /*:: import type { VariableWithValidator } from './types' */ 3 | 4 | module.exports.getAppliedRules = /*:: */ ( 5 | rules /*: T[] */, 6 | ownProps /*: Object */ 7 | ) /*: T[] */ => 8 | rules.filter(rule => (rule.validate ? rule.validate(ownProps) : true)); 9 | -------------------------------------------------------------------------------- /packages/babel-plugin-cssta/fixtures/string-attribute/expected.css: -------------------------------------------------------------------------------- 1 | /* File generated by babel-plugin-cssta */ 2 | 3 | /* Start /fixtures/string-attribute/actual.js */ 4 | 5 | 6 | .A.B { 7 | color: red; 8 | } 9 | 10 | .A.C { 11 | color: red; 12 | } 13 | 14 | /* End /fixtures/string-attribute/actual.js */ 15 | -------------------------------------------------------------------------------- /packages/babel-plugin-cssta/fixtures/css-variables-single-source-web/expected.js: -------------------------------------------------------------------------------- 1 | import _createComponent from 'cssta/lib/web/createComponent'; 2 | 3 | 4 | _createComponent('div', null, { 5 | 'defaultClassName': 'A', 6 | 'classNameMap': {} 7 | }); 8 | 9 | _createComponent('div', null, { 10 | 'defaultClassName': 'B', 11 | 'classNameMap': {} 12 | }); -------------------------------------------------------------------------------- /packages/babel-plugin-cssta/fixtures/multiple-attributes/actual.js: -------------------------------------------------------------------------------- 1 | import cssta from 'cssta'; 2 | 3 | cssta.button` 4 | color: red; 5 | 6 | &[@booleanAttribute] { 7 | color: green; 8 | } 9 | 10 | &[@stringAttribute = "1"] { 11 | color: blue; 12 | } 13 | 14 | &[@stringAttribute = "2"] { 15 | color: yellow; 16 | } 17 | `; 18 | -------------------------------------------------------------------------------- /examples/cssta-demo/config-overrides.js: -------------------------------------------------------------------------------- 1 | module.exports = (config) => { 2 | const babelLoader = config.module.loaders.find(loader => loader.loader === 'babel'); 3 | babelLoader.query.plugins = [ 4 | ...(babelLoader.query.plugins || []), 5 | ['babel-plugin-cssta', { 6 | output: 'build/styles.css', 7 | }], 8 | ]; 9 | return config; 10 | }; 11 | -------------------------------------------------------------------------------- /packages/babel-plugin-cssta/fixtures/css-variables-single-source-native/actual.js: -------------------------------------------------------------------------------- 1 | import cssta from 'cssta/native'; 2 | import { View } from 'react-native'; 3 | 4 | cssta(View)` 5 | --large: 100; 6 | --small: 50; 7 | --margin: var(--large) var(--small); 8 | 9 | width: var(--large); 10 | `; 11 | 12 | cssta(View)` 13 | width: var(--small); 14 | `; 15 | -------------------------------------------------------------------------------- /packages/babel-plugin-cssta/fixtures/native-keyframes-variables/actual.js: -------------------------------------------------------------------------------- 1 | import cssta from 'cssta/native'; 2 | import { Animated } from 'react-native'; 3 | 4 | cssta(Animated.View)` 5 | color: red; 6 | animation: test 1s linear; 7 | 8 | @keyframes test { 9 | start { color: rgba(0, 0, 0, 0); } 10 | end { color: var(--primary); } 11 | } 12 | `; 13 | -------------------------------------------------------------------------------- /packages/babel-plugin-cssta/fixtures/native-keyframes/actual.js: -------------------------------------------------------------------------------- 1 | import cssta from 'cssta/native'; 2 | import { Animated } from 'react-native'; 3 | 4 | cssta(Animated.View)` 5 | color: red; 6 | animation: test 1s linear; 7 | 8 | @keyframes test { 9 | start { opacity: 0; } 10 | 50% { opacity: 0.2; } 11 | end { opacity: 1; } 12 | } 13 | `; 14 | -------------------------------------------------------------------------------- /packages/cssta/src/css-transforms/variables.js: -------------------------------------------------------------------------------- 1 | // @flow 2 | const { varRegExp } = require('../util'); 3 | 4 | module.exports = ( 5 | value /*: string */, 6 | appliedVariables /*: { [key:string]: string } */ 7 | ) /*: string */ => ( 8 | value.replace(varRegExp, (m, variableName, fallback) => ( 9 | appliedVariables[variableName] || fallback 10 | )) 11 | ); 12 | -------------------------------------------------------------------------------- /examples/CsstaNativeDemo/__tests__/index.ios.js: -------------------------------------------------------------------------------- 1 | import 'react-native'; 2 | import React from 'react'; 3 | import Index from '../index.ios.js'; 4 | 5 | // Note: test renderer must be required after react-native. 6 | import renderer from 'react-test-renderer'; 7 | 8 | it('renders correctly', () => { 9 | const tree = renderer.create( 10 | 11 | ); 12 | }); 13 | -------------------------------------------------------------------------------- /examples/CsstaNativeDemo/__tests__/index.android.js: -------------------------------------------------------------------------------- 1 | import 'react-native'; 2 | import React from 'react'; 3 | import Index from '../index.android.js'; 4 | 5 | // Note: test renderer must be required after react-native. 6 | import renderer from 'react-test-renderer'; 7 | 8 | it('renders correctly', () => { 9 | const tree = renderer.create( 10 | 11 | ); 12 | }); 13 | -------------------------------------------------------------------------------- /packages/babel-plugin-cssta/fixtures/native-keyframes-without-variables/actual.js: -------------------------------------------------------------------------------- 1 | import cssta from 'cssta/native'; 2 | import { Animated } from 'react-native'; 3 | 4 | cssta(Animated.View)` 5 | color: var(--primary); 6 | animation: test 1s linear; 7 | 8 | @keyframes test { 9 | start { color: rgba(0, 0, 0, 0); } 10 | end { rgba: rgba(0, 0, 0, 1); } 11 | } 12 | `; 13 | -------------------------------------------------------------------------------- /packages/cssta/flow-typed/dependency-graph.js: -------------------------------------------------------------------------------- 1 | // @flow 2 | /*:: 3 | declare class DepGraph { 4 | addNode: (node: string) => void, 5 | addDependency: (tom: string, jerry: string) => void, 6 | overallOrder: (chuckNorris: boolean) => string[] 7 | } 8 | 9 | declare module 'dependency-graph' { 10 | declare module.exports: { 11 | DepGraph: Class 12 | } 13 | } 14 | */ 15 | -------------------------------------------------------------------------------- /packages/babel-plugin-cssta/fixtures/multiple-attributes/expected.js: -------------------------------------------------------------------------------- 1 | import _createComponent from 'cssta/lib/web/createComponent'; 2 | 3 | 4 | _createComponent('button', null, { 5 | 'defaultClassName': 'A', 6 | 'classNameMap': { 7 | 'booleanAttribute': { 8 | 'true': 'B' 9 | }, 10 | 'stringAttribute': { 11 | '1': 'C', 12 | '2': 'D' 13 | } 14 | } 15 | }); -------------------------------------------------------------------------------- /packages/babel-plugin-cssta/fixtures/multiple-calls-in-file/expected.css: -------------------------------------------------------------------------------- 1 | /* File generated by babel-plugin-cssta */ 2 | 3 | /* Start /fixtures/multiple-calls-in-file/actual.js */ 4 | 5 | .A { 6 | color: red 7 | } 8 | 9 | 10 | .B { 11 | color: green 12 | } 13 | 14 | 15 | .C { 16 | color: blue 17 | } 18 | 19 | /* End /fixtures/multiple-calls-in-file/actual.js */ 20 | -------------------------------------------------------------------------------- /packages/babel-plugin-cssta/fixtures/native-multiple-attributes/actual.js: -------------------------------------------------------------------------------- 1 | import cssta from 'cssta/native'; 2 | import { View } from 'react-native'; 3 | 4 | cssta(View)` 5 | color: red; 6 | 7 | &[@booleanAttribute] { 8 | color: green; 9 | } 10 | 11 | &[@stringAttribute = "1"] { 12 | color: blue; 13 | } 14 | 15 | &[@stringAttribute = "2"] { 16 | color: yellow; 17 | } 18 | `; 19 | -------------------------------------------------------------------------------- /packages/babel-plugin-cssta/fixtures/css-variables-single-source-web/expected.css: -------------------------------------------------------------------------------- 1 | /* File generated by babel-plugin-cssta */ 2 | 3 | /* Start /fixtures/css-variables-single-source-web/actual.js */ 4 | 5 | .A { 6 | --large: 100; 7 | --small: 50; 8 | --margin: 100 50; 9 | width: 100 10 | } 11 | 12 | 13 | .B { 14 | width: 50 15 | } 16 | 17 | /* End /fixtures/css-variables-single-source-web/actual.js */ 18 | -------------------------------------------------------------------------------- /packages/babel-plugin-cssta/fixtures/multiple-attributes/expected.css: -------------------------------------------------------------------------------- 1 | /* File generated by babel-plugin-cssta */ 2 | 3 | /* Start /fixtures/multiple-attributes/actual.js */ 4 | 5 | .A { 6 | color: red; 7 | } 8 | 9 | .A.B { 10 | color: green; 11 | } 12 | 13 | .A.C { 14 | color: blue; 15 | } 16 | 17 | .A.D { 18 | color: yellow; 19 | } 20 | 21 | /* End /fixtures/multiple-attributes/actual.js */ 22 | -------------------------------------------------------------------------------- /packages/babel-plugin-cssta/fixtures/multiple-calls-in-file/expected.js: -------------------------------------------------------------------------------- 1 | import _createComponent from 'cssta/lib/web/createComponent'; 2 | 3 | 4 | _createComponent('button', null, { 5 | 'defaultClassName': 'A', 6 | 'classNameMap': {} 7 | }); 8 | 9 | _createComponent('span', null, { 10 | 'defaultClassName': 'B', 11 | 'classNameMap': {} 12 | }); 13 | 14 | _createComponent('div', null, { 15 | 'defaultClassName': 'C', 16 | 'classNameMap': {} 17 | }); -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "scripts": { 3 | "readme": "cp ./docs/index.md ./README.md; sed -i -e 's%\\./%https://jacobp100.github.io/cssta/%g' ./README.md; sed -i -e 's/\\.md//g' ./README.md; sed -i -e 's/{% raw %}//g' ./README.md; sed -i -e 's/{% endraw %}//g' ./README.md; sed -i -e '1,5d' ./README.md; rm ./README.md-e" 4 | }, 5 | "devDependencies": { 6 | "eslint-config-prettier": "^1.5.0", 7 | "lerna": "2.0.0-beta.37", 8 | "prettier": "^0.22.0" 9 | } 10 | } 11 | -------------------------------------------------------------------------------- /examples/cssta-demo/upgrade/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "upgrade", 3 | "version": "0.0.1", 4 | "private": true, 5 | "devDependencies": { 6 | "react-scripts": "0.7.0" 7 | }, 8 | "dependencies": { 9 | "react": "^15.4.1", 10 | "react-dom": "^15.4.1" 11 | }, 12 | "scripts": { 13 | "start": "react-scripts start", 14 | "build": "react-scripts build", 15 | "test": "react-scripts test --env=jsdom", 16 | "eject": "react-scripts eject" 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /examples/cssta-demo/upgrade/src/App.css: -------------------------------------------------------------------------------- 1 | .App { 2 | text-align: center; 3 | } 4 | 5 | .App-logo { 6 | animation: App-logo-spin infinite 20s linear; 7 | height: 80px; 8 | } 9 | 10 | .App-header { 11 | background-color: #222; 12 | height: 150px; 13 | padding: 20px; 14 | color: white; 15 | } 16 | 17 | .App-intro { 18 | font-size: large; 19 | } 20 | 21 | @keyframes App-logo-spin { 22 | from { transform: rotate(0deg); } 23 | to { transform: rotate(360deg); } 24 | } 25 | -------------------------------------------------------------------------------- /packages/babel-plugin-cssta/fixtures/native-simple-interpolation/actual.js: -------------------------------------------------------------------------------- 1 | import cssta from 'cssta/native'; 2 | import { View } from 'react-native'; 3 | 4 | const color = 'red'; 5 | 6 | cssta(View)` 7 | &[@attr1] { 8 | margin-top: 10px; 9 | color: ${color}; 10 | } 11 | 12 | &[@attr2] { 13 | color: ${color}; 14 | margin-top: 10px; 15 | } 16 | 17 | &[@attr3] { 18 | margin-top: 10px; 19 | color: ${color}; 20 | margin-bottom: 10px; 21 | } 22 | `; 23 | -------------------------------------------------------------------------------- /packages/babel-plugin-cssta/fixtures/native-interpolation-no-optimisation/expected.js: -------------------------------------------------------------------------------- 1 | import cssta from 'cssta/native'; 2 | import { View } from 'react-native'; 3 | 4 | const color = 'red'; 5 | 6 | cssta(View)` 7 | &[@attr1] { 8 | margin-top: 10px; 9 | color: ${color}; 10 | } 11 | 12 | &[@attr2] { 13 | color: ${color}; 14 | margin-top: 10px; 15 | } 16 | 17 | &[@attr3] { 18 | margin-top: 10px; 19 | color: ${color}; 20 | margin-bottom: 10px; 21 | } 22 | `; -------------------------------------------------------------------------------- /packages/babel-plugin-cssta/fixtures/native-interpolation-no-optimisation/actual.js: -------------------------------------------------------------------------------- 1 | import cssta from 'cssta/native'; 2 | import { View } from 'react-native'; 3 | 4 | const color = 'red'; 5 | 6 | cssta(View)` 7 | &[@attr1] { 8 | margin-top: 10px; 9 | color: ${color}; 10 | } 11 | 12 | &[@attr2] { 13 | color: ${color}; 14 | margin-top: 10px; 15 | } 16 | 17 | &[@attr3] { 18 | margin-top: 10px; 19 | color: ${color}; 20 | margin-bottom: 10px; 21 | } 22 | `; 23 | -------------------------------------------------------------------------------- /packages/babel-plugin-cssta/fixtures/native-shorthand-interpolation/actual.js: -------------------------------------------------------------------------------- 1 | import cssta from 'cssta/native'; 2 | import { View } from 'react-native'; 3 | 4 | const font = '10px "Helvetica"'; 5 | 6 | cssta(View)` 7 | &[@attr1] { 8 | margin-top: 10px; 9 | font: ${font}; 10 | } 11 | 12 | &[@attr2] { 13 | font: ${font}; 14 | margin-top: 10px; 15 | } 16 | 17 | &[@attr3] { 18 | margin-top: 10px; 19 | font: ${font}; 20 | margin-bottom: 10px; 21 | } 22 | `; 23 | -------------------------------------------------------------------------------- /packages/babel-plugin-cssta/fixtures/native-transitions-shorthand/actual.js: -------------------------------------------------------------------------------- 1 | import cssta from 'cssta/native'; 2 | import { Animated } from 'react-native'; 3 | 4 | cssta(Animated.View)` 5 | background-color: #e74c3c; 6 | height: 20px; 7 | margin-bottom: 20px; 8 | transform: scaleX(1) rotate(0deg); 9 | transition: background-color 0.5s linear, transform 0.75s linear; 10 | 11 | &[@active] { 12 | background-color: #1abc9c; 13 | transform: scaleX(0.5) rotate(6deg); 14 | } 15 | `; 16 | -------------------------------------------------------------------------------- /examples/CsstaNativeDemo/android/app/src/main/java/com/csstanativedemo/MainActivity.java: -------------------------------------------------------------------------------- 1 | package com.csstanativedemo; 2 | 3 | import com.facebook.react.ReactActivity; 4 | 5 | public class MainActivity extends ReactActivity { 6 | 7 | /** 8 | * Returns the name of the main component registered from JavaScript. 9 | * This is used to schedule rendering of the component. 10 | */ 11 | @Override 12 | protected String getMainComponentName() { 13 | return "CsstaNativeDemo"; 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /packages/babel-plugin-cssta/src/webUtil.js: -------------------------------------------------------------------------------- 1 | const startEndMarkers = (commentMarkerBody) => { 2 | const commentStartMarker = `/* Start ${commentMarkerBody} */\n`; 3 | const commentEndMarker = `/* End ${commentMarkerBody} */\n`; 4 | 5 | return { commentStartMarker, commentEndMarker }; 6 | }; 7 | module.exports.startEndMarkers = startEndMarkers; 8 | 9 | module.exports.fileStartEndCommentMarkers = (state) => { 10 | const filename = state.file.opts.filename; 11 | return startEndMarkers(filename.replace(/\*/g, '')); 12 | }; 13 | -------------------------------------------------------------------------------- /packages/babel-plugin-cssta/.eslintrc.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | "extends": "airbnb-base", 3 | "installedESLint": true, 4 | "plugins": [ 5 | "import" 6 | ], 7 | "rules": { 8 | "react/jsx-filename-extension": [0], 9 | "comma-dangle": ["error", { 10 | arrays: "always-multiline", 11 | objects: "always-multiline", 12 | imports: "always-multiline", 13 | exports: "always-multiline", 14 | functions: "never", 15 | }], 16 | } 17 | }; 18 | -------------------------------------------------------------------------------- /examples/CsstaNativeDemo/ios/CsstaNativeDemo/AppDelegate.h: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright (c) 2015-present, Facebook, Inc. 3 | * All rights reserved. 4 | * 5 | * This source code is licensed under the BSD-style license found in the 6 | * LICENSE file in the root directory of this source tree. An additional grant 7 | * of patent rights can be found in the PATENTS file in the same directory. 8 | */ 9 | 10 | #import 11 | 12 | @interface AppDelegate : UIResponder 13 | 14 | @property (nonatomic, strong) UIWindow *window; 15 | 16 | @end 17 | -------------------------------------------------------------------------------- /examples/CsstaNativeDemo/README.md: -------------------------------------------------------------------------------- 1 | # CsstaNativeDemo 2 | 3 | Simple React-Native project using cssta. Setup: 4 | 5 | ```bash 6 | npm install --save cssta 7 | npm install --save-dev babel-plugin-cssta 8 | ``` 9 | 10 | Add following to `.babelrc`: 11 | 12 | ```json 13 | { 14 | "presets": ["react-native"], 15 | "env": { 16 | "production": { 17 | "plugins": ["babel-plugin-cssta"] 18 | } 19 | } 20 | } 21 | ``` 22 | 23 | That's all! You probably want to enable some [optimisations](https://jacobp100.gitbooks.io/cssta/content/production_builds.html) if you use template interpolation. 24 | -------------------------------------------------------------------------------- /packages/babel-plugin-cssta/fixtures/native-complex-interpolation/actual.js: -------------------------------------------------------------------------------- 1 | import cssta from 'cssta/native'; 2 | import { View } from 'react-native'; 3 | 4 | const marginSmall = 10; 5 | const marginLarge = 10; 6 | 7 | cssta(View)` 8 | &[@attr1] { 9 | padding-top: 10px; 10 | margin: ${marginLarge}px ${marginSmall}px; 11 | } 12 | 13 | &[@attr2] { 14 | margin: ${marginLarge}px ${marginSmall}px; 15 | padding-top: 10px; 16 | } 17 | 18 | &[@attr3] { 19 | padding-top: 10px; 20 | margin: ${marginLarge}px ${marginSmall}px; 21 | padding-bottom: 10px; 22 | } 23 | `; 24 | -------------------------------------------------------------------------------- /packages/cssta/src/native/createComponent.js: -------------------------------------------------------------------------------- 1 | // @flow 2 | /* eslint-disable no-param-reassign */ 3 | const componentFactory = require('../factories/componentFactory'); 4 | const { getAppliedRules } = require('./util'); 5 | /*:: import type { Args } from './types' */ 6 | 7 | module.exports = componentFactory((ownProps, passedProps, args /*: Args */) => { 8 | let style = getAppliedRules(args.rules, ownProps).map(rule => rule.style); 9 | 10 | if ('style' in passedProps) style = style.concat(passedProps.style); 11 | if (style.length > 0) passedProps.style = style; 12 | 13 | return passedProps; 14 | }); 15 | -------------------------------------------------------------------------------- /packages/babel-plugin-cssta/fixtures/native/expected.js: -------------------------------------------------------------------------------- 1 | import { StyleSheet as _StyleSheet } from 'react-native'; 2 | import _createComponent from 'cssta/lib/native/createComponent'; 3 | 4 | import { View } from 'react-native'; 5 | 6 | var _csstaStyle = _StyleSheet.create({ 7 | 0: { 8 | 'color': 'red' 9 | } 10 | }); 11 | 12 | _createComponent(View, [], { 13 | 'transitionedProperties': [], 14 | 'keyframes': {}, 15 | 'rules': [{ 16 | 'validate': function (p) { 17 | return true; 18 | }, 19 | 'transitions': {}, 20 | 'animation': null, 21 | 'style': _csstaStyle[0] 22 | }] 23 | }); -------------------------------------------------------------------------------- /examples/CsstaNativeDemo/.gitignore: -------------------------------------------------------------------------------- 1 | # OSX 2 | # 3 | .DS_Store 4 | 5 | # Xcode 6 | # 7 | build/ 8 | *.pbxuser 9 | !default.pbxuser 10 | *.mode1v3 11 | !default.mode1v3 12 | *.mode2v3 13 | !default.mode2v3 14 | *.perspectivev3 15 | !default.perspectivev3 16 | xcuserdata 17 | *.xccheckout 18 | *.moved-aside 19 | DerivedData 20 | *.hmap 21 | *.ipa 22 | *.xcuserstate 23 | project.xcworkspace 24 | 25 | # Android/IJ 26 | # 27 | *.iml 28 | .idea 29 | .gradle 30 | local.properties 31 | 32 | # node.js 33 | # 34 | node_modules/ 35 | npm-debug.log 36 | 37 | # BUCK 38 | buck-out/ 39 | \.buckd/ 40 | android/app/libs 41 | *.keystore 42 | -------------------------------------------------------------------------------- /examples/cssta-demo/upgrade/src/App.js: -------------------------------------------------------------------------------- 1 | import React, { Component } from 'react'; 2 | import logo from './logo.svg'; 3 | import './App.css'; 4 | 5 | class App extends Component { 6 | render() { 7 | return ( 8 |
    9 |
    10 | logo 11 |

    Welcome to React

    12 |
    13 |

    14 | To get started, edit src/App.js and save to reload. 15 |

    16 |
    17 | ); 18 | } 19 | } 20 | 21 | export default App; 22 | -------------------------------------------------------------------------------- /examples/CsstaNativeDemo/ios/CsstaNativeDemo/main.m: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright (c) 2015-present, Facebook, Inc. 3 | * All rights reserved. 4 | * 5 | * This source code is licensed under the BSD-style license found in the 6 | * LICENSE file in the root directory of this source tree. An additional grant 7 | * of patent rights can be found in the PATENTS file in the same directory. 8 | */ 9 | 10 | #import 11 | 12 | #import "AppDelegate.h" 13 | 14 | int main(int argc, char * argv[]) { 15 | @autoreleasepool { 16 | return UIApplicationMain(argc, argv, nil, NSStringFromClass([AppDelegate class])); 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /packages/babel-plugin-cssta/fixtures/native-color-function/expected.js: -------------------------------------------------------------------------------- 1 | import { StyleSheet as _StyleSheet } from 'react-native'; 2 | import _createComponent from 'cssta/lib/native/createComponent'; 3 | 4 | import { View } from 'react-native'; 5 | 6 | var _csstaStyle = _StyleSheet.create({ 7 | 0: { 8 | 'color': 'rgb(255, 128, 128)' 9 | } 10 | }); 11 | 12 | _createComponent(View, [], { 13 | 'transitionedProperties': [], 14 | 'keyframes': {}, 15 | 'rules': [{ 16 | 'validate': function (p) { 17 | return true; 18 | }, 19 | 'transitions': {}, 20 | 'animation': null, 21 | 'style': _csstaStyle[0] 22 | }] 23 | }); -------------------------------------------------------------------------------- /packages/cssta/.eslintrc.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | "extends": "airbnb-base", 3 | "installedESLint": true, 4 | "plugins": [ 5 | "import", 6 | "flowtype" 7 | ], 8 | "rules": { 9 | "react/jsx-filename-extension": [0], 10 | "flowtype/require-valid-file-annotation": [2, "always"], 11 | "comma-dangle": ["error", { 12 | arrays: "always-multiline", 13 | objects: "always-multiline", 14 | imports: "always-multiline", 15 | exports: "always-multiline", 16 | functions: "never", 17 | }], 18 | "spaced-comment": [0], 19 | "arrow-parens": [0] 20 | } 21 | }; 22 | -------------------------------------------------------------------------------- /examples/CsstaNativeDemo/demos/Basic.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import { View, Text } from 'react-native'; 3 | import cssta from 'cssta/native'; 4 | 5 | const HeadingContainer = cssta(View)` 6 | margin: 10px 50px; 7 | padding: 10px 15px; 8 | border-radius: 5px; 9 | border: 1px solid #e67e22; 10 | `; 11 | 12 | const HeadingText = cssta(Text)` 13 | color: #e67e22; 14 | `; 15 | 16 | export default () => ( 17 | 18 | Hello World! 19 | 20 | ); 21 | 22 | export const code = 23 | ` margin: 10px 50px; 24 | padding: 10px 15px; 25 | border-radius: 5px; 26 | border: 1px solid #e67e22;`; 27 | -------------------------------------------------------------------------------- /packages/babel-plugin-cssta/src/platforms/native/createArgsStatic.js: -------------------------------------------------------------------------------- 1 | const t = require('babel-types'); 2 | const createKeyframes = require('./createKeyframes'); 3 | const createStyleSheet = require('./createStyleSheet'); 4 | const { commonArgs } = require('./createUtil'); 5 | 6 | module.exports = (path, substitutionMap, args) => 7 | t.objectExpression([ 8 | ...commonArgs(args), 9 | t.objectProperty( 10 | t.stringLiteral('keyframes'), 11 | createKeyframes(path, substitutionMap, args.keyframesStyleTuples) 12 | ), 13 | t.objectProperty( 14 | t.stringLiteral('rules'), 15 | createStyleSheet(path, substitutionMap, args.ruleTuples) 16 | ), 17 | ]); 18 | -------------------------------------------------------------------------------- /examples/CsstaNativeDemo/demos/ColorFunction.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import { View, Text } from 'react-native'; 3 | import cssta from 'cssta/native'; 4 | 5 | const HeadingContainer = cssta(View)` 6 | --color: red; 7 | `; 8 | 9 | const HeadingText = cssta(Text)` 10 | color: color(var(--color) tint(50%)); 11 | `; 12 | 13 | const Code = cssta(Text)` 14 | font-family: "courier"; 15 | background-color: #eee; 16 | color: black; 17 | `; 18 | 19 | export default () => ( 20 | 21 | Text styled using CSS’s color(...) function 22 | 23 | ); 24 | 25 | export const code = 'color: color(red tint(50%));'; 26 | -------------------------------------------------------------------------------- /packages/babel-plugin-cssta/fixtures/css-variables-import-native/expected.js: -------------------------------------------------------------------------------- 1 | import _withEnhancers from 'cssta/lib/native/withEnhancers'; 2 | import _VariablesStyleSheetManager from 'cssta/lib/native/enhancers/VariablesStyleSheetManager'; 3 | 4 | import { View } from 'react-native'; 5 | 6 | _withEnhancers([_VariablesStyleSheetManager])(View, [], { 7 | 'transitionedProperties': [], 8 | 'importedVariables': ['color'], 9 | 'keyframesStyleTuples': {}, 10 | 'ruleTuples': [{ 11 | 'validate': function (p) { 12 | return true; 13 | }, 14 | 'exportedVariables': {}, 15 | 'transitionParts': {}, 16 | 'animationParts': null, 17 | 'styleTuples': [['color', 'var(--color)']] 18 | }] 19 | }); -------------------------------------------------------------------------------- /packages/babel-plugin-cssta/fixtures/native-within-closure/expected.js: -------------------------------------------------------------------------------- 1 | import { StyleSheet as _StyleSheet } from 'react-native'; 2 | import _createComponent from 'cssta/lib/native/createComponent'; 3 | 4 | import { View } from 'react-native'; 5 | 6 | function test() { 7 | var _csstaStyle = _StyleSheet.create({ 8 | 0: { 9 | 'color': 'red' 10 | } 11 | }); 12 | 13 | const Component = _createComponent(View, [], { 14 | 'transitionedProperties': [], 15 | 'keyframes': {}, 16 | 'rules': [{ 17 | 'validate': function (p) { 18 | return true; 19 | }, 20 | 'transitions': {}, 21 | 'animation': null, 22 | 'style': _csstaStyle[0] 23 | }] 24 | }); 25 | } -------------------------------------------------------------------------------- /packages/babel-plugin-cssta/fixtures/css-variables-single-source-native-interpolated-defaults/expected.js: -------------------------------------------------------------------------------- 1 | import { StyleSheet as _StyleSheet } from 'react-native'; 2 | import _createComponent from 'cssta/lib/native/createComponent'; 3 | 4 | import { View } from 'react-native'; 5 | 6 | const color = 'blue'; 7 | 8 | View; 9 | 10 | var _csstaStyle = _StyleSheet.create({ 11 | 0: { 12 | 'color': 'red' 13 | } 14 | }); 15 | 16 | _createComponent(View, [], { 17 | 'transitionedProperties': [], 18 | 'keyframes': {}, 19 | 'rules': [{ 20 | 'validate': function (p) { 21 | return true; 22 | }, 23 | 'transitions': {}, 24 | 'animation': null, 25 | 'style': _csstaStyle[0] 26 | }] 27 | }); -------------------------------------------------------------------------------- /packages/babel-plugin-cssta/src/visitors/program/removeUnusedCsstaImports.js: -------------------------------------------------------------------------------- 1 | /* eslint-disable no-param-reassign */ 2 | const t = require('babel-types'); 3 | const _ = require('lodash/fp'); 4 | const { csstaModules, getImportReferences } = require('../../util'); 5 | 6 | 7 | module.exports = (path) => { 8 | const unreferencedCsstaImportReferences = _.flow( 9 | _.flatMap(moduleName => getImportReferences(path, moduleName, 'default')), 10 | _.filter({ references: 0 }) 11 | )(_.keys(csstaModules)); 12 | 13 | _.forEach((reference) => { 14 | const importDeclaration = reference.path.findParent(t.isImportDeclaration); 15 | importDeclaration.remove(); 16 | }, unreferencedCsstaImportReferences); 17 | }; 18 | -------------------------------------------------------------------------------- /packages/babel-plugin-cssta/fixtures/native-simple-interpolation-suffix/expected.js: -------------------------------------------------------------------------------- 1 | import { transformRawValue as _transformRawValue } from 'cssta/lib/native/cssUtil'; 2 | import _createComponent from 'cssta/lib/native/createComponent'; 3 | 4 | import { View, StyleSheet } from 'react-native'; 5 | 6 | var _csstaStyle = StyleSheet.create({ 7 | 0: { 8 | 'borderBottomWidth': _transformRawValue(`${StyleSheet.hairlineWidth}px`) 9 | } 10 | }); 11 | 12 | _createComponent(View, [], { 13 | 'transitionedProperties': [], 14 | 'keyframes': {}, 15 | 'rules': [{ 16 | 'validate': function (p) { 17 | return true; 18 | }, 19 | 'transitions': {}, 20 | 'animation': null, 21 | 'style': _csstaStyle[0] 22 | }] 23 | }); -------------------------------------------------------------------------------- /packages/babel-plugin-cssta/fixtures/native-variables-provider/expected.js: -------------------------------------------------------------------------------- 1 | import { StyleSheet as _StyleSheet } from 'react-native'; 2 | import _createComponent from 'cssta/lib/native/createComponent'; 3 | import VariablesProvider from 'cssta/lib/native/VariablesProvider'; 4 | 5 | import { View } from 'react-native'; 6 | 7 | var _csstaStyle = _StyleSheet.create({ 8 | 0: { 9 | 'color': 'red' 10 | } 11 | }); 12 | 13 | _createComponent(View, [], { 14 | 'transitionedProperties': [], 15 | 'keyframes': {}, 16 | 'rules': [{ 17 | 'validate': function (p) { 18 | return true; 19 | }, 20 | 'transitions': {}, 21 | 'animation': null, 22 | 'style': _csstaStyle[0] 23 | }] 24 | }); 25 | 26 | VariablesProvider; -------------------------------------------------------------------------------- /packages/babel-plugin-cssta/fixtures/css-variables-native/expected.js: -------------------------------------------------------------------------------- 1 | import _withEnhancers from 'cssta/lib/native/withEnhancers'; 2 | import _VariablesStyleSheetManager from 'cssta/lib/native/enhancers/VariablesStyleSheetManager'; 3 | 4 | import { View } from 'react-native'; 5 | 6 | _withEnhancers([_VariablesStyleSheetManager])(View, [], { 7 | 'transitionedProperties': [], 8 | 'importedVariables': ['color'], 9 | 'keyframesStyleTuples': {}, 10 | 'ruleTuples': [{ 11 | 'validate': function (p) { 12 | return true; 13 | }, 14 | 'exportedVariables': { 15 | 'color': 'red' 16 | }, 17 | 'transitionParts': {}, 18 | 'animationParts': null, 19 | 'styleTuples': [['color', 'var(--color)']] 20 | }] 21 | }); -------------------------------------------------------------------------------- /packages/benchmark/webpack.config.js: -------------------------------------------------------------------------------- 1 | const { join } = require('path'); 2 | const webpack = require('webpack'); 3 | 4 | module.exports = { 5 | entry: './tests', 6 | output: { 7 | filename: 'tests-compiled.js', 8 | libraryTarget: 'commonjs', 9 | }, 10 | resolve: { 11 | alias: { 12 | 'react-native': join(__dirname, '/mocks/react-native'), 13 | }, 14 | }, 15 | module: { 16 | rules: [ 17 | { test: /\.js$/, exclude: /node_modules/, use: ['babel-loader'] }, 18 | ], 19 | }, 20 | plugins: [ 21 | new webpack.DefinePlugin({ 22 | 'process.env': { 23 | NODE_ENV: JSON.stringify('production'), 24 | }, 25 | }), 26 | new webpack.optimize.UglifyJsPlugin(), 27 | ], 28 | }; 29 | -------------------------------------------------------------------------------- /packages/babel-plugin-cssta/fixtures/native-shorthand/expected.js: -------------------------------------------------------------------------------- 1 | import { StyleSheet as _StyleSheet } from 'react-native'; 2 | import _createComponent from 'cssta/lib/native/createComponent'; 3 | 4 | import { View } from 'react-native'; 5 | 6 | var _csstaStyle = _StyleSheet.create({ 7 | 0: { 8 | 'fontStyle': 'italic', 9 | 'fontWeight': 'bold', 10 | 'fontVariant': [], 11 | 'fontSize': 12, 12 | 'fontFamily': 'Helvetica', 13 | 'lineHeight': 18 14 | } 15 | }); 16 | 17 | _createComponent(View, [], { 18 | 'transitionedProperties': [], 19 | 'keyframes': {}, 20 | 'rules': [{ 21 | 'validate': function (p) { 22 | return true; 23 | }, 24 | 'transitions': {}, 25 | 'animation': null, 26 | 'style': _csstaStyle[0] 27 | }] 28 | }); -------------------------------------------------------------------------------- /packages/babel-plugin-cssta/src/platforms/native/createUtil.js: -------------------------------------------------------------------------------- 1 | const t = require('babel-types'); 2 | const { parse } = require('babylon'); 3 | const _ = require('lodash/fp'); 4 | const { getValidatorSourceForSelector } = require('cssta/src/native/selectorTransform'); 5 | const { jsonObjectProperties } = require('./util'); 6 | 7 | 8 | const createValidatorNodeForSelector = selector => 9 | parse(getValidatorSourceForSelector(selector)).program.body[0].expression; 10 | 11 | module.exports.baseRuleElements = rule => [ 12 | t.objectProperty( 13 | t.stringLiteral('validate'), 14 | createValidatorNodeForSelector(rule.selector) 15 | ), 16 | ]; 17 | 18 | module.exports.commonArgs = _.flow( 19 | _.pick(['transitionedProperties']), 20 | jsonObjectProperties 21 | ); 22 | -------------------------------------------------------------------------------- /examples/CsstaNativeDemo/android/build.gradle: -------------------------------------------------------------------------------- 1 | // Top-level build file where you can add configuration options common to all sub-projects/modules. 2 | 3 | buildscript { 4 | repositories { 5 | jcenter() 6 | } 7 | dependencies { 8 | classpath 'com.android.tools.build:gradle:1.3.1' 9 | 10 | // NOTE: Do not place your application dependencies here; they belong 11 | // in the individual module build.gradle files 12 | } 13 | } 14 | 15 | allprojects { 16 | repositories { 17 | mavenLocal() 18 | jcenter() 19 | maven { 20 | // All of React Native (JS, Obj-C sources, Android binaries) is installed from npm 21 | url "$rootDir/../node_modules/react-native/android" 22 | } 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /examples/CsstaNativeDemo/ios/CsstaNativeDemo/Images.xcassets/AppIcon.appiconset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "iphone", 5 | "size" : "29x29", 6 | "scale" : "2x" 7 | }, 8 | { 9 | "idiom" : "iphone", 10 | "size" : "29x29", 11 | "scale" : "3x" 12 | }, 13 | { 14 | "idiom" : "iphone", 15 | "size" : "40x40", 16 | "scale" : "2x" 17 | }, 18 | { 19 | "idiom" : "iphone", 20 | "size" : "40x40", 21 | "scale" : "3x" 22 | }, 23 | { 24 | "idiom" : "iphone", 25 | "size" : "60x60", 26 | "scale" : "2x" 27 | }, 28 | { 29 | "idiom" : "iphone", 30 | "size" : "60x60", 31 | "scale" : "3x" 32 | } 33 | ], 34 | "info" : { 35 | "version" : 1, 36 | "author" : "xcode" 37 | } 38 | } -------------------------------------------------------------------------------- /examples/cssta-demo/public/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | React App 8 | 9 | 10 | 11 |
    12 | 22 | 23 | 24 | -------------------------------------------------------------------------------- /packages/babel-plugin-cssta/src/platforms/native/createArgsVariables.js: -------------------------------------------------------------------------------- 1 | const t = require('babel-types'); 2 | const { jsonToNode } = require('../../util'); 3 | const createKeyframes = require('./createKeyframes'); 4 | const createStyleSheet = require('./createStyleSheet'); 5 | const { commonArgs } = require('./createUtil'); 6 | 7 | module.exports = (path, substitutionMap, args) => 8 | t.objectExpression([ 9 | ...commonArgs(args), 10 | t.objectProperty( 11 | t.stringLiteral('importedVariables'), 12 | jsonToNode(args.importedVariables) 13 | ), 14 | t.objectProperty( 15 | t.stringLiteral('keyframesStyleTuples'), 16 | createKeyframes(path, substitutionMap, args.keyframesStyleTuples) 17 | ), 18 | t.objectProperty( 19 | t.stringLiteral('ruleTuples'), 20 | createStyleSheet(path, substitutionMap, args.ruleTuples) 21 | ), 22 | ]); 23 | -------------------------------------------------------------------------------- /examples/cssta-demo/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "cssta-demo", 3 | "version": "0.0.1", 4 | "private": true, 5 | "devDependencies": { 6 | "babel-eslint": "^6.1.2", 7 | "babel-plugin-cssta": "^0.4.0", 8 | "eslint": "^3.5.0", 9 | "eslint-config-airbnb": "^11.1.0", 10 | "eslint-plugin-import": "^1.15.0", 11 | "eslint-plugin-jsx-a11y": "^2.2.2", 12 | "eslint-plugin-react": "^6.5.0", 13 | "react-app-rewired": "^0.1.0", 14 | "react-scripts": "^0.7.0" 15 | }, 16 | "dependencies": { 17 | "cssnano-cli": "^1.0.5", 18 | "cssta": "^0.4.0", 19 | "react": "^15.3.2", 20 | "react-dom": "^15.3.2" 21 | }, 22 | "scripts": { 23 | "start": "react-scripts start", 24 | "build": "react-app-rewired build; cssnano build/styles.css build/styles.css", 25 | "test": "react-scripts test --env=jsdom", 26 | "eject": "react-scripts eject" 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /examples/CsstaNativeDemo/ios/CsstaNativeDemoTests/Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | en 7 | CFBundleExecutable 8 | $(EXECUTABLE_NAME) 9 | CFBundleIdentifier 10 | org.reactjs.native.example.$(PRODUCT_NAME:rfc1034identifier) 11 | CFBundleInfoDictionaryVersion 12 | 6.0 13 | CFBundleName 14 | $(PRODUCT_NAME) 15 | CFBundlePackageType 16 | BNDL 17 | CFBundleShortVersionString 18 | 1.0 19 | CFBundleSignature 20 | ???? 21 | CFBundleVersion 22 | 1 23 | 24 | 25 | -------------------------------------------------------------------------------- /packages/cssta/src/factories/types.js: -------------------------------------------------------------------------------- 1 | // @flow 2 | /*:: import React from 'react' */ 3 | 4 | /*:: 5 | export type ComponentPropTypes = { [key:string]: any } | string[] 6 | 7 | export type ComponentFactory = ( 8 | component: any, 9 | propTypes: ComponentPropTypes, 10 | // Let each enhancer/createComponent typecheck themselves 11 | // since an enhancer may change the type of args 12 | args: any, 13 | enhancer: ?EnhancerConstructor 14 | ) => (props: Object) => any // a React element 15 | 16 | export type Props = { 17 | Element: any, 18 | ownProps: Object, 19 | passedProps: Object, 20 | args: T, 21 | } 22 | 23 | export type DynamicProps = Props & { 24 | children: any,// (props: Props) => any, 25 | } 26 | 27 | export type EnhancerConstructor = (endNode: any) => (props: Object) => any // a react Element 28 | 29 | export type Enhancer = Class, *>> 30 | */ 31 | -------------------------------------------------------------------------------- /examples/CsstaNativeDemo/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "CsstaNativeDemo", 3 | "version": "0.0.1", 4 | "private": true, 5 | "scripts": { 6 | "start": "node node_modules/react-native/local-cli/cli.js start", 7 | "devtools": "react-devtools", 8 | "test": "jest" 9 | }, 10 | "dependencies": { 11 | "cssta": "^0.4.0", 12 | "react": "15.4.1", 13 | "react-native": "0.38.0" 14 | }, 15 | "jest": { 16 | "preset": "react-native" 17 | }, 18 | "devDependencies": { 19 | "babel-jest": "17.0.2", 20 | "babel-plugin-cssta": "^0.4.0", 21 | "babel-preset-react-native": "1.9.0", 22 | "eslint": "^3.16.1", 23 | "eslint-config-airbnb": "^14.1.0", 24 | "eslint-plugin-import": "^2.2.0", 25 | "eslint-plugin-jsx-a11y": "^4.0.0", 26 | "eslint-plugin-react": "^6.10.0", 27 | "jest": "17.0.3", 28 | "react-devtools": "^2.0.12", 29 | "react-test-renderer": "15.4.1" 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /packages/cssta/src/native/cssUtil.js: -------------------------------------------------------------------------------- 1 | // @flow 2 | const { default: cssToReactNative, transformRawValue } = require('css-to-react-native'); 3 | const transformVariables = require('../css-transforms/variables'); 4 | const transformColors = require('../css-transforms/colors'); 5 | /*:: import type { StyleTuple, VariablesStore } from './types' */ 6 | 7 | module.exports.transformRawValue = transformRawValue; 8 | 9 | module.exports.transformStyleTuples = ( 10 | styleTuples /*: StyleTuple[] */, 11 | appliedVariables /*: VariablesStore */ 12 | ) /*: Object */ => { 13 | const transformedStyleTuples = styleTuples.map(([property, value]) => { 14 | let transformedValue = value; 15 | if (appliedVariables) transformedValue = transformVariables(transformedValue, appliedVariables); 16 | transformedValue = transformColors(transformedValue); 17 | return [property, transformedValue]; 18 | }); 19 | return cssToReactNative(transformedStyleTuples); 20 | }; 21 | -------------------------------------------------------------------------------- /packages/babel-plugin-cssta/fixtures/css-variables-export-native/expected.js: -------------------------------------------------------------------------------- 1 | import _withEnhancers from 'cssta/lib/native/withEnhancers'; 2 | import _VariablesStyleSheetManager from 'cssta/lib/native/enhancers/VariablesStyleSheetManager'; 3 | 4 | import { View } from 'react-native'; 5 | 6 | _withEnhancers([_VariablesStyleSheetManager])(View, ['blue'], { 7 | 'transitionedProperties': [], 8 | 'importedVariables': [], 9 | 'keyframesStyleTuples': {}, 10 | 'ruleTuples': [{ 11 | 'validate': function (p) { 12 | return true; 13 | }, 14 | 'exportedVariables': { 15 | 'color': 'red' 16 | }, 17 | 'transitionParts': {}, 18 | 'animationParts': null, 19 | 'styleTuples': [] 20 | }, { 21 | 'validate': function (p) { 22 | return !!p["blue"]; 23 | }, 24 | 'exportedVariables': { 25 | 'color': 'blue' 26 | }, 27 | 'transitionParts': {}, 28 | 'animationParts': null, 29 | 'styleTuples': [] 30 | }] 31 | }); -------------------------------------------------------------------------------- /packages/babel-plugin-cssta/src/visitors/importSpecifier/redirectImports.js: -------------------------------------------------------------------------------- 1 | /* eslint-disable no-param-reassign */ 2 | const t = require('babel-types'); 3 | const _ = require('lodash/fp'); 4 | 5 | const redirectImports = { 6 | 'cssta/native': { 7 | VariablesProvider: 'cssta/lib/native/VariablesProvider', 8 | }, 9 | }; 10 | 11 | module.exports = (path) => { 12 | const importName = path.node.imported.name; 13 | const importDeclaration = path.findParent(t.isImportDeclaration); 14 | const moduleName = importDeclaration.node.source.value; 15 | const redirect = _.get([moduleName, importName], redirectImports); 16 | if (!redirect) return; 17 | const redirectImport = t.importDeclaration([ 18 | t.importDefaultSpecifier(path.node.local), 19 | ], t.stringLiteral(redirect)); 20 | importDeclaration.insertBefore(redirectImport); 21 | 22 | if (importDeclaration.node.specifiers.length === 1) { 23 | importDeclaration.remove(); 24 | } else { 25 | path.remove(); 26 | } 27 | }; 28 | -------------------------------------------------------------------------------- /examples/CsstaNativeDemo/android/gradle.properties: -------------------------------------------------------------------------------- 1 | # Project-wide Gradle settings. 2 | 3 | # IDE (e.g. Android Studio) users: 4 | # Gradle settings configured through the IDE *will override* 5 | # any settings specified in this file. 6 | 7 | # For more details on how to configure your build environment visit 8 | # http://www.gradle.org/docs/current/userguide/build_environment.html 9 | 10 | # Specifies the JVM arguments used for the daemon process. 11 | # The setting is particularly useful for tweaking memory settings. 12 | # Default value: -Xmx10248m -XX:MaxPermSize=256m 13 | # org.gradle.jvmargs=-Xmx2048m -XX:MaxPermSize=512m -XX:+HeapDumpOnOutOfMemoryError -Dfile.encoding=UTF-8 14 | 15 | # When configured, Gradle will run in incubating parallel mode. 16 | # This option should only be used with decoupled projects. More details, visit 17 | # http://www.gradle.org/docs/current/userguide/multi_project_builds.html#sec:decoupled_projects 18 | # org.gradle.parallel=true 19 | 20 | android.useDeprecatedNdk=true 21 | -------------------------------------------------------------------------------- /packages/babel-plugin-cssta/fixtures/native-transitions/expected.js: -------------------------------------------------------------------------------- 1 | import { StyleSheet as _StyleSheet } from 'react-native'; 2 | import _withEnhancers from 'cssta/lib/native/withEnhancers'; 3 | import _Transition from 'cssta/lib/native/enhancers/Transition'; 4 | 5 | import { Animated } from 'react-native'; 6 | 7 | var _csstaStyle = _StyleSheet.create({ 8 | 0: { 9 | 'color': 'red' 10 | }, 11 | 1: { 12 | 'color': 'blue' 13 | } 14 | }); 15 | 16 | _withEnhancers([_Transition])(Animated.View, ['boolAttr'], { 17 | 'transitionedProperties': ['color'], 18 | 'keyframes': {}, 19 | 'rules': [{ 20 | 'validate': function (p) { 21 | return true; 22 | }, 23 | 'transitions': { 24 | 'color': ['1s', 'linear'] 25 | }, 26 | 'animation': null, 27 | 'style': _csstaStyle[0] 28 | }, { 29 | 'validate': function (p) { 30 | return !!p["boolAttr"]; 31 | }, 32 | 'transitions': {}, 33 | 'animation': null, 34 | 'style': _csstaStyle[1] 35 | }] 36 | }); -------------------------------------------------------------------------------- /packages/babel-plugin-cssta/fixtures/css-variables-single-source-native/expected.js: -------------------------------------------------------------------------------- 1 | import { StyleSheet as _StyleSheet } from 'react-native'; 2 | import _createComponent from 'cssta/lib/native/createComponent'; 3 | 4 | import { View } from 'react-native'; 5 | 6 | var _csstaStyle = _StyleSheet.create({ 7 | 0: { 8 | 'width': 100 9 | } 10 | }); 11 | 12 | _createComponent(View, [], { 13 | 'transitionedProperties': [], 14 | 'keyframes': {}, 15 | 'rules': [{ 16 | 'validate': function (p) { 17 | return true; 18 | }, 19 | 'transitions': {}, 20 | 'animation': null, 21 | 'style': _csstaStyle[0] 22 | }] 23 | }); 24 | 25 | var _csstaStyle2 = _StyleSheet.create({ 26 | 0: { 27 | 'width': 50 28 | } 29 | }); 30 | 31 | _createComponent(View, [], { 32 | 'transitionedProperties': [], 33 | 'keyframes': {}, 34 | 'rules': [{ 35 | 'validate': function (p) { 36 | return true; 37 | }, 38 | 'transitions': {}, 39 | 'animation': null, 40 | 'style': _csstaStyle2[0] 41 | }] 42 | }); -------------------------------------------------------------------------------- /packages/cssta/src/web/createComponent.js: -------------------------------------------------------------------------------- 1 | // @flow 2 | /* eslint-disable no-param-reassign */ 3 | const componentFactory = require('../factories/componentFactory'); 4 | /*:: import type { Args } from './types' */ 5 | 6 | const factory = componentFactory((ownProps, passedProps, args /*: Args */) => { 7 | const { defaultClassName, classNameMap } = args; 8 | const classNames = Object.keys(ownProps) 9 | .map(propName => classNameMap[propName][ownProps[propName]]) 10 | .filter(Boolean); // remove undefined values 11 | 12 | if (defaultClassName) classNames.push(defaultClassName); 13 | if (passedProps.className) classNames.push(passedProps.className); 14 | 15 | const className = classNames.join(' '); 16 | if (className) passedProps.className = className; 17 | 18 | return passedProps; 19 | }); 20 | 21 | // Optimisation allows not passing propTypes on prod 22 | module.exports = (component /*: any */, propTypes /*: ?Object */, args /*: Args */) => 23 | factory(component, propTypes || Object.keys(args.classNameMap), args); 24 | -------------------------------------------------------------------------------- /packages/babel-plugin-cssta/fixtures/native-keyframes/expected.js: -------------------------------------------------------------------------------- 1 | import { StyleSheet as _StyleSheet } from 'react-native'; 2 | import _withEnhancers from 'cssta/lib/native/withEnhancers'; 3 | import _Animation from 'cssta/lib/native/enhancers/Animation'; 4 | 5 | import { Animated } from 'react-native'; 6 | 7 | var _csstaStyle = _StyleSheet.create({ 8 | 0: { 9 | 'color': 'red' 10 | } 11 | }); 12 | 13 | _withEnhancers([_Animation])(Animated.View, [], { 14 | 'transitionedProperties': [], 15 | 'keyframes': { 16 | 'test': [{ 17 | 'time': 0, 18 | 'styles': { 19 | 'opacity': 0 20 | } 21 | }, { 22 | 'time': 0.5, 23 | 'styles': { 24 | 'opacity': 0.2 25 | } 26 | }, { 27 | 'time': 1, 28 | 'styles': { 29 | 'opacity': 1 30 | } 31 | }] 32 | }, 33 | 'rules': [{ 34 | 'validate': function (p) { 35 | return true; 36 | }, 37 | 'transitions': {}, 38 | 'animation': ['test', '1s', 'linear'], 39 | 'style': _csstaStyle[0] 40 | }] 41 | }); -------------------------------------------------------------------------------- /docs/Gemfile: -------------------------------------------------------------------------------- 1 | source "https://rubygems.org" 2 | ruby RUBY_VERSION 3 | 4 | # Hello! This is where you manage which Jekyll version is used to run. 5 | # When you want to use a different version, change it below, save the 6 | # file and run `bundle install`. Run Jekyll with `bundle exec`, like so: 7 | # 8 | # bundle exec jekyll serve 9 | # 10 | # This will help ensure the proper Jekyll version is running. 11 | # Happy Jekylling! 12 | gem "jekyll", "3.4.1" 13 | 14 | # This is the default theme for new Jekyll sites. You may change this to anything you like. 15 | gem "minima", "~> 2.0" 16 | 17 | # If you want to use GitHub Pages, remove the "gem "jekyll"" above and 18 | # uncomment the line below. To upgrade, run `bundle update github-pages`. 19 | # gem "github-pages", group: :jekyll_plugins 20 | 21 | # If you have any plugins, put them here! 22 | gem 'github-pages', group: :jekyll_plugins 23 | 24 | # Windows does not include zoneinfo files, so bundle the tzinfo-data gem 25 | gem 'tzinfo-data', platforms: [:mingw, :mswin, :x64_mingw, :jruby] 26 | 27 | -------------------------------------------------------------------------------- /packages/cssta/src/native/__mocks__/react-native.js: -------------------------------------------------------------------------------- 1 | /* eslint-disable flowtype/require-valid-file-annotation */ 2 | /* global jest */ 3 | /* eslint-disable no-param-reassign */ 4 | 5 | module.exports.StyleSheet = { 6 | create: body => body, 7 | flatten: styles => Object.assign({}, ...[].concat(styles)), 8 | }; 9 | 10 | class Value { 11 | constructor() { this.isAnimatedValue = true; } 12 | 13 | interpolate(value) { // eslint-disable-line 14 | const nextValue = new Value(); 15 | nextValue.interpolation = value; 16 | return nextValue; 17 | } 18 | 19 | setValue() { return this; } 20 | } 21 | 22 | module.exports.Animated = { 23 | timing: jest.fn().mockImplementation(() => module.exports.Animated), 24 | parallel: jest.fn().mockImplementation(() => module.exports.Animated), 25 | start: jest.fn().mockImplementation(() => module.exports.Animated), 26 | Value, 27 | }; 28 | 29 | module.exports.Easing = { 30 | linear: () => {}, 31 | ease: () => {}, 32 | in: () => {}, 33 | out: () => {}, 34 | inOut: () => {}, 35 | }; 36 | -------------------------------------------------------------------------------- /packages/babel-plugin-cssta/fixtures/native-keyframes-without-variables/expected.js: -------------------------------------------------------------------------------- 1 | import _withEnhancers from 'cssta/lib/native/withEnhancers'; 2 | import _Animation from 'cssta/lib/native/enhancers/Animation'; 3 | import _VariablesStyleSheetManager from 'cssta/lib/native/enhancers/VariablesStyleSheetManager'; 4 | 5 | import { Animated } from 'react-native'; 6 | 7 | _withEnhancers([_VariablesStyleSheetManager, _Animation])(Animated.View, [], { 8 | 'transitionedProperties': [], 9 | 'importedVariables': ['primary'], 10 | 'keyframesStyleTuples': { 11 | 'test': [{ 12 | 'time': 0, 13 | 'styles': { 14 | 'color': 'rgba(0, 0, 0, 0)' 15 | } 16 | }, { 17 | 'time': 1, 18 | 'styles': { 19 | 'rgba': 'rgba(0, 0, 0, 1)' 20 | } 21 | }] 22 | }, 23 | 'ruleTuples': [{ 24 | 'validate': function (p) { 25 | return true; 26 | }, 27 | 'exportedVariables': {}, 28 | 'transitionParts': {}, 29 | 'animationParts': ['test', '1s', 'linear'], 30 | 'styleTuples': [['color', 'var(--primary)']] 31 | }] 32 | }); -------------------------------------------------------------------------------- /packages/babel-plugin-cssta/src/visitors/program/singleSourceOfVariables.js: -------------------------------------------------------------------------------- 1 | /* eslint-disable no-param-reassign */ 2 | const p = require('path'); 3 | const singleSourceOfVariables = require('../../optimizations/singleSourceOfVariables'); 4 | const { getOptimisationOpts } = require('../../util'); 5 | 6 | module.exports = (path, state) => { 7 | const singleSourceVariableOpts = !state.singleSourceOfVariables 8 | ? getOptimisationOpts(state, 'singleSourceOfVariables') 9 | : null; 10 | 11 | if (singleSourceVariableOpts && !singleSourceVariableOpts.sourceFilename) { 12 | throw new Error( 13 | 'You must provide `sourceFilename` in the options for singleSourceOfVariables' 14 | ); 15 | } 16 | 17 | if (singleSourceVariableOpts) { 18 | const fileContainingVariables = p.join( 19 | state.opts.cwd || process.cwd(), 20 | singleSourceVariableOpts.sourceFilename 21 | ); 22 | const exportedVariables = 23 | singleSourceOfVariables(fileContainingVariables, state.file.opts); 24 | state.singleSourceOfVariables = exportedVariables; 25 | } 26 | }; 27 | -------------------------------------------------------------------------------- /packages/babel-plugin-cssta/fixtures/native-keyframes-variables/expected.js: -------------------------------------------------------------------------------- 1 | import { StyleSheet as _StyleSheet } from 'react-native'; 2 | import _withEnhancers from 'cssta/lib/native/withEnhancers'; 3 | import _Animation from 'cssta/lib/native/enhancers/Animation'; 4 | import _VariablesStyleSheetManager from 'cssta/lib/native/enhancers/VariablesStyleSheetManager'; 5 | 6 | import { Animated } from 'react-native'; 7 | 8 | var _csstaStyle = _StyleSheet.create({ 9 | 0: { 10 | 'color': 'red' 11 | } 12 | }); 13 | 14 | _withEnhancers([_VariablesStyleSheetManager, _Animation])(Animated.View, [], { 15 | 'transitionedProperties': [], 16 | 'importedVariables': ['primary'], 17 | 'keyframesStyleTuples': { 18 | 'test': [{ 19 | 'time': 0, 20 | 'styleTuples': [['color', 'rgba(0, 0, 0, 0)']] 21 | }, { 22 | 'time': 1, 23 | 'styleTuples': [['color', 'var(--primary)']] 24 | }] 25 | }, 26 | 'ruleTuples': [{ 27 | 'validate': function (p) { 28 | return true; 29 | }, 30 | 'transitions': {}, 31 | 'animation': ['test', '1s', 'linear'], 32 | 'style': _csstaStyle[0] 33 | }] 34 | }); -------------------------------------------------------------------------------- /packages/cssta/src/native/__tests__/index.js: -------------------------------------------------------------------------------- 1 | /* eslint-disable flowtype/require-valid-file-annotation */ 2 | /* global jest it, expect */ 3 | const React = require('react'); 4 | const renderer = require('react-test-renderer'); // eslint-disable-line 5 | const cssta = require('..'); 6 | 7 | const runTest = (csstaFactory) => { 8 | const Element = csstaFactory(); 9 | 10 | const component = renderer.create(React.createElement(Element, {})).toJSON(); 11 | 12 | expect(component.props.style).toEqual([{ color: 'red' }]); 13 | expect(component.children).toEqual(null); 14 | }; 15 | 16 | it('creates a component', () => runTest(() => ( 17 | cssta('dummy')` 18 | color: red; 19 | ` 20 | ))); 21 | 22 | it('allows value interpolation', () => runTest(() => { 23 | const color = 'red'; 24 | return cssta('dummy')` 25 | color: ${color}; 26 | `; 27 | })); 28 | 29 | it('allows rule interpolation', () => runTest(() => { 30 | const rule = 'color: red;'; 31 | return cssta('dummy')` 32 | ${rule} 33 | `; 34 | })); 35 | 36 | it('allows non-template-literals', () => runTest(() => ( 37 | cssta('dummy')('color: red;') 38 | ))); 39 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | ########## 2 | # GITHUB # 3 | ########## 4 | 5 | # See http://help.github.com/ignore-files/ for more about ignoring files. 6 | 7 | # dependencies 8 | node_modules 9 | 10 | # testing 11 | coverage 12 | 13 | # production 14 | build 15 | 16 | # misc 17 | .DS_Store 18 | npm-debug.log 19 | 20 | ################ 21 | # REACT NATIVE # 22 | ################ 23 | 24 | # OSX 25 | # 26 | .DS_Store 27 | 28 | # Xcode 29 | # 30 | build/ 31 | *.pbxuser 32 | !default.pbxuser 33 | *.mode1v3 34 | !default.mode1v3 35 | *.mode2v3 36 | !default.mode2v3 37 | *.perspectivev3 38 | !default.perspectivev3 39 | xcuserdata 40 | *.xccheckout 41 | *.moved-aside 42 | DerivedData 43 | *.hmap 44 | *.ipa 45 | *.xcuserstate 46 | project.xcworkspace 47 | 48 | # Android/IJ 49 | # 50 | *.iml 51 | .idea 52 | .gradle 53 | local.properties 54 | 55 | # node.js 56 | # 57 | node_modules/ 58 | npm-debug.log 59 | 60 | # BUCK 61 | buck-out/ 62 | \.buckd/ 63 | android/app/libs 64 | *.keystore 65 | 66 | ########## 67 | # CUSTOM # 68 | ########## 69 | 70 | packages/cssta/lib 71 | packages/cssta/vendor 72 | packages/cssta/README.md 73 | packages/babel-plugin-cssta/README.md 74 | examples/cssta-demo/dist 75 | -------------------------------------------------------------------------------- /examples/CsstaNativeDemo/demos/Transitions.js: -------------------------------------------------------------------------------- 1 | import React, { Component } from 'react'; 2 | import { Animated, View, Button } from 'react-native'; 3 | import cssta from 'cssta/native'; 4 | 5 | const Palette = cssta(Animated.View)` 6 | background-color: #e74c3c; 7 | height: 20px; 8 | margin-bottom: 20px; 9 | transform: scaleX(1) rotate(0deg); 10 | transition: background-color 0.5s linear, transform 0.75s linear; 11 | 12 | &[@active] { 13 | background-color: #1abc9c; 14 | transform: scaleX(0.5) rotate(6deg); 15 | } 16 | `; 17 | 18 | export default class VariablesProviderDemo extends Component { 19 | constructor() { 20 | super(); 21 | 22 | this.state = { active: false }; 23 | this.toggleActive = () => this.setState(({ active }) => ({ active: !active })); 24 | } 25 | 26 | render() { 27 | const { active } = this.state; 28 | 29 | return ( 30 | 31 | 32 | 62 | 63 | 64 | 65 | 66 | 67 | ); 68 | 69 | export default App; 70 | -------------------------------------------------------------------------------- /docs/external-modules.md: -------------------------------------------------------------------------------- 1 | --- 2 | layout: page 3 | title: External Modules 4 | permalink: /external-modules 5 | --- 6 | 7 | # Ⓜ️ External Modules 8 | 9 | As a user of external Cssta modules, all you have to do is ensure these modules are run through babel using `babel-plugin-cssta`. On the web, this will add the module’s CSS in the outputted CSS file, and on native, this will mostly perform optimizations. 10 | 11 | ## Webpack 12 | 13 | To get Cssta modules working, you have to make sure that you are running all files in `node_modules` through babel. Most guides recommend you exclude `node_modules`, so make sure you haven’t copied this over. 14 | 15 | ```jsx 16 | { 17 | module: { 18 | loaders: [{ 19 | test: /\.jsx?$/, 20 | loader: 'babel-loader', 21 | options: { 22 | // Your normal config, just make sure you include babel-plugin-cssta 23 | presets: ['env'], 24 | plugins: [ 25 | ['babel-plugin-cssta', { output: 'dist/styles.css' }], 26 | ], 27 | }, 28 | }], 29 | }, 30 | } 31 | ``` 32 | 33 | However, if this is causing serious performance issues, you can do two runs through babel ([example](https://gist.github.com/jacobp100/4f0b08bf485bfcdcb17741cbabf85c75)). 34 | 35 | ## 🅿️ Publishers 36 | 37 | All Cssta modules have to use the ES `import … from 'cssta'` syntax for the babel plugin to work. For users, this means ensuring your build process can handle this. For publishers, just make sure your users get this syntax! 38 | -------------------------------------------------------------------------------- /docs/_config.yml: -------------------------------------------------------------------------------- 1 | # Welcome to Jekyll! 2 | # 3 | # This config file is meant for settings that affect your whole blog, values 4 | # which you are expected to set up once and rarely edit after that. If you find 5 | # yourself editing this file very often, consider using Jekyll's data files 6 | # feature for the data you need to update frequently. 7 | # 8 | # For technical reasons, this file is *NOT* reloaded automatically when you use 9 | # 'bundle exec jekyll serve'. If you change this file, please restart the server process. 10 | 11 | # Site settings 12 | # These are used to personalize your new site. If you look in the HTML files, 13 | # you will see them accessed via {{ site.title }}, {{ site.email }}, and so on. 14 | # You can create any custom variable you would like, and they will be accessible 15 | # in the templates via {{ site.myvariable }}. 16 | title: Cssta 17 | email: your-email@domain.com 18 | description: > # this means to ignore newlines until "baseurl:" 19 | Write an awesome description for your new site here. You can edit this 20 | line in _config.yml. It will appear in your document head meta (for 21 | Google search results) and in your feed.xml site description. 22 | baseurl: "" # the subpath of your site, e.g. /blog 23 | url: "" # the base hostname & protocol for your site, e.g. http://example.com 24 | twitter_username: jekyllrb 25 | github_username: jekyll 26 | 27 | # Build settings 28 | markdown: kramdown 29 | theme: minima 30 | exclude: 31 | - Gemfile 32 | - Gemfile.lock 33 | highlighter: rouge 34 | -------------------------------------------------------------------------------- /packages/babel-plugin-cssta/fixtures/native-complex-interpolation/expected.js: -------------------------------------------------------------------------------- 1 | import { StyleSheet as _StyleSheet } from 'react-native'; 2 | import { transformStyleTuples as _transformStyleTuples } from 'cssta/lib/native/cssUtil'; 3 | import _createComponent from 'cssta/lib/native/createComponent'; 4 | 5 | import { View } from 'react-native'; 6 | 7 | const marginSmall = 10; 8 | const marginLarge = 10; 9 | 10 | var _csstaStyle = _StyleSheet.create({ 11 | 0: Object.assign({ 12 | 'paddingTop': 10 13 | }, _transformStyleTuples([['margin', `${marginLarge}px ${marginSmall}px`]])), 14 | 1: Object.assign(_transformStyleTuples([['margin', `${marginLarge}px ${marginSmall}px`]]), { 15 | 'paddingTop': 10 16 | }), 17 | 2: Object.assign({ 18 | 'paddingTop': 10 19 | }, _transformStyleTuples([['margin', `${marginLarge}px ${marginSmall}px`]]), { 20 | 'paddingBottom': 10 21 | }) 22 | }); 23 | 24 | _createComponent(View, ['attr1', 'attr2', 'attr3'], { 25 | 'transitionedProperties': [], 26 | 'keyframes': {}, 27 | 'rules': [{ 28 | 'validate': function (p) { 29 | return !!p["attr1"]; 30 | }, 31 | 'transitions': {}, 32 | 'animation': null, 33 | 'style': _csstaStyle[0] 34 | }, { 35 | 'validate': function (p) { 36 | return !!p["attr2"]; 37 | }, 38 | 'transitions': {}, 39 | 'animation': null, 40 | 'style': _csstaStyle[1] 41 | }, { 42 | 'validate': function (p) { 43 | return !!p["attr3"]; 44 | }, 45 | 'transitions': {}, 46 | 'animation': null, 47 | 'style': _csstaStyle[2] 48 | }] 49 | }); -------------------------------------------------------------------------------- /examples/CsstaNativeDemo/ios/CsstaNativeDemo/AppDelegate.m: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright (c) 2015-present, Facebook, Inc. 3 | * All rights reserved. 4 | * 5 | * This source code is licensed under the BSD-style license found in the 6 | * LICENSE file in the root directory of this source tree. An additional grant 7 | * of patent rights can be found in the PATENTS file in the same directory. 8 | */ 9 | 10 | #import "AppDelegate.h" 11 | 12 | #import "RCTBundleURLProvider.h" 13 | #import "RCTRootView.h" 14 | 15 | @implementation AppDelegate 16 | 17 | - (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions 18 | { 19 | NSURL *jsCodeLocation; 20 | 21 | jsCodeLocation = [[RCTBundleURLProvider sharedSettings] jsBundleURLForBundleRoot:@"index.ios" fallbackResource:nil]; 22 | 23 | RCTRootView *rootView = [[RCTRootView alloc] initWithBundleURL:jsCodeLocation 24 | moduleName:@"CsstaNativeDemo" 25 | initialProperties:nil 26 | launchOptions:launchOptions]; 27 | rootView.backgroundColor = [[UIColor alloc] initWithRed:1.0f green:1.0f blue:1.0f alpha:1]; 28 | 29 | self.window = [[UIWindow alloc] initWithFrame:[UIScreen mainScreen].bounds]; 30 | UIViewController *rootViewController = [UIViewController new]; 31 | rootViewController.view = rootView; 32 | self.window.rootViewController = rootViewController; 33 | [self.window makeKeyAndVisible]; 34 | return YES; 35 | } 36 | 37 | @end 38 | -------------------------------------------------------------------------------- /packages/cssta/src/factories/enhancerFactory.js: -------------------------------------------------------------------------------- 1 | // @flow 2 | const React = require('react'); 3 | /*:: import type { ComponentFactory, ComponentPropTypes, Props, Enhancer } from './types' */ 4 | 5 | const mergeTransformers = (enhancers, EndNode) => 6 | enhancers.reduceRight((NextElement, CurrentElement) => ( 7 | props => React.createElement(CurrentElement, props, NextElement) 8 | ), EndNode); 9 | 10 | const nextCacheNode = (node, transform) => { 11 | let value = node.get(transform); 12 | 13 | if (!value) { 14 | value = new Map(); 15 | node.set(transform, value); 16 | } 17 | 18 | return value; 19 | }; 20 | 21 | const LEAF = 'leaf'; 22 | const mergeTransformersCached = (getTransformer, enhancers, cache) /*: any */ => { 23 | const cacheEntry = enhancers.reduce(nextCacheNode, cache); 24 | 25 | let value = cacheEntry.get(LEAF); 26 | 27 | if (!value) { 28 | value = new Map(); 29 | cacheEntry.set(LEAF, value); 30 | } 31 | 32 | return value; 33 | }; 34 | 35 | module.exports = (constructor /*: ComponentFactory */) => { 36 | const cache = new Map(); 37 | 38 | return (enhancers /*: Enhancer[] */) => ( 39 | component /*: any */, 40 | propTypes /*: ComponentPropTypes */, 41 | args /*: any */ 42 | ) => constructor(component, propTypes, args, (EndNode) => { 43 | const RootNode = mergeTransformersCached( 44 | passedEnhancers => mergeTransformers(passedEnhancers, EndNode), 45 | enhancers, 46 | cache 47 | ); 48 | const render = props => React.createElement(RootNode, props); 49 | return render; 50 | }); 51 | }; 52 | -------------------------------------------------------------------------------- /packages/cssta/src/util/resolveVariableDependencies.js: -------------------------------------------------------------------------------- 1 | // @flow 2 | /* eslint-disable no-param-reassign */ 3 | const DepGraph = require('dependency-graph').DepGraph; 4 | const { varRegExp, varRegExpNonGlobal } = require('./index'); 5 | 6 | module.exports = ( 7 | definedVariables /*: { [key:string]: string } */, 8 | variablesFromScope /*: { [key:string]: string } */ 9 | ) /*: { [key:string]: string } */ => { 10 | const graph = new DepGraph(); 11 | 12 | const variableNames = Object.keys(definedVariables); 13 | 14 | variableNames.forEach((variableName) => { 15 | graph.addNode(variableName); 16 | }); 17 | 18 | variableNames.forEach((variableName) => { 19 | const referencedVariableMatches = definedVariables[variableName].match(varRegExp); 20 | if (!referencedVariableMatches) return; 21 | 22 | const referencedVariables = referencedVariableMatches 23 | .map(match => { 24 | const matchedVariable = match.match(varRegExpNonGlobal); 25 | return matchedVariable ? matchedVariable[1] : null; 26 | }) 27 | .filter(Boolean); 28 | 29 | referencedVariables.forEach((referencedVariableName) => { 30 | if (referencedVariableName in definedVariables) { 31 | graph.addDependency(variableName, referencedVariableName); 32 | } 33 | }); 34 | }); 35 | 36 | const appliedVariables = graph.overallOrder(false).reduce((accum, variableName) => { 37 | const value = definedVariables[variableName].replace(varRegExp, (m, reference, fallback) => ( 38 | accum[reference] || variablesFromScope[reference] || fallback 39 | )); 40 | accum[variableName] = value; 41 | return accum; 42 | }, {}); 43 | 44 | return appliedVariables; 45 | }; 46 | -------------------------------------------------------------------------------- /docs/theming.md: -------------------------------------------------------------------------------- 1 | --- 2 | layout: page 3 | title: Theming 4 | permalink: /theming 5 | --- 6 | 7 | # 🏳️‍🌈 Theming 8 | 9 | As said in the introduction, theming sholud be done by CSS custom properties. Cssta for React Native has custom property polyfills built in. On the web, you can either rely on native browser support or [a postCSS plugin](https://github.com/MadLittleMods/postcss-css-variables#differences-from-postcss-custom-properties). 10 | 11 | To define global variables, you can do the following on the web. 12 | 13 | ```jsx 14 | // Web 15 | cssta.injectGlobal` 16 | :root { 17 | --primary: red; 18 | } 19 | ` 20 | ``` 21 | 22 | But in React Native, you’ll have to create a wrapper View. 23 | 24 | ```jsx 25 | // Native 26 | const Root = cssta(View)` 27 | --primary: red; 28 | ` 29 | ``` 30 | 31 | You can then use the variables as normal. 32 | 33 | ```jsx 34 | const Button = cssta.button` 35 | color: var(--primary); 36 | border: 1px solid var(--primary); 37 | padding: 0.5rem 1rem; 38 | ` 39 | ``` 40 | 41 | To add dynamic styling based upon context, just redefine variables. 42 | 43 | ```jsx 44 | const LightBox = cssta.div` 45 | background-color: black; 46 | --primary: white; 47 | ` 48 | 49 | const Example = ( 50 | 51 | 52 | 53 | ) 54 | ``` 55 | 56 | And to make dynamic styling even more dynamic. 57 | 58 | ```jsx 59 | const LightBox = cssta.div` 60 | background-color: black; 61 | --primary: white; 62 | 63 | &[@inverted] { 64 | background-color: white; 65 | --primary: black; 66 | } 67 | ` 68 | 69 | const Example = ( 70 | 71 | 72 | 73 | ) 74 | ``` 75 | -------------------------------------------------------------------------------- /docs/web.md: -------------------------------------------------------------------------------- 1 | --- 2 | layout: page 3 | title: Web 4 | permalink: /web 5 | --- 6 | 7 | # 🌍 Web 8 | 9 | Except for the limitations of selectors, CSS otherwise works as normal. This includes all `@-rules`. 10 | 11 | ```jsx 12 | const Button = cssta.button` 13 | font-size: 12pt; 14 | 15 | &:hover { 16 | font-weight: bold; 17 | } 18 | 19 | @media (max-width: 768px) { 20 | font-size: 12pt; 21 | } 22 | 23 | @supports (background: linear-gradient(to bottom, red, green)) { 24 | &[@christmas] { 25 | background: linear-gradient(to bottom, red, green); 26 | } 27 | } 28 | ` 29 | ``` 30 | 31 | Animations work too, and CSSTA will scope the keyframe names to avoid conflicts. 32 | 33 | ```jsx 34 | const Button = cssta.button` 35 | animation: 1s scoped-animation; 36 | 37 | @keyframes scoped-animation { 38 | 0% { opacity: 0; } 39 | } 40 | ` 41 | ``` 42 | 43 | ## 🌐 Globals 44 | 45 | It is common to define some base styles to tag names, such as `h1`s and `p`s. This can be done with `injectGlobal`, and supports all CSS. 46 | 47 | ```jsx 48 | cssta.injectGlobal` 49 | :root { 50 | --margin: 25pt; 51 | } 52 | 53 | body { 54 | margin: var(--margin); 55 | } 56 | ` 57 | ``` 58 | 59 | This can also create global animations. 60 | 61 | ```jsx 62 | cssta.injectGlobal` 63 | @keyframes fade-in { 64 | 0% { opacity: 0; } 65 | } 66 | ` 67 | 68 | const Button = cssta.button` 69 | animation: 1s fade-in; 70 | ` 71 | ``` 72 | 73 | ## 🎚 Polyfills 74 | 75 | We don’t auto-prefix your CSS, so you’ll likely want to use [autoprefixer](https://github.com/postcss/autoprefixer). If you’re using CSS custom properties, you’ll also want to use [postcss-css-variables](https://github.com/MadLittleMods/postcss-css-variables) 76 | -------------------------------------------------------------------------------- /docs/_layouts/page.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | {{ page.title }} 6 | 7 | 8 | 23 | {{ content }} 24 | 25 | 26 | 27 | 28 | -------------------------------------------------------------------------------- /examples/CsstaNativeDemo/android/app/BUCK: -------------------------------------------------------------------------------- 1 | import re 2 | 3 | # To learn about Buck see [Docs](https://buckbuild.com/). 4 | # To run your application with Buck: 5 | # - install Buck 6 | # - `npm start` - to start the packager 7 | # - `cd android` 8 | # - `keytool -genkey -v -keystore keystores/debug.keystore -storepass android -alias androiddebugkey -keypass android -dname "CN=Android Debug,O=Android,C=US"` 9 | # - `./gradlew :app:copyDownloadableDepsToLibs` - make all Gradle compile dependencies available to Buck 10 | # - `buck install -r android/app` - compile, install and run application 11 | # 12 | 13 | lib_deps = [] 14 | for jarfile in glob(['libs/*.jar']): 15 | name = 'jars__' + re.sub(r'^.*/([^/]+)\.jar$', r'\1', jarfile) 16 | lib_deps.append(':' + name) 17 | prebuilt_jar( 18 | name = name, 19 | binary_jar = jarfile, 20 | ) 21 | 22 | for aarfile in glob(['libs/*.aar']): 23 | name = 'aars__' + re.sub(r'^.*/([^/]+)\.aar$', r'\1', aarfile) 24 | lib_deps.append(':' + name) 25 | android_prebuilt_aar( 26 | name = name, 27 | aar = aarfile, 28 | ) 29 | 30 | android_library( 31 | name = 'all-libs', 32 | exported_deps = lib_deps 33 | ) 34 | 35 | android_library( 36 | name = 'app-code', 37 | srcs = glob([ 38 | 'src/main/java/**/*.java', 39 | ]), 40 | deps = [ 41 | ':all-libs', 42 | ':build_config', 43 | ':res', 44 | ], 45 | ) 46 | 47 | android_build_config( 48 | name = 'build_config', 49 | package = 'com.csstanativedemo', 50 | ) 51 | 52 | android_resource( 53 | name = 'res', 54 | res = 'src/main/res', 55 | package = 'com.csstanativedemo', 56 | ) 57 | 58 | android_binary( 59 | name = 'app', 60 | package_type = 'debug', 61 | manifest = 'src/main/AndroidManifest.xml', 62 | keystore = '//android/keystores:debug', 63 | deps = [ 64 | ':app-code', 65 | ], 66 | ) 67 | -------------------------------------------------------------------------------- /examples/CsstaNativeDemo/demos/CSSVariables.js: -------------------------------------------------------------------------------- 1 | import React, { Component } from 'react'; 2 | import { View, Button, Animated } from 'react-native'; 3 | import cssta from 'cssta/native'; 4 | 5 | const PickerRow = cssta(View)` 6 | flex-direction: row; 7 | justify-content: space-between; 8 | margin-top: 20px; 9 | `; 10 | 11 | const DynamicContainer = cssta(View)` 12 | --color: black; 13 | 14 | &[@color="red"] { --color: #e74c3c; } 15 | &[@color="green"] { --color: #2ecc71; } 16 | &[@color="blue"] { --color: #3498db; } 17 | `; 18 | 19 | class DynamicPicker extends Component { 20 | constructor() { 21 | super(); 22 | this.state = { color: 'red' }; 23 | this.setColor = color => this.setState({ color }); 24 | } 25 | 26 | render() { 27 | const { color } = this.state; 28 | const { children } = this.props; 29 | 30 | return ( 31 | 32 | {children} 33 | 34 | ; 68 | -------------------------------------------------------------------------------- /packages/cssta/src/native/types.js: -------------------------------------------------------------------------------- 1 | // @flow 2 | /* 3 | Arrays of each word in value 4 | i.e. `transition: color 1s linear` => ['color', '1s', 'linear'] 5 | transitionParts keyed by property name 6 | These might have variables 7 | */ 8 | 9 | /*:: 10 | export type VariablesStore = { [key:string]: string } 11 | export type StyleTuple = [string, string] 12 | export type Style = { [key:string]: any } | string 13 | 14 | export type VariableWithValidator = { 15 | validate?: (props: Object) => boolean, 16 | } 17 | 18 | export type BaseVariableArgs = {| 19 | transitionedProperties: string[], 20 | importedVariables: string[], 21 | keyframesStyleTuples: { [key:string]: (VariableKeyframeTuple | Keyframe)[] }, 22 | |} 23 | 24 | export type RawVariableArgs = {| 25 | ...BaseVariableArgs, 26 | ruleTuples: RawVariableRuleTuple[], 27 | |} 28 | 29 | export type VariableArgs = {| 30 | ...BaseVariableArgs, 31 | ruleTuples: (VariableRuleTuple | Rule)[], 32 | |} 33 | 34 | export type BaseVariableRuleTuple = {| 35 | exportedVariables: VariablesStore, 36 | transitionParts: ?{ [key:string]: string[] }, 37 | animationParts: ?string[], 38 | styleTuples: StyleTuple[], 39 | |} 40 | 41 | export type RawVariableRuleTuple = {| 42 | ...BaseVariableRuleTuple , 43 | selector: string, 44 | |} 45 | 46 | export type VariableRuleTuple = {| 47 | ...BaseVariableRuleTuple, 48 | ...$Exact, 49 | |} 50 | 51 | export type VariableKeyframeTuple = {| 52 | time: number, 53 | styleTuples: StyleTuple[], 54 | |} 55 | 56 | export type Args = {| 57 | transitionedProperties: string[], 58 | keyframes: { [key:string]: Keyframe[] }, 59 | rules: Rule[], 60 | |} 61 | 62 | export type Rule = {| 63 | ...$Exact, 64 | style: Style, 65 | transitions?: ?{ [key:string]: string[] }, 66 | animation?: ?string[], 67 | |} 68 | 69 | export type Keyframe = {| 70 | time: number, 71 | styles: any, 72 | |} 73 | */ 74 | -------------------------------------------------------------------------------- /examples/CsstaNativeDemo/.flowconfig: -------------------------------------------------------------------------------- 1 | [ignore] 2 | 3 | # We fork some components by platform. 4 | .*/*[.]android.js 5 | 6 | # Ignore templates with `@flow` in header 7 | .*/local-cli/generator.* 8 | 9 | # Ignore malformed json 10 | .*/node_modules/y18n/test/.*\.json 11 | 12 | # Ignore the website subdir 13 | /website/.* 14 | 15 | # Ignore BUCK generated dirs 16 | /\.buckd/ 17 | 18 | # Ignore unexpected extra @providesModule 19 | .*/node_modules/commoner/test/source/widget/share.js 20 | .*/node_modules/.*/node_modules/fbjs/.* 21 | 22 | # Ignore duplicate module providers 23 | # For RN Apps installed via npm, "Libraries" folder is inside node_modules/react-native but in the source repo it is in the root 24 | .*/Libraries/react-native/React.js 25 | .*/Libraries/react-native/ReactNative.js 26 | .*/node_modules/jest-runtime/build/__tests__/.* 27 | 28 | [include] 29 | 30 | [libs] 31 | node_modules/react-native/Libraries/react-native/react-native-interface.js 32 | node_modules/react-native/flow 33 | flow/ 34 | 35 | [options] 36 | module.system=haste 37 | 38 | esproposal.class_static_fields=enable 39 | esproposal.class_instance_fields=enable 40 | 41 | experimental.strict_type_args=true 42 | 43 | munge_underscores=true 44 | 45 | module.name_mapper='^image![a-zA-Z0-9$_-]+$' -> 'GlobalImageStub' 46 | module.name_mapper='^[./a-zA-Z0-9$_-]+\.\(bmp\|gif\|jpg\|jpeg\|png\|psd\|svg\|webp\|m4v\|mov\|mp4\|mpeg\|mpg\|webm\|aac\|aiff\|caf\|m4a\|mp3\|wav\|html\|pdf\)$' -> 'RelativeImageStub' 47 | 48 | suppress_type=$FlowIssue 49 | suppress_type=$FlowFixMe 50 | suppress_type=$FixMe 51 | 52 | suppress_comment=\\(.\\|\n\\)*\\$FlowFixMe\\($\\|[^(]\\|(\\(>=0\\.\\(3[0-3]\\|[1-2][0-9]\\|[0-9]\\).[0-9]\\)? *\\(site=[a-z,_]*react_native[a-z,_]*\\)?)\\) 53 | suppress_comment=\\(.\\|\n\\)*\\$FlowIssue\\((\\(>=0\\.\\(3[0-3]\\|1[0-9]\\|[1-2][0-9]\\).[0-9]\\)? *\\(site=[a-z,_]*react_native[a-z,_]*\\)?)\\)?:? #[0-9]+ 54 | suppress_comment=\\(.\\|\n\\)*\\$FlowFixedInNextDeploy 55 | 56 | unsafe.enable_getters_and_setters=true 57 | 58 | [version] 59 | ^0.33.0 60 | -------------------------------------------------------------------------------- /packages/babel-plugin-cssta/src/visitors/program/web.js: -------------------------------------------------------------------------------- 1 | /* eslint-disable no-param-reassign */ 2 | const fs = require('fs'); 3 | const p = require('path'); 4 | const mkdirp = require('mkdirp'); 5 | const _ = require('lodash/fp'); 6 | const { fileStartEndCommentMarkers } = require('../../webUtil'); 7 | 8 | const getCssFilename = state => p.resolve( 9 | state.opts.cwd || process.cwd(), 10 | _.getOr('styles.css', ['opts', 'output'], state) 11 | ); 12 | 13 | const writeCssToFile = (state) => { 14 | const { currentWebCss } = state; 15 | const cssFilename = getCssFilename(state); 16 | 17 | mkdirp.sync(p.dirname(cssFilename)); 18 | fs.writeFileSync(cssFilename, currentWebCss, { 19 | encoding: 'utf-8', 20 | flag: 'w+', 21 | }); 22 | }; 23 | 24 | const removeRange = (startIndex, endIndex, text) => 25 | text.slice(0, startIndex) + text.slice(endIndex + 1); 26 | 27 | const removeBetween = (startToken, endToken, text) => { 28 | const startIndex = text.indexOf(startToken); 29 | const endIndex = text.lastIndexOf(endToken); 30 | return (startIndex !== -1 && endIndex !== -1) 31 | ? removeRange(startIndex, endIndex, text) 32 | : text; 33 | }; 34 | 35 | let previousCss; 36 | module.exports = { 37 | enter: (path, state) => { 38 | const cssFilename = getCssFilename(state); 39 | 40 | if (!state.didRemoveCssFile) { 41 | _.attempt(() => fs.unlinkSync(cssFilename)); 42 | state.didRemoveCssFile = true; 43 | } 44 | 45 | let { currentWebCss } = state; 46 | 47 | if (!currentWebCss && !state.didRemoveCssFile) { 48 | _.attempt(() => fs.readFileSync(cssFilename, 'utf-8')); 49 | } 50 | 51 | if (!currentWebCss || _.isError(currentWebCss)) { 52 | currentWebCss = '/* File generated by babel-plugin-cssta */\n'; 53 | } 54 | 55 | const { commentStartMarker, commentEndMarker } = fileStartEndCommentMarkers(state); 56 | currentWebCss = removeBetween(commentStartMarker, commentEndMarker, currentWebCss); 57 | 58 | state.currentWebCss = currentWebCss; 59 | previousCss = currentWebCss; 60 | }, 61 | exit: (path, state) => { 62 | if (state.currentWebCss !== previousCss) writeCssToFile(state); 63 | }, 64 | }; 65 | -------------------------------------------------------------------------------- /examples/CsstaNativeDemo/demos/Animations.js: -------------------------------------------------------------------------------- 1 | import React, { Component } from 'react'; 2 | import { Animated, View, Button } from 'react-native'; 3 | import cssta from 'cssta/native'; 4 | 5 | const Center = cssta(View)` 6 | align-items: center; 7 | `; 8 | 9 | const Row = cssta(View)` 10 | flex-direction: row; 11 | `; 12 | 13 | const Palette = cssta(Animated.View)` 14 | background-color: #e74c3c; 15 | height: 50px; 16 | width: 50px; 17 | margin-bottom: 20px; 18 | 19 | &[@animation = "fade-in"] { 20 | animation: 0.5s fade-in; 21 | } 22 | 23 | &[@animation = "slide-in"] { 24 | animation: 0.5s slide-in; 25 | } 26 | 27 | &[@animation = "zoom-in"] { 28 | animation: 0.5s zoom-in; 29 | } 30 | 31 | @keyframes fade-in { 32 | start { opacity: 0; } 33 | end { opacity: 1; } 34 | } 35 | 36 | @keyframes slide-in { 37 | 0% { 38 | opacity: 0; 39 | transform: translateX(-50px); 40 | } 41 | 42 | 80% { 43 | transform: translateX(5px); 44 | } 45 | 46 | 100% { 47 | opacity: 1; 48 | transform: translateX(0); 49 | } 50 | } 51 | 52 | @keyframes zoom-in { 53 | 0% { 54 | opacity: 0; 55 | transform: scale(0); 56 | } 57 | 58 | 80% { 59 | transform: scale(1.25); 60 | } 61 | 62 | 100% { 63 | opacity: 1; 64 | transform: scale(1); 65 | } 66 | } 67 | `; 68 | 69 | export default class VariablesProviderDemo extends Component { 70 | constructor() { 71 | super(); 72 | this.state = { animation: 'fade-in' }; 73 | this.setAnimation = animation => () => this.setState({ animation }); 74 | } 75 | 76 | render() { 77 | const { animation } = this.state; 78 | 79 | return ( 80 |
    81 | 82 | 83 |
    88 | ); 89 | } 90 | } 91 | 92 | export const code = '@keyframes { ... }'; 93 | -------------------------------------------------------------------------------- /packages/cssta/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "cssta", 3 | "version": "0.7.0", 4 | "description": "", 5 | "main": "index.js", 6 | "scripts": { 7 | "test": "jest", 8 | "test:watch": "jest --watch", 9 | "test:coverage": "jest --coverage", 10 | "flow": "flow; test $? -eq 0 -o $? -eq 2", 11 | "lint": "eslint ./src/**/*.js", 12 | "clean": "npm run clean:lib; npm run clean:vendor", 13 | "clean:lib": "rm -rf ./lib || :;", 14 | "clean:vendor": "rm -rf ./vendor || :;", 15 | "build": "npm run clean; npm run build:lib; npm run build:vendor", 16 | "build:lib": "babel ./src --out-dir ./lib --ignore __tests__,__mocks__", 17 | "build:vendor": "webpack --config ./webpack.config.js", 18 | "prepublish": "npm run build; cp ../../docs/index.md README.md" 19 | }, 20 | "keywords": [ 21 | "postcss", 22 | "modules", 23 | "css-modules", 24 | "css", 25 | "minify", 26 | "min", 27 | "class", 28 | "className", 29 | "react", 30 | "css-in-js" 31 | ], 32 | "repository": { 33 | "type": "git", 34 | "url": "https://github.com/jacobp100/cssta" 35 | }, 36 | "author": "", 37 | "license": "ISC", 38 | "dependencies": { 39 | "css-color-function": "^1.3.0", 40 | "css-to-react-native": "^2.0.0", 41 | "dependency-graph": "^0.5.0", 42 | "events": "^1.1.1", 43 | "fbjs": "^0.8.6", 44 | "postcss": "^5.2.6", 45 | "postcss-selector-parser": "^2.2.2", 46 | "postcss-transform-animations": "^1.0.1", 47 | "prop-types": "^15.5.10" 48 | }, 49 | "devDependencies": { 50 | "babel-cli": "^6.18.0", 51 | "babel-preset-es2015": "^6.18.0", 52 | "eslint": "^3.11.1", 53 | "eslint-config-airbnb-base": "^10.0.1", 54 | "eslint-plugin-flowtype": "^2.30.3", 55 | "eslint-plugin-import": "^2.2.0", 56 | "flow-bin": "^0.42.0", 57 | "jest": "^17.0.3", 58 | "react": "^15.4.1", 59 | "react-test-renderer": "^15.4.1", 60 | "webpack": "^1.14.0" 61 | }, 62 | "peerDependencies": { 63 | "react": "^15.0.0" 64 | }, 65 | "jest": { 66 | "testEnvironment": "node", 67 | "collectCoverageFrom": [ 68 | "src/**/*.js", 69 | "!src/index.js", 70 | "!src/web/index.js", 71 | "!src/packages/**/*.js" 72 | ] 73 | } 74 | } 75 | -------------------------------------------------------------------------------- /packages/babel-plugin-cssta/src/visitors/csstaCall.js: -------------------------------------------------------------------------------- 1 | /* eslint-disable no-param-reassign */ 2 | const { varRegExp } = require('cssta/lib/util'); 3 | const transformWeb = require('../platforms/web'); 4 | const transformNative = require('../platforms/native'); 5 | const { 6 | getCsstaReferences, interpolationTypes, extractCsstaCallParts, 7 | } = require('../transformUtil/extractCsstaCallParts'); 8 | const { getOptimisationOpts } = require('../util'); 9 | 10 | const canInterpolate = { 11 | web: false, 12 | native: true, 13 | }; 14 | 15 | const transformCsstaTypes = { 16 | web: transformWeb, 17 | native: transformNative, 18 | }; 19 | 20 | const transformCsstaCall = (path, state, target, stringArg) => { 21 | const csstaReferenceParts = getCsstaReferences(path, target); 22 | if (!csstaReferenceParts) return; 23 | 24 | const { callee, component, csstaType } = csstaReferenceParts; 25 | 26 | let interpolationType; 27 | const interpolateValuesOnly = Boolean(getOptimisationOpts(state, 'interpolateValuesOnly')); 28 | 29 | if (!canInterpolate[csstaType]) { 30 | interpolationType = interpolationTypes.DISALLOW; 31 | } else if (!interpolateValuesOnly) { 32 | interpolationType = interpolationTypes.IGNORE; 33 | } else { 34 | interpolationType = interpolationTypes.ALLOW; 35 | } 36 | 37 | const callParts = extractCsstaCallParts(stringArg, interpolationType); 38 | if (!callParts) return; 39 | 40 | let { cssText, substitutionMap } = callParts; // eslint-disable-line 41 | 42 | if (state.singleSourceOfVariables) { 43 | cssText = cssText.replace(varRegExp, (m, variableName, fallback) => ( 44 | state.singleSourceOfVariables[variableName] || fallback 45 | )); 46 | } 47 | 48 | transformCsstaTypes[csstaType](path, state, component, cssText, substitutionMap); 49 | const binding = path.scope.getBinding(callee.name); 50 | binding.dereference(); 51 | }; 52 | 53 | module.exports = { 54 | CallExpression(path, state) { 55 | const { node } = path; 56 | const { callee } = node; 57 | const [arg] = node.arguments; 58 | transformCsstaCall(path, state, callee, arg); 59 | }, 60 | TaggedTemplateExpression(path, state) { 61 | const { quasi, tag } = path.node; 62 | transformCsstaCall(path, state, tag, quasi); 63 | }, 64 | }; 65 | module.exports.resetGenerators = transformWeb.resetGenerators; 66 | -------------------------------------------------------------------------------- /docs/editor-integration.md: -------------------------------------------------------------------------------- 1 | --- 2 | layout: page 3 | title: Editor Integration 4 | permalink: /editor-integration 5 | --- 6 | 7 | # 🖇 Editor Integration 8 | 9 | Plenty of work has been done for styled-components integration into text editors, including syntax highlighting and linting. 10 | 11 | All styled-components tools should work with Cssta: you just have call your Cssta import `styled`. 12 | 13 | ```jsx 14 | import styled from 'cssta' 15 | 16 | styled.h1` 17 | color: red; 18 | ` 19 | ``` 20 | 21 | ## 🖍 Syntax Highlighting 22 | 23 | The most up-to-date list of plugins can be viewed on the [styled-components page](https://github.com/styled-components/styled-components#syntax-highlighting). Below is a copy (verbatim from styled-components). 24 | 25 | ![syntax highlighting](http://imgur.com/k7h45c3.jpg) 26 | 27 | ### Atom 28 | 29 | [@gandm](https://github.com/gandm), the creator of language-babel, has added support for styled-components in Atom! 30 | 31 | To get proper syntax highlighting, all you have to do is install and use the language-babel package for your JavaScript files! 32 | 33 | ### Sublime Text 34 | 35 | There is an [open PR](https://github.com/babel/babel-sublime/pull/289) by [@garetmckinley](https://github.com/garetmckinley) to add support for styled-components to babel-sublime! (if you want the PR to land, feel free to 👍 the initial comment to let the maintainers know there's a need for this!) 36 | 37 | As soon as that PR is merged and a new version released, all you'll have to do is install and use babel-sublime to highlight your JavaScript files! 38 | 39 | ### Visual Studio Code 40 | 41 | The [vscode-styled-components](https://github.com/styled-components/vscode-styled-components) extension provides syntax highlighting inside your Javascript files. You can install it as usual from the [Marketplace](https://marketplace.visualstudio.com/items?itemName=jpoissonnier.vscode-styled-components). 42 | 43 | ### VIM 44 | 45 | The [vim-styled-components](https://github.com/fleischie/vim-styled-components) plugin gives you syntax highlighting inside your Javascript files. Install it with your usual plugin manager like [Plug](https://github.com/junegunn/vim-plug), [Vundle](https://github.com/VundleVim/Vundle.vim), [Pathogen](https://github.com/tpope/vim-pathogen), etc. 46 | 47 | Also if you're looking for an awesome javascript syntax package you can never go wrong with [YAJS.vim](https://github.com/othree/yajs.vim). 48 | -------------------------------------------------------------------------------- /examples/CsstaNativeDemo/ios/CsstaNativeDemoTests/CsstaNativeDemoTests.m: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright (c) 2015-present, Facebook, Inc. 3 | * All rights reserved. 4 | * 5 | * This source code is licensed under the BSD-style license found in the 6 | * LICENSE file in the root directory of this source tree. An additional grant 7 | * of patent rights can be found in the PATENTS file in the same directory. 8 | */ 9 | 10 | #import 11 | #import 12 | 13 | #import "RCTLog.h" 14 | #import "RCTRootView.h" 15 | 16 | #define TIMEOUT_SECONDS 600 17 | #define TEXT_TO_LOOK_FOR @"Welcome to React Native!" 18 | 19 | @interface CsstaNativeDemoTests : XCTestCase 20 | 21 | @end 22 | 23 | @implementation CsstaNativeDemoTests 24 | 25 | - (BOOL)findSubviewInView:(UIView *)view matching:(BOOL(^)(UIView *view))test 26 | { 27 | if (test(view)) { 28 | return YES; 29 | } 30 | for (UIView *subview in [view subviews]) { 31 | if ([self findSubviewInView:subview matching:test]) { 32 | return YES; 33 | } 34 | } 35 | return NO; 36 | } 37 | 38 | - (void)testRendersWelcomeScreen 39 | { 40 | UIViewController *vc = [[[[UIApplication sharedApplication] delegate] window] rootViewController]; 41 | NSDate *date = [NSDate dateWithTimeIntervalSinceNow:TIMEOUT_SECONDS]; 42 | BOOL foundElement = NO; 43 | 44 | __block NSString *redboxError = nil; 45 | RCTSetLogFunction(^(RCTLogLevel level, RCTLogSource source, NSString *fileName, NSNumber *lineNumber, NSString *message) { 46 | if (level >= RCTLogLevelError) { 47 | redboxError = message; 48 | } 49 | }); 50 | 51 | while ([date timeIntervalSinceNow] > 0 && !foundElement && !redboxError) { 52 | [[NSRunLoop mainRunLoop] runMode:NSDefaultRunLoopMode beforeDate:[NSDate dateWithTimeIntervalSinceNow:0.1]]; 53 | [[NSRunLoop mainRunLoop] runMode:NSRunLoopCommonModes beforeDate:[NSDate dateWithTimeIntervalSinceNow:0.1]]; 54 | 55 | foundElement = [self findSubviewInView:vc.view matching:^BOOL(UIView *view) { 56 | if ([view.accessibilityLabel isEqualToString:TEXT_TO_LOOK_FOR]) { 57 | return YES; 58 | } 59 | return NO; 60 | }]; 61 | } 62 | 63 | RCTSetLogFunction(RCTDefaultLogFunction); 64 | 65 | XCTAssertNil(redboxError, @"RedBox error: %@", redboxError); 66 | XCTAssertTrue(foundElement, @"Couldn't find element with text '%@' in %d seconds", TEXT_TO_LOOK_FOR, TIMEOUT_SECONDS); 67 | } 68 | 69 | 70 | @end 71 | -------------------------------------------------------------------------------- /packages/cssta/src/factories/componentFactory.js: -------------------------------------------------------------------------------- 1 | // @flow 2 | /* eslint-disable no-param-reassign */ 3 | const React = require('react'); 4 | const PropTypes = require('prop-types'); 5 | /*:: import type { ComponentFactory, Props, EnhancerConstructor } from './types' */ 6 | 7 | const getOwnPropKeys = propTypes => 8 | (Array.isArray(propTypes) ? propTypes : Object.keys(propTypes)); 9 | 10 | const getComponentProps = (ownPropKeys, component, props) => ( 11 | Object.keys(props).reduce((accum, key) => { 12 | const prop = props[key]; 13 | 14 | if (key === 'component') { 15 | accum.Element = prop; 16 | } else if (ownPropKeys.indexOf(key) !== -1) { 17 | accum.ownProps[key] = prop; 18 | } else { 19 | accum.passedProps[key] = prop; 20 | } 21 | 22 | return accum; 23 | }, { 24 | Element: component, 25 | ownProps: {}, 26 | passedProps: {}, 27 | }) 28 | ); 29 | 30 | let getPropTypes; 31 | if (process.env.NODE_ENV !== 'production') { 32 | getPropTypes = (ownPropKeys, propTypes) => ownPropKeys.reduce((out, key) => { 33 | const styleMap = propTypes[key]; 34 | const propType = styleMap.type; 35 | 36 | if (propType === 'oneOf') { 37 | out[key] = PropTypes.oneOf(styleMap.values); 38 | } else if (propType === 'bool') { 39 | out[key] = PropTypes.bool; 40 | } 41 | 42 | return out; 43 | }, { 44 | component: PropTypes.oneOfType([ 45 | PropTypes.string, 46 | PropTypes.func, 47 | ]), 48 | }); 49 | } 50 | 51 | module.exports = ( 52 | transformProps /*: (ownProps: Object, passedProps: Object, args: any) => Object */ 53 | ) /*: ComponentFactory */ => { 54 | const baseRender = ({ Element, ownProps, passedProps, args }) => 55 | React.createElement(Element, transformProps(ownProps, passedProps, args)); 56 | 57 | return (component, propTypes, args, enhancer) => { 58 | const render = enhancer ? enhancer(baseRender) : baseRender; 59 | const ownPropKeys = getOwnPropKeys(propTypes); 60 | 61 | const StaticComponent = (props /*: Object */) => { 62 | const { Element, ownProps, passedProps } = getComponentProps(ownPropKeys, component, props); 63 | return render({ Element, ownProps, passedProps, args }); 64 | }; 65 | 66 | if (process.env.NODE_ENV !== 'production' && !Array.isArray(propTypes)) { 67 | StaticComponent.propTypes = getPropTypes(ownPropKeys, propTypes); 68 | } 69 | 70 | return StaticComponent; 71 | }; 72 | }; 73 | -------------------------------------------------------------------------------- /examples/cssta-demo/upgrade/src/logo.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /packages/cssta/src/factories/__tests__/componentFactory.js: -------------------------------------------------------------------------------- 1 | /* eslint-disable flowtype/require-valid-file-annotation */ 2 | /* global jest it, expect */ 3 | const React = require('react'); 4 | const renderer = require('react-test-renderer'); // eslint-disable-line 5 | const componentFactory = require('../componentFactory'); 6 | 7 | 8 | const createComponent = componentFactory((ownProps, passedProps) => passedProps); 9 | 10 | const runTest = ({ 11 | type = 'button', 12 | propTypes = [], 13 | inputProps = {}, 14 | inputChildren = [], 15 | validProps = [], 16 | invalidProps = [], 17 | expectedType = type, 18 | expectedProps = {}, 19 | expectedChildren = null, 20 | } = {}) => { 21 | const Element = createComponent(type, propTypes); 22 | 23 | const validator = Element.propTypes; 24 | Object.keys(inputProps).forEach((prop) => { 25 | validProps.forEach((value) => { 26 | validator(inputProps, prop, value); 27 | }); 28 | 29 | invalidProps.forEach((value) => { 30 | expect(() => validator(inputProps, prop, value)).toThrow(); 31 | }); 32 | }); 33 | 34 | const component = 35 | renderer.create(React.createElement(Element, inputProps, ...inputChildren)).toJSON(); 36 | 37 | expect(component.type).toEqual(expectedType); 38 | expect(component.props).toEqual(expectedProps); 39 | expect(component.children).toEqual(expectedChildren); 40 | }; 41 | 42 | it('allows constructing with another component', () => runTest({ 43 | type: 'span', 44 | })); 45 | 46 | it('allows overriding the component', () => runTest({ 47 | inputProps: { component: 'span' }, 48 | expectedType: 'span', 49 | expectedProps: {}, 50 | })); 51 | 52 | it('adds boolean propTypes', () => runTest({ 53 | propTypes: { 54 | booleanAttribute: { type: 'bool' }, 55 | }, 56 | validProps: [ 57 | { booleanAttribute: true }, 58 | { booleanAttribute: false }, 59 | ], 60 | invalidProps: [ 61 | { booleanAttribute: 5 }, 62 | { booleanAttribute: 'string' }, 63 | { booleanAttribute: () => {} }, 64 | ], 65 | })); 66 | 67 | it('adds string propTypes', () => runTest({ 68 | propTypes: { 69 | booleanAttribute: { 70 | type: 'oneOf', 71 | values: ['value1', 'value2'], 72 | }, 73 | }, 74 | validProps: [ 75 | { stringAttribute: 'value1' }, 76 | { stringAttribute: 'value2' }, 77 | ], 78 | invalidProps: [ 79 | { stringAttribute: true }, 80 | { stringAttribute: false }, 81 | { stringAttribute: 5 }, 82 | { stringAttribute: 'other string' }, 83 | { stringAttribute: () => {} }, 84 | ], 85 | })); 86 | 87 | it('allows children', () => runTest({ 88 | inputChildren: ['text'], 89 | expectedChildren: ['text'], 90 | })); 91 | -------------------------------------------------------------------------------- /packages/cssta/src/native/enhancers/animationUtil.js: -------------------------------------------------------------------------------- 1 | // @flow 2 | /* eslint-disable */ 3 | // $FlowFixMe 4 | const { StyleSheet, Easing } = require('react-native'); 5 | /* eslint-enable */ 6 | const { getAppliedRules } = require('../util'); 7 | /*:: import type { Args } from '../types' */ 8 | 9 | module.exports.mergeStyles = (props /*: { ownProps: Object, args: Args } */) => 10 | StyleSheet.flatten(getAppliedRules(props.args.rules, props.ownProps).map(rule => rule.style)); 11 | 12 | /*:: 13 | export type Interpolation = number | string | [{ [key:string]: Interpolation }] 14 | export type OutputRange = Interpolation[] 15 | export type InterpolatedValue = Object | Object[] 16 | */ 17 | 18 | module.exports.interpolateValue = ( 19 | inputRange /*: number[] */, 20 | outputRange /*: OutputRange */, 21 | animation /*: any */, 22 | interpolateNumbers /*: boolean */ = false 23 | ) /*: InterpolatedValue */ => { 24 | const firstValue = outputRange[0]; 25 | if (interpolateNumbers && typeof firstValue === 'number') { 26 | return animation; 27 | } else if (!Array.isArray(firstValue)) { 28 | return animation.interpolate({ inputRange, outputRange }); 29 | } 30 | 31 | // transforms 32 | if (process.env.NODE_ENV !== 'production') { 33 | const currentProperties = String(firstValue.map(Object.keys)); 34 | // Not the *best* practise here... 35 | const transformsAreConsistent = outputRange.every((range) => { 36 | // $FlowFixMe 37 | const rangeProperties = String(range.map(Object.keys)); 38 | return currentProperties === rangeProperties; 39 | }); 40 | 41 | if (!transformsAreConsistent) { 42 | throw new Error('Expected transforms to have same shape between transitions'); 43 | } 44 | } 45 | 46 | return firstValue.map((transform, index) => { 47 | const property = Object.keys(transform)[0]; 48 | // $FlowFixMe 49 | const innerOutputRange = outputRange.map(range => range[index][property]); 50 | 51 | // We *have* to interpolate even numeric values, as we will always animate between 0--1 52 | const interpolation = animation.interpolate({ inputRange, outputRange: innerOutputRange }); 53 | 54 | return { [property]: interpolation }; 55 | }); 56 | }; 57 | 58 | module.exports.getDurationInMs = (duration /*: string */) /*: number */ => { 59 | const time = parseFloat(duration); 60 | const factor = /ms$/i.test(duration) ? 1 : 1000; 61 | return time * factor; 62 | }; 63 | 64 | module.exports.easingFunctions = { 65 | linear: Easing.linear, 66 | ease: Easing.ease, 67 | 'ease-in': Easing.in, 68 | 'ease-out': Easing.out, 69 | 'ease-in-out': Easing.inOut, 70 | }; 71 | 72 | module.exports.durationRegExp = /^\d/; 73 | 74 | module.exports.easingRegExp = /(linear|ease(?:-in)?(?:-out)+)/i; 75 | -------------------------------------------------------------------------------- /examples/CsstaNativeDemo/android/app/proguard-rules.pro: -------------------------------------------------------------------------------- 1 | # Add project specific ProGuard rules here. 2 | # By default, the flags in this file are appended to flags specified 3 | # in /usr/local/Cellar/android-sdk/24.3.3/tools/proguard/proguard-android.txt 4 | # You can edit the include path and order by changing the proguardFiles 5 | # directive in build.gradle. 6 | # 7 | # For more details, see 8 | # http://developer.android.com/guide/developing/tools/proguard.html 9 | 10 | # Add any project specific keep options here: 11 | 12 | # If your project uses WebView with JS, uncomment the following 13 | # and specify the fully qualified class name to the JavaScript interface 14 | # class: 15 | #-keepclassmembers class fqcn.of.javascript.interface.for.webview { 16 | # public *; 17 | #} 18 | 19 | # Disabling obfuscation is useful if you collect stack traces from production crashes 20 | # (unless you are using a system that supports de-obfuscate the stack traces). 21 | -dontobfuscate 22 | 23 | # React Native 24 | 25 | # Keep our interfaces so they can be used by other ProGuard rules. 26 | # See http://sourceforge.net/p/proguard/bugs/466/ 27 | -keep,allowobfuscation @interface com.facebook.proguard.annotations.DoNotStrip 28 | -keep,allowobfuscation @interface com.facebook.proguard.annotations.KeepGettersAndSetters 29 | -keep,allowobfuscation @interface com.facebook.common.internal.DoNotStrip 30 | 31 | # Do not strip any method/class that is annotated with @DoNotStrip 32 | -keep @com.facebook.proguard.annotations.DoNotStrip class * 33 | -keep @com.facebook.common.internal.DoNotStrip class * 34 | -keepclassmembers class * { 35 | @com.facebook.proguard.annotations.DoNotStrip *; 36 | @com.facebook.common.internal.DoNotStrip *; 37 | } 38 | 39 | -keepclassmembers @com.facebook.proguard.annotations.KeepGettersAndSetters class * { 40 | void set*(***); 41 | *** get*(); 42 | } 43 | 44 | -keep class * extends com.facebook.react.bridge.JavaScriptModule { *; } 45 | -keep class * extends com.facebook.react.bridge.NativeModule { *; } 46 | -keepclassmembers,includedescriptorclasses class * { native ; } 47 | -keepclassmembers class * { @com.facebook.react.uimanager.UIProp ; } 48 | -keepclassmembers class * { @com.facebook.react.uimanager.annotations.ReactProp ; } 49 | -keepclassmembers class * { @com.facebook.react.uimanager.annotations.ReactPropGroup ; } 50 | 51 | -dontwarn com.facebook.react.** 52 | 53 | # okhttp 54 | 55 | -keepattributes Signature 56 | -keepattributes *Annotation* 57 | -keep class okhttp3.** { *; } 58 | -keep interface okhttp3.** { *; } 59 | -dontwarn okhttp3.** 60 | 61 | # okio 62 | 63 | -keep class sun.misc.Unsafe { *; } 64 | -dontwarn java.nio.file.* 65 | -dontwarn org.codehaus.mojo.animal_sniffer.IgnoreJRERequirement 66 | -dontwarn okio.** 67 | -------------------------------------------------------------------------------- /examples/CsstaNativeDemo/android/gradlew.bat: -------------------------------------------------------------------------------- 1 | @if "%DEBUG%" == "" @echo off 2 | @rem ########################################################################## 3 | @rem 4 | @rem Gradle startup script for Windows 5 | @rem 6 | @rem ########################################################################## 7 | 8 | @rem Set local scope for the variables with windows NT shell 9 | if "%OS%"=="Windows_NT" setlocal 10 | 11 | @rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. 12 | set DEFAULT_JVM_OPTS= 13 | 14 | set DIRNAME=%~dp0 15 | if "%DIRNAME%" == "" set DIRNAME=. 16 | set APP_BASE_NAME=%~n0 17 | set APP_HOME=%DIRNAME% 18 | 19 | @rem Find java.exe 20 | if defined JAVA_HOME goto findJavaFromJavaHome 21 | 22 | set JAVA_EXE=java.exe 23 | %JAVA_EXE% -version >NUL 2>&1 24 | if "%ERRORLEVEL%" == "0" goto init 25 | 26 | echo. 27 | echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 28 | echo. 29 | echo Please set the JAVA_HOME variable in your environment to match the 30 | echo location of your Java installation. 31 | 32 | goto fail 33 | 34 | :findJavaFromJavaHome 35 | set JAVA_HOME=%JAVA_HOME:"=% 36 | set JAVA_EXE=%JAVA_HOME%/bin/java.exe 37 | 38 | if exist "%JAVA_EXE%" goto init 39 | 40 | echo. 41 | echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% 42 | echo. 43 | echo Please set the JAVA_HOME variable in your environment to match the 44 | echo location of your Java installation. 45 | 46 | goto fail 47 | 48 | :init 49 | @rem Get command-line arguments, handling Windowz variants 50 | 51 | if not "%OS%" == "Windows_NT" goto win9xME_args 52 | if "%@eval[2+2]" == "4" goto 4NT_args 53 | 54 | :win9xME_args 55 | @rem Slurp the command line arguments. 56 | set CMD_LINE_ARGS= 57 | set _SKIP=2 58 | 59 | :win9xME_args_slurp 60 | if "x%~1" == "x" goto execute 61 | 62 | set CMD_LINE_ARGS=%* 63 | goto execute 64 | 65 | :4NT_args 66 | @rem Get arguments from the 4NT Shell from JP Software 67 | set CMD_LINE_ARGS=%$ 68 | 69 | :execute 70 | @rem Setup the command line 71 | 72 | set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar 73 | 74 | @rem Execute Gradle 75 | "%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %CMD_LINE_ARGS% 76 | 77 | :end 78 | @rem End local scope for the variables with windows NT shell 79 | if "%ERRORLEVEL%"=="0" goto mainEnd 80 | 81 | :fail 82 | rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of 83 | rem the _cmd.exe /c_ return code! 84 | if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1 85 | exit /b 1 86 | 87 | :mainEnd 88 | if "%OS%"=="Windows_NT" endlocal 89 | 90 | :omega 91 | -------------------------------------------------------------------------------- /packages/cssta/src/web/extractRules.js: -------------------------------------------------------------------------------- 1 | // @flow 2 | /* eslint-disable no-param-reassign */ 3 | const selectorParser = require('postcss-selector-parser'); 4 | const { transformAnimationNames } = require('postcss-transform-animations'); 5 | const getRoot = require('../util/getRoot'); 6 | const { isDirectChildOfKeyframes } = require('../util'); 7 | /*:: import type { Args } from './types' */ 8 | 9 | /*:: 10 | type Generators = { 11 | generateClassName: () => string, 12 | generateAnimationName: () => string, 13 | } 14 | */ 15 | 16 | module.exports = ( 17 | inputCss /*: string */, 18 | { generateClassName, generateAnimationName } /*: Generators */ 19 | ) /*: { css: string, propTypes: Object, args: Args } */ => { 20 | const classNameMap = {}; // { propName: { propValue: className } } 21 | const animationNameMap = {}; 22 | 23 | let defaultClassName = null; 24 | const getDefaultClassName = () => { 25 | if (defaultClassName === null) defaultClassName = generateClassName(); 26 | return defaultClassName; 27 | }; 28 | 29 | const getClassNameFor = (attribute, value) => { 30 | if (!classNameMap[attribute]) { 31 | classNameMap[attribute] = {}; 32 | } 33 | 34 | if (!classNameMap[attribute][value]) { 35 | classNameMap[attribute][value] = generateClassName(); 36 | } 37 | 38 | return classNameMap[attribute][value]; 39 | }; 40 | 41 | const transformSelectors = selectorParser((container) => { 42 | container.each((selector) => { 43 | container.walkNesting((node) => { 44 | const className = getDefaultClassName(); 45 | const replacementNode = selectorParser.className({ value: className }); 46 | node.replaceWith(replacementNode); 47 | }); 48 | 49 | selector.walkAttributes((node) => { 50 | const attribute = node.attribute.trim(); 51 | if (attribute[0] !== '*') return; 52 | const prop = attribute.slice(1); 53 | const value = node.value ? node.raws.unquoted : 'true'; 54 | const className = getClassNameFor(prop, value); 55 | const replacementNode = selectorParser.className({ value: className }); 56 | node.replaceWith(replacementNode); 57 | }); 58 | }); 59 | }); 60 | 61 | const { root, propTypes } = getRoot(inputCss, true); 62 | 63 | root.walkRules((node) => { 64 | if (!isDirectChildOfKeyframes(node)) { 65 | node.selector = transformSelectors.process(node.selector).result; 66 | } 67 | }); 68 | 69 | transformAnimationNames({ 70 | transform: (value) => { 71 | if (value in animationNameMap) return animationNameMap[value]; 72 | 73 | const transformValue = generateAnimationName(); 74 | animationNameMap[value] = transformValue; 75 | return transformValue; 76 | }, 77 | }, root); 78 | 79 | const css = root.toString(); 80 | const args = { defaultClassName, classNameMap }; 81 | 82 | return { css, propTypes, args }; 83 | }; 84 | -------------------------------------------------------------------------------- /examples/CsstaNativeDemo/App.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import { View, Text, ScrollView, StyleSheet } from 'react-native'; 3 | import cssta from 'cssta/native'; 4 | import Basic, { code as basicCode } from './demos/Basic'; 5 | import Animations, { code as animationsCode } from './demos/Animations'; 6 | import Transitions, { code as transitionsCode } from './demos/Transitions'; 7 | import CSSVariables, { code as cssVariablesCode } from './demos/CSSVariables'; 8 | import VariablesProvider, { code as variablesProviderCode } from './demos/VariablesProvider'; 9 | import StyleOverrides, { code as styleOverridesCode } from './demos/StyleOverrides'; 10 | import CustomAnimations from './demos/CustomAnimations'; 11 | import ColorFunction, { code as colorFunctionCode } from './demos/ColorFunction'; 12 | import CustomButton from './demos/CustomButton'; 13 | 14 | const App = cssta(ScrollView)` 15 | padding: 20px 0px; 16 | background: #ecf0f1; 17 | `; 18 | 19 | const DemoContainer = cssta(View)` 20 | margin: 10px 0px; 21 | `; 22 | 23 | const DemoTitle = cssta(Text)` 24 | padding: 5px 20px; 25 | color: #7f8c8d; 26 | `; 27 | 28 | const DemoBody = cssta(View)` 29 | padding: 20px; 30 | background-color: white; 31 | border-color: #bdc3c7; 32 | border-top-width: ${StyleSheet.hairlineWidth}px; 33 | 34 | &[@noCode] { 35 | border-bottom-width: ${StyleSheet.hairlineWidth}px; 36 | } 37 | `; 38 | 39 | const CodeContainer = cssta(View)` 40 | background-color: #7f8c8d; 41 | padding: 5px 20px; 42 | `; 43 | 44 | const CodeBody = cssta(Text)` 45 | font: 8px "courier"; 46 | color: #ecf0f1; 47 | `; 48 | 49 | const Demo = ({ title, children, code }) => ( 50 | 51 | 52 | {title} 53 | 54 | 55 | {children} 56 | 57 | {code && 58 | {code} 59 | } 60 | 61 | ); 62 | 63 | export default () => ( 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | 78 | 79 | 80 | 81 | 82 | 83 | 84 | 85 | 86 | 87 | 88 | 89 | 90 | 91 | 92 | 93 | ); 94 | -------------------------------------------------------------------------------- /packages/cssta/src/web/__tests__/createComponent.js: -------------------------------------------------------------------------------- 1 | /* eslint-disable flowtype/require-valid-file-annotation */ 2 | /* global jest it, expect */ 3 | const React = require('react'); 4 | const renderer = require('react-test-renderer'); // eslint-disable-line 5 | const createComponent = require('../createComponent'); 6 | 7 | 8 | const runTest = ({ 9 | type = 'button', 10 | defaultClassName = null, 11 | classNameMap = [], 12 | propTypes = Object.keys(classNameMap), 13 | inputProps = {}, 14 | expectedType = type, 15 | expectedProps = {}, 16 | expectedChildren = null, 17 | } = {}) => { 18 | const Element = createComponent(type, propTypes, { defaultClassName, classNameMap }); 19 | 20 | const component = renderer.create(React.createElement(Element, inputProps)).toJSON(); 21 | 22 | expect(component.type).toEqual(expectedType); 23 | expect(component.props).toEqual(expectedProps); 24 | expect(component.children).toEqual(expectedChildren); 25 | }; 26 | 27 | it('renders an element', () => runTest()); 28 | 29 | it('adds a default class', () => runTest({ 30 | defaultClassName: 'default', 31 | expectedProps: { className: 'default' }, 32 | })); 33 | 34 | it('adds adds a boolean property', () => runTest({ 35 | classNameMap: { booleanAttribute: { true: 'class' } }, 36 | inputProps: { booleanAttribute: true }, 37 | expectedProps: { className: 'class' }, 38 | })); 39 | 40 | it('does not add boolean properties if they are not passed', () => runTest({ 41 | classNameMap: { booleanAttribute: { true: 'class' } }, 42 | inputProps: {}, 43 | })); 44 | 45 | it('adds adds a string property', () => runTest({ 46 | classNameMap: { stringAttribute: { value: 'class' } }, 47 | inputProps: { stringAttribute: 'value' }, 48 | expectedProps: { className: 'class' }, 49 | })); 50 | 51 | it('does not add string properties if they are not passed', () => runTest({ 52 | classNameMap: { stringAttribute: { value: 'class' } }, 53 | inputProps: {}, 54 | })); 55 | 56 | it('passes extraneous props down', () => runTest({ 57 | classNameMap: { booleanAttribute: { true: 'class' } }, 58 | inputProps: { type: 'button', booleanAttribute: true }, 59 | expectedProps: { type: 'button', className: 'class' }, 60 | })); 61 | 62 | it('allows setting className', () => runTest({ 63 | inputProps: { className: 'test' }, 64 | expectedProps: { className: 'test' }, 65 | })); 66 | 67 | it('allows extending className with default class name', () => runTest({ 68 | defaultClassName: 'default', 69 | inputProps: { className: 'test' }, 70 | expectedProps: { className: 'default test' }, 71 | })); 72 | 73 | it('allows extending className with props', () => runTest({ 74 | classNameMap: { booleanAttribute: { true: 'class' } }, 75 | inputProps: { className: 'test', booleanAttribute: true }, 76 | expectedProps: { className: 'class test' }, 77 | })); 78 | 79 | it('allows setting style', () => runTest({ 80 | inputProps: { style: { top: 5 } }, 81 | expectedProps: { style: { top: 5 } }, 82 | })); 83 | -------------------------------------------------------------------------------- /packages/babel-plugin-cssta/src/platforms/web.js: -------------------------------------------------------------------------------- 1 | /* eslint-disable no-param-reassign */ 2 | const t = require('babel-types'); 3 | const _ = require('lodash/fp'); 4 | const extractRules = require('cssta/src/web/extractRules'); 5 | const cssNameGenerator = require('css-class-generator'); 6 | const { jsonToNode, getOrCreateImportReference } = require('../util'); 7 | const { startEndMarkers, fileStartEndCommentMarkers } = require('../webUtil'); 8 | 9 | 10 | // Make sure we don't somehow generate an animation name that's a keyword 11 | // This is almost impossible anyway, but whatever 12 | const animationKeywords = ` 13 | alternate alternate-reverse backwards both ease ease-in ease-in-out ease-out forwards infinite 14 | linear none normal paused reverse running step-end step-start initial inherit unset 15 | `.split(/[^\s]+/g); 16 | 17 | let classGenerator = null; 18 | let animationGenerator = null; 19 | 20 | const resetGenerators = () => { 21 | classGenerator = cssNameGenerator(); 22 | animationGenerator = (function* gen() { 23 | for (const value of cssNameGenerator()) { // eslint-disable-line 24 | if (!_.includes(value, animationKeywords)) yield value; 25 | } 26 | }()); 27 | }; 28 | 29 | resetGenerators(); 30 | 31 | module.exports = (path, state, component, cssText, substititionMap) => { 32 | if (!_.isEmpty(substititionMap)) { 33 | throw new Error('You cannot use interpolation in template strings (i.e. `color: ${primary}`)'); // eslint-disable-line 34 | } 35 | 36 | const isInjectGlobal = t.isStringLiteral(component) && component.value === 'injectGlobal'; 37 | 38 | const { commentStartMarker, commentEndMarker } = isInjectGlobal 39 | ? startEndMarkers('Injected Globals') 40 | : fileStartEndCommentMarkers(state); 41 | 42 | let { currentWebCss } = state; 43 | let newElement = null; 44 | 45 | if (!isInjectGlobal) { 46 | const { css: output, args } = extractRules(cssText, { 47 | generateClassName: () => classGenerator.next().value, 48 | generateAnimationName: () => animationGenerator.next().value, 49 | }); 50 | 51 | const cssBefore = _.endsWith(commentEndMarker, currentWebCss) 52 | ? currentWebCss.slice(0, -commentEndMarker.length) 53 | : `${currentWebCss}\n${commentStartMarker}`; 54 | currentWebCss = `${cssBefore}\n${output}\n${commentEndMarker}`; 55 | 56 | const createComponent = getOrCreateImportReference( 57 | path, 58 | 'cssta/lib/web/createComponent', 59 | 'default' 60 | ); 61 | 62 | newElement = t.callExpression(createComponent, [ 63 | component, 64 | t.nullLiteral(), // Gets replaced with Object.keys(classNameMap) 65 | jsonToNode(args), 66 | ]); 67 | } else { 68 | currentWebCss = `${commentStartMarker}${cssText}\n${commentEndMarker}${currentWebCss}`; 69 | } 70 | 71 | state.currentWebCss = currentWebCss; 72 | 73 | if (newElement) { 74 | path.replaceWith(newElement); 75 | } else { 76 | path.remove(); 77 | } 78 | }; 79 | 80 | module.exports.resetGenerators = resetGenerators; 81 | -------------------------------------------------------------------------------- /packages/cssta/src/web/index.js: -------------------------------------------------------------------------------- 1 | // @flow 2 | /* global document */ 3 | const extractRules = require('./extractRules'); 4 | const createComponent = require('./createComponent'); 5 | 6 | let devId = 0; 7 | const getDevId = name => () => { 8 | devId += 1; 9 | return `${name}-${devId}`; 10 | }; 11 | 12 | const assertNoTemplateParams = (cssTextFragments) => { 13 | if (!Array.isArray(cssTextFragments)) { 14 | return cssTextFragments; 15 | } else if (cssTextFragments.length === 1) { 16 | return cssTextFragments[0]; 17 | } 18 | throw new Error('You cannot use string interpolation with cssta for web'); 19 | }; 20 | 21 | let styleElement = null; 22 | const updateCss = (cssText) => { 23 | if (!styleElement) { 24 | styleElement = document.createElement('style'); 25 | const styleBody = document.createTextNode(cssText); 26 | styleElement.appendChild(styleBody); 27 | document.getElementsByTagName('head')[0].appendChild(styleElement); 28 | } else { 29 | const existingStyleBody = styleElement.firstChild; 30 | if (!existingStyleBody) throw new Error('Unexpected error creating styles'); 31 | const newStyleBody = document.createTextNode(cssText); 32 | styleElement.replaceChild(newStyleBody, existingStyleBody); 33 | } 34 | }; 35 | 36 | const opts = { 37 | generateClassName: getDevId('rule'), 38 | generateAnimationName: getDevId('animation'), 39 | }; 40 | 41 | let styleContents = ''; 42 | const style = (element /*: any */) => (cssTextFragments /*: string[] | string */) => { 43 | const cssText = assertNoTemplateParams(cssTextFragments); 44 | 45 | const { css, propTypes, args } = extractRules(cssText, opts); 46 | 47 | styleContents += css; 48 | updateCss(styleContents); 49 | 50 | return createComponent(element, propTypes, args); 51 | }; 52 | 53 | let didInjectGlobal = false; 54 | style.injectGlobal = (cssTextFragments) => { 55 | const cssText = assertNoTemplateParams(cssTextFragments); 56 | 57 | if (didInjectGlobal) { 58 | throw new Error('To help with consistency, you can only call injectGlobal once'); 59 | } 60 | 61 | didInjectGlobal = true; 62 | styleContents = cssText + styleContents; 63 | updateCss(styleContents); 64 | }; 65 | 66 | `a abbr address area article aside audio b base bdi bdo big blockquote body br button canvas 67 | caption cite code col colgroup data datalist dd del details dfn dialog div dl dt em embed fieldset 68 | figcaption figure footer form h1 h2 h3 h4 h5 h6 head header hgroup hr html i iframe img input ins 69 | kbd keygen label legend li link main map mark menu menuitem meta meter nav noscript object ol 70 | optgroup option output p param picture pre progress q rp rt ruby s samp script section select small 71 | source span strong style sub summary sup table tbody td textarea tfoot th thead time title tr track 72 | u ul var video wbr circle clipPath defs ellipse g image line linearGradient mask path pattern 73 | polygon polyline radialGradient rect stop svg text tspan`.split(/\s+/m).forEach((tagName) => { 74 | style[tagName] = style(tagName); 75 | }); 76 | 77 | module.exports = style; 78 | -------------------------------------------------------------------------------- /packages/babel-plugin-cssta/src/index.test.js: -------------------------------------------------------------------------------- 1 | /* global it expect */ 2 | const path = require('path'); 3 | const fs = require('fs'); 4 | const glob = require('glob'); 5 | const { transformFileSync } = require('babel-core'); 6 | const tempfile = require('tempfile'); 7 | const plugin = require('.'); 8 | 9 | const approve = process.argv.includes('--approve'); 10 | const printJs = process.argv.includes('--print-js'); 11 | const printCss = process.argv.includes('--print-css'); 12 | const filterIndex = process.argv.indexOf('--filter'); 13 | const filter = filterIndex === -1 ? '' : process.argv[filterIndex + 1]; 14 | const baseDir = path.join(__dirname, '..'); 15 | 16 | const normaliseCss = (str) => { 17 | let output = str; 18 | do { 19 | output = output.replace(baseDir, ''); 20 | } while (output.indexOf(baseDir) !== -1); 21 | return output; 22 | }; 23 | 24 | const getMockCssPath = () => tempfile('.css'); 25 | 26 | const getActual = (testPath, actualJsPath, optionsPath) => { 27 | plugin.resetGenerators(); 28 | 29 | const dummyCssPath = getMockCssPath(); 30 | const options = { output: dummyCssPath, cwd: testPath }; 31 | 32 | if (fs.existsSync(optionsPath)) { 33 | const userOptions = JSON.parse(fs.readFileSync(optionsPath, 'utf8')); 34 | Object.assign(options, userOptions); 35 | } 36 | 37 | const actualJs = transformFileSync(actualJsPath, { 38 | plugins: [[plugin, options]], 39 | }).code; 40 | 41 | let actualCss = fs.existsSync(dummyCssPath) 42 | ? fs.readFileSync(dummyCssPath, 'utf8') 43 | : ''; 44 | actualCss = normaliseCss(actualCss); 45 | 46 | return { actualJs, actualCss }; 47 | }; 48 | 49 | glob.sync(path.join(baseDir, 'fixtures/*/')).filter(name => ( 50 | !filter || name.indexOf(filter) !== -1 51 | )).forEach((testPath) => { 52 | const testName = path.relative(path.join(baseDir, 'fixtures'), testPath); 53 | 54 | const expectedJsPath = path.join(testPath, 'expected.js'); 55 | const expectedCssPath = path.join(testPath, 'expected.css'); 56 | const actualJsPath = path.join(testPath, 'actual.js'); 57 | const optionsPath = path.join(testPath, 'options.json'); 58 | 59 | if (global.it) { 60 | it(`should work with ${testName}`, () => { 61 | const { actualJs, actualCss } = getActual(testPath, actualJsPath, optionsPath); 62 | const expectedJs = fs.readFileSync(expectedJsPath, 'utf8'); 63 | const expectedCss = actualCss && fs.readFileSync(expectedCssPath, 'utf8'); 64 | 65 | expect(actualJs.trim()).toEqual(expectedJs.trim()); 66 | if (actualCss) expect(actualCss.trim()).toEqual(expectedCss.trim()); 67 | }); 68 | } else { 69 | const { actualJs, actualCss } = getActual(testPath, actualJsPath, optionsPath); 70 | 71 | if (approve) { 72 | const options = { flag: 'w+', encoding: 'utf8' }; 73 | fs.writeFileSync(expectedJsPath, actualJs, options); 74 | if (actualCss) fs.writeFileSync(expectedCssPath, actualCss, options); 75 | } 76 | 77 | if (printJs) console.log(actualJs); // eslint-disable-line 78 | if (printCss) console.log(actualCss); // eslint-disable-line 79 | } 80 | }); 81 | -------------------------------------------------------------------------------- /packages/cssta/src/native/__tests__/selectorTransform.js: -------------------------------------------------------------------------------- 1 | /* eslint-disable flowtype/require-valid-file-annotation */ 2 | /* global jest it, expect */ 3 | const { 4 | getValidatorSourceForSelector, createValidatorForSelector, 5 | } = require('../selectorTransform'); 6 | 7 | const runTest = (selector, { valid = [], invalid = [] }) => { 8 | const validator = createValidatorForSelector(selector); 9 | 10 | valid.forEach((props) => { 11 | expect(validator(props)).toBe(true); 12 | }); 13 | 14 | invalid.forEach((props) => { 15 | expect(validator(props)).toBe(false); 16 | }); 17 | }; 18 | 19 | // Note that getRoot replaces @ with * (so it parses). We have to use * here. 20 | 21 | it('creates a function that validates boolean attributes', () => runTest('[*bool]', { 22 | valid: [{ bool: true }, { bool: true, otherAttribute: true }], 23 | invalid: [{}, { bool: false }, { otherAttribute: true }], 24 | })); 25 | 26 | it('creates a function that validates string attributes', () => runTest('[*string = "test"]', { 27 | valid: [{ string: 'test' }, { string: 'test', otherAttribute: true }], 28 | invalid: [{}, { string: 'other' }, { otherAttribute: true }], 29 | })); 30 | 31 | it('combines multiple validators', () => runTest('[*bool][*string = "test"]', { 32 | valid: [ 33 | { bool: true, string: 'test' }, 34 | { bool: true, string: 'test', otherAttribute: true }, 35 | ], 36 | invalid: [ 37 | {}, 38 | { string: 'test' }, 39 | { bool: true }, 40 | { string: 'other', bool: true }, 41 | { string: 'test', bool: false }, 42 | { otherAttribute: true }, 43 | ], 44 | })); 45 | 46 | it('validates everything for & selector', () => runTest('&', { 47 | valid: [{}, { otherAttribute: true }], 48 | })); 49 | 50 | it('works with :matches for same prop', () => runTest(':matches([*string = "a"], [*string = "b"])', { 51 | valid: [{ string: 'a' }, { string: 'b' }, { string: 'a', otherAttribute: true }], 52 | invalid: [{}, { string: 'other' }, { otherAttribute: true }], 53 | })); 54 | 55 | it('works with :matches for different props', () => runTest(':matches([*string = "a"], [*bool])', { 56 | valid: [{ string: 'a' }, { bool: true }, { string: 'a', otherAttribute: true }], 57 | invalid: [{}, { string: 'other' }, { bool: false }, { otherAttribute: true }], 58 | })); 59 | 60 | it('works with :not for same prop', () => runTest(':not([*string = "a"], [*string = "b"])', { 61 | valid: [{}, { string: 'other' }, { otherAttribute: true }], 62 | invalid: [{ string: 'a' }, { string: 'b' }, { string: 'a', otherAttribute: true }], 63 | })); 64 | 65 | it('works with :not for different props', () => runTest(':not([*string = "a"], [*bool])', { 66 | valid: [{}, { string: 'other' }, { bool: false }, { otherAttribute: true }], 67 | invalid: [{ string: 'a' }, { bool: true }, { string: 'a', otherAttribute: true }], 68 | })); 69 | 70 | it('does not allow other pseudo selectors', () => { 71 | expect(() => createValidatorForSelector(':first-child')).toThrow(); 72 | }); 73 | 74 | it('creates function source for node', () => { 75 | const node = getValidatorSourceForSelector('&'); 76 | expect(node).toEqual('(function(p) {return true;})'); 77 | }); 78 | -------------------------------------------------------------------------------- /packages/babel-plugin-cssta/src/transformUtil/extractCsstaCallParts.js: -------------------------------------------------------------------------------- 1 | const t = require('babel-types'); 2 | const _ = require('lodash/fp'); 3 | const { csstaModules } = require('../util'); 4 | 5 | 6 | const interpolationTypes = { 7 | ALLOW: 0, 8 | IGNORE: 1, 9 | DISALLOW: 2, 10 | }; 11 | 12 | const csstaConstructorExpressionTypes = { 13 | CallExpression: node => [node.callee, node.arguments[0]], 14 | MemberExpression: node => [ 15 | node.object, 16 | node.computed ? node.property : t.stringLiteral(node.property.name), 17 | ], 18 | }; 19 | 20 | const getCsstaTypeForCallee = (path, callee) => { 21 | if (!t.isIdentifier(callee)) return null; 22 | 23 | const importScopePath = path.findParent(_.has(['scope', 'bindings', callee.name])); 24 | if (!importScopePath) return null; 25 | 26 | const importSpecifier = _.get(['scope', 'bindings', callee.name, 'path'], importScopePath); 27 | if (!importSpecifier || !t.isImportDefaultSpecifier(importSpecifier)) return null; 28 | 29 | const importDeclaration = importSpecifier.findParent(t.isImportDeclaration); 30 | if (!importDeclaration) return null; 31 | 32 | const source = importDeclaration.node.source.value; 33 | const csstaType = csstaModules[source]; 34 | if (!csstaType) return null; 35 | 36 | return { csstaType, importDeclaration }; 37 | }; 38 | 39 | module.exports.getCsstaReferences = (path, node) => { 40 | if (!(node.type in csstaConstructorExpressionTypes)) return null; 41 | 42 | const [callee, component] = csstaConstructorExpressionTypes[node.type](node); 43 | 44 | const csstaTypeParts = getCsstaTypeForCallee(path, callee); 45 | if (!csstaTypeParts) return null; 46 | 47 | const { csstaType, importBinding } = csstaTypeParts; 48 | 49 | return { callee, importBinding, component, csstaType }; 50 | }; 51 | 52 | module.exports.interpolationTypes = interpolationTypes; 53 | module.exports.extractCsstaCallParts = (stringArg, interpolationType) => { 54 | if (!t.isTemplateLiteral(stringArg) && !t.isStringLiteral(stringArg)) return null; 55 | 56 | const hasInterpolation = t.isTemplateLiteral(stringArg) && !_.isEmpty(stringArg.expressions); 57 | 58 | if (hasInterpolation && interpolationType === interpolationTypes.DISALLOW) { 59 | const ex = '`color: ${primary}`'; // eslint-disable-line 60 | throw new Error(`You cannot interpolation in template strings (i.e. ${ex})`); 61 | } 62 | 63 | const allowInterpolation = interpolationType === interpolationTypes.ALLOW; 64 | 65 | let cssText = null; 66 | let substitutionMap = {}; 67 | 68 | if (t.isTemplateLiteral(stringArg) && (allowInterpolation || !hasInterpolation)) { 69 | const { quasis, expressions } = stringArg; 70 | const substitutionNames = expressions.map((value, index) => `__substitution-${index}__`); 71 | cssText = 72 | quasis[0].value.cooked + 73 | substitutionNames.map((name, index) => name + quasis[index + 1].value.cooked).join(''); 74 | substitutionMap = _.fromPairs(_.zip(substitutionNames, expressions)); 75 | } else if (t.isStringLiteral(stringArg)) { 76 | cssText = stringArg.value; 77 | } 78 | 79 | if (cssText === null) return null; 80 | 81 | return { cssText, substitutionMap }; 82 | }; 83 | -------------------------------------------------------------------------------- /packages/cssta/src/native/selectorTransform.js: -------------------------------------------------------------------------------- 1 | // @flow 2 | // We generate babel nodes that represent a validation function 3 | // In prod, we use the babel nodes to make a function 4 | // In dev, we eval to generate a function 5 | 6 | const selectorParser = require('postcss-selector-parser'); 7 | 8 | const propArg = 'p'; 9 | 10 | const createLogicalValidator = (nodes, operator) => { 11 | if (nodes.length === 0) throw new Error('Cannot construct logical validaton'); 12 | const nodeValidators = nodes 13 | .map(createValidator) // eslint-disable-line 14 | .filter(validator => validator !== null); 15 | if (nodeValidators.length === 0) return null; 16 | return nodeValidators.slice(1).reduce((accum, validator) => ( 17 | `(${accum} ${operator} ${validator})` 18 | ), nodeValidators[0]); 19 | }; 20 | 21 | const createNestingValidator = () => null; 22 | 23 | const createAttributeValidator = (node) => { 24 | const { raws, value } = node; 25 | const attribute = node.attribute.trim(); 26 | if (attribute[0] !== '*') { 27 | throw new Error(`You can only use prop selectors (did you forget a * before ${attribute})`); 28 | } 29 | 30 | const prop = attribute.slice(1); 31 | const memberExpression = `${propArg}[${JSON.stringify(prop)}]`; 32 | 33 | if (!value) return `!!${memberExpression}`; 34 | 35 | const unquoted = raws.unquoted.trim(); 36 | return `(${memberExpression} === ${JSON.stringify(unquoted)})`; 37 | }; 38 | 39 | const createPseudoValidator = (node) => { 40 | const { value, nodes } = node; 41 | 42 | if (value === ':matches') { 43 | return createLogicalValidator(nodes, '||'); 44 | } else if (value === ':not') { 45 | const baseValidator = createLogicalValidator(nodes, '||'); 46 | return baseValidator ? `!${baseValidator}` : null; 47 | } 48 | throw new Error(`Invalid selector part: ${node}`); 49 | }; 50 | 51 | const createSelectorValidator = node => createLogicalValidator(node.nodes, '&&'); 52 | 53 | const validators = { 54 | nesting: createNestingValidator, 55 | attribute: createAttributeValidator, 56 | pseudo: createPseudoValidator, 57 | selector: createSelectorValidator, 58 | root: createSelectorValidator, 59 | }; 60 | 61 | const createValidator = (node) => { 62 | if (!(node.type in validators)) throw new Error(`Invalid selector part: ${node}`); 63 | return validators[node.type](node); 64 | }; 65 | 66 | const getBaseValidatorSourceForSelector = (selector) => { 67 | let selectorNode; 68 | selectorParser((node) => { selectorNode = node; }).process(selector); 69 | if (!selectorNode) throw new Error('Expected to parse selector'); 70 | const validatorNode = createValidator(selectorNode) || 'true'; 71 | const returnNode = `return ${validatorNode};`; 72 | return returnNode; 73 | }; 74 | 75 | module.exports.getValidatorSourceForSelector = (selector /*: string */) => 76 | `(function(${propArg}) {${getBaseValidatorSourceForSelector(selector)}})`; 77 | 78 | module.exports.createValidatorForSelector = ( 79 | selector /*: string */ 80 | ) /*: (props: Object) => boolean */ => { 81 | const source = getBaseValidatorSourceForSelector(selector); 82 | // $FlowFixMe 83 | const validator /*: (props: Object) => boolean */ = new Function(propArg, source); // eslint-disable-line 84 | return validator; 85 | }; 86 | -------------------------------------------------------------------------------- /packages/babel-plugin-cssta/src/optimizations/singleSourceOfVariables.js: -------------------------------------------------------------------------------- 1 | const fs = require('fs'); 2 | const { parse } = require('babylon'); 3 | const { default: traverse } = require('babel-traverse'); 4 | const _ = require('lodash/fp'); 5 | const extractRules = require('cssta/src/native/extractRules'); // Is this really native-only? 6 | const resolveVariableDependencies = require('cssta/src/util/resolveVariableDependencies'); 7 | const { 8 | getCsstaReferences, interpolationTypes, extractCsstaCallParts, 9 | } = require('../transformUtil/extractCsstaCallParts'); 10 | const { containsSubstitution } = require('../util'); 11 | 12 | 13 | const extractVariables = (path, target, stringArg) => { 14 | const csstaReferenceParts = getCsstaReferences(path, target); 15 | if (!csstaReferenceParts) return null; 16 | 17 | const callParts = extractCsstaCallParts(stringArg, interpolationTypes.ALLOW); 18 | if (!callParts) return null; 19 | 20 | const { cssText, substitutionMap } = callParts; 21 | 22 | const { args: { ruleTuples } } = extractRules(cssText); 23 | const rulesWithExportedVariables = _.reject( 24 | _.conforms({ exportedVariables: _.isEmpty }), 25 | ruleTuples 26 | ); 27 | 28 | if (_.isEmpty(rulesWithExportedVariables)) return null; 29 | 30 | if (!_.every({ selector: '&' }, rulesWithExportedVariables)) { 31 | throw new Error('When using singleSourceOfVariables, all variables must be top-level'); 32 | } 33 | 34 | const exportedVariables = _.flow( 35 | _.map('exportedVariables'), 36 | _.reduce(_.assign, {}) 37 | )(rulesWithExportedVariables); 38 | 39 | const exportedVariablesContainingSubstitution = _.flow( 40 | _.values, 41 | _.filter(containsSubstitution(substitutionMap)) 42 | )(exportedVariables); 43 | 44 | if (!_.isEmpty(exportedVariablesContainingSubstitution)) { 45 | throw new Error( 46 | 'When using singleSourceOfVariables, you cannot use interpolation within variables' 47 | ); 48 | } 49 | 50 | return exportedVariables; 51 | }; 52 | 53 | module.exports = (filename, fileOpts) => { 54 | const source = fs.readFileSync(filename, 'utf-8'); 55 | const ast = parse(source, fileOpts); 56 | 57 | let exportedVariables = null; 58 | const doExtractVariables = (path, node, stringArg) => { 59 | const newExportedVariables = extractVariables(path, node, stringArg); 60 | if (exportedVariables && newExportedVariables) { 61 | throw new Error('When using singleSourceOfVariables, only one component can define variables'); 62 | } else if (newExportedVariables) { 63 | exportedVariables = newExportedVariables; 64 | } 65 | }; 66 | 67 | traverse(ast, { 68 | CallExpression(path) { 69 | const { node } = path; 70 | const [arg] = node.arguments; 71 | doExtractVariables(path, node, arg); 72 | }, 73 | TaggedTemplateExpression(path) { 74 | const { quasi, tag } = path.node; 75 | doExtractVariables(path, tag, quasi); 76 | }, 77 | }, null, { 78 | file: { opts: { filename: 'intermediate-file' } }, 79 | }); 80 | 81 | if (!exportedVariables) { 82 | throw new Error('Expected given file to contain CSS variables for singleSourceOfVariables'); 83 | } 84 | 85 | const resolvedVariables = resolveVariableDependencies(exportedVariables, {}); 86 | return resolvedVariables; 87 | }; 88 | -------------------------------------------------------------------------------- /packages/babel-plugin-cssta/src/platforms/native/index.js: -------------------------------------------------------------------------------- 1 | /* eslint-disable no-param-reassign */ 2 | const t = require('babel-types'); 3 | const _ = require('lodash/fp'); 4 | const resolveVariableDependencies = require('cssta/src/util/resolveVariableDependencies'); 5 | const extractRules = require('cssta/src/native/extractRules'); 6 | const { getOrCreateImportReference, jsonToNode } = require('../../util'); 7 | const createArgsStatic = require('./createArgsStatic'); 8 | const createArgsVariables = require('./createArgsVariables'); 9 | 10 | 11 | const everyIsEmpty = _.every(_.isEmpty); 12 | const argsIsEmpty = _.flow( 13 | _.update('ruleTuples', _.map(_.omit(['selector']))), 14 | _.update('ruleTuples', _.reject(everyIsEmpty)), 15 | everyIsEmpty 16 | ); 17 | 18 | module.exports = (path, state, component, cssText, substitutionMap) => { 19 | const { singleSourceOfVariables } = state; 20 | // eslint-disable-next-line 21 | let { propTypes, args } = extractRules(cssText); 22 | 23 | if (singleSourceOfVariables) { 24 | args = _.flow( 25 | _.set('importedVariables', []), 26 | _.update('ruleTuples', _.map(_.set('exportedVariables', {}))) 27 | )(args); 28 | } 29 | 30 | const exportedVariables = _.reduce(_.assign, {}, _.map('exportedVariables', args.ruleTuples)); 31 | const exportsVariables = !_.isEmpty(exportedVariables); 32 | 33 | const resolvedVariables = (singleSourceOfVariables && exportsVariables) 34 | ? resolveVariableDependencies(exportedVariables, {}) 35 | : null; 36 | 37 | if (resolvedVariables && !_.isEqual(resolvedVariables, singleSourceOfVariables)) { 38 | throw new Error('When using singleSourceOfVariables, only one component can define variables'); 39 | } 40 | 41 | // If we end up with nothing after removing configs, and we don't filter props, 42 | // we can just return the component 43 | if (argsIsEmpty(args) && _.isEmpty(propTypes)) { 44 | path.replaceWith(component); 45 | return; 46 | } 47 | 48 | const hasVariables = 49 | exportsVariables || !_.isEmpty(args.importedVariables) || !_.isEmpty(exportedVariables); 50 | 51 | if (hasVariables && singleSourceOfVariables) { 52 | throw new Error('Internal error: expected no variables with singleSourceOfVariables'); 53 | } 54 | 55 | const componentRoot = 'cssta/lib/native'; 56 | const enhancersRoot = `${componentRoot}/enhancers`; 57 | const enhancers = []; 58 | 59 | const addEnhancer = enhancer => 60 | enhancers.push(getOrCreateImportReference(path, `${enhancersRoot}/${enhancer}`, 'default')); 61 | 62 | if (hasVariables) addEnhancer('VariablesStyleSheetManager'); 63 | if (!_.isEmpty(args.transitionedProperties)) addEnhancer('Transition'); 64 | if (!_.isEmpty(args.keyframesStyleTuples)) addEnhancer('Animation'); 65 | 66 | let componentConstructor; 67 | if (_.isEmpty(enhancers)) { 68 | const createComponent = 69 | getOrCreateImportReference(path, `${componentRoot}/createComponent`, 'default'); 70 | componentConstructor = createComponent; 71 | } else { 72 | const withEnhancers = 73 | getOrCreateImportReference(path, `${componentRoot}/withEnhancers`, 'default'); 74 | componentConstructor = t.callExpression(withEnhancers, [ 75 | t.arrayExpression(enhancers), 76 | ]); 77 | } 78 | 79 | const argsNode = hasVariables 80 | ? createArgsVariables(path, substitutionMap, args) 81 | : createArgsStatic(path, substitutionMap, args); 82 | 83 | const newElement = t.callExpression(componentConstructor, [ 84 | component, 85 | jsonToNode(Object.keys(propTypes)), 86 | argsNode, 87 | ]); 88 | 89 | path.replaceWith(newElement); 90 | }; 91 | -------------------------------------------------------------------------------- /packages/babel-plugin-cssta/src/platforms/native/simpleInterpolationMap.js: -------------------------------------------------------------------------------- 1 | /* eslint-disable no-param-reassign */ 2 | const t = require('babel-types'); 3 | const { getOrCreateImportReference } = require('../../util'); 4 | 5 | const convertValue = transform => (path, value) => 6 | t.callExpression(t.identifier(transform), [value]); 7 | 8 | const stringInterpolation = (path, value) => 9 | t.callExpression(t.memberExpression(convertValue('String')(path, value), t.identifier('trim')), []); 10 | 11 | const lengthInterpolation = (path, value) => { 12 | const transformRawValue = getOrCreateImportReference( 13 | path, 14 | 'cssta/lib/native/cssUtil', 15 | 'transformRawValue' 16 | ); 17 | 18 | return t.callExpression(transformRawValue, [value]); 19 | }; 20 | 21 | const numberInterpolation = convertValue('Number'); 22 | 23 | /* 24 | All the values we can work out easily. 25 | 26 | E.g. 27 | fontSize: ${value} can only be a number -> { fontSize: Number(value) } 28 | position: ${value} can only be a string -> { position: String(value).trim() } 29 | 30 | Some values, like 'margin', have shorthands, so cannot be included. 31 | */ 32 | module.exports = { 33 | /* View */ 34 | backfaceVisibility: stringInterpolation, 35 | background: stringInterpolation, 36 | backgroundColor: stringInterpolation, 37 | borderBottomColor: stringInterpolation, 38 | borderBottomLeftRadius: lengthInterpolation, 39 | borderBottomRightRadius: lengthInterpolation, 40 | borderBottomWidth: lengthInterpolation, 41 | borderLeftColor: stringInterpolation, 42 | borderLeftWidth: lengthInterpolation, 43 | borderRightColor: stringInterpolation, 44 | borderRightWidth: lengthInterpolation, 45 | borderTopColor: stringInterpolation, 46 | borderTopLeftRadius: lengthInterpolation, 47 | borderTopRightRadius: lengthInterpolation, 48 | borderTopWidth: lengthInterpolation, 49 | opacity: numberInterpolation, 50 | elevation: numberInterpolation, 51 | /* Layout */ 52 | alignItems: stringInterpolation, 53 | alignSelf: stringInterpolation, 54 | bottom: lengthInterpolation, 55 | flexBasis: lengthInterpolation, 56 | flexDirection: stringInterpolation, 57 | flexGrow: numberInterpolation, 58 | flexShrink: numberInterpolation, 59 | flexWrap: stringInterpolation, 60 | height: lengthInterpolation, 61 | justifyContent: stringInterpolation, 62 | left: lengthInterpolation, 63 | marginBottomWidth: lengthInterpolation, 64 | marginLeftWidth: lengthInterpolation, 65 | marginRightWidth: lengthInterpolation, 66 | marginTopWidth: lengthInterpolation, 67 | maxHeight: lengthInterpolation, 68 | maxWidth: lengthInterpolation, 69 | minHeight: lengthInterpolation, 70 | minWidth: lengthInterpolation, 71 | overflow: stringInterpolation, 72 | paddingBottomWidth: lengthInterpolation, 73 | paddingLeftWidth: lengthInterpolation, 74 | paddingRightWidth: lengthInterpolation, 75 | paddingTopWidth: lengthInterpolation, 76 | position: stringInterpolation, 77 | right: lengthInterpolation, 78 | top: lengthInterpolation, 79 | width: lengthInterpolation, 80 | zIndex: numberInterpolation, 81 | /* Text */ 82 | color: stringInterpolation, 83 | fontSize: lengthInterpolation, 84 | fontStyle: stringInterpolation, 85 | fontWeight: stringInterpolation, 86 | lineHeight: lengthInterpolation, 87 | textAlign: stringInterpolation, 88 | textDecorationLine: stringInterpolation, 89 | textShadowColor: stringInterpolation, 90 | textShadowRadius: lengthInterpolation, 91 | textAlignVertical: stringInterpolation, 92 | letterSpacing: lengthInterpolation, 93 | textDecorationColor: stringInterpolation, 94 | textDecorationStyle: stringInterpolation, 95 | writingDirection: stringInterpolation, 96 | }; 97 | --------------------------------------------------------------------------------