From cc54852555bf4129c53345ad688a284602f5d614 Mon Sep 17 00:00:00 2001 From: gucong Date: Sat, 29 Dec 2018 13:21:08 +0800 Subject: [PATCH] Fix support for styled options --- README.md | 8 ++ extract.js | 128 ++++++++++++++++++++--------- package.json | 2 +- test/fixtures/styled-opts.mjs | 9 ++ test/fixtures/styled-opts.mjs.json | 81 ++++++++++++++++++ test/supports.js | 9 +- 6 files changed, 192 insertions(+), 45 deletions(-) create mode 100644 test/fixtures/styled-opts.mjs create mode 100644 test/fixtures/styled-opts.mjs.json diff --git a/README.md b/README.md index 12ce6ea..80ec0e3 100644 --- a/README.md +++ b/README.md @@ -15,15 +15,23 @@ PostCSS JSX Syntax - [aphrodite](https://github.com/Khan/aphrodite) - [astroturf](https://github.com/4Catalyzer/astroturf) +- [csjs](https://github.com/rtsao/csjs) +- [css-light](https://github.com/streamich/css-light) +- [cssobj](https://github.com/cssobj/cssobj) +- [electron-css](https://github.com/azukaar/electron-css) - [emotion](https://github.com/emotion-js/emotion) +- [freestyler](https://github.com/streamich/freestyler) - [glamor](https://github.com/threepointone/glamor) - [glamorous](https://github.com/paypal/glamorous) +- [j2c](https://github.com/j2css/j2c) +- [linaria](https://github.com/callstack/linaria) - [lit-css](https://github.com/bashmish/lit-css) - [react-native](https://github.com/necolas/react-native-web) - [react-style](https://github.com/js-next/react-style) - [reactcss](https://github.com/casesandberg/reactcss) - [styled-components](https://github.com/styled-components/styled-components) - [styletron-react](https://github.com/rtsao/styletron) +- [styling](https://github.com/andreypopp/styling) - [typestyle](https://github.com/typestyle/typestyle) ## Getting Started diff --git a/extract.js b/extract.js index 2e005b3..4a1d646 100644 --- a/extract.js +++ b/extract.js @@ -8,53 +8,79 @@ const { const getTemplate = require("./get-template"); const loadSyntax = require("postcss-syntax/load-syntax"); +const isStyleSheetCreate = expectAdjacentSibling(["create"]); const supports = { - // https://github.com/Khan/aphrodite - // https://github.com/necolas/react-native-web - StyleSheet: true, - - // https://github.com/emotion-js/emotion // import styled from '@emotion/styled' - // https://github.com/threepointone/glamor // import { styled } from 'glamor/styled' - // https://github.com/rtsao/styletron // import { styled } from "styletron-react"; + // import { styled } from 'linaria/react'; styled: true, - // https://github.com/typestyle/typestyle // import { style } from "typestyle"; style: true, - // https://github.com/4Catalyzer/astroturf - // import { css } from 'astroturf'; - // https://github.com/bashmish/lit-css + // import { StyleSheet, css } from 'aphrodite'; + // import styled, { css } from 'astroturf'; // import { css } from 'lit-css'; - // https://github.com/threepointone/glamor // import { css } from 'glamor' + // require('css-light').css({color: 'red'}); + // import { css } from 'linaria'; css: true, - // https://github.com/emotion-js/emotion + // import { StyleSheet, css } from 'aphrodite'; + // import { AppRegistry, StyleSheet, Text, View } from 'react-native'; + StyleSheet: isStyleSheetCreate, + + // import styled, { css } from 'astroturf'; + astroturf: true, + + // require('csjs')`css`; + csjs: true, + + // require('cssobj')({color: 'red'}) + cssobj: true, + + // require('electron-css')({color: 'red'}) + "electron-css": true, + // import styled from "react-emotion"; - emotion: true, "react-emotion": true, + + // import styled from 'preact-emotion' "preact-emotion": true, + // https://github.com/streamich/freestyler + freestyler: true, + // https://github.com/paypal/glamorous glamorous: true, - // https://github.com/js-next/react-style - "react-style": true, + // https://github.com/irom-io/i-css + // "i-css": (i, nameSpace) => nameSpace[i + 1] === "addStyles" && nameSpace[i + 2] === "wrapper", + + // https://github.com/j2css/j2c + j2c: expectAdjacentSibling(["inline", "sheet"]), - // https://github.com/casesandberg/reactcss + // var styles = StyleSheet.create({color: 'red'}) + "react-inline": isStyleSheetCreate, + "react-style": isStyleSheetCreate, + + // import reactCSS from 'reactcss' reactcss: true, - // https://github.com/styled-components/styled-components + // const StyledButton = injectSheet(styles)(Button) + "react-jss": true, + + // import styled from 'styled-components'; "styled-components": true, - // https://github.com/rtsao/styletron - // import {styled} from "styletron-react"; // import {withStyle} from "styletron-react"; - "styletron-react": true, + "styletron-react": expectAdjacentSibling(["withStyle"]), + + "styling": true, + + // const rule = superstyle({ color: 'blue' }) + "superstyle": true, }; const plugins = [ @@ -71,38 +97,45 @@ const plugins = [ "optionalCatchBinding", ]; -function getSourceType (filename) { - if (filename && /\.m[tj]sx?$/.test(filename)) { - return "module"; - } - try { - return loadOptions({ - filename, - }).sourceType; - } catch (ex) { - // - } +function expectAdjacentSibling (names) { + return (i, nameSpace) => ( + names.some(name => nameSpace[i + 1] === name) + ); } -function getOptions (opts) { +function loadBabelOpts (opts) { const filename = opts.from && opts.from.replace(/\?.*$/, ""); - - return { + opts = { sourceFilename: filename, - sourceType: getSourceType(filename) || "unambiguous", + sourceType: filename && /\.m[tj]sx?$/.test(filename) ? "module" : "unambiguous", plugins, allowImportExportEverywhere: true, allowAwaitOutsideFunction: true, allowReturnOutsideFunction: true, allowSuperOutsideMethod: true, }; + let fileOpts; + try { + fileOpts = filename && loadOptions({ + filename, + }); + } catch (ex) { + // + } + for (const key in fileOpts) { + if (Array.isArray(fileOpts[key]) && !fileOpts[key].length) { + continue; + } + opts[key] = fileOpts[key]; + } + return opts; } function literalParser (source, opts, styles) { let ast; try { ast = parse(source, { - parserOpts: getOptions(opts), + parserOpts: loadBabelOpts(opts), }); } catch (ex) { // console.error(ex); @@ -113,6 +146,7 @@ function literalParser (source, opts, styles) { const variableDeclarator = new Map(); let objLiteral = new Set(); let tplLiteral = new Set(); + const tplCallee = new Set(); const jobs = []; function addObjectJob (path) { @@ -200,7 +234,18 @@ function literalParser (source, opts, styles) { } function isStylePath (path) { - return getNameSpace(path, []).some(name => name && supports[name]); + return getNameSpace(path, []).some(function (name) { + let result = name && (supports[name] || opts.syntax.config[name]); + switch (typeof result) { + case "function": { + result = result.apply(this, Array.prototype.slice.call(arguments, 1)); + } + // eslint-disable-next-line no-fallthrough + case "boolean": { + return result; + } + } + }); } const visitor = { @@ -215,7 +260,7 @@ function literalParser (source, opts, styles) { }); }, JSXAttribute: (path) => { - if (supports[path.node.name.name]) { + if (/^(?:css|style)$/.test(path.node.name.name)) { addObjectJob(path.get("value.expression")); } }, @@ -259,7 +304,7 @@ function literalParser (source, opts, styles) { break; } while (currPath); }); - } else if (isStylePath(path.get("callee"))) { + } else if (!tplCallee.has(callee) && isStylePath(path.get("callee"))) { path.get("arguments").forEach((arg) => { addObjectJob(arg.isFunction() ? arg.get("body") : arg); }); @@ -268,6 +313,9 @@ function literalParser (source, opts, styles) { TaggedTemplateExpression: (path) => { if (isStylePath(path.get("tag"))) { tplLiteral.add(path.node.quasi); + if (path.node.tag.callee) { + tplCallee.add(path.node.tag.callee); + } } }, }; diff --git a/package.json b/package.json index 49167e8..8a65c4b 100644 --- a/package.json +++ b/package.json @@ -43,7 +43,7 @@ "debug": "npm run mocha -- --inspect-brk" }, "dependencies": { - "@babel/core": "^7.1.2" + "@babel/core": ">=7.1.0" }, "peerDependencies": { "postcss": ">=5.0.0", diff --git a/test/fixtures/styled-opts.mjs b/test/fixtures/styled-opts.mjs new file mode 100644 index 0000000..cc4b5d0 --- /dev/null +++ b/test/fixtures/styled-opts.mjs @@ -0,0 +1,9 @@ +import styled from "astroturf"; +import Footer from "footer"; + +const Button = styled(Footer, { allowAs: true })` + position: relative; + display: flex; +`; + +export default Button; diff --git a/test/fixtures/styled-opts.mjs.json b/test/fixtures/styled-opts.mjs.json new file mode 100644 index 0000000..df4a4aa --- /dev/null +++ b/test/fixtures/styled-opts.mjs.json @@ -0,0 +1,81 @@ +{ + "raws": {}, + "type": "root", + "nodes": [ + { + "raws": { + "semicolon": true, + "after": "\n" + }, + "type": "root", + "nodes": [ + { + "raws": { + "before": "\n\t", + "between": ": " + }, + "type": "decl", + "source": { + "start": { + "line": 5, + "column": 2 + }, + "input": { + "file": "styled-opts.mjs" + }, + "end": { + "line": 5, + "column": 20 + } + }, + "prop": "position", + "value": "relative" + }, + { + "raws": { + "before": "\n\t", + "between": ": " + }, + "type": "decl", + "source": { + "start": { + "line": 6, + "column": 2 + }, + "input": { + "file": "styled-opts.mjs" + }, + "end": { + "line": 6, + "column": 15 + } + }, + "prop": "display", + "value": "flex" + } + ], + "source": { + "input": { + "file": "styled-opts.mjs" + }, + "start": { + "line": 4, + "column": 50 + }, + "inline": false, + "lang": "css", + "syntax": {} + } + } + ], + "source": { + "input": { + "file": "styled-opts.mjs" + }, + "start": { + "line": 1, + "column": 1 + }, + "lang": "jsx" + } +} diff --git a/test/supports.js b/test/supports.js index 3a9ba25..a35271e 100644 --- a/test/supports.js +++ b/test/supports.js @@ -28,17 +28,18 @@ function clean (node) { describe("should support for each CSS in JS package", () => { [ + "emotion-10.jsx", + "glamorous.jsx", "interpolation-content.mjs", "jsx.jsx", - "emotion-10.jsx", + "lit-css.mjs", "react-emotion.jsx", "react-native.mjs", - "glamorous.jsx", - "lit-css.mjs", "styled-components.js", + "styled-opts.mjs", + "tpl-decl.mjs", "tpl-in-tpl.mjs", "tpl-selector.mjs", - "tpl-decl.mjs", "tpl-special.mjs", ].forEach(file => { it(file, () => {