├── .gitignore ├── .npmignore ├── .npmrc ├── .travis.yml ├── LICENSE ├── README.md ├── camel-case.js ├── extract.js ├── get-template.js ├── index.js ├── literal.js ├── object-parse.js ├── object-parser.js ├── object-stringifier.js ├── object-stringify.js ├── object-syntax.js ├── object.js ├── package.json ├── template-parse.js ├── template-parser-helper.js ├── template-parser.js ├── template-safe-parse.js ├── template-safe-parser.js ├── template-stringifier.js ├── template-stringify.js ├── template-tokenize.js ├── test ├── camel-case.js ├── css-in-js.js ├── emotion.js ├── fixtures │ ├── emotion-10.jsx │ ├── emotion-10.jsx.json │ ├── glamorous.jsx │ ├── glamorous.jsx.json │ ├── interpolation-content.mjs │ ├── interpolation-content.mjs.json │ ├── jsx.jsx │ ├── jsx.jsx.json │ ├── lit-css.mjs │ ├── lit-css.mjs.json │ ├── material-ui.jsx │ ├── material-ui.jsx.json │ ├── multiline-arrow-function.mjs │ ├── react-emotion.jsx │ ├── react-emotion.jsx.json │ ├── react-native.mjs │ ├── react-native.mjs.json │ ├── styled-components.js │ ├── styled-components.js.json │ ├── styled-opts.mjs │ ├── styled-opts.mjs.json │ ├── styled-props.jsx │ ├── styled-props.jsx.json │ ├── toLocaleString.js │ ├── tpl-decl.mjs │ ├── tpl-decl.mjs.json │ ├── tpl-in-tpl.mjs │ ├── tpl-in-tpl.mjs.json │ ├── tpl-selector.mjs │ ├── tpl-selector.mjs.json │ ├── tpl-special.mjs │ └── tpl-special.mjs.json ├── glamorous.js ├── literals.js ├── non-style.js ├── react-native.js ├── react.js ├── styled-components.js └── supports.js └── un-camel-case.js /.gitignore: -------------------------------------------------------------------------------- 1 | # Created by https://www.gitignore.io/api/node 2 | 3 | ### Node ### 4 | # Logs 5 | logs 6 | *.log 7 | npm-debug.log* 8 | yarn-debug.log* 9 | yarn-error.log* 10 | 11 | # Runtime data 12 | pids 13 | *.pid 14 | *.seed 15 | *.pid.lock 16 | 17 | # Directory for instrumented libs generated by jscoverage/JSCover 18 | lib-cov 19 | 20 | # Coverage directory used by tools like istanbul 21 | coverage 22 | 23 | # nyc test coverage 24 | .nyc_output 25 | 26 | # Grunt intermediate storage (http://gruntjs.com/creating-plugins#storing-task-files) 27 | .grunt 28 | 29 | # Bower dependency directory (https://bower.io/) 30 | bower_components 31 | 32 | # node-waf configuration 33 | .lock-wscript 34 | 35 | # Compiled binary addons (http://nodejs.org/api/addons.html) 36 | build/Release 37 | 38 | # Dependency directories 39 | node_modules/ 40 | jspm_packages/ 41 | 42 | # Typescript v1 declaration files 43 | typings/ 44 | 45 | # Optional npm cache directory 46 | .npm 47 | 48 | # Optional eslint cache 49 | .eslintcache 50 | 51 | # Optional REPL history 52 | .node_repl_history 53 | 54 | # Output of 'npm pack' 55 | *.tgz 56 | 57 | # Yarn Integrity file 58 | .yarn-integrity 59 | 60 | # dotenv environment variables file 61 | .env 62 | 63 | 64 | 65 | # End of https://www.gitignore.io/api/node 66 | -------------------------------------------------------------------------------- /.npmignore: -------------------------------------------------------------------------------- 1 | *.log 2 | *.pid 3 | *.seed 4 | .editorconfig 5 | .eslintrc* 6 | .git 7 | .gitignore 8 | .grunt 9 | .lock-wscript 10 | .node_repl_history 11 | .nyc_output 12 | .stylelintrc* 13 | .travis.yml 14 | .vscode 15 | appveyor.yml 16 | coverage 17 | gulpfile.js 18 | lib-cov 19 | logs 20 | node_modules 21 | npm-debug.log* 22 | pids 23 | test 24 | -------------------------------------------------------------------------------- /.npmrc: -------------------------------------------------------------------------------- 1 | package-lock = false 2 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | dist: trusty 2 | sudo: false 3 | language: node_js 4 | 5 | node_js: 6 | - stable 7 | 8 | before_install: 9 | - curl -s https://raw.githubusercontent.com/gucong3000/postcss-syntaxes/HEAD/deps.js | node 10 | 11 | after_script: 12 | - codecov 13 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2018 刘祺 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | PostCSS JSX Syntax 2 | ==== 3 | 4 | [![NPM version](https://img.shields.io/npm/v/postcss-jsx.svg?style=flat-square)](https://www.npmjs.com/package/postcss-jsx) 5 | [![Travis](https://img.shields.io/travis/gucong3000/postcss-jsx.svg)](https://travis-ci.org/gucong3000/postcss-jsx) 6 | [![Travis](https://img.shields.io/travis/gucong3000/postcss-syntaxes.svg?label=integration)](https://travis-ci.org/gucong3000/postcss-syntaxes) 7 | [![Codecov](https://img.shields.io/codecov/c/github/gucong3000/postcss-jsx.svg)](https://codecov.io/gh/gucong3000/postcss-jsx) 8 | [![David](https://img.shields.io/david/gucong3000/postcss-jsx.svg)](https://david-dm.org/gucong3000/postcss-jsx) 9 | 10 | 13 | 14 | [PostCSS](https://github.com/postcss/postcss) syntax for parsing [CSS in JS](https://github.com/MicheleBertoli/css-in-js) literals: 15 | 16 | - [aphrodite](https://github.com/Khan/aphrodite) 17 | - [astroturf](https://github.com/4Catalyzer/astroturf) 18 | - [csjs](https://github.com/rtsao/csjs) 19 | - [css-light](https://github.com/streamich/css-light) 20 | - [cssobj](https://github.com/cssobj/cssobj) 21 | - [electron-css](https://github.com/azukaar/electron-css) 22 | - [emotion](https://github.com/emotion-js/emotion) 23 | - [freestyler](https://github.com/streamich/freestyler) 24 | - [glamor](https://github.com/threepointone/glamor) 25 | - [glamorous](https://github.com/paypal/glamorous) 26 | - [j2c](https://github.com/j2css/j2c) 27 | - [linaria](https://github.com/callstack/linaria) 28 | - [lit-css](https://github.com/bashmish/lit-css) 29 | - [react-native](https://github.com/necolas/react-native-web) 30 | - [react-style](https://github.com/js-next/react-style) 31 | - [reactcss](https://github.com/casesandberg/reactcss) 32 | - [styled-components](https://github.com/styled-components/styled-components) 33 | - [styletron-react](https://github.com/rtsao/styletron) 34 | - [styling](https://github.com/andreypopp/styling) 35 | - [typestyle](https://github.com/typestyle/typestyle) 36 | 37 | ## Getting Started 38 | 39 | First thing's first, install the module: 40 | 41 | ``` 42 | npm install postcss-syntax postcss-jsx --save-dev 43 | ``` 44 | 45 | ## Use Cases 46 | 47 | ```js 48 | const postcss = require('postcss'); 49 | const stylelint = require('stylelint'); 50 | const syntax = require('postcss-syntax'); 51 | postcss([stylelint({ fix: true })]).process(source, { syntax: syntax }).then(function (result) { 52 | // An alias for the result.css property. Use it with syntaxes that generate non-CSS output. 53 | result.content 54 | }); 55 | ``` 56 | 57 | input: 58 | ```javascript 59 | import glm from 'glamorous'; 60 | const Component1 = glm.a({ 61 | flexDirectionn: 'row', 62 | display: 'inline-block', 63 | color: '#fff', 64 | }); 65 | ``` 66 | 67 | output: 68 | ```javascript 69 | import glm from 'glamorous'; 70 | const Component1 = glm.a({ 71 | color: '#fff', 72 | display: 'inline-block', 73 | flexDirectionn: 'row', 74 | }); 75 | ``` 76 | 77 | ## Advanced Use Cases 78 | 79 | Add support for more `css-in-js` package: 80 | ```js 81 | const syntax = require('postcss-syntax')({ 82 | "i-css": (index, namespace) => namespace[index + 1] === "addStyles", 83 | "styled-components": true, 84 | }); 85 | ``` 86 | 87 | See: [postcss-syntax](https://github.com/gucong3000/postcss-syntax) 88 | 89 | ## Style Transformations 90 | 91 | The main use case of this plugin is to apply PostCSS transformations to CSS code in template literals & styles as object literals. 92 | -------------------------------------------------------------------------------- /camel-case.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | function camelCase (str) { 3 | return str.replace(/[\w-]+/g, (s) => ( 4 | /^-?[a-z]+(?:-[a-z]+)+$/.test(s) 5 | ? s.replace( 6 | /^-(ms|moz|khtml|epub|(\w+-?)*webkit)(?=-)/i, 7 | "$1" 8 | ).replace( 9 | /-\w/g, 10 | s => ( 11 | s[1].toUpperCase() 12 | ) 13 | ) 14 | : s 15 | )); 16 | } 17 | 18 | module.exports = camelCase; 19 | -------------------------------------------------------------------------------- /extract.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | const { 3 | parse, 4 | types, 5 | traverse, 6 | loadOptions, 7 | } = require("@babel/core"); 8 | const getTemplate = require("./get-template"); 9 | const loadSyntax = require("postcss-syntax/load-syntax"); 10 | 11 | const isStyleSheetCreate = expectAdjacentSibling(["create"]); 12 | const supports = { 13 | // import styled from '@emotion/styled' 14 | // import { styled } from 'glamor/styled' 15 | // import { styled } from "styletron-react"; 16 | // import { styled } from 'linaria/react'; 17 | // import { styled } from '@material-ui/styles' 18 | styled: true, 19 | 20 | // import { style } from "typestyle"; 21 | style: true, 22 | 23 | // import { StyleSheet, css } from 'aphrodite'; 24 | // import styled, { css } from 'astroturf'; 25 | // import { css } from 'lit-css'; 26 | // import { css } from 'glamor' 27 | // require('css-light').css({color: 'red'}); 28 | // import { css } from 'linaria'; 29 | css: true, 30 | 31 | // import { StyleSheet, css } from 'aphrodite'; 32 | // import { AppRegistry, StyleSheet, Text, View } from 'react-native'; 33 | StyleSheet: isStyleSheetCreate, 34 | 35 | // import styled, { css } from 'astroturf'; 36 | astroturf: true, 37 | 38 | // require('csjs')`css`; 39 | csjs: true, 40 | 41 | // require('cssobj')({color: 'red'}) 42 | cssobj: true, 43 | 44 | // require('electron-css')({color: 'red'}) 45 | "electron-css": true, 46 | 47 | // import styled from "react-emotion"; 48 | "react-emotion": true, 49 | 50 | // import styled from 'preact-emotion' 51 | "preact-emotion": true, 52 | 53 | // https://github.com/streamich/freestyler 54 | freestyler: true, 55 | 56 | // https://github.com/paypal/glamorous 57 | glamorous: true, 58 | 59 | // https://github.com/irom-io/i-css 60 | // "i-css": (i, nameSpace) => nameSpace[i + 1] === "addStyles" && nameSpace[i + 2] === "wrapper", 61 | 62 | // https://github.com/j2css/j2c 63 | j2c: expectAdjacentSibling(["inline", "sheet"]), 64 | 65 | // var styles = StyleSheet.create({color: 'red'}) 66 | "react-inline": isStyleSheetCreate, 67 | "react-style": isStyleSheetCreate, 68 | 69 | // import reactCSS from 'reactcss' 70 | reactcss: true, 71 | 72 | // const StyledButton = injectSheet(styles)(Button) 73 | "react-jss": true, 74 | 75 | // import styled from 'styled-components'; 76 | "styled-components": true, 77 | 78 | // import {withStyle} from "styletron-react"; 79 | "styletron-react": expectAdjacentSibling(["withStyle"]), 80 | 81 | "styling": true, 82 | 83 | // const rule = superstyle({ color: 'blue' }) 84 | "superstyle": true, 85 | 86 | // import { makeStyles } from '@material-ui/styles' 87 | 'styles': expectAdjacentSibling(["makeStyles"]), 88 | }; 89 | 90 | const plugins = [ 91 | "jsx", 92 | "typescript", 93 | "objectRestSpread", 94 | ["decorators", { "decoratorsBeforeExport": false }], 95 | "classProperties", 96 | "exportExtensions", 97 | "asyncGenerators", 98 | "functionBind", 99 | "functionSent", 100 | "dynamicImport", 101 | "optionalCatchBinding", 102 | ]; 103 | 104 | function expectAdjacentSibling (names) { 105 | return (i, nameSpace) => ( 106 | names.some(name => nameSpace[i + 1] === name) 107 | ); 108 | } 109 | 110 | function loadBabelOpts (opts) { 111 | const filename = opts.from && opts.from.replace(/\?.*$/, ""); 112 | opts = { 113 | filename, 114 | parserOpts: { 115 | plugins, 116 | sourceFilename: filename, 117 | sourceType: filename && /\.m[tj]sx?$/.test(filename) ? "module" : "unambiguous", 118 | allowImportExportEverywhere: true, 119 | allowAwaitOutsideFunction: true, 120 | allowReturnOutsideFunction: true, 121 | allowSuperOutsideMethod: true, 122 | }, 123 | }; 124 | let fileOpts; 125 | try { 126 | fileOpts = filename && loadOptions({ 127 | filename, 128 | }); 129 | } catch (ex) { 130 | // 131 | } 132 | for (const key in fileOpts) { 133 | if (Array.isArray(fileOpts[key]) && !fileOpts[key].length) { 134 | continue; 135 | } 136 | // because some options need to be passed to parser also 137 | opts[key] = fileOpts[key]; 138 | opts.parserOpts[key] = fileOpts[key]; 139 | } 140 | return opts; 141 | } 142 | 143 | function literalParser (source, opts, styles) { 144 | let ast; 145 | try { 146 | ast = parse(source, loadBabelOpts(opts)); 147 | } catch (ex) { 148 | // console.error(ex); 149 | return styles || []; 150 | } 151 | 152 | const specifiers = new Map(); 153 | const variableDeclarator = new Map(); 154 | let objLiteral = new Set(); 155 | let tplLiteral = new Set(); 156 | const tplCallee = new Set(); 157 | const jobs = []; 158 | 159 | function addObjectJob (path) { 160 | jobs.push(() => { 161 | addObjectValue(path); 162 | }); 163 | } 164 | 165 | function addObjectValue (path) { 166 | if (path.isIdentifier()) { 167 | const identifier = path.scope.getBindingIdentifier(path.node.name); 168 | if (identifier) { 169 | path = variableDeclarator.get(identifier); 170 | if (path) { 171 | variableDeclarator.delete(identifier); 172 | path.forEach(addObjectExpression); 173 | } 174 | } 175 | } else { 176 | addObjectExpression(path); 177 | } 178 | } 179 | 180 | function addObjectExpression (path) { 181 | if (path.isObjectExpression()) { 182 | path.get("properties").forEach(prop => { 183 | if (prop.isSpreadElement()) { 184 | addObjectValue(prop.get("argument")); 185 | } 186 | }); 187 | objLiteral.add(path.node); 188 | return path; 189 | } 190 | } 191 | 192 | function setSpecifier (id, nameSpace) { 193 | nameSpace.unshift.apply( 194 | nameSpace, 195 | nameSpace.shift().replace(/^\W+/, "").split(/[/\\]+/g) 196 | ); 197 | 198 | if (types.isIdentifier(id)) { 199 | specifiers.set(id.name, nameSpace); 200 | specifiers.set(id, nameSpace); 201 | } else if (types.isObjectPattern(id)) { 202 | id.properties.forEach(property => { 203 | if (types.isObjectProperty(property)) { 204 | const key = property.key; 205 | nameSpace = nameSpace.concat(key.name || key.value); 206 | id = property.value; 207 | } else { 208 | id = property.argument; 209 | } 210 | setSpecifier(id, nameSpace); 211 | }); 212 | } else if (types.isArrayPattern(id)) { 213 | id.elements.forEach((element, i) => { 214 | setSpecifier(element, nameSpace.concat(String(i))); 215 | }); 216 | } 217 | } 218 | 219 | function getNameSpace (path, nameSpace) { 220 | let node = path.node; 221 | if (path.isIdentifier() || path.isJSXIdentifier()) { 222 | node = path.scope.getBindingIdentifier(node.name) || node; 223 | const specifier = specifiers.get(node) || specifiers.get(node.name); 224 | if (specifier) { 225 | nameSpace.unshift.apply(nameSpace, specifier); 226 | } else { 227 | nameSpace.unshift(node.name); 228 | } 229 | } else { 230 | [ 231 | "name", 232 | "property", 233 | "object", 234 | "callee", 235 | ].forEach(prop => { 236 | node[prop] && getNameSpace(path.get(prop), nameSpace); 237 | }); 238 | } 239 | 240 | return nameSpace; 241 | } 242 | 243 | function isStylePath (path) { 244 | return getNameSpace(path, []).some(function (name) { 245 | let result = name && ((supports.hasOwnProperty(name) && supports[name]) || (opts.syntax.config.hasOwnProperty(name) && opts.syntax.config[name])); 246 | switch (typeof result) { 247 | case "function": { 248 | result = result.apply(this, Array.prototype.slice.call(arguments, 1)); 249 | } 250 | // eslint-disable-next-line no-fallthrough 251 | case "boolean": { 252 | return result; 253 | } 254 | } 255 | }); 256 | } 257 | 258 | const visitor = { 259 | ImportDeclaration: (path) => { 260 | const moduleId = path.node.source.value; 261 | path.node.specifiers.forEach(specifier => { 262 | const nameSpace = [moduleId]; 263 | if (specifier.imported) { 264 | nameSpace.push(specifier.imported.name); 265 | } 266 | setSpecifier(specifier.local, nameSpace); 267 | }); 268 | }, 269 | JSXAttribute: (path) => { 270 | if (/^(?:css|style)$/.test(path.node.name.name)) { 271 | addObjectJob(path.get("value.expression")); 272 | } 273 | }, 274 | VariableDeclarator: (path) => { 275 | variableDeclarator.set(path.node.id, path.node.init ? [path.get("init")] : []); 276 | }, 277 | AssignmentExpression: (path) => { 278 | if (types.isIdentifier(path.node.left) && types.isObjectExpression(path.node.right)) { 279 | const identifier = path.scope.getBindingIdentifier(path.node.left.name); 280 | const variable = variableDeclarator.get(identifier); 281 | const valuePath = path.get("right"); 282 | if (variable) { 283 | variable.push(valuePath); 284 | } else { 285 | variableDeclarator.set(identifier, [valuePath]); 286 | } 287 | } 288 | }, 289 | CallExpression: (path) => { 290 | const callee = path.node.callee; 291 | if (types.isIdentifier(callee, { name: "require" }) && !path.scope.getBindingIdentifier(callee.name)) { 292 | path.node.arguments.filter(types.isStringLiteral).forEach(arg => { 293 | const moduleId = arg.value; 294 | const nameSpace = [moduleId]; 295 | let currPath = path; 296 | do { 297 | let id = currPath.parent.id; 298 | if (!id) { 299 | id = currPath.parent.left; 300 | if (id) { 301 | id = path.scope.getBindingIdentifier(id.name) || id; 302 | } else { 303 | if (types.isIdentifier(currPath.parent.property)) { 304 | nameSpace.push(currPath.parent.property.name); 305 | } 306 | currPath = currPath.parentPath; 307 | continue; 308 | } 309 | }; 310 | setSpecifier(id, nameSpace); 311 | break; 312 | } while (currPath); 313 | }); 314 | } else if (!tplCallee.has(callee) && isStylePath(path.get("callee"))) { 315 | path.get("arguments").forEach((arg) => { 316 | addObjectJob(arg.isFunction() ? arg.get("body") : arg); 317 | }); 318 | } 319 | }, 320 | TaggedTemplateExpression: (path) => { 321 | if (isStylePath(path.get("tag"))) { 322 | tplLiteral.add(path.node.quasi); 323 | if (path.node.tag.callee) { 324 | tplCallee.add(path.node.tag.callee); 325 | } 326 | } 327 | }, 328 | }; 329 | 330 | traverse(ast, visitor); 331 | jobs.forEach(job => job()); 332 | 333 | objLiteral = Array.from(objLiteral).map(endNode => { 334 | const objectSyntax = require("./object-syntax"); 335 | let startNode = endNode; 336 | if (startNode.leadingComments && startNode.leadingComments.length) { 337 | startNode = startNode.leadingComments[0]; 338 | } 339 | let startIndex = startNode.start; 340 | const before = source.slice(startNode.start - startNode.loc.start.column, startNode.start); 341 | if (/^\s+$/.test(before)) { 342 | startIndex -= before.length; 343 | } 344 | return { 345 | startIndex, 346 | endIndex: endNode.end, 347 | skipConvert: true, 348 | content: source, 349 | opts: { 350 | node: endNode, 351 | }, 352 | syntax: objectSyntax, 353 | lang: "object-literal", 354 | }; 355 | }); 356 | 357 | tplLiteral = Array.from(tplLiteral).filter(node => ( 358 | objLiteral.every(style => ( 359 | node.start > style.endIndex || node.end < style.startIndex 360 | )) 361 | )).map(node => { 362 | const quasis = node.quasis.map(node => ({ 363 | start: node.start, 364 | end: node.end, 365 | })); 366 | const style = { 367 | startIndex: quasis[0].start, 368 | endIndex: quasis[quasis.length - 1].end, 369 | content: getTemplate(node, source), 370 | }; 371 | if (node.expressions.length) { 372 | style.syntax = loadSyntax(opts, __dirname); 373 | style.lang = "template-literal"; 374 | style.opts = { 375 | quasis: quasis, 376 | }; 377 | } else { 378 | style.lang = "css"; 379 | } 380 | return style; 381 | }); 382 | 383 | return (styles || []).concat(objLiteral).concat(tplLiteral); 384 | }; 385 | 386 | module.exports = literalParser; 387 | -------------------------------------------------------------------------------- /get-template.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | function getTemplate (node, source) { 3 | return source.slice(node.quasis[0].start, node.quasis[node.quasis.length - 1].end); 4 | } 5 | module.exports = getTemplate; 6 | -------------------------------------------------------------------------------- /index.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | const extract = require("./extract"); 3 | const syntax = require("postcss-syntax/syntax")(extract, "jsx"); 4 | 5 | module.exports = syntax; 6 | -------------------------------------------------------------------------------- /literal.js: -------------------------------------------------------------------------------- 1 | 2 | "use strict"; 3 | const Node = require("postcss/lib/node"); 4 | 5 | /** 6 | * Represents a JS literal 7 | * 8 | * @extends Container 9 | * 10 | * @example 11 | * const root = postcss.parse('{}'); 12 | * const literal = root.first; 13 | * literal.type //=> 'literal' 14 | * literal.toString() //=> 'a{}' 15 | */ 16 | class Literal extends Node { 17 | constructor (defaults) { 18 | super(defaults); 19 | this.type = "literal"; 20 | } 21 | } 22 | 23 | module.exports = Literal; 24 | -------------------------------------------------------------------------------- /object-parse.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | 3 | const ObjectParser = require("./object-parser"); 4 | const Input = require("postcss/lib/input"); 5 | 6 | function objectParse (source, opts) { 7 | const input = new Input(source, opts); 8 | const parser = new ObjectParser(input); 9 | parser.parse(opts.node); 10 | return parser.root; 11 | } 12 | module.exports = objectParse; 13 | -------------------------------------------------------------------------------- /object-parser.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | const getTemplate = require("./get-template"); 3 | const ObjectLiteral = require("./object"); 4 | const camelCase = require("./camel-case"); 5 | const unCamelCase = require("./un-camel-case"); 6 | const Literal = require("./literal"); 7 | const postcss = require("postcss"); 8 | 9 | function forEach (arr, callback) { 10 | arr && arr.forEach(callback); 11 | } 12 | 13 | const replaceProp = (fn) => (value) => ( 14 | value.replace(/(\(\s*)(.*?)(\s*:)/g, (s, prefix, prop, suffix) => ( 15 | prefix + fn(prop) + suffix 16 | )) 17 | ); 18 | const camelCaseProp = replaceProp(camelCase); 19 | const unCamelCaseProp = replaceProp(unCamelCase); 20 | 21 | function defineRaws (node, prop, prefix, suffix, props) { 22 | if (!props) { 23 | props = {}; 24 | } 25 | const descriptor = { 26 | enumerable: true, 27 | get: () => ( 28 | node[prop] 29 | ), 30 | set: (value) => { 31 | node[prop] = value; 32 | }, 33 | }; 34 | 35 | if (!props.raw) { 36 | props.raw = descriptor; 37 | } else if (props.raw === "camel") { 38 | props.raw = { 39 | enumerable: true, 40 | get: () => ( 41 | camelCase(node[prop]) 42 | ), 43 | set: (value) => { 44 | node[prop] = unCamelCase(value); 45 | }, 46 | }; 47 | } 48 | 49 | props.value = descriptor; 50 | 51 | node.raws[prop] = Object.defineProperties({ 52 | prefix, 53 | suffix, 54 | }, props); 55 | } 56 | 57 | class objectParser { 58 | constructor (input) { 59 | this.input = input; 60 | } 61 | parse (node) { 62 | const root = postcss.root({ 63 | source: { 64 | input: this.input, 65 | start: node.loc.start, 66 | }, 67 | }); 68 | root.raws.node = node; 69 | const obj = new ObjectLiteral({ 70 | raws: { 71 | node, 72 | }, 73 | }); 74 | root.push(obj); 75 | this.process(node, obj); 76 | this.sort(root); 77 | this.raws(root); 78 | 79 | const startNode = root.first.raws.node; 80 | const endNode = root.last.raws.node; 81 | 82 | const start = { 83 | line: startNode.loc.start.line, 84 | }; 85 | 86 | let before = root.source.input.css.slice(startNode.start - startNode.loc.start.column, startNode.start); 87 | if (/^\s+$/.test(before)) { 88 | start.column = 1; 89 | } else { 90 | before = ""; 91 | start.column = startNode.loc.start.column; 92 | } 93 | 94 | root.first.raws.before = before; 95 | root.source.input.css = before + root.source.input.css.slice(startNode.start, endNode.end); 96 | root.source.start = start; 97 | 98 | this.root = root; 99 | } 100 | 101 | process (node, parent) { 102 | [ 103 | "leadingComments", 104 | "innerComments", 105 | "trailingComments", 106 | ].forEach(prop => { 107 | forEach(node[prop], child => { 108 | this.source(child, this.comment(child, parent)); 109 | }); 110 | }); 111 | 112 | const child = (this[node.type] || this.literal).apply(this, [node, parent]); 113 | this.source(node, child); 114 | return child; 115 | } 116 | source (node, parent) { 117 | parent.source = { 118 | input: this.input, 119 | start: node.loc.start, 120 | end: node.loc.end, 121 | }; 122 | return parent; 123 | } 124 | raws (parent, node) { 125 | const source = this.input.css; 126 | parent.nodes.forEach((child, i) => { 127 | if (i) { 128 | child.raws.before = source.slice(parent.nodes[i - 1].raws.node.end, child.raws.node.start).replace(/^\s*,+/, ""); 129 | } else if (node) { 130 | child.raws.before = source.slice(node.start, child.raws.node.start).replace(/^\s*{+/, ""); 131 | } 132 | }); 133 | if (node) { 134 | let semicolon; 135 | let after; 136 | if (parent.nodes.length) { 137 | after = source.slice(parent.last.raws.node.end, node.end).replace(/^\s*,+/, () => { 138 | semicolon = true; 139 | return ""; 140 | }); 141 | } else { 142 | after = source.slice(node.start, node.end).replace(/^\s*{/, ""); 143 | } 144 | parent.raws.after = after.replace(/}+\s*$/, ""); 145 | parent.raws.semicolon = semicolon || false; 146 | } 147 | } 148 | 149 | sort (node) { 150 | node.nodes = node.nodes.sort((a, b) => ( 151 | a.raws.node.start - b.raws.node.start 152 | )); 153 | } 154 | 155 | getNodeValue (node, wrappedValue) { 156 | const source = this.input.css; 157 | let rawValue; 158 | let cookedValue; 159 | switch (node.type) { 160 | case "Identifier": { 161 | const isCssFloat = node.name === "cssFloat"; 162 | return { 163 | prefix: "", 164 | suffix: "", 165 | raw: isCssFloat && node.name, 166 | value: isCssFloat ? "float" : node.name, 167 | }; 168 | } 169 | case "StringLiteral": { 170 | rawValue = node.extra.raw.slice(1, -1); 171 | cookedValue = node.value; 172 | break; 173 | } 174 | case "TemplateLiteral": { 175 | rawValue = getTemplate(node, source); 176 | break; 177 | } 178 | default: { 179 | rawValue = source.slice(node.start, node.end); 180 | break; 181 | } 182 | } 183 | const valueWrap = wrappedValue.split(rawValue); 184 | return { 185 | prefix: valueWrap[0], 186 | suffix: valueWrap[1], 187 | value: cookedValue || rawValue, 188 | }; 189 | } 190 | 191 | ObjectExpression (node, parent) { 192 | forEach(node.properties, child => { 193 | this.process(child, parent); 194 | }); 195 | this.sort(parent); 196 | this.raws(parent, node); 197 | return parent; 198 | } 199 | 200 | ObjectProperty (node, parent) { 201 | const source = this.input.css; 202 | let between = source.indexOf(":", node.key.end); 203 | const rawKey = source.slice(node.start, between).trimRight(); 204 | const rawValue = source.slice(between + 1, node.end).trimLeft(); 205 | between = source.slice(node.start + rawKey.length, node.end - rawValue.length); 206 | const key = this.getNodeValue(node.key, rawKey); 207 | if (node.value.type === "ObjectExpression") { 208 | let rule; 209 | if (/^@(\S+)(\s*)(.*)$/.test(key.value)) { 210 | const name = RegExp.$1; 211 | const afterName = RegExp.$2; 212 | const params = RegExp.$3; 213 | const atRule = postcss.atRule({ 214 | name: unCamelCase(name), 215 | raws: { 216 | afterName: afterName, 217 | }, 218 | nodes: [], 219 | }); 220 | defineRaws(atRule, "name", key.prefix + "@", params ? "" : key.suffix, { 221 | raw: "camel", 222 | }); 223 | if (params) { 224 | atRule.params = unCamelCaseProp(params); 225 | defineRaws(atRule, "params", "", key.suffix, { 226 | raw: { 227 | enumerable: true, 228 | get: () => ( 229 | camelCaseProp(atRule.params) 230 | ), 231 | set: (value) => { 232 | atRule.params = unCamelCaseProp(value); 233 | }, 234 | }, 235 | }); 236 | } 237 | rule = atRule; 238 | } else { 239 | // rule = this.rule(key, keyWrap, node.value, parent); 240 | rule = postcss.rule({ 241 | selector: key.value, 242 | }); 243 | defineRaws(rule, "selector", key.prefix, key.suffix); 244 | } 245 | raw(rule); 246 | this.ObjectExpression(node.value, rule); 247 | return rule; 248 | } 249 | 250 | const value = this.getNodeValue(node.value, rawValue); 251 | 252 | if (key.value[0] === "@") { 253 | const atRule = postcss.atRule({ 254 | name: unCamelCase(key.value), 255 | params: value.value, 256 | }); 257 | defineRaws(atRule, "name", key.prefix, key.suffix, { 258 | raw: "camel", 259 | }); 260 | 261 | defineRaws(atRule, "params", value.prefix, value.suffix); 262 | raw(atRule); 263 | return atRule; 264 | } else { 265 | let decl; 266 | if (key.raw) { 267 | decl = postcss.decl({ 268 | prop: key.value, 269 | value: value.value, 270 | raws: { 271 | prop: key, 272 | }, 273 | }); 274 | } else { 275 | decl = postcss.decl({ 276 | prop: unCamelCase(key.value), 277 | value: value.value, 278 | }); 279 | 280 | defineRaws(decl, "prop", key.prefix, key.suffix, { 281 | raw: "camel", 282 | }); 283 | } 284 | 285 | defineRaws(decl, "value", value.prefix, value.suffix); 286 | raw(decl); 287 | return decl; 288 | } 289 | 290 | function raw (postcssNode) { 291 | postcssNode.raws.between = between; 292 | postcssNode.raws.node = node; 293 | parent.push(postcssNode); 294 | } 295 | } 296 | 297 | literal (node, parent) { 298 | const literal = new Literal({ 299 | text: this.input.css.slice(node.start, node.end), 300 | raws: { 301 | node, 302 | }, 303 | }); 304 | parent.push(literal); 305 | return literal; 306 | } 307 | 308 | comment (node, parent) { 309 | if (!parent.nodes || (node.start < parent.raws.node.start && parent.type !== "root" && parent.parent)) { 310 | return this.comment(node, parent.parent); 311 | } 312 | const text = node.value.match(/^(\s*)((?:\S[\s\S]*?)?)(\s*)$/); 313 | const comment = postcss.comment({ 314 | text: text[2], 315 | raws: { 316 | node, 317 | left: text[1], 318 | right: text[3], 319 | inline: node.type === "CommentLine", 320 | }, 321 | }); 322 | 323 | parent.push(comment); 324 | return comment; 325 | } 326 | } 327 | module.exports = objectParser; 328 | -------------------------------------------------------------------------------- /object-stringifier.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | const Stringifier = require("postcss/lib/stringifier"); 3 | const camelCase = require("./camel-case"); 4 | 5 | class ObjectStringifier extends Stringifier { 6 | object (node, semicolon) { 7 | this.builder("{", node, "start"); 8 | 9 | let after; 10 | if (node.nodes && node.nodes.length) { 11 | this.body(node); 12 | after = this.raw(node, "after"); 13 | } else { 14 | after = this.raw(node, "after", "emptyBody"); 15 | } 16 | 17 | if (after) this.builder(after); 18 | this.builder("}", node, "end"); 19 | } 20 | literal (node, semicolon) { 21 | this.builder(node.text + (semicolon ? "," : ""), node); 22 | } 23 | decl (node, semicolon) { 24 | let prop = this.rawValue(node, "prop"); 25 | if (prop === "float") { 26 | prop = "cssFloat"; 27 | } 28 | let string = prop; 29 | 30 | const isObjectShorthand = node.raws.node && node.raws.node.shorthand; 31 | if (!isObjectShorthand) { 32 | const between = this.raw(node, "between", "colon"); 33 | const value = this.rawValue(node, "value"); 34 | string += between + value; 35 | } 36 | 37 | if (semicolon) string += ","; 38 | this.builder(string, node); 39 | } 40 | rule (node, semicolon) { 41 | this.block(node, this.rawValue(node, "selector"), semicolon); 42 | } 43 | atrule (node, semicolon) { 44 | const name = this.rawValue(node, "name"); 45 | const params = this.rawValue(node, "params"); 46 | if (node.nodes) { 47 | let string; 48 | if (params) { 49 | const afterName = this.raw(node, "afterName"); 50 | string = name + afterName + params; 51 | } else { 52 | string = name; 53 | } 54 | this.block(node, string, semicolon); 55 | } else { 56 | const between = this.raw(node, "between", "colon"); 57 | let string = name + between + params; 58 | if (semicolon) string += ","; 59 | this.builder(string, node); 60 | } 61 | } 62 | block (node, start, semicolon) { 63 | super.block(node, start); 64 | if (semicolon) { 65 | this.builder(",", node); 66 | } 67 | } 68 | comment (node) { 69 | const left = this.raw(node, "left", "commentLeft"); 70 | const right = this.raw(node, "right", "commentRight"); 71 | 72 | if (node.raws.inline) { 73 | const text = node.raws.text || node.text; 74 | this.builder("//" + left + text + right, node); 75 | } else { 76 | this.builder("/*" + left + node.text + right + "*/", node); 77 | } 78 | } 79 | raw (node, own, detect) { 80 | let value = super.raw(node, own, detect); 81 | if ((own === "between" || (own === "afterName" && node.type === "atrule" && !node.nodes)) && !/:/.test(value)) { 82 | value = ":" + value; 83 | } else if (own === "before" && /^(decl|rule)$/.test(node.type)) { 84 | value = value.replace(/\S+$/, ""); 85 | } 86 | return value; 87 | } 88 | rawValue (node, prop) { 89 | const raw = node.raws[prop]; 90 | if (raw) { 91 | const descriptor = Object.getOwnPropertyDescriptor(raw, "raw"); 92 | if (descriptor && descriptor.get) { 93 | return raw.prefix + raw.raw + raw.suffix; 94 | } 95 | } 96 | 97 | let value = super.rawValue(node, prop); 98 | if (value == null) { 99 | return value; 100 | } 101 | if (/^(prop|selector)$/i.test(prop)) { 102 | value = camelCase(value); 103 | if (node.raws.before && /(\S+)$/.test(node.raws.before)) { 104 | value = RegExp.$1 + value; 105 | } else if (value && !/\W/.test(value)) { 106 | return value; 107 | } 108 | } else if (node.type === "atrule") { 109 | if (prop === "name") { 110 | value = "@" + value; 111 | } else if (node.nodes) { 112 | return; 113 | } 114 | if (node.nodes) { 115 | value += this.raw(node, "afterName"); 116 | value += super.rawValue(node, "params"); 117 | } 118 | } 119 | value = JSON.stringify(value); 120 | return value; 121 | } 122 | }; 123 | 124 | module.exports = ObjectStringifier; 125 | -------------------------------------------------------------------------------- /object-stringify.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | const ObjectStringifier = require("./object-stringifier"); 3 | 4 | module.exports = function objectStringify (node, builder) { 5 | const str = new ObjectStringifier(builder); 6 | str.stringify(node); 7 | }; 8 | -------------------------------------------------------------------------------- /object-syntax.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | const stringify = require("./object-stringify"); 3 | const parse = require("./object-parse"); 4 | 5 | const syntax = { 6 | parse, 7 | stringify, 8 | }; 9 | 10 | module.exports = syntax; 11 | -------------------------------------------------------------------------------- /object.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | const Container = require("postcss/lib/container"); 3 | 4 | /** 5 | * Represents a JS Object Literal 6 | * 7 | * @extends Container 8 | * 9 | * @example 10 | * const root = postcss.parse('{}'); 11 | * const obj = root.first; 12 | * obj.type //=> 'object' 13 | * obj.toString() //=> '{}' 14 | */ 15 | class ObjectLiteral extends Container { 16 | constructor (defaults) { 17 | super(defaults); 18 | this.type = "object"; 19 | this.nodes = []; 20 | } 21 | } 22 | 23 | module.exports = ObjectLiteral; 24 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "postcss-jsx", 3 | "version": "0.36.3", 4 | "description": "PostCSS syntax for parsing CSS in JS literals", 5 | "repository": { 6 | "type": "git", 7 | "url": "git+https://github.com/gucong3000/postcss-jsx.git" 8 | }, 9 | "keywords": [ 10 | "postcss", 11 | "syntax", 12 | "emotion", 13 | "aphrodite", 14 | "glamor", 15 | "glamorous", 16 | "react-native", 17 | "react-style", 18 | "reactcss", 19 | "styled-components", 20 | "styletron-react", 21 | "typestyle", 22 | "css-in-js", 23 | "css" 24 | ], 25 | "author": "gucong3000", 26 | "license": "MIT", 27 | "bugs": { 28 | "url": "https://github.com/gucong3000/postcss-jsx/issues" 29 | }, 30 | "homepage": "https://github.com/gucong3000/postcss-jsx#readme", 31 | "nyc": { 32 | "reporter": [ 33 | "lcov", 34 | "text" 35 | ], 36 | "all": true, 37 | "cache": true, 38 | "check-coverage": true 39 | }, 40 | "scripts": { 41 | "mocha": "mocha --no-timeouts", 42 | "test": "nyc npm run mocha", 43 | "debug": "npm run mocha -- --inspect-brk" 44 | }, 45 | "dependencies": { 46 | "@babel/core": ">=7.2.2" 47 | }, 48 | "peerDependencies": { 49 | "postcss": ">=5.0.0", 50 | "postcss-syntax": ">=0.36.0" 51 | }, 52 | "devDependencies": { 53 | "autoprefixer": "^9.4.4", 54 | "chai": "^4.2.0", 55 | "codecov": "^3.1.0", 56 | "json5": "^2.1.0", 57 | "mocha": "^5.2.0", 58 | "nyc": "^13.1.0", 59 | "postcss": "^7.0.7", 60 | "postcss-parser-tests": "^6.3.1", 61 | "postcss-safe-parser": "^4.0.1", 62 | "postcss-syntax": ">=0.36.0" 63 | } 64 | } 65 | -------------------------------------------------------------------------------- /template-parse.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | 3 | const TemplateParser = require("./template-parser"); 4 | const Input = require("postcss/lib/input"); 5 | 6 | function templateParse (css, opts) { 7 | const input = new Input(css, opts); 8 | input.quasis = opts.quasis; 9 | const parser = new TemplateParser(input); 10 | parser.parse(); 11 | 12 | return parser.root; 13 | } 14 | module.exports = templateParse; 15 | -------------------------------------------------------------------------------- /template-parser-helper.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | const Literal = require("./literal"); 3 | const isLiteral = token => token[0] === "word" && /^\$\{[\s\S]*\}$/.test(token[1]); 4 | function literal (start) { 5 | if (!isLiteral(start)) { 6 | return; 7 | } 8 | const tokens = []; 9 | let hasWord; 10 | let type; 11 | let token; 12 | while ((token = this.tokenizer.nextToken())) { 13 | tokens.push(token); 14 | type = token[0]; 15 | if (type.length === 1) { 16 | break; 17 | } else if (type === "word") { 18 | hasWord = true; 19 | } 20 | } 21 | 22 | while (tokens.length) { 23 | this.tokenizer.back(tokens.pop()); 24 | } 25 | 26 | if (type === "{" || (type === ":" && !hasWord)) { 27 | return; 28 | } 29 | 30 | const node = new Literal({ 31 | text: start[1], 32 | }); 33 | 34 | this.init(node, start[2], start[3]); 35 | 36 | return node; 37 | } 38 | 39 | function freeSemicolon (token) { 40 | this.spaces += token[1]; 41 | const nodes = this.current.nodes; 42 | const prev = nodes && nodes[nodes.length - 1]; 43 | if (prev && /^(rule|literal)$/.test(prev.type) && !prev.raws.ownSemicolon) { 44 | prev.raws.ownSemicolon = this.spaces; 45 | this.spaces = ""; 46 | } 47 | } 48 | 49 | module.exports = { 50 | freeSemicolon: freeSemicolon, 51 | literal: literal, 52 | }; 53 | -------------------------------------------------------------------------------- /template-parser.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | const Parser = require("postcss/lib/parser"); 3 | const templateTokenize = require("./template-tokenize"); 4 | const helper = require("./template-parser-helper"); 5 | 6 | class TemplateParser extends Parser { 7 | createTokenizer () { 8 | this.tokenizer = templateTokenize(this.input); 9 | } 10 | other () { 11 | const args = arguments; 12 | return helper.literal.apply(this, args) || super.other.apply(this, args); 13 | } 14 | freeSemicolon () { 15 | return helper.freeSemicolon.apply(this, arguments); 16 | } 17 | } 18 | module.exports = TemplateParser; 19 | -------------------------------------------------------------------------------- /template-safe-parse.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | 3 | const TemplateSafeParser = require("./template-safe-parser"); 4 | const Input = require("postcss/lib/input"); 5 | 6 | function templateSafeParse (css, opts) { 7 | const input = new Input(css, opts); 8 | input.quasis = opts.quasis; 9 | const parser = new TemplateSafeParser(input); 10 | parser.parse(); 11 | 12 | return parser.root; 13 | } 14 | module.exports = templateSafeParse; 15 | -------------------------------------------------------------------------------- /template-safe-parser.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | const SafeParser = require("postcss-safe-parser/lib/safe-parser"); 3 | const templateTokenize = require("./template-tokenize"); 4 | const helper = require("./template-parser-helper"); 5 | 6 | class TemplateSafeParser extends SafeParser { 7 | createTokenizer () { 8 | this.tokenizer = templateTokenize(this.input, { ignoreErrors: true }); 9 | } 10 | other () { 11 | const args = arguments; 12 | return helper.literal.apply(this, args) || super.other.apply(this, args); 13 | } 14 | freeSemicolon () { 15 | return helper.freeSemicolon.apply(this, arguments); 16 | } 17 | } 18 | module.exports = TemplateSafeParser; 19 | -------------------------------------------------------------------------------- /template-stringifier.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | const Stringifier = require("postcss/lib/stringifier"); 3 | 4 | class TemplateStringifier extends Stringifier { 5 | literal (node) { 6 | this.builder(node.text, node); 7 | if (node.raws.ownSemicolon) { 8 | this.builder(node.raws.ownSemicolon, node, "end"); 9 | } 10 | } 11 | }; 12 | 13 | module.exports = TemplateStringifier; 14 | -------------------------------------------------------------------------------- /template-stringify.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | const TemplateStringifier = require("./template-stringifier"); 3 | 4 | module.exports = function TemplateStringify (node, builder) { 5 | const str = new TemplateStringifier(builder); 6 | str.stringify(node); 7 | }; 8 | -------------------------------------------------------------------------------- /template-tokenize.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | const tokenize = require("postcss/lib/tokenize"); 3 | 4 | function templateTokenize (input) { 5 | let pos = input.quasis[0].start; 6 | const quasis = input.quasis.filter(quasi => quasi.start !== quasi.end); 7 | const tokenizer = tokenize.apply(this, arguments); 8 | 9 | function tokenInExpressions (token, returned) { 10 | const start = pos; 11 | pos += token[1].length; 12 | if (!quasis.some(quasi => start >= quasi.start && pos <= quasi.end) || (returned.length && token[0] === returned[0][0])) { 13 | return true; 14 | } else if (returned.length) { 15 | back(token); 16 | } 17 | } 18 | 19 | function back (token) { 20 | pos -= token[1].length; 21 | return tokenizer.back.apply(tokenizer, arguments); 22 | } 23 | 24 | function nextToken () { 25 | const args = arguments; 26 | const returned = []; 27 | let token; 28 | let line; 29 | let column; 30 | 31 | while ( 32 | (token = tokenizer.nextToken.apply(tokenizer, args)) && 33 | tokenInExpressions(token, returned) 34 | ) { 35 | line = token[4] || token[2] || line; 36 | column = token[5] || token[3] || column; 37 | returned.push(token); 38 | } 39 | if (returned.length) { 40 | token = [ 41 | returned[0][0], 42 | returned.map(token => token[1]).join(""), 43 | returned[0][2], 44 | returned[0][3], 45 | line, 46 | column, 47 | ]; 48 | } 49 | return token; 50 | } 51 | return Object.assign({}, tokenizer, { 52 | back, 53 | nextToken, 54 | }); 55 | } 56 | 57 | module.exports = templateTokenize; 58 | -------------------------------------------------------------------------------- /test/camel-case.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | 3 | const expect = require("chai").expect; 4 | const camelCase = require("../camel-case"); 5 | const unCamelCase = require("../un-camel-case"); 6 | 7 | const data = { 8 | borderTopLeftRadius: "border-top-left-radius", 9 | backgroundImage: "background-image", 10 | xwebkitAnimation: "-xwebkit-animation", 11 | webkitAnimation: "-webkit-animation", 12 | epubAnimation: "-epub-animation", 13 | mozAnimation: "-moz-animation", 14 | msAnimation: "-ms-animation", 15 | OAnimation: "-o-animation", 16 | XAnimation: "-x-animation", 17 | webkitApp: "-webkit-app", 18 | onChange: "on-change", 19 | OnChange: "-on-change", 20 | overflowWrap: "overflow-wrap", 21 | overflowX: "overflow-x", 22 | zIndex: "z-index", 23 | "::selection": "::selection", 24 | "::mozSelection": "::-moz-selection", 25 | "::mozSelection,::selection": "::-moz-selection,::selection", 26 | "--margin-top": "--margin-top", 27 | "margin--top": "margin--top", 28 | "height: webkitCalc(2vh-20px);": "height: -webkit-calc(2vh-20px);", 29 | "calc(2vh-20px)": "calc(2vh-20px)", 30 | "calc(2vh--20px)": "calc(2vh--20px)", 31 | }; 32 | 33 | const testCases = Object.keys(data).map(prop => { 34 | return { 35 | camel: prop, 36 | unCamel: data[prop], 37 | }; 38 | }); 39 | 40 | const symbols = Array.from("@*:;\n,(){} "); 41 | 42 | describe("camelCase", () => { 43 | testCases.forEach(testCase => { 44 | it(`${testCase.unCamel} => ${testCase.camel}`, () => { 45 | expect(camelCase(testCase.unCamel)).to.equal(testCase.camel); 46 | }); 47 | }); 48 | describe("symbols", () => { 49 | symbols.forEach(symbol => { 50 | it(JSON.stringify(symbol), () => { 51 | expect(camelCase(testCases.map(testCase => testCase.unCamel).join(symbol))).to.equal(testCases.map(testCase => testCase.camel).join(symbol)); 52 | }); 53 | }); 54 | }); 55 | }); 56 | 57 | describe("unCamelCase", () => { 58 | testCases.forEach(testCase => { 59 | it(`${testCase.camel} => ${testCase.unCamel}`, () => { 60 | expect(unCamelCase(testCase.camel)).to.equal(testCase.unCamel); 61 | }); 62 | }); 63 | describe("symbols", () => { 64 | symbols.forEach(symbol => { 65 | it(JSON.stringify(symbol), () => { 66 | expect(unCamelCase(testCases.map(testCase => testCase.camel).join(symbol))).to.equal(testCases.map(testCase => testCase.unCamel).join(symbol)); 67 | }); 68 | }); 69 | }); 70 | }); 71 | -------------------------------------------------------------------------------- /test/css-in-js.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | 3 | const expect = require("chai").expect; 4 | const postcss = require("postcss"); 5 | const syntax = require("../"); 6 | const autoprefixer = require("autoprefixer"); 7 | const cases = require("postcss-parser-tests"); 8 | const JSON5 = require("json5"); 9 | const objectStringify = require("../object-stringify"); 10 | 11 | describe("CSS in JS", () => { 12 | it("basic js", () => { 13 | const document = syntax.parse("x().y(z => {});", { 14 | from: "/fixtures/basic.js", 15 | }); 16 | expect(document.nodes).to.lengthOf(0); 17 | }); 18 | it("glamorous", () => { 19 | const code = ` 20 | import glm from 'glamorous'; 21 | const Component1 = glm.a({ 22 | "::placeholder": { 23 | color: "gray", 24 | }, 25 | }); 26 | `; 27 | const out = ` 28 | import glm from 'glamorous'; 29 | const Component1 = glm.a({ 30 | "::webkitInputPlaceholder": { 31 | color: "gray", 32 | }, 33 | "::placeholder": { 34 | color: "gray", 35 | }, 36 | }); 37 | `; 38 | return postcss([ 39 | autoprefixer({ 40 | overrideBrowserslist: ["Chrome > 10"], 41 | }), 42 | ]).process( 43 | code, 44 | { 45 | syntax, 46 | from: "/fixtures/glamorous-prefix.jsx", 47 | } 48 | ).then(result => { 49 | expect(result.content).equal(out); 50 | }); 51 | }); 52 | 53 | describe("setter for object literals", () => { 54 | it("decl.raws.prop.raw & decl.raws.value.raw", () => { 55 | const decl = syntax.parse(` 56 | import glm from 'glamorous'; 57 | const Component1 = glm.a({ 58 | borderRadius: '5px' 59 | }); 60 | `, { 61 | from: "/fixtures/glamorous-atRule.jsx", 62 | }).first.first.first; 63 | decl.raws.prop.raw = "WebkitBorderRadius"; 64 | expect(decl.prop).to.equal("-webkit-border-radius"); 65 | decl.raws.value.raw = "15px"; 66 | expect(decl.value).to.equal("15px"); 67 | }); 68 | it("atRule.raws.params.raw", () => { 69 | const atRule = syntax.parse(` 70 | import glm from 'glamorous'; 71 | const Component1 = glm.a({ 72 | '@media (maxWidth: 500px)': { 73 | borderRadius: '5px' 74 | } 75 | }); 76 | `, { 77 | from: "/fixtures/glamorous-atRule.jsx", 78 | }).first.first.first; 79 | atRule.raws.params.raw = "(minWidth: ' + minWidth + ')"; 80 | expect(atRule.params).to.equal("(min-width: ' + minWidth + ')"); 81 | }); 82 | }); 83 | 84 | it("empty object literals", () => { 85 | const code = ` 86 | import glm from 'glamorous'; 87 | const Component1 = glm.a({ 88 | }); 89 | `; 90 | const root = syntax.parse(code, { 91 | from: "/fixtures/glamorous-empty-object-literals.jsx", 92 | }); 93 | 94 | expect(root.toString()).to.equal(code); 95 | 96 | root.first.first.raws.after = ""; 97 | expect(root.toString()).to.equal(` 98 | import glm from 'glamorous'; 99 | const Component1 = glm.a({}); 100 | `); 101 | }); 102 | 103 | it("float", () => { 104 | const code = ` 105 | import glm from 'glamorous'; 106 | const Component1 = glm.a({ 107 | cssFloat: "left", 108 | }); 109 | `; 110 | 111 | const root = syntax.parse(code, { 112 | from: "/fixtures/glamorous-float.jsx", 113 | }); 114 | expect(root.first.first.first).to.haveOwnProperty("prop", "float"); 115 | 116 | expect(root.toString()).to.equal(` 117 | import glm from 'glamorous'; 118 | const Component1 = glm.a({ 119 | cssFloat: "left", 120 | }); 121 | `); 122 | 123 | root.first.first.nodes = [ 124 | postcss.decl({ 125 | prop: "float", 126 | value: "right", 127 | raws: { 128 | before: root.first.first.first.raws.before, 129 | }, 130 | }), 131 | ]; 132 | 133 | expect(root.toString()).to.equal(` 134 | import glm from 'glamorous'; 135 | const Component1 = glm.a({ 136 | cssFloat: "right", 137 | }); 138 | `); 139 | }); 140 | 141 | describe("objectify for css", () => { 142 | cases.each((name, css) => { 143 | if (name === "bom.css") return; 144 | if (name === "custom-properties.css") return; 145 | 146 | it("objectStringifier " + name, () => { 147 | const root = postcss.parse(css); 148 | const jsSource = root.toString(objectStringify).trim(); 149 | const jsonSource = "{\n" + jsSource.replace(/,$/, "").replace(/[\s;]+$/gm, "") + "\n}"; 150 | expect(JSON5.parse(jsonSource)).be.ok; 151 | }); 152 | }); 153 | }); 154 | 155 | it("incomplete code", () => { 156 | const filename = "fixtures/incomplete- react-native.mjs"; 157 | const code = [ 158 | `StyleSheet.create({ 159 | box: { padding: 10 }, 160 | text: { fontWeight: "bold" }, 161 | });`, 162 | "styled.div`a{display: block}`", 163 | ].join("\n"); 164 | 165 | const document = syntax.parse(code, { 166 | from: filename, 167 | }); 168 | expect(document.nodes).to.have.lengthOf(2); 169 | }); 170 | }); 171 | -------------------------------------------------------------------------------- /test/emotion.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | const expect = require("chai").expect; 3 | const syntax = require("../"); 4 | const fs = require("fs"); 5 | 6 | describe("javascript tests", () => { 7 | it("react-emotion", () => { 8 | const filename = require.resolve("./fixtures/react-emotion.jsx"); 9 | let code = fs.readFileSync(filename); 10 | 11 | const document = syntax.parse(code, { 12 | from: filename, 13 | }); 14 | 15 | code = code.toString(); 16 | 17 | expect(document.toString()).to.equal(code); 18 | expect(document.nodes).to.lengthOf(4); 19 | 20 | document.nodes.forEach(root => { 21 | expect(root.last.toString()).to.be.a("string"); 22 | expect(root.source).to.haveOwnProperty("input"); 23 | 24 | expect(code).to.includes(root.source.input.css); 25 | expect(root.source.input.css.length).lessThan(code.length); 26 | expect(root.source).to.haveOwnProperty("start").to.haveOwnProperty("line").to.greaterThan(1); 27 | 28 | root.walk(node => { 29 | expect(node).to.haveOwnProperty("source"); 30 | 31 | expect(node.source).to.haveOwnProperty("input").to.haveOwnProperty("css").equal(root.source.input.css); 32 | 33 | expect(node.source).to.haveOwnProperty("start").to.haveOwnProperty("line"); 34 | expect(node.source).to.haveOwnProperty("end").to.haveOwnProperty("line"); 35 | }); 36 | }); 37 | }); 38 | 39 | it("emotion-10", () => { 40 | const filename = require.resolve("./fixtures/emotion-10.jsx"); 41 | let code = fs.readFileSync(filename); 42 | 43 | const document = syntax.parse(code, { 44 | from: filename, 45 | }); 46 | 47 | code = code.toString(); 48 | 49 | expect(document.toString()).to.equal(code); 50 | expect(document.nodes).to.lengthOf(6); 51 | 52 | document.nodes.forEach(root => { 53 | expect(root.last.toString()).to.be.a("string"); 54 | expect(root.source).to.haveOwnProperty("input"); 55 | 56 | expect(code).to.includes(root.source.input.css); 57 | expect(root.source.input.css.length).lessThan(code.length); 58 | expect(root.source).to.haveOwnProperty("start").to.haveOwnProperty("line").to.greaterThan(1); 59 | 60 | root.walk(node => { 61 | expect(node).to.haveOwnProperty("source"); 62 | 63 | expect(node.source).to.haveOwnProperty("input").to.haveOwnProperty("css").equal(root.source.input.css); 64 | 65 | expect(node.source).to.haveOwnProperty("start").to.haveOwnProperty("line"); 66 | expect(node.source).to.haveOwnProperty("end").to.haveOwnProperty("line"); 67 | }); 68 | }); 69 | }); 70 | }); 71 | -------------------------------------------------------------------------------- /test/fixtures/emotion-10.jsx: -------------------------------------------------------------------------------- 1 | /* global render */ 2 | /** @jsx jsx */ 3 | import { css } from "@emotion/core"; 4 | import styled from "@emotion/styled"; 5 | 6 | const SomeComponent = styled.div` 7 | display: flex; 8 | background-color: ${props => props.color}; 9 | `; 10 | 11 | const AnotherComponent = styled.h1( 12 | { 13 | color: "hotpink", 14 | }, 15 | props => ({ flex: props.flex }) 16 | ); 17 | 18 | render( 19 | 20 | 21 | 24 | Some text. 25 | 26 | 29 | Some other text. 30 | 31 | 32 | 33 | ); 34 | const app = document.getElementById("root"); 35 | const myStyle = css` 36 | color: rebeccapurple; 37 | `; 38 | app.classList.add(myStyle); 39 | 40 | export default { 41 | SomeComponent, 42 | AnotherComponent, 43 | }; 44 | -------------------------------------------------------------------------------- /test/fixtures/emotion-10.jsx.json: -------------------------------------------------------------------------------- 1 | { 2 | "raws": {}, 3 | "type": "root", 4 | "nodes": [ 5 | { 6 | "raws": { 7 | "semicolon": true, 8 | "after": "\n" 9 | }, 10 | "type": "root", 11 | "nodes": [ 12 | { 13 | "raws": { 14 | "before": "\n\t", 15 | "between": ": " 16 | }, 17 | "type": "decl", 18 | "source": { 19 | "start": { 20 | "line": 7, 21 | "column": 2 22 | }, 23 | "input": { 24 | "file": "emotion-10.jsx", 25 | "quasis": [ 26 | { 27 | "start": 145, 28 | "end": 181 29 | }, 30 | { 31 | "start": 204, 32 | "end": 206 33 | } 34 | ] 35 | }, 36 | "end": { 37 | "line": 7, 38 | "column": 15 39 | } 40 | }, 41 | "prop": "display", 42 | "value": "flex" 43 | }, 44 | { 45 | "raws": { 46 | "before": "\n\t", 47 | "between": ": " 48 | }, 49 | "type": "decl", 50 | "source": { 51 | "start": { 52 | "line": 8, 53 | "column": 2 54 | }, 55 | "input": { 56 | "file": "emotion-10.jsx", 57 | "quasis": [ 58 | { 59 | "start": 145, 60 | "end": 181 61 | }, 62 | { 63 | "start": 204, 64 | "end": 206 65 | } 66 | ] 67 | }, 68 | "end": { 69 | "line": 8, 70 | "column": 43 71 | } 72 | }, 73 | "prop": "background-color", 74 | "value": "${props => props.color}" 75 | } 76 | ], 77 | "source": { 78 | "input": { 79 | "file": "emotion-10.jsx", 80 | "quasis": [ 81 | { 82 | "start": 145, 83 | "end": 181 84 | }, 85 | { 86 | "start": 204, 87 | "end": 206 88 | } 89 | ] 90 | }, 91 | "start": { 92 | "line": 6, 93 | "column": 34 94 | }, 95 | "inline": false, 96 | "lang": "template-literal", 97 | "syntax": {} 98 | } 99 | }, 100 | { 101 | "raws": {}, 102 | "source": { 103 | "input": { 104 | "file": "emotion-10.jsx" 105 | }, 106 | "start": { 107 | "line": 12, 108 | "column": 1 109 | }, 110 | "inline": false, 111 | "lang": "object-literal", 112 | "syntax": {} 113 | }, 114 | "type": "root", 115 | "nodes": [ 116 | { 117 | "raws": { 118 | "after": "\n\t", 119 | "semicolon": true, 120 | "before": "\t" 121 | }, 122 | "type": "object", 123 | "nodes": [ 124 | { 125 | "raws": { 126 | "prop": { 127 | "prefix": "", 128 | "suffix": "", 129 | "raw": "color", 130 | "value": "color" 131 | }, 132 | "value": { 133 | "prefix": "\"", 134 | "suffix": "\"", 135 | "raw": "hotpink", 136 | "value": "hotpink" 137 | }, 138 | "between": ": ", 139 | "before": "\n\t\t" 140 | }, 141 | "prop": "color", 142 | "value": "hotpink", 143 | "type": "decl", 144 | "source": { 145 | "input": { 146 | "file": "emotion-10.jsx" 147 | }, 148 | "start": { 149 | "line": 13, 150 | "column": 2 151 | }, 152 | "end": { 153 | "line": 13, 154 | "column": 18 155 | } 156 | } 157 | } 158 | ], 159 | "source": { 160 | "input": { 161 | "file": "emotion-10.jsx" 162 | }, 163 | "start": { 164 | "line": 12, 165 | "column": 1 166 | }, 167 | "end": { 168 | "line": 14, 169 | "column": 2 170 | } 171 | } 172 | } 173 | ] 174 | }, 175 | { 176 | "raws": {}, 177 | "source": { 178 | "input": { 179 | "file": "emotion-10.jsx" 180 | }, 181 | "start": { 182 | "line": 15, 183 | "column": 11 184 | }, 185 | "inline": false, 186 | "lang": "object-literal", 187 | "syntax": {} 188 | }, 189 | "type": "root", 190 | "nodes": [ 191 | { 192 | "raws": { 193 | "after": " ", 194 | "semicolon": false, 195 | "before": "" 196 | }, 197 | "type": "object", 198 | "nodes": [ 199 | { 200 | "raws": { 201 | "prop": { 202 | "prefix": "", 203 | "suffix": "", 204 | "raw": "flex", 205 | "value": "flex" 206 | }, 207 | "value": { 208 | "prefix": "", 209 | "suffix": "", 210 | "raw": "props.flex", 211 | "value": "props.flex" 212 | }, 213 | "between": ": ", 214 | "before": " " 215 | }, 216 | "prop": "flex", 217 | "value": "props.flex", 218 | "type": "decl", 219 | "source": { 220 | "input": { 221 | "file": "emotion-10.jsx" 222 | }, 223 | "start": { 224 | "line": 15, 225 | "column": 13 226 | }, 227 | "end": { 228 | "line": 15, 229 | "column": 29 230 | } 231 | } 232 | } 233 | ], 234 | "source": { 235 | "input": { 236 | "file": "emotion-10.jsx" 237 | }, 238 | "start": { 239 | "line": 15, 240 | "column": 11 241 | }, 242 | "end": { 243 | "line": 15, 244 | "column": 31 245 | } 246 | } 247 | } 248 | ] 249 | }, 250 | { 251 | "raws": { 252 | "semicolon": true, 253 | "after": "\n\t\t\t" 254 | }, 255 | "type": "root", 256 | "nodes": [ 257 | { 258 | "raws": { 259 | "before": "\n\t\t\t\t", 260 | "between": ": " 261 | }, 262 | "type": "decl", 263 | "source": { 264 | "start": { 265 | "line": 22, 266 | "column": 5 267 | }, 268 | "input": { 269 | "file": "emotion-10.jsx" 270 | }, 271 | "end": { 272 | "line": 22, 273 | "column": 22 274 | } 275 | }, 276 | "prop": "color", 277 | "value": "sarahgreen" 278 | } 279 | ], 280 | "source": { 281 | "input": { 282 | "file": "emotion-10.jsx" 283 | }, 284 | "start": { 285 | "line": 21, 286 | "column": 19 287 | }, 288 | "inline": false, 289 | "lang": "css", 290 | "syntax": {} 291 | } 292 | }, 293 | { 294 | "raws": {}, 295 | "source": { 296 | "input": { 297 | "file": "emotion-10.jsx" 298 | }, 299 | "start": { 300 | "line": 26, 301 | "column": 14 302 | }, 303 | "inline": false, 304 | "lang": "object-literal", 305 | "syntax": {} 306 | }, 307 | "type": "root", 308 | "nodes": [ 309 | { 310 | "raws": { 311 | "after": "\n\t\t\t", 312 | "semicolon": true, 313 | "before": "" 314 | }, 315 | "type": "object", 316 | "nodes": [ 317 | { 318 | "raws": { 319 | "prop": { 320 | "prefix": "", 321 | "suffix": "", 322 | "raw": "color", 323 | "value": "color" 324 | }, 325 | "value": { 326 | "prefix": "\"", 327 | "suffix": "\"", 328 | "raw": "sarahgreen", 329 | "value": "sarahgreen" 330 | }, 331 | "between": ": ", 332 | "before": "\n\t\t\t\t" 333 | }, 334 | "prop": "color", 335 | "value": "sarahgreen", 336 | "type": "decl", 337 | "source": { 338 | "input": { 339 | "file": "emotion-10.jsx" 340 | }, 341 | "start": { 342 | "line": 27, 343 | "column": 4 344 | }, 345 | "end": { 346 | "line": 27, 347 | "column": 23 348 | } 349 | } 350 | } 351 | ], 352 | "source": { 353 | "input": { 354 | "file": "emotion-10.jsx" 355 | }, 356 | "start": { 357 | "line": 26, 358 | "column": 14 359 | }, 360 | "end": { 361 | "line": 28, 362 | "column": 4 363 | } 364 | } 365 | } 366 | ] 367 | }, 368 | { 369 | "raws": { 370 | "semicolon": true, 371 | "after": "\n" 372 | }, 373 | "type": "root", 374 | "nodes": [ 375 | { 376 | "raws": { 377 | "before": "\n\t", 378 | "between": ": " 379 | }, 380 | "type": "decl", 381 | "source": { 382 | "start": { 383 | "line": 36, 384 | "column": 2 385 | }, 386 | "input": { 387 | "file": "emotion-10.jsx" 388 | }, 389 | "end": { 390 | "line": 36, 391 | "column": 22 392 | } 393 | }, 394 | "prop": "color", 395 | "value": "rebeccapurple" 396 | } 397 | ], 398 | "source": { 399 | "input": { 400 | "file": "emotion-10.jsx" 401 | }, 402 | "start": { 403 | "line": 35, 404 | "column": 21 405 | }, 406 | "inline": false, 407 | "lang": "css", 408 | "syntax": {} 409 | } 410 | } 411 | ], 412 | "source": { 413 | "input": { 414 | "file": "emotion-10.jsx" 415 | }, 416 | "start": { 417 | "line": 1, 418 | "column": 1 419 | }, 420 | "lang": "jsx" 421 | } 422 | } 423 | -------------------------------------------------------------------------------- /test/fixtures/glamorous.jsx: -------------------------------------------------------------------------------- 1 | import glm from "glamorous"; 2 | 3 | const minWidth = 700; 4 | const a = 1; 5 | const Component1 = glm.a( 6 | /* start */ 7 | { 8 | // stylelint-disable-next-line 9 | "unknownProperty1": "1.8em", // must not trigger any warnings 10 | unknownProperty2: "1.8em", // must not trigger any warnings 11 | [`unknownPropertyaa${a}`]: "1.8em", // must not trigger any warnings 12 | ["unknownProperty" + 1 + "a"]: "1.8em", // must not trigger any warnings 13 | display: "inline-block", 14 | [`@media (minWidth: ${minWidth}px)`]: { 15 | color: "red", 16 | }, 17 | // unkown pseudo class selector 18 | ":focused": { 19 | backgroundColor: "red", 20 | }, 21 | "@fontFace": { 22 | "fontFamily": "diyfont", 23 | }, 24 | "@page:first": { 25 | margin: "300px", 26 | }, 27 | "@charset": "utf-8", 28 | }, 29 | // end 30 | ({ primary }) => ({ 31 | unknownProperty: "1.8em", // unknown prop 32 | ...minWidth.length, 33 | color: primary ? "#fff" : "#DA233C", 34 | }) 35 | ); 36 | 37 | const Component2 = glm(Component1, { 38 | displayName: "Component2", 39 | forwardProps: ["shouldRender"], 40 | rootEl: "div", 41 | })(props => ({ 42 | fontFamily: "Arial, Arial, sans-serif", // duplicate font-family names 43 | fontSize: props.big ? 36 : 24, 44 | })); 45 | 46 | const Component3 = glm.div({ 47 | padding: "8px 12px", 48 | ...Component2, 49 | }); 50 | 51 | export default { 52 | Component1, 53 | Component2, 54 | Component3, 55 | }; 56 | -------------------------------------------------------------------------------- /test/fixtures/interpolation-content.mjs: -------------------------------------------------------------------------------- 1 | import styled, { css } from "styled-components"; 2 | 3 | export const buttonStyles = css` 4 | display: inline-block; 5 | `; 6 | 7 | export const ButtonStyled1 = styled.button` 8 | ${buttonStyles} 9 | color: red; 10 | `; 11 | 12 | export const ButtonStyled2 = styled.button` 13 | ${buttonStyles}; 14 | color: red; 15 | `; 16 | 17 | export const ButtonStyled3 = styled.button` 18 | ; 19 | color: red; 20 | ${buttonStyles} 21 | `; 22 | 23 | export const ButtonStyled4 = styled.button` 24 | ; 25 | color: red; 26 | ${buttonStyles}; 27 | `; 28 | 29 | export const ButtonStyled5 = styled.button` 30 | ${buttonStyles 31 | } 32 | color: red; 33 | `; 34 | 35 | export const ButtonStyled6 = styled.button` 36 | ${buttonStyles 37 | }; 38 | color: red; 39 | `; 40 | 41 | export const ButtonStyled7 = styled.button` 42 | ; 43 | color: red; 44 | ${buttonStyles 45 | } 46 | `; 47 | 48 | export const ButtonStyled8 = styled.button` 49 | ; 50 | color: red; 51 | ${buttonStyles 52 | }; 53 | `; 54 | -------------------------------------------------------------------------------- /test/fixtures/interpolation-content.mjs.json: -------------------------------------------------------------------------------- 1 | { 2 | "raws": {}, 3 | "type": "root", 4 | "nodes": [ 5 | { 6 | "raws": { 7 | "semicolon": true, 8 | "after": "\n" 9 | }, 10 | "type": "root", 11 | "nodes": [ 12 | { 13 | "raws": { 14 | "before": "\n\t", 15 | "between": ": " 16 | }, 17 | "type": "decl", 18 | "source": { 19 | "start": { 20 | "line": 4, 21 | "column": 2 22 | }, 23 | "input": { 24 | "file": "interpolation-content.mjs" 25 | }, 26 | "end": { 27 | "line": 4, 28 | "column": 23 29 | } 30 | }, 31 | "prop": "display", 32 | "value": "inline-block" 33 | } 34 | ], 35 | "source": { 36 | "input": { 37 | "file": "interpolation-content.mjs" 38 | }, 39 | "start": { 40 | "line": 3, 41 | "column": 33 42 | }, 43 | "inline": false, 44 | "lang": "css", 45 | "syntax": {} 46 | } 47 | }, 48 | { 49 | "raws": { 50 | "semicolon": true, 51 | "after": "\n" 52 | }, 53 | "type": "root", 54 | "nodes": [ 55 | { 56 | "raws": { 57 | "before": "\n\t" 58 | }, 59 | "text": "${buttonStyles}", 60 | "type": "literal", 61 | "source": { 62 | "start": { 63 | "line": 8, 64 | "column": 2 65 | }, 66 | "input": { 67 | "file": "interpolation-content.mjs", 68 | "quasis": [ 69 | { 70 | "start": 154, 71 | "end": 156 72 | }, 73 | { 74 | "start": 171, 75 | "end": 185 76 | } 77 | ] 78 | } 79 | } 80 | }, 81 | { 82 | "raws": { 83 | "before": "\n\t", 84 | "between": ": " 85 | }, 86 | "type": "decl", 87 | "source": { 88 | "start": { 89 | "line": 9, 90 | "column": 2 91 | }, 92 | "input": { 93 | "file": "interpolation-content.mjs", 94 | "quasis": [ 95 | { 96 | "start": 154, 97 | "end": 156 98 | }, 99 | { 100 | "start": 171, 101 | "end": 185 102 | } 103 | ] 104 | }, 105 | "end": { 106 | "line": 9, 107 | "column": 12 108 | } 109 | }, 110 | "prop": "color", 111 | "value": "red" 112 | } 113 | ], 114 | "source": { 115 | "input": { 116 | "file": "interpolation-content.mjs", 117 | "quasis": [ 118 | { 119 | "start": 154, 120 | "end": 156 121 | }, 122 | { 123 | "start": 171, 124 | "end": 185 125 | } 126 | ] 127 | }, 128 | "start": { 129 | "line": 7, 130 | "column": 44 131 | }, 132 | "inline": false, 133 | "lang": "template-literal", 134 | "syntax": {} 135 | } 136 | }, 137 | { 138 | "raws": { 139 | "semicolon": true, 140 | "after": "\n" 141 | }, 142 | "type": "root", 143 | "nodes": [ 144 | { 145 | "raws": { 146 | "before": "\n\t", 147 | "ownSemicolon": ";" 148 | }, 149 | "text": "${buttonStyles}", 150 | "type": "literal", 151 | "source": { 152 | "start": { 153 | "line": 13, 154 | "column": 2 155 | }, 156 | "input": { 157 | "file": "interpolation-content.mjs", 158 | "quasis": [ 159 | { 160 | "start": 232, 161 | "end": 234 162 | }, 163 | { 164 | "start": 249, 165 | "end": 264 166 | } 167 | ] 168 | } 169 | } 170 | }, 171 | { 172 | "raws": { 173 | "before": "\n\t", 174 | "between": ": " 175 | }, 176 | "type": "decl", 177 | "source": { 178 | "start": { 179 | "line": 14, 180 | "column": 2 181 | }, 182 | "input": { 183 | "file": "interpolation-content.mjs", 184 | "quasis": [ 185 | { 186 | "start": 232, 187 | "end": 234 188 | }, 189 | { 190 | "start": 249, 191 | "end": 264 192 | } 193 | ] 194 | }, 195 | "end": { 196 | "line": 14, 197 | "column": 12 198 | } 199 | }, 200 | "prop": "color", 201 | "value": "red" 202 | } 203 | ], 204 | "source": { 205 | "input": { 206 | "file": "interpolation-content.mjs", 207 | "quasis": [ 208 | { 209 | "start": 232, 210 | "end": 234 211 | }, 212 | { 213 | "start": 249, 214 | "end": 264 215 | } 216 | ] 217 | }, 218 | "start": { 219 | "line": 12, 220 | "column": 44 221 | }, 222 | "inline": false, 223 | "lang": "template-literal", 224 | "syntax": {} 225 | } 226 | }, 227 | { 228 | "raws": { 229 | "semicolon": false, 230 | "after": "\n" 231 | }, 232 | "type": "root", 233 | "nodes": [ 234 | { 235 | "raws": { 236 | "before": "\n;\n\t", 237 | "between": ": " 238 | }, 239 | "type": "decl", 240 | "source": { 241 | "start": { 242 | "line": 19, 243 | "column": 2 244 | }, 245 | "input": { 246 | "file": "interpolation-content.mjs", 247 | "quasis": [ 248 | { 249 | "start": 311, 250 | "end": 328 251 | }, 252 | { 253 | "start": 343, 254 | "end": 344 255 | } 256 | ] 257 | }, 258 | "end": { 259 | "line": 19, 260 | "column": 12 261 | } 262 | }, 263 | "prop": "color", 264 | "value": "red" 265 | }, 266 | { 267 | "raws": { 268 | "before": "\n\t" 269 | }, 270 | "text": "${buttonStyles}", 271 | "type": "literal", 272 | "source": { 273 | "start": { 274 | "line": 20, 275 | "column": 2 276 | }, 277 | "input": { 278 | "file": "interpolation-content.mjs", 279 | "quasis": [ 280 | { 281 | "start": 311, 282 | "end": 328 283 | }, 284 | { 285 | "start": 343, 286 | "end": 344 287 | } 288 | ] 289 | } 290 | } 291 | } 292 | ], 293 | "source": { 294 | "input": { 295 | "file": "interpolation-content.mjs", 296 | "quasis": [ 297 | { 298 | "start": 311, 299 | "end": 328 300 | }, 301 | { 302 | "start": 343, 303 | "end": 344 304 | } 305 | ] 306 | }, 307 | "start": { 308 | "line": 17, 309 | "column": 44 310 | }, 311 | "inline": false, 312 | "lang": "template-literal", 313 | "syntax": {} 314 | } 315 | }, 316 | { 317 | "raws": { 318 | "semicolon": false, 319 | "after": "\n" 320 | }, 321 | "type": "root", 322 | "nodes": [ 323 | { 324 | "raws": { 325 | "before": "\n;\n\t", 326 | "between": ": " 327 | }, 328 | "type": "decl", 329 | "source": { 330 | "start": { 331 | "line": 25, 332 | "column": 2 333 | }, 334 | "input": { 335 | "file": "interpolation-content.mjs", 336 | "quasis": [ 337 | { 338 | "start": 391, 339 | "end": 408 340 | }, 341 | { 342 | "start": 423, 343 | "end": 425 344 | } 345 | ] 346 | }, 347 | "end": { 348 | "line": 25, 349 | "column": 12 350 | } 351 | }, 352 | "prop": "color", 353 | "value": "red" 354 | }, 355 | { 356 | "raws": { 357 | "before": "\n\t", 358 | "ownSemicolon": ";" 359 | }, 360 | "text": "${buttonStyles}", 361 | "type": "literal", 362 | "source": { 363 | "start": { 364 | "line": 26, 365 | "column": 2 366 | }, 367 | "input": { 368 | "file": "interpolation-content.mjs", 369 | "quasis": [ 370 | { 371 | "start": 391, 372 | "end": 408 373 | }, 374 | { 375 | "start": 423, 376 | "end": 425 377 | } 378 | ] 379 | } 380 | } 381 | } 382 | ], 383 | "source": { 384 | "input": { 385 | "file": "interpolation-content.mjs", 386 | "quasis": [ 387 | { 388 | "start": 391, 389 | "end": 408 390 | }, 391 | { 392 | "start": 423, 393 | "end": 425 394 | } 395 | ] 396 | }, 397 | "start": { 398 | "line": 23, 399 | "column": 44 400 | }, 401 | "inline": false, 402 | "lang": "template-literal", 403 | "syntax": {} 404 | } 405 | }, 406 | { 407 | "raws": { 408 | "semicolon": true, 409 | "after": "\n" 410 | }, 411 | "type": "root", 412 | "nodes": [ 413 | { 414 | "raws": { 415 | "before": "\n\t" 416 | }, 417 | "text": "${buttonStyles\n\t}", 418 | "type": "literal", 419 | "source": { 420 | "start": { 421 | "line": 30, 422 | "column": 2 423 | }, 424 | "input": { 425 | "file": "interpolation-content.mjs", 426 | "quasis": [ 427 | { 428 | "start": 472, 429 | "end": 474 430 | }, 431 | { 432 | "start": 491, 433 | "end": 505 434 | } 435 | ] 436 | } 437 | } 438 | }, 439 | { 440 | "raws": { 441 | "before": "\n\t", 442 | "between": ": " 443 | }, 444 | "type": "decl", 445 | "source": { 446 | "start": { 447 | "line": 32, 448 | "column": 2 449 | }, 450 | "input": { 451 | "file": "interpolation-content.mjs", 452 | "quasis": [ 453 | { 454 | "start": 472, 455 | "end": 474 456 | }, 457 | { 458 | "start": 491, 459 | "end": 505 460 | } 461 | ] 462 | }, 463 | "end": { 464 | "line": 32, 465 | "column": 12 466 | } 467 | }, 468 | "prop": "color", 469 | "value": "red" 470 | } 471 | ], 472 | "source": { 473 | "input": { 474 | "file": "interpolation-content.mjs", 475 | "quasis": [ 476 | { 477 | "start": 472, 478 | "end": 474 479 | }, 480 | { 481 | "start": 491, 482 | "end": 505 483 | } 484 | ] 485 | }, 486 | "start": { 487 | "line": 29, 488 | "column": 44 489 | }, 490 | "inline": false, 491 | "lang": "template-literal", 492 | "syntax": {} 493 | } 494 | }, 495 | { 496 | "raws": { 497 | "semicolon": true, 498 | "after": "\n" 499 | }, 500 | "type": "root", 501 | "nodes": [ 502 | { 503 | "raws": { 504 | "before": "\n\t", 505 | "ownSemicolon": ";" 506 | }, 507 | "text": "${buttonStyles\n\t}", 508 | "type": "literal", 509 | "source": { 510 | "start": { 511 | "line": 36, 512 | "column": 2 513 | }, 514 | "input": { 515 | "file": "interpolation-content.mjs", 516 | "quasis": [ 517 | { 518 | "start": 552, 519 | "end": 554 520 | }, 521 | { 522 | "start": 571, 523 | "end": 586 524 | } 525 | ] 526 | } 527 | } 528 | }, 529 | { 530 | "raws": { 531 | "before": "\n\t", 532 | "between": ": " 533 | }, 534 | "type": "decl", 535 | "source": { 536 | "start": { 537 | "line": 38, 538 | "column": 2 539 | }, 540 | "input": { 541 | "file": "interpolation-content.mjs", 542 | "quasis": [ 543 | { 544 | "start": 552, 545 | "end": 554 546 | }, 547 | { 548 | "start": 571, 549 | "end": 586 550 | } 551 | ] 552 | }, 553 | "end": { 554 | "line": 38, 555 | "column": 12 556 | } 557 | }, 558 | "prop": "color", 559 | "value": "red" 560 | } 561 | ], 562 | "source": { 563 | "input": { 564 | "file": "interpolation-content.mjs", 565 | "quasis": [ 566 | { 567 | "start": 552, 568 | "end": 554 569 | }, 570 | { 571 | "start": 571, 572 | "end": 586 573 | } 574 | ] 575 | }, 576 | "start": { 577 | "line": 35, 578 | "column": 44 579 | }, 580 | "inline": false, 581 | "lang": "template-literal", 582 | "syntax": {} 583 | } 584 | }, 585 | { 586 | "raws": { 587 | "semicolon": false, 588 | "after": "\n" 589 | }, 590 | "type": "root", 591 | "nodes": [ 592 | { 593 | "raws": { 594 | "before": "\n;\n\t", 595 | "between": ": " 596 | }, 597 | "type": "decl", 598 | "source": { 599 | "start": { 600 | "line": 43, 601 | "column": 2 602 | }, 603 | "input": { 604 | "file": "interpolation-content.mjs", 605 | "quasis": [ 606 | { 607 | "start": 633, 608 | "end": 650 609 | }, 610 | { 611 | "start": 667, 612 | "end": 668 613 | } 614 | ] 615 | }, 616 | "end": { 617 | "line": 43, 618 | "column": 12 619 | } 620 | }, 621 | "prop": "color", 622 | "value": "red" 623 | }, 624 | { 625 | "raws": { 626 | "before": "\n\t" 627 | }, 628 | "text": "${buttonStyles\n\t}", 629 | "type": "literal", 630 | "source": { 631 | "start": { 632 | "line": 44, 633 | "column": 2 634 | }, 635 | "input": { 636 | "file": "interpolation-content.mjs", 637 | "quasis": [ 638 | { 639 | "start": 633, 640 | "end": 650 641 | }, 642 | { 643 | "start": 667, 644 | "end": 668 645 | } 646 | ] 647 | } 648 | } 649 | } 650 | ], 651 | "source": { 652 | "input": { 653 | "file": "interpolation-content.mjs", 654 | "quasis": [ 655 | { 656 | "start": 633, 657 | "end": 650 658 | }, 659 | { 660 | "start": 667, 661 | "end": 668 662 | } 663 | ] 664 | }, 665 | "start": { 666 | "line": 41, 667 | "column": 44 668 | }, 669 | "inline": false, 670 | "lang": "template-literal", 671 | "syntax": {} 672 | } 673 | }, 674 | { 675 | "raws": { 676 | "semicolon": false, 677 | "after": "\n" 678 | }, 679 | "type": "root", 680 | "nodes": [ 681 | { 682 | "raws": { 683 | "before": "\n;\n\t", 684 | "between": ": " 685 | }, 686 | "type": "decl", 687 | "source": { 688 | "start": { 689 | "line": 50, 690 | "column": 2 691 | }, 692 | "input": { 693 | "file": "interpolation-content.mjs", 694 | "quasis": [ 695 | { 696 | "start": 715, 697 | "end": 732 698 | }, 699 | { 700 | "start": 749, 701 | "end": 751 702 | } 703 | ] 704 | }, 705 | "end": { 706 | "line": 50, 707 | "column": 12 708 | } 709 | }, 710 | "prop": "color", 711 | "value": "red" 712 | }, 713 | { 714 | "raws": { 715 | "before": "\n\t", 716 | "ownSemicolon": ";" 717 | }, 718 | "text": "${buttonStyles\n\t}", 719 | "type": "literal", 720 | "source": { 721 | "start": { 722 | "line": 51, 723 | "column": 2 724 | }, 725 | "input": { 726 | "file": "interpolation-content.mjs", 727 | "quasis": [ 728 | { 729 | "start": 715, 730 | "end": 732 731 | }, 732 | { 733 | "start": 749, 734 | "end": 751 735 | } 736 | ] 737 | } 738 | } 739 | } 740 | ], 741 | "source": { 742 | "input": { 743 | "file": "interpolation-content.mjs", 744 | "quasis": [ 745 | { 746 | "start": 715, 747 | "end": 732 748 | }, 749 | { 750 | "start": 749, 751 | "end": 751 752 | } 753 | ] 754 | }, 755 | "start": { 756 | "line": 48, 757 | "column": 44 758 | }, 759 | "inline": false, 760 | "lang": "template-literal", 761 | "syntax": {} 762 | } 763 | } 764 | ], 765 | "source": { 766 | "input": { 767 | "file": "interpolation-content.mjs" 768 | }, 769 | "start": { 770 | "line": 1, 771 | "column": 1 772 | }, 773 | "lang": "jsx" 774 | } 775 | } 776 | -------------------------------------------------------------------------------- /test/fixtures/jsx.jsx: -------------------------------------------------------------------------------- 1 | import React from "react"; 2 | /* eslint comma-dangle: ["error", "never"] */ 3 | /* global notExist */ 4 | const imgUrl = "https://www.google.com/images/branding/googlelogo/1x/googlelogo_color_272x92dp.png"; 5 | const baseStyle = { 6 | color: "blue" 7 | }; 8 | let divStyle; 9 | require(); 10 | 11 | function HelloWorldComponent () { 12 | divStyle = { 13 | backgroundImage: `url(${imgUrl})`, 14 | ...baseStyle 15 | }; 16 | return
17 | Hello World! 18 | Hello World! 19 |
; 20 | } 21 | 22 | divStyle = { 23 | backgroundImage: `url(${imgUrl})` 24 | }; 25 | 26 | const App = props => ( 27 |
42 | ); 43 | 44 | function ObjectShorthandComponent({color}) { 45 | return
46 | } 47 | 48 | export default { 49 | HelloWorldComponent, 50 | React, 51 | App, 52 | ObjectShorthandComponent 53 | }; 54 | -------------------------------------------------------------------------------- /test/fixtures/jsx.jsx.json: -------------------------------------------------------------------------------- 1 | { 2 | "raws": {}, 3 | "type": "root", 4 | "nodes": [ 5 | { 6 | "raws": {}, 7 | "source": { 8 | "input": { 9 | "file": "jsx.jsx" 10 | }, 11 | "start": { 12 | "line": 5, 13 | "column": 18 14 | }, 15 | "inline": false, 16 | "lang": "object-literal", 17 | "syntax": {} 18 | }, 19 | "type": "root", 20 | "nodes": [ 21 | { 22 | "raws": { 23 | "after": "\n", 24 | "semicolon": false, 25 | "before": "" 26 | }, 27 | "type": "object", 28 | "nodes": [ 29 | { 30 | "raws": { 31 | "prop": { 32 | "prefix": "", 33 | "suffix": "", 34 | "raw": "color", 35 | "value": "color" 36 | }, 37 | "value": { 38 | "prefix": "\"", 39 | "suffix": "\"", 40 | "raw": "blue", 41 | "value": "blue" 42 | }, 43 | "between": ": ", 44 | "before": "\n\t" 45 | }, 46 | "prop": "color", 47 | "value": "blue", 48 | "type": "decl", 49 | "source": { 50 | "input": { 51 | "file": "jsx.jsx" 52 | }, 53 | "start": { 54 | "line": 6, 55 | "column": 1 56 | }, 57 | "end": { 58 | "line": 6, 59 | "column": 14 60 | } 61 | } 62 | } 63 | ], 64 | "source": { 65 | "input": { 66 | "file": "jsx.jsx" 67 | }, 68 | "start": { 69 | "line": 5, 70 | "column": 18 71 | }, 72 | "end": { 73 | "line": 7, 74 | "column": 1 75 | } 76 | } 77 | } 78 | ] 79 | }, 80 | { 81 | "raws": {}, 82 | "source": { 83 | "input": { 84 | "file": "jsx.jsx" 85 | }, 86 | "start": { 87 | "line": 12, 88 | "column": 12 89 | }, 90 | "inline": false, 91 | "lang": "object-literal", 92 | "syntax": {} 93 | }, 94 | "type": "root", 95 | "nodes": [ 96 | { 97 | "raws": { 98 | "after": "\n\t", 99 | "semicolon": false, 100 | "before": "" 101 | }, 102 | "type": "object", 103 | "nodes": [ 104 | { 105 | "raws": { 106 | "prop": { 107 | "prefix": "", 108 | "suffix": "", 109 | "raw": "backgroundImage", 110 | "value": "background-image" 111 | }, 112 | "value": { 113 | "prefix": "`", 114 | "suffix": "`", 115 | "raw": "url(${imgUrl})", 116 | "value": "url(${imgUrl})" 117 | }, 118 | "between": ": ", 119 | "before": "\n\t\t" 120 | }, 121 | "prop": "background-image", 122 | "value": "url(${imgUrl})", 123 | "type": "decl", 124 | "source": { 125 | "input": { 126 | "file": "jsx.jsx" 127 | }, 128 | "start": { 129 | "line": 13, 130 | "column": 2 131 | }, 132 | "end": { 133 | "line": 13, 134 | "column": 35 135 | } 136 | } 137 | }, 138 | { 139 | "raws": { 140 | "before": "\n\t\t" 141 | }, 142 | "text": "...baseStyle", 143 | "type": "literal", 144 | "source": { 145 | "input": { 146 | "file": "jsx.jsx" 147 | }, 148 | "start": { 149 | "line": 14, 150 | "column": 2 151 | }, 152 | "end": { 153 | "line": 14, 154 | "column": 14 155 | } 156 | } 157 | } 158 | ], 159 | "source": { 160 | "input": { 161 | "file": "jsx.jsx" 162 | }, 163 | "start": { 164 | "line": 12, 165 | "column": 12 166 | }, 167 | "end": { 168 | "line": 15, 169 | "column": 2 170 | } 171 | } 172 | } 173 | ] 174 | }, 175 | { 176 | "raws": {}, 177 | "source": { 178 | "input": { 179 | "file": "jsx.jsx" 180 | }, 181 | "start": { 182 | "line": 22, 183 | "column": 11 184 | }, 185 | "inline": false, 186 | "lang": "object-literal", 187 | "syntax": {} 188 | }, 189 | "type": "root", 190 | "nodes": [ 191 | { 192 | "raws": { 193 | "after": "\n", 194 | "semicolon": false, 195 | "before": "" 196 | }, 197 | "type": "object", 198 | "nodes": [ 199 | { 200 | "raws": { 201 | "prop": { 202 | "prefix": "", 203 | "suffix": "", 204 | "raw": "backgroundImage", 205 | "value": "background-image" 206 | }, 207 | "value": { 208 | "prefix": "`", 209 | "suffix": "`", 210 | "raw": "url(${imgUrl})", 211 | "value": "url(${imgUrl})" 212 | }, 213 | "between": ": ", 214 | "before": "\n\t" 215 | }, 216 | "prop": "background-image", 217 | "value": "url(${imgUrl})", 218 | "type": "decl", 219 | "source": { 220 | "input": { 221 | "file": "jsx.jsx" 222 | }, 223 | "start": { 224 | "line": 23, 225 | "column": 1 226 | }, 227 | "end": { 228 | "line": 23, 229 | "column": 34 230 | } 231 | } 232 | } 233 | ], 234 | "source": { 235 | "input": { 236 | "file": "jsx.jsx" 237 | }, 238 | "start": { 239 | "line": 22, 240 | "column": 11 241 | }, 242 | "end": { 243 | "line": 24, 244 | "column": 1 245 | } 246 | } 247 | } 248 | ] 249 | }, 250 | { 251 | "raws": {}, 252 | "source": { 253 | "input": { 254 | "file": "jsx.jsx" 255 | }, 256 | "start": { 257 | "line": 31, 258 | "column": 7 259 | }, 260 | "inline": false, 261 | "lang": "object-literal", 262 | "syntax": {} 263 | }, 264 | "type": "root", 265 | "nodes": [ 266 | { 267 | "raws": { 268 | "after": "\n\t\t", 269 | "semicolon": false, 270 | "before": "" 271 | }, 272 | "type": "object", 273 | "nodes": [ 274 | { 275 | "raws": { 276 | "prop": { 277 | "prefix": "", 278 | "suffix": "", 279 | "raw": "display", 280 | "value": "display" 281 | }, 282 | "value": { 283 | "prefix": "\"", 284 | "suffix": "\"", 285 | "raw": "flex", 286 | "value": "flex" 287 | }, 288 | "between": ": ", 289 | "before": "\n\t\t\t" 290 | }, 291 | "prop": "display", 292 | "value": "flex", 293 | "type": "decl", 294 | "source": { 295 | "input": { 296 | "file": "jsx.jsx" 297 | }, 298 | "start": { 299 | "line": 32, 300 | "column": 3 301 | }, 302 | "end": { 303 | "line": 32, 304 | "column": 18 305 | } 306 | } 307 | }, 308 | { 309 | "raws": { 310 | "prop": { 311 | "prefix": "", 312 | "suffix": "", 313 | "raw": "paddingTop", 314 | "value": "padding-top" 315 | }, 316 | "value": { 317 | "prefix": "", 318 | "suffix": "", 319 | "raw": "6", 320 | "value": "6" 321 | }, 322 | "between": ": ", 323 | "before": "\n\t\t\t" 324 | }, 325 | "prop": "padding-top", 326 | "value": "6", 327 | "type": "decl", 328 | "source": { 329 | "input": { 330 | "file": "jsx.jsx" 331 | }, 332 | "start": { 333 | "line": 33, 334 | "column": 3 335 | }, 336 | "end": { 337 | "line": 33, 338 | "column": 16 339 | } 340 | } 341 | }, 342 | { 343 | "raws": { 344 | "prop": { 345 | "prefix": "", 346 | "suffix": "", 347 | "raw": "padding", 348 | "value": "padding" 349 | }, 350 | "value": { 351 | "prefix": "\"", 352 | "suffix": "\"", 353 | "raw": "8px 12px", 354 | "value": "8px 12px" 355 | }, 356 | "between": ": ", 357 | "before": "\n\t\t\t" 358 | }, 359 | "prop": "padding", 360 | "value": "8px 12px", 361 | "type": "decl", 362 | "source": { 363 | "input": { 364 | "file": "jsx.jsx" 365 | }, 366 | "start": { 367 | "line": 34, 368 | "column": 3 369 | }, 370 | "end": { 371 | "line": 34, 372 | "column": 22 373 | } 374 | } 375 | }, 376 | { 377 | "raws": { 378 | "left": " ", 379 | "right": "", 380 | "inline": true, 381 | "before": " " 382 | }, 383 | "text": "shorthand prop override", 384 | "type": "comment", 385 | "source": { 386 | "input": { 387 | "file": "jsx.jsx" 388 | }, 389 | "start": { 390 | "line": 34, 391 | "column": 24 392 | }, 393 | "end": { 394 | "line": 34, 395 | "column": 50 396 | } 397 | } 398 | }, 399 | { 400 | "raws": { 401 | "selector": { 402 | "prefix": "\"", 403 | "suffix": "\"", 404 | "raw": ":hover", 405 | "value": ":hover" 406 | }, 407 | "between": ": ", 408 | "after": "\n\t\t\t", 409 | "semicolon": false, 410 | "before": "\n\t\t\t" 411 | }, 412 | "selector": ":hover", 413 | "type": "rule", 414 | "nodes": [ 415 | { 416 | "raws": { 417 | "prop": { 418 | "prefix": "", 419 | "suffix": "", 420 | "raw": "flexDirectionn", 421 | "value": "flex-directionn" 422 | }, 423 | "value": { 424 | "prefix": "\"", 425 | "suffix": "\"", 426 | "raw": "row", 427 | "value": "row" 428 | }, 429 | "between": ": ", 430 | "before": "\n\t\t\t\t" 431 | }, 432 | "prop": "flex-directionn", 433 | "value": "row", 434 | "type": "decl", 435 | "source": { 436 | "input": { 437 | "file": "jsx.jsx" 438 | }, 439 | "start": { 440 | "line": 36, 441 | "column": 4 442 | }, 443 | "end": { 444 | "line": 36, 445 | "column": 25 446 | } 447 | } 448 | }, 449 | { 450 | "raws": { 451 | "left": " ", 452 | "right": "", 453 | "inline": true, 454 | "before": " " 455 | }, 456 | "text": "prop error", 457 | "type": "comment", 458 | "source": { 459 | "input": { 460 | "file": "jsx.jsx" 461 | }, 462 | "start": { 463 | "line": 36, 464 | "column": 27 465 | }, 466 | "end": { 467 | "line": 36, 468 | "column": 40 469 | } 470 | } 471 | }, 472 | { 473 | "raws": { 474 | "prop": { 475 | "prefix": "", 476 | "suffix": "", 477 | "raw": "color", 478 | "value": "color" 479 | }, 480 | "value": { 481 | "prefix": "", 482 | "suffix": "", 483 | "raw": "props.color", 484 | "value": "props.color" 485 | }, 486 | "between": ": ", 487 | "before": "\n\t\t\t\t" 488 | }, 489 | "prop": "color", 490 | "value": "props.color", 491 | "type": "decl", 492 | "source": { 493 | "input": { 494 | "file": "jsx.jsx" 495 | }, 496 | "start": { 497 | "line": 37, 498 | "column": 4 499 | }, 500 | "end": { 501 | "line": 37, 502 | "column": 22 503 | } 504 | } 505 | }, 506 | { 507 | "raws": { 508 | "prop": { 509 | "prefix": "", 510 | "suffix": "", 511 | "raw": "backgroundColor", 512 | "value": "background-color" 513 | }, 514 | "value": { 515 | "prefix": "", 516 | "suffix": "", 517 | "raw": "props.big ? \"#fff\" : \"#000x\"", 518 | "value": "props.big ? \"#fff\" : \"#000x\"" 519 | }, 520 | "between": ": ", 521 | "before": "\n\t\t\t\t" 522 | }, 523 | "prop": "background-color", 524 | "value": "props.big ? \"#fff\" : \"#000x\"", 525 | "type": "decl", 526 | "source": { 527 | "input": { 528 | "file": "jsx.jsx" 529 | }, 530 | "start": { 531 | "line": 38, 532 | "column": 4 533 | }, 534 | "end": { 535 | "line": 38, 536 | "column": 49 537 | } 538 | } 539 | } 540 | ], 541 | "source": { 542 | "input": { 543 | "file": "jsx.jsx" 544 | }, 545 | "start": { 546 | "line": 35, 547 | "column": 3 548 | }, 549 | "end": { 550 | "line": 39, 551 | "column": 4 552 | } 553 | } 554 | } 555 | ], 556 | "source": { 557 | "input": { 558 | "file": "jsx.jsx" 559 | }, 560 | "start": { 561 | "line": 31, 562 | "column": 7 563 | }, 564 | "end": { 565 | "line": 40, 566 | "column": 3 567 | } 568 | } 569 | } 570 | ] 571 | }, 572 | { 573 | "raws": {}, 574 | "source": { 575 | "input": { 576 | "file": "jsx.jsx" 577 | }, 578 | "start": { 579 | "line": 45, 580 | "column": 20 581 | }, 582 | "inline": false, 583 | "lang": "object-literal", 584 | "syntax": {} 585 | }, 586 | "type": "root", 587 | "nodes": [ 588 | { 589 | "raws": { 590 | "after": "", 591 | "semicolon": false, 592 | "before": "" 593 | }, 594 | "type": "object", 595 | "nodes": [ 596 | { 597 | "raws": { 598 | "prop": { 599 | "prefix": "", 600 | "suffix": "", 601 | "raw": "color", 602 | "value": "color" 603 | }, 604 | "value": { 605 | "prefix": "", 606 | "suffix": "", 607 | "raw": "color", 608 | "value": "color" 609 | }, 610 | "between": "", 611 | "before": "" 612 | }, 613 | "prop": "color", 614 | "value": "color", 615 | "type": "decl", 616 | "source": { 617 | "input": { 618 | "file": "jsx.jsx" 619 | }, 620 | "start": { 621 | "line": 45, 622 | "column": 21 623 | }, 624 | "end": { 625 | "line": 45, 626 | "column": 26 627 | } 628 | } 629 | } 630 | ], 631 | "source": { 632 | "input": { 633 | "file": "jsx.jsx" 634 | }, 635 | "start": { 636 | "line": 45, 637 | "column": 20 638 | }, 639 | "end": { 640 | "line": 45, 641 | "column": 27 642 | } 643 | } 644 | } 645 | ] 646 | } 647 | ], 648 | "source": { 649 | "input": { 650 | "file": "jsx.jsx" 651 | }, 652 | "start": { 653 | "line": 1, 654 | "column": 1 655 | }, 656 | "lang": "jsx" 657 | } 658 | } 659 | -------------------------------------------------------------------------------- /test/fixtures/lit-css.mjs: -------------------------------------------------------------------------------- 1 | import { css } from "lit-css"; 2 | const customPropStyle = "customPropStyle"; 3 | const tableStyle = "tableStyle"; 4 | const fancyTableStyle = "fancyTableStyle"; 5 | export default css` 6 | /* @define --table-border-color */ 7 | :host { 8 | --table-border-color: green; 9 | } 10 | ${customPropStyle} ${tableStyle} ${fancyTableStyle} 11 | `; 12 | -------------------------------------------------------------------------------- /test/fixtures/lit-css.mjs.json: -------------------------------------------------------------------------------- 1 | { 2 | "raws": {}, 3 | "type": "root", 4 | "nodes": [ 5 | { 6 | "raws": { 7 | "semicolon": false, 8 | "after": "\n" 9 | }, 10 | "type": "root", 11 | "nodes": [ 12 | { 13 | "raws": { 14 | "before": "\n\t", 15 | "left": " ", 16 | "right": " " 17 | }, 18 | "type": "comment", 19 | "source": { 20 | "start": { 21 | "line": 6, 22 | "column": 2 23 | }, 24 | "input": { 25 | "file": "lit-css.mjs", 26 | "quasis": [ 27 | { 28 | "start": 169, 29 | "end": 250 30 | }, 31 | { 32 | "start": 268, 33 | "end": 269 34 | }, 35 | { 36 | "start": 282, 37 | "end": 283 38 | }, 39 | { 40 | "start": 301, 41 | "end": 302 42 | } 43 | ] 44 | }, 45 | "end": { 46 | "line": 6, 47 | "column": 35 48 | } 49 | }, 50 | "text": "@define --table-border-color" 51 | }, 52 | { 53 | "raws": { 54 | "before": "\n\t", 55 | "between": " ", 56 | "semicolon": true, 57 | "after": "\n\t" 58 | }, 59 | "type": "rule", 60 | "nodes": [ 61 | { 62 | "raws": { 63 | "before": "\n\t\t", 64 | "between": ": " 65 | }, 66 | "type": "decl", 67 | "source": { 68 | "start": { 69 | "line": 8, 70 | "column": 3 71 | }, 72 | "input": { 73 | "file": "lit-css.mjs", 74 | "quasis": [ 75 | { 76 | "start": 169, 77 | "end": 250 78 | }, 79 | { 80 | "start": 268, 81 | "end": 269 82 | }, 83 | { 84 | "start": 282, 85 | "end": 283 86 | }, 87 | { 88 | "start": 301, 89 | "end": 302 90 | } 91 | ] 92 | }, 93 | "end": { 94 | "line": 8, 95 | "column": 30 96 | } 97 | }, 98 | "prop": "--table-border-color", 99 | "value": "green" 100 | } 101 | ], 102 | "source": { 103 | "start": { 104 | "line": 7, 105 | "column": 2 106 | }, 107 | "input": { 108 | "file": "lit-css.mjs", 109 | "quasis": [ 110 | { 111 | "start": 169, 112 | "end": 250 113 | }, 114 | { 115 | "start": 268, 116 | "end": 269 117 | }, 118 | { 119 | "start": 282, 120 | "end": 283 121 | }, 122 | { 123 | "start": 301, 124 | "end": 302 125 | } 126 | ] 127 | }, 128 | "end": { 129 | "line": 9, 130 | "column": 2 131 | } 132 | }, 133 | "selector": ":host" 134 | }, 135 | { 136 | "raws": { 137 | "before": "\n\t" 138 | }, 139 | "text": "${customPropStyle}", 140 | "type": "literal", 141 | "source": { 142 | "start": { 143 | "line": 10, 144 | "column": 2 145 | }, 146 | "input": { 147 | "file": "lit-css.mjs", 148 | "quasis": [ 149 | { 150 | "start": 169, 151 | "end": 250 152 | }, 153 | { 154 | "start": 268, 155 | "end": 269 156 | }, 157 | { 158 | "start": 282, 159 | "end": 283 160 | }, 161 | { 162 | "start": 301, 163 | "end": 302 164 | } 165 | ] 166 | } 167 | } 168 | }, 169 | { 170 | "raws": { 171 | "before": " " 172 | }, 173 | "text": "${tableStyle}", 174 | "type": "literal", 175 | "source": { 176 | "start": { 177 | "line": 10, 178 | "column": 21 179 | }, 180 | "input": { 181 | "file": "lit-css.mjs", 182 | "quasis": [ 183 | { 184 | "start": 169, 185 | "end": 250 186 | }, 187 | { 188 | "start": 268, 189 | "end": 269 190 | }, 191 | { 192 | "start": 282, 193 | "end": 283 194 | }, 195 | { 196 | "start": 301, 197 | "end": 302 198 | } 199 | ] 200 | } 201 | } 202 | }, 203 | { 204 | "raws": { 205 | "before": " " 206 | }, 207 | "text": "${fancyTableStyle}", 208 | "type": "literal", 209 | "source": { 210 | "start": { 211 | "line": 10, 212 | "column": 35 213 | }, 214 | "input": { 215 | "file": "lit-css.mjs", 216 | "quasis": [ 217 | { 218 | "start": 169, 219 | "end": 250 220 | }, 221 | { 222 | "start": 268, 223 | "end": 269 224 | }, 225 | { 226 | "start": 282, 227 | "end": 283 228 | }, 229 | { 230 | "start": 301, 231 | "end": 302 232 | } 233 | ] 234 | } 235 | } 236 | } 237 | ], 238 | "source": { 239 | "input": { 240 | "file": "lit-css.mjs", 241 | "quasis": [ 242 | { 243 | "start": 169, 244 | "end": 250 245 | }, 246 | { 247 | "start": 268, 248 | "end": 269 249 | }, 250 | { 251 | "start": 282, 252 | "end": 283 253 | }, 254 | { 255 | "start": 301, 256 | "end": 302 257 | } 258 | ] 259 | }, 260 | "start": { 261 | "line": 5, 262 | "column": 20 263 | }, 264 | "inline": false, 265 | "lang": "template-literal", 266 | "syntax": {} 267 | } 268 | } 269 | ], 270 | "source": { 271 | "input": { 272 | "file": "lit-css.mjs" 273 | }, 274 | "start": { 275 | "line": 1, 276 | "column": 1 277 | }, 278 | "lang": "jsx" 279 | } 280 | } 281 | -------------------------------------------------------------------------------- /test/fixtures/material-ui.jsx: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import { makeStyles } from '@material-ui/styles'; 3 | import Button from '@material-ui/core/Button'; 4 | 5 | const useStyles = makeStyles({ 6 | root: { 7 | background: 'linear-gradient(45deg, #FE6B8B 30%, #FF8E53 90%)', 8 | border: 0, 9 | borderRadius: 3, 10 | boxShadow: '0 3px 5px 2px rgba(255, 105, 135, .3)', 11 | color: 'white', 12 | height: 48, 13 | padding: '0 30px', 14 | }, 15 | }); 16 | 17 | export default function Hook() { 18 | const classes = useStyles(); 19 | return ; 20 | } 21 | -------------------------------------------------------------------------------- /test/fixtures/material-ui.jsx.json: -------------------------------------------------------------------------------- 1 | { 2 | "raws": {}, 3 | "type": "root", 4 | "nodes": [ 5 | { 6 | "raws": {}, 7 | "source": { 8 | "input": { 9 | "file": "material-ui.jsx" 10 | }, 11 | "start": { 12 | "line": 5, 13 | "column": 29 14 | }, 15 | "inline": false, 16 | "lang": "object-literal", 17 | "syntax": {} 18 | }, 19 | "type": "root", 20 | "nodes": [ 21 | { 22 | "raws": { 23 | "after": "\n", 24 | "semicolon": true, 25 | "before": "" 26 | }, 27 | "type": "object", 28 | "nodes": [ 29 | { 30 | "raws": { 31 | "selector": { 32 | "prefix": "", 33 | "suffix": "", 34 | "raw": "root", 35 | "value": "root" 36 | }, 37 | "between": ": ", 38 | "after": "\n ", 39 | "semicolon": true, 40 | "before": "\n " 41 | }, 42 | "selector": "root", 43 | "type": "rule", 44 | "nodes": [ 45 | { 46 | "raws": { 47 | "prop": { 48 | "prefix": "", 49 | "suffix": "", 50 | "raw": "background", 51 | "value": "background" 52 | }, 53 | "value": { 54 | "prefix": "'", 55 | "suffix": "'", 56 | "raw": "linear-gradient(45deg, #FE6B8B 30%, #FF8E53 90%)", 57 | "value": "linear-gradient(45deg, #FE6B8B 30%, #FF8E53 90%)" 58 | }, 59 | "between": ": ", 60 | "before": "\n " 61 | }, 62 | "prop": "background", 63 | "value": "linear-gradient(45deg, #FE6B8B 30%, #FF8E53 90%)", 64 | "type": "decl", 65 | "source": { 66 | "input": { 67 | "file": "material-ui.jsx" 68 | }, 69 | "start": { 70 | "line": 7, 71 | "column": 4 72 | }, 73 | "end": { 74 | "line": 7, 75 | "column": 66 76 | } 77 | } 78 | }, 79 | { 80 | "raws": { 81 | "prop": { 82 | "prefix": "", 83 | "suffix": "", 84 | "raw": "border", 85 | "value": "border" 86 | }, 87 | "value": { 88 | "prefix": "", 89 | "suffix": "", 90 | "raw": "0", 91 | "value": "0" 92 | }, 93 | "between": ": ", 94 | "before": "\n " 95 | }, 96 | "prop": "border", 97 | "value": "0", 98 | "type": "decl", 99 | "source": { 100 | "input": { 101 | "file": "material-ui.jsx" 102 | }, 103 | "start": { 104 | "line": 8, 105 | "column": 4 106 | }, 107 | "end": { 108 | "line": 8, 109 | "column": 13 110 | } 111 | } 112 | }, 113 | { 114 | "raws": { 115 | "prop": { 116 | "prefix": "", 117 | "suffix": "", 118 | "raw": "borderRadius", 119 | "value": "border-radius" 120 | }, 121 | "value": { 122 | "prefix": "", 123 | "suffix": "", 124 | "raw": "3", 125 | "value": "3" 126 | }, 127 | "between": ": ", 128 | "before": "\n " 129 | }, 130 | "prop": "border-radius", 131 | "value": "3", 132 | "type": "decl", 133 | "source": { 134 | "input": { 135 | "file": "material-ui.jsx" 136 | }, 137 | "start": { 138 | "line": 9, 139 | "column": 4 140 | }, 141 | "end": { 142 | "line": 9, 143 | "column": 19 144 | } 145 | } 146 | }, 147 | { 148 | "raws": { 149 | "prop": { 150 | "prefix": "", 151 | "suffix": "", 152 | "raw": "boxShadow", 153 | "value": "box-shadow" 154 | }, 155 | "value": { 156 | "prefix": "'", 157 | "suffix": "'", 158 | "raw": "0 3px 5px 2px rgba(255, 105, 135, .3)", 159 | "value": "0 3px 5px 2px rgba(255, 105, 135, .3)" 160 | }, 161 | "between": ": ", 162 | "before": "\n " 163 | }, 164 | "prop": "box-shadow", 165 | "value": "0 3px 5px 2px rgba(255, 105, 135, .3)", 166 | "type": "decl", 167 | "source": { 168 | "input": { 169 | "file": "material-ui.jsx" 170 | }, 171 | "start": { 172 | "line": 10, 173 | "column": 4 174 | }, 175 | "end": { 176 | "line": 10, 177 | "column": 54 178 | } 179 | } 180 | }, 181 | { 182 | "raws": { 183 | "prop": { 184 | "prefix": "", 185 | "suffix": "", 186 | "raw": "color", 187 | "value": "color" 188 | }, 189 | "value": { 190 | "prefix": "'", 191 | "suffix": "'", 192 | "raw": "white", 193 | "value": "white" 194 | }, 195 | "between": ": ", 196 | "before": "\n " 197 | }, 198 | "prop": "color", 199 | "value": "white", 200 | "type": "decl", 201 | "source": { 202 | "input": { 203 | "file": "material-ui.jsx" 204 | }, 205 | "start": { 206 | "line": 11, 207 | "column": 4 208 | }, 209 | "end": { 210 | "line": 11, 211 | "column": 18 212 | } 213 | } 214 | }, 215 | { 216 | "raws": { 217 | "prop": { 218 | "prefix": "", 219 | "suffix": "", 220 | "raw": "height", 221 | "value": "height" 222 | }, 223 | "value": { 224 | "prefix": "", 225 | "suffix": "", 226 | "raw": "48", 227 | "value": "48" 228 | }, 229 | "between": ": ", 230 | "before": "\n " 231 | }, 232 | "prop": "height", 233 | "value": "48", 234 | "type": "decl", 235 | "source": { 236 | "input": { 237 | "file": "material-ui.jsx" 238 | }, 239 | "start": { 240 | "line": 12, 241 | "column": 4 242 | }, 243 | "end": { 244 | "line": 12, 245 | "column": 14 246 | } 247 | } 248 | }, 249 | { 250 | "raws": { 251 | "prop": { 252 | "prefix": "", 253 | "suffix": "", 254 | "raw": "padding", 255 | "value": "padding" 256 | }, 257 | "value": { 258 | "prefix": "'", 259 | "suffix": "'", 260 | "raw": "0 30px", 261 | "value": "0 30px" 262 | }, 263 | "between": ": ", 264 | "before": "\n " 265 | }, 266 | "prop": "padding", 267 | "value": "0 30px", 268 | "type": "decl", 269 | "source": { 270 | "input": { 271 | "file": "material-ui.jsx" 272 | }, 273 | "start": { 274 | "line": 13, 275 | "column": 4 276 | }, 277 | "end": { 278 | "line": 13, 279 | "column": 21 280 | } 281 | } 282 | } 283 | ], 284 | "source": { 285 | "input": { 286 | "file": "material-ui.jsx" 287 | }, 288 | "start": { 289 | "line": 6, 290 | "column": 2 291 | }, 292 | "end": { 293 | "line": 14, 294 | "column": 3 295 | } 296 | } 297 | } 298 | ], 299 | "source": { 300 | "input": { 301 | "file": "material-ui.jsx" 302 | }, 303 | "start": { 304 | "line": 5, 305 | "column": 29 306 | }, 307 | "end": { 308 | "line": 15, 309 | "column": 1 310 | } 311 | } 312 | } 313 | ] 314 | } 315 | ], 316 | "source": { 317 | "input": { 318 | "file": "material-ui.jsx" 319 | }, 320 | "start": { 321 | "line": 1, 322 | "column": 1 323 | }, 324 | "lang": "jsx" 325 | } 326 | } 327 | -------------------------------------------------------------------------------- /test/fixtures/multiline-arrow-function.mjs: -------------------------------------------------------------------------------- 1 | import styled from "styled-components"; 2 | 3 | export const StatusText = styled.div` 4 | color: ${(props) => 5 | (props.status === "signed" && "red") || 6 | "blue"}; 7 | `; 8 | -------------------------------------------------------------------------------- /test/fixtures/react-emotion.jsx: -------------------------------------------------------------------------------- 1 | /* global render */ 2 | 3 | import styled, { css } from "react-emotion"; 4 | const SomeComponent = styled("div")` 5 | display: flex; 6 | background-color: ${props => props.color}; 7 | `; 8 | 9 | const AnotherComponent = styled("h1")( 10 | { 11 | color: "hotpink", 12 | }, 13 | props => ({ flex: props.flex }) 14 | ); 15 | 16 | render( 17 | 18 | 19 | Some text. 20 | 21 | 22 | ); 23 | const app = document.getElementById("root"); 24 | const myStyle = css` 25 | color: rebeccapurple; 26 | `; 27 | app.classList.add(myStyle); 28 | 29 | export default { 30 | SomeComponent, 31 | AnotherComponent, 32 | }; 33 | -------------------------------------------------------------------------------- /test/fixtures/react-emotion.jsx.json: -------------------------------------------------------------------------------- 1 | { 2 | "raws": {}, 3 | "type": "root", 4 | "nodes": [ 5 | { 6 | "raws": { 7 | "semicolon": true, 8 | "after": "\n" 9 | }, 10 | "type": "root", 11 | "nodes": [ 12 | { 13 | "raws": { 14 | "before": "\n\t", 15 | "between": ": " 16 | }, 17 | "type": "decl", 18 | "source": { 19 | "start": { 20 | "line": 5, 21 | "column": 2 22 | }, 23 | "input": { 24 | "file": "react-emotion.jsx", 25 | "quasis": [ 26 | { 27 | "start": 102, 28 | "end": 138 29 | }, 30 | { 31 | "start": 161, 32 | "end": 163 33 | } 34 | ] 35 | }, 36 | "end": { 37 | "line": 5, 38 | "column": 15 39 | } 40 | }, 41 | "prop": "display", 42 | "value": "flex" 43 | }, 44 | { 45 | "raws": { 46 | "before": "\n\t", 47 | "between": ": " 48 | }, 49 | "type": "decl", 50 | "source": { 51 | "start": { 52 | "line": 6, 53 | "column": 2 54 | }, 55 | "input": { 56 | "file": "react-emotion.jsx", 57 | "quasis": [ 58 | { 59 | "start": 102, 60 | "end": 138 61 | }, 62 | { 63 | "start": 161, 64 | "end": 163 65 | } 66 | ] 67 | }, 68 | "end": { 69 | "line": 6, 70 | "column": 43 71 | } 72 | }, 73 | "prop": "background-color", 74 | "value": "${props => props.color}" 75 | } 76 | ], 77 | "source": { 78 | "input": { 79 | "file": "react-emotion.jsx", 80 | "quasis": [ 81 | { 82 | "start": 102, 83 | "end": 138 84 | }, 85 | { 86 | "start": 161, 87 | "end": 163 88 | } 89 | ] 90 | }, 91 | "start": { 92 | "line": 4, 93 | "column": 37 94 | }, 95 | "inline": false, 96 | "lang": "template-literal", 97 | "syntax": {} 98 | } 99 | }, 100 | { 101 | "raws": {}, 102 | "source": { 103 | "input": { 104 | "file": "react-emotion.jsx" 105 | }, 106 | "start": { 107 | "line": 10, 108 | "column": 1 109 | }, 110 | "inline": false, 111 | "lang": "object-literal", 112 | "syntax": {} 113 | }, 114 | "type": "root", 115 | "nodes": [ 116 | { 117 | "raws": { 118 | "after": "\n\t", 119 | "semicolon": true, 120 | "before": "\t" 121 | }, 122 | "type": "object", 123 | "nodes": [ 124 | { 125 | "raws": { 126 | "prop": { 127 | "prefix": "", 128 | "suffix": "", 129 | "raw": "color", 130 | "value": "color" 131 | }, 132 | "value": { 133 | "prefix": "\"", 134 | "suffix": "\"", 135 | "raw": "hotpink", 136 | "value": "hotpink" 137 | }, 138 | "between": ": ", 139 | "before": "\n\t\t" 140 | }, 141 | "prop": "color", 142 | "value": "hotpink", 143 | "type": "decl", 144 | "source": { 145 | "input": { 146 | "file": "react-emotion.jsx" 147 | }, 148 | "start": { 149 | "line": 11, 150 | "column": 2 151 | }, 152 | "end": { 153 | "line": 11, 154 | "column": 18 155 | } 156 | } 157 | } 158 | ], 159 | "source": { 160 | "input": { 161 | "file": "react-emotion.jsx" 162 | }, 163 | "start": { 164 | "line": 10, 165 | "column": 1 166 | }, 167 | "end": { 168 | "line": 12, 169 | "column": 2 170 | } 171 | } 172 | } 173 | ] 174 | }, 175 | { 176 | "raws": {}, 177 | "source": { 178 | "input": { 179 | "file": "react-emotion.jsx" 180 | }, 181 | "start": { 182 | "line": 13, 183 | "column": 11 184 | }, 185 | "inline": false, 186 | "lang": "object-literal", 187 | "syntax": {} 188 | }, 189 | "type": "root", 190 | "nodes": [ 191 | { 192 | "raws": { 193 | "after": " ", 194 | "semicolon": false, 195 | "before": "" 196 | }, 197 | "type": "object", 198 | "nodes": [ 199 | { 200 | "raws": { 201 | "prop": { 202 | "prefix": "", 203 | "suffix": "", 204 | "raw": "flex", 205 | "value": "flex" 206 | }, 207 | "value": { 208 | "prefix": "", 209 | "suffix": "", 210 | "raw": "props.flex", 211 | "value": "props.flex" 212 | }, 213 | "between": ": ", 214 | "before": " " 215 | }, 216 | "prop": "flex", 217 | "value": "props.flex", 218 | "type": "decl", 219 | "source": { 220 | "input": { 221 | "file": "react-emotion.jsx" 222 | }, 223 | "start": { 224 | "line": 13, 225 | "column": 13 226 | }, 227 | "end": { 228 | "line": 13, 229 | "column": 29 230 | } 231 | } 232 | } 233 | ], 234 | "source": { 235 | "input": { 236 | "file": "react-emotion.jsx" 237 | }, 238 | "start": { 239 | "line": 13, 240 | "column": 11 241 | }, 242 | "end": { 243 | "line": 13, 244 | "column": 31 245 | } 246 | } 247 | } 248 | ] 249 | }, 250 | { 251 | "raws": { 252 | "semicolon": true, 253 | "after": "\n" 254 | }, 255 | "type": "root", 256 | "nodes": [ 257 | { 258 | "raws": { 259 | "before": "\n\t", 260 | "between": ": " 261 | }, 262 | "type": "decl", 263 | "source": { 264 | "start": { 265 | "line": 25, 266 | "column": 2 267 | }, 268 | "input": { 269 | "file": "react-emotion.jsx" 270 | }, 271 | "end": { 272 | "line": 25, 273 | "column": 22 274 | } 275 | }, 276 | "prop": "color", 277 | "value": "rebeccapurple" 278 | } 279 | ], 280 | "source": { 281 | "input": { 282 | "file": "react-emotion.jsx" 283 | }, 284 | "start": { 285 | "line": 24, 286 | "column": 21 287 | }, 288 | "inline": false, 289 | "lang": "css", 290 | "syntax": {} 291 | } 292 | } 293 | ], 294 | "source": { 295 | "input": { 296 | "file": "react-emotion.jsx" 297 | }, 298 | "start": { 299 | "line": 1, 300 | "column": 1 301 | }, 302 | "lang": "jsx" 303 | } 304 | } 305 | -------------------------------------------------------------------------------- /test/fixtures/react-native.mjs: -------------------------------------------------------------------------------- 1 | import React from "react"; 2 | import { AppRegistry, StyleSheet } from "react-native"; 3 | 4 | class App extends React.Component { 5 | render () { 6 | return ( 7 | 8 | Hello, world! 9 | 10 | ); 11 | } 12 | } 13 | 14 | const styles = StyleSheet.create({ 15 | box: { padding: 10 }, 16 | text: { fontWeight: "bold" }, 17 | }); 18 | 19 | AppRegistry.registerComponent("App", () => App); 20 | AppRegistry.runApplication("App", { rootTag: document.getElementById("react-root") }); 21 | -------------------------------------------------------------------------------- /test/fixtures/react-native.mjs.json: -------------------------------------------------------------------------------- 1 | { 2 | "raws": {}, 3 | "type": "root", 4 | "nodes": [ 5 | { 6 | "raws": {}, 7 | "source": { 8 | "input": { 9 | "file": "react-native.mjs" 10 | }, 11 | "start": { 12 | "line": 14, 13 | "column": 33 14 | }, 15 | "inline": false, 16 | "lang": "object-literal", 17 | "syntax": {} 18 | }, 19 | "type": "root", 20 | "nodes": [ 21 | { 22 | "raws": { 23 | "after": "\n", 24 | "semicolon": true, 25 | "before": "" 26 | }, 27 | "type": "object", 28 | "nodes": [ 29 | { 30 | "raws": { 31 | "selector": { 32 | "prefix": "", 33 | "suffix": "", 34 | "raw": "box", 35 | "value": "box" 36 | }, 37 | "between": ": ", 38 | "after": " ", 39 | "semicolon": false, 40 | "before": "\n\t" 41 | }, 42 | "selector": "box", 43 | "type": "rule", 44 | "nodes": [ 45 | { 46 | "raws": { 47 | "prop": { 48 | "prefix": "", 49 | "suffix": "", 50 | "raw": "padding", 51 | "value": "padding" 52 | }, 53 | "value": { 54 | "prefix": "", 55 | "suffix": "", 56 | "raw": "10", 57 | "value": "10" 58 | }, 59 | "between": ": ", 60 | "before": " " 61 | }, 62 | "prop": "padding", 63 | "value": "10", 64 | "type": "decl", 65 | "source": { 66 | "input": { 67 | "file": "react-native.mjs" 68 | }, 69 | "start": { 70 | "line": 15, 71 | "column": 8 72 | }, 73 | "end": { 74 | "line": 15, 75 | "column": 19 76 | } 77 | } 78 | } 79 | ], 80 | "source": { 81 | "input": { 82 | "file": "react-native.mjs" 83 | }, 84 | "start": { 85 | "line": 15, 86 | "column": 1 87 | }, 88 | "end": { 89 | "line": 15, 90 | "column": 21 91 | } 92 | } 93 | }, 94 | { 95 | "raws": { 96 | "selector": { 97 | "prefix": "", 98 | "suffix": "", 99 | "raw": "text", 100 | "value": "text" 101 | }, 102 | "between": ": ", 103 | "after": " ", 104 | "semicolon": false, 105 | "before": "\n\t" 106 | }, 107 | "selector": "text", 108 | "type": "rule", 109 | "nodes": [ 110 | { 111 | "raws": { 112 | "prop": { 113 | "prefix": "", 114 | "suffix": "", 115 | "raw": "fontWeight", 116 | "value": "font-weight" 117 | }, 118 | "value": { 119 | "prefix": "\"", 120 | "suffix": "\"", 121 | "raw": "bold", 122 | "value": "bold" 123 | }, 124 | "between": ": ", 125 | "before": " " 126 | }, 127 | "prop": "font-weight", 128 | "value": "bold", 129 | "type": "decl", 130 | "source": { 131 | "input": { 132 | "file": "react-native.mjs" 133 | }, 134 | "start": { 135 | "line": 16, 136 | "column": 9 137 | }, 138 | "end": { 139 | "line": 16, 140 | "column": 27 141 | } 142 | } 143 | } 144 | ], 145 | "source": { 146 | "input": { 147 | "file": "react-native.mjs" 148 | }, 149 | "start": { 150 | "line": 16, 151 | "column": 1 152 | }, 153 | "end": { 154 | "line": 16, 155 | "column": 29 156 | } 157 | } 158 | } 159 | ], 160 | "source": { 161 | "input": { 162 | "file": "react-native.mjs" 163 | }, 164 | "start": { 165 | "line": 14, 166 | "column": 33 167 | }, 168 | "end": { 169 | "line": 17, 170 | "column": 1 171 | } 172 | } 173 | } 174 | ] 175 | } 176 | ], 177 | "source": { 178 | "input": { 179 | "file": "react-native.mjs" 180 | }, 181 | "start": { 182 | "line": 1, 183 | "column": 1 184 | }, 185 | "lang": "jsx" 186 | } 187 | } 188 | -------------------------------------------------------------------------------- /test/fixtures/styled-components.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | const styled = require("styled-components"); 3 | const Button = styled.button` 4 | /* Adapt the colours based on primary prop */ 5 | background: ${props => props.primary ? "palevioletred" : "white"}; 6 | color: ${props => props.primary ? "white" : "palevioletred"}; 7 | 8 | font-size: 1em; 9 | margin: 1em; 10 | padding: 0.25em 1em; 11 | border: 2px solid palevioletred; 12 | border-radius: 3px; 13 | `; 14 | require("styled"); 15 | const StyledCounter = require("styled-components").div; 16 | StyledCounter(require("styled-components").div.b); 17 | module.exports = Button; 18 | -------------------------------------------------------------------------------- /test/fixtures/styled-components.js.json: -------------------------------------------------------------------------------- 1 | { 2 | "raws": {}, 3 | "type": "root", 4 | "nodes": [ 5 | { 6 | "raws": { 7 | "semicolon": true, 8 | "after": "\n" 9 | }, 10 | "type": "root", 11 | "nodes": [ 12 | { 13 | "raws": { 14 | "before": "\n", 15 | "left": " ", 16 | "right": " " 17 | }, 18 | "type": "comment", 19 | "source": { 20 | "start": { 21 | "line": 4, 22 | "column": 1 23 | }, 24 | "input": { 25 | "file": "styled-components.js", 26 | "quasis": [ 27 | { 28 | "start": 88, 29 | "end": 147 30 | }, 31 | { 32 | "start": 200, 33 | "end": 209 34 | }, 35 | { 36 | "start": 262, 37 | "end": 368 38 | } 39 | ] 40 | }, 41 | "end": { 42 | "line": 4, 43 | "column": 45 44 | } 45 | }, 46 | "text": "Adapt the colours based on primary prop" 47 | }, 48 | { 49 | "raws": { 50 | "before": "\n", 51 | "between": ": " 52 | }, 53 | "type": "decl", 54 | "source": { 55 | "start": { 56 | "line": 5, 57 | "column": 1 58 | }, 59 | "input": { 60 | "file": "styled-components.js", 61 | "quasis": [ 62 | { 63 | "start": 88, 64 | "end": 147 65 | }, 66 | { 67 | "start": 200, 68 | "end": 209 69 | }, 70 | { 71 | "start": 262, 72 | "end": 368 73 | } 74 | ] 75 | }, 76 | "end": { 77 | "line": 5, 78 | "column": 66 79 | } 80 | }, 81 | "prop": "background", 82 | "value": "${props => props.primary ? \"palevioletred\" : \"white\"}" 83 | }, 84 | { 85 | "raws": { 86 | "before": "\n", 87 | "between": ": " 88 | }, 89 | "type": "decl", 90 | "source": { 91 | "start": { 92 | "line": 6, 93 | "column": 1 94 | }, 95 | "input": { 96 | "file": "styled-components.js", 97 | "quasis": [ 98 | { 99 | "start": 88, 100 | "end": 147 101 | }, 102 | { 103 | "start": 200, 104 | "end": 209 105 | }, 106 | { 107 | "start": 262, 108 | "end": 368 109 | } 110 | ] 111 | }, 112 | "end": { 113 | "line": 6, 114 | "column": 61 115 | } 116 | }, 117 | "prop": "color", 118 | "value": "${props => props.primary ? \"white\" : \"palevioletred\"}" 119 | }, 120 | { 121 | "raws": { 122 | "before": "\n\n", 123 | "between": ": " 124 | }, 125 | "type": "decl", 126 | "source": { 127 | "start": { 128 | "line": 8, 129 | "column": 1 130 | }, 131 | "input": { 132 | "file": "styled-components.js", 133 | "quasis": [ 134 | { 135 | "start": 88, 136 | "end": 147 137 | }, 138 | { 139 | "start": 200, 140 | "end": 209 141 | }, 142 | { 143 | "start": 262, 144 | "end": 368 145 | } 146 | ] 147 | }, 148 | "end": { 149 | "line": 8, 150 | "column": 15 151 | } 152 | }, 153 | "prop": "font-size", 154 | "value": "1em" 155 | }, 156 | { 157 | "raws": { 158 | "before": "\n", 159 | "between": ": " 160 | }, 161 | "type": "decl", 162 | "source": { 163 | "start": { 164 | "line": 9, 165 | "column": 1 166 | }, 167 | "input": { 168 | "file": "styled-components.js", 169 | "quasis": [ 170 | { 171 | "start": 88, 172 | "end": 147 173 | }, 174 | { 175 | "start": 200, 176 | "end": 209 177 | }, 178 | { 179 | "start": 262, 180 | "end": 368 181 | } 182 | ] 183 | }, 184 | "end": { 185 | "line": 9, 186 | "column": 12 187 | } 188 | }, 189 | "prop": "margin", 190 | "value": "1em" 191 | }, 192 | { 193 | "raws": { 194 | "before": "\n", 195 | "between": ": " 196 | }, 197 | "type": "decl", 198 | "source": { 199 | "start": { 200 | "line": 10, 201 | "column": 1 202 | }, 203 | "input": { 204 | "file": "styled-components.js", 205 | "quasis": [ 206 | { 207 | "start": 88, 208 | "end": 147 209 | }, 210 | { 211 | "start": 200, 212 | "end": 209 213 | }, 214 | { 215 | "start": 262, 216 | "end": 368 217 | } 218 | ] 219 | }, 220 | "end": { 221 | "line": 10, 222 | "column": 20 223 | } 224 | }, 225 | "prop": "padding", 226 | "value": "0.25em 1em" 227 | }, 228 | { 229 | "raws": { 230 | "before": "\n", 231 | "between": ": " 232 | }, 233 | "type": "decl", 234 | "source": { 235 | "start": { 236 | "line": 11, 237 | "column": 1 238 | }, 239 | "input": { 240 | "file": "styled-components.js", 241 | "quasis": [ 242 | { 243 | "start": 88, 244 | "end": 147 245 | }, 246 | { 247 | "start": 200, 248 | "end": 209 249 | }, 250 | { 251 | "start": 262, 252 | "end": 368 253 | } 254 | ] 255 | }, 256 | "end": { 257 | "line": 11, 258 | "column": 32 259 | } 260 | }, 261 | "prop": "border", 262 | "value": "2px solid palevioletred" 263 | }, 264 | { 265 | "raws": { 266 | "before": "\n", 267 | "between": ": " 268 | }, 269 | "type": "decl", 270 | "source": { 271 | "start": { 272 | "line": 12, 273 | "column": 1 274 | }, 275 | "input": { 276 | "file": "styled-components.js", 277 | "quasis": [ 278 | { 279 | "start": 88, 280 | "end": 147 281 | }, 282 | { 283 | "start": 200, 284 | "end": 209 285 | }, 286 | { 287 | "start": 262, 288 | "end": 368 289 | } 290 | ] 291 | }, 292 | "end": { 293 | "line": 12, 294 | "column": 19 295 | } 296 | }, 297 | "prop": "border-radius", 298 | "value": "3px" 299 | } 300 | ], 301 | "source": { 302 | "input": { 303 | "file": "styled-components.js", 304 | "quasis": [ 305 | { 306 | "start": 88, 307 | "end": 147 308 | }, 309 | { 310 | "start": 200, 311 | "end": 209 312 | }, 313 | { 314 | "start": 262, 315 | "end": 368 316 | } 317 | ] 318 | }, 319 | "start": { 320 | "line": 3, 321 | "column": 30 322 | }, 323 | "inline": false, 324 | "lang": "template-literal", 325 | "syntax": {} 326 | } 327 | } 328 | ], 329 | "source": { 330 | "input": { 331 | "file": "styled-components.js" 332 | }, 333 | "start": { 334 | "line": 1, 335 | "column": 1 336 | }, 337 | "lang": "jsx" 338 | } 339 | } 340 | -------------------------------------------------------------------------------- /test/fixtures/styled-opts.mjs: -------------------------------------------------------------------------------- 1 | import styled from "astroturf"; 2 | import Footer from "footer"; 3 | 4 | const Button = styled(Footer, { allowAs: true })` 5 | position: relative; 6 | display: flex; 7 | `; 8 | 9 | export default Button; 10 | -------------------------------------------------------------------------------- /test/fixtures/styled-opts.mjs.json: -------------------------------------------------------------------------------- 1 | { 2 | "raws": {}, 3 | "type": "root", 4 | "nodes": [ 5 | { 6 | "raws": { 7 | "semicolon": true, 8 | "after": "\n" 9 | }, 10 | "type": "root", 11 | "nodes": [ 12 | { 13 | "raws": { 14 | "before": "\n\t", 15 | "between": ": " 16 | }, 17 | "type": "decl", 18 | "source": { 19 | "start": { 20 | "line": 5, 21 | "column": 2 22 | }, 23 | "input": { 24 | "file": "styled-opts.mjs" 25 | }, 26 | "end": { 27 | "line": 5, 28 | "column": 20 29 | } 30 | }, 31 | "prop": "position", 32 | "value": "relative" 33 | }, 34 | { 35 | "raws": { 36 | "before": "\n\t", 37 | "between": ": " 38 | }, 39 | "type": "decl", 40 | "source": { 41 | "start": { 42 | "line": 6, 43 | "column": 2 44 | }, 45 | "input": { 46 | "file": "styled-opts.mjs" 47 | }, 48 | "end": { 49 | "line": 6, 50 | "column": 15 51 | } 52 | }, 53 | "prop": "display", 54 | "value": "flex" 55 | } 56 | ], 57 | "source": { 58 | "input": { 59 | "file": "styled-opts.mjs" 60 | }, 61 | "start": { 62 | "line": 4, 63 | "column": 50 64 | }, 65 | "inline": false, 66 | "lang": "css", 67 | "syntax": {} 68 | } 69 | } 70 | ], 71 | "source": { 72 | "input": { 73 | "file": "styled-opts.mjs" 74 | }, 75 | "start": { 76 | "line": 1, 77 | "column": 1 78 | }, 79 | "lang": "jsx" 80 | } 81 | } 82 | -------------------------------------------------------------------------------- /test/fixtures/styled-props.jsx: -------------------------------------------------------------------------------- 1 | import styled from "styled-components"; 2 | 3 | const Component = styled.div.attrs({ 4 | className: "someClassName", 5 | })` 6 | color: red; 7 | `; 8 | 9 | export default Component; 10 | -------------------------------------------------------------------------------- /test/fixtures/styled-props.jsx.json: -------------------------------------------------------------------------------- 1 | { 2 | "raws": {}, 3 | "type": "root", 4 | "nodes": [ 5 | { 6 | "raws": { 7 | "semicolon": true, 8 | "after": "\n" 9 | }, 10 | "type": "root", 11 | "nodes": [ 12 | { 13 | "raws": { 14 | "before": "\n\t", 15 | "between": ": " 16 | }, 17 | "type": "decl", 18 | "source": { 19 | "start": { 20 | "line": 6, 21 | "column": 2 22 | }, 23 | "input": { 24 | "file": "styled-props.jsx" 25 | }, 26 | "end": { 27 | "line": 6, 28 | "column": 12 29 | } 30 | }, 31 | "prop": "color", 32 | "value": "red" 33 | } 34 | ], 35 | "source": { 36 | "input": { 37 | "file": "styled-props.jsx" 38 | }, 39 | "start": { 40 | "line": 5, 41 | "column": 4 42 | }, 43 | "inline": false, 44 | "lang": "css", 45 | "syntax": {} 46 | } 47 | } 48 | ], 49 | "source": { 50 | "input": { 51 | "file": "styled-props.jsx" 52 | }, 53 | "start": { 54 | "line": 1, 55 | "column": 1 56 | }, 57 | "lang": "jsx" 58 | } 59 | } 60 | -------------------------------------------------------------------------------- /test/fixtures/toLocaleString.js: -------------------------------------------------------------------------------- 1 | // eslint-disable-next-line 2 | (positionsChecked / scansCount).toLocaleString("de-DE", { style: "percent" }); 3 | -------------------------------------------------------------------------------- /test/fixtures/tpl-decl.mjs: -------------------------------------------------------------------------------- 1 | import styled from "styled-components"; 2 | 3 | const prop = "prop"; 4 | const value = "value"; 5 | 6 | export const Row = styled.div` 7 | prop { 8 | ${prop}: value 9 | } 10 | prop prefix { 11 | prefix-${prop}: value 12 | } 13 | prop suffix { 14 | ${prop}-suffix: value 15 | } 16 | value { 17 | prop: ${value} 18 | } 19 | value prefix { 20 | prop: prefix-${value} 21 | } 22 | value suffix { 23 | prop: ${value}-suffix 24 | } 25 | value semicolon { 26 | prop: ${value}; 27 | } 28 | value prefix semicolon { 29 | prop: prefix-${value}; 30 | } 31 | value suffix semicolon { 32 | prop: ${value}-suffix; 33 | } 34 | `; 35 | -------------------------------------------------------------------------------- /test/fixtures/tpl-decl.mjs.json: -------------------------------------------------------------------------------- 1 | { 2 | "raws": {}, 3 | "type": "root", 4 | "nodes": [ 5 | { 6 | "raws": { 7 | "semicolon": false, 8 | "after": "\n" 9 | }, 10 | "type": "root", 11 | "nodes": [ 12 | { 13 | "raws": { 14 | "before": "\n", 15 | "between": " ", 16 | "semicolon": false, 17 | "after": "\n" 18 | }, 19 | "type": "rule", 20 | "nodes": [ 21 | { 22 | "raws": { 23 | "before": "\n\t", 24 | "between": ": " 25 | }, 26 | "type": "decl", 27 | "source": { 28 | "start": { 29 | "line": 8, 30 | "column": 2 31 | }, 32 | "input": { 33 | "file": "tpl-decl.mjs", 34 | "quasis": [ 35 | { 36 | "start": 116, 37 | "end": 125 38 | }, 39 | { 40 | "start": 132, 41 | "end": 164 42 | }, 43 | { 44 | "start": 171, 45 | "end": 196 46 | }, 47 | { 48 | "start": 203, 49 | "end": 235 50 | }, 51 | { 52 | "start": 243, 53 | "end": 275 54 | }, 55 | { 56 | "start": 283, 57 | "end": 308 58 | }, 59 | { 60 | "start": 316, 61 | "end": 351 62 | }, 63 | { 64 | "start": 359, 65 | "end": 402 66 | }, 67 | { 68 | "start": 410, 69 | "end": 446 70 | }, 71 | { 72 | "start": 454, 73 | "end": 465 74 | } 75 | ] 76 | }, 77 | "end": { 78 | "line": 8, 79 | "column": 15 80 | } 81 | }, 82 | "prop": "${prop}", 83 | "value": "value" 84 | } 85 | ], 86 | "source": { 87 | "start": { 88 | "line": 7, 89 | "column": 1 90 | }, 91 | "input": { 92 | "file": "tpl-decl.mjs", 93 | "quasis": [ 94 | { 95 | "start": 116, 96 | "end": 125 97 | }, 98 | { 99 | "start": 132, 100 | "end": 164 101 | }, 102 | { 103 | "start": 171, 104 | "end": 196 105 | }, 106 | { 107 | "start": 203, 108 | "end": 235 109 | }, 110 | { 111 | "start": 243, 112 | "end": 275 113 | }, 114 | { 115 | "start": 283, 116 | "end": 308 117 | }, 118 | { 119 | "start": 316, 120 | "end": 351 121 | }, 122 | { 123 | "start": 359, 124 | "end": 402 125 | }, 126 | { 127 | "start": 410, 128 | "end": 446 129 | }, 130 | { 131 | "start": 454, 132 | "end": 465 133 | } 134 | ] 135 | }, 136 | "end": { 137 | "line": 9, 138 | "column": 1 139 | } 140 | }, 141 | "selector": "prop" 142 | }, 143 | { 144 | "raws": { 145 | "before": "\n", 146 | "between": " ", 147 | "semicolon": false, 148 | "after": "\n" 149 | }, 150 | "type": "rule", 151 | "nodes": [ 152 | { 153 | "raws": { 154 | "before": "\n\t", 155 | "between": ": " 156 | }, 157 | "type": "decl", 158 | "source": { 159 | "start": { 160 | "line": 11, 161 | "column": 2 162 | }, 163 | "input": { 164 | "file": "tpl-decl.mjs", 165 | "quasis": [ 166 | { 167 | "start": 116, 168 | "end": 125 169 | }, 170 | { 171 | "start": 132, 172 | "end": 164 173 | }, 174 | { 175 | "start": 171, 176 | "end": 196 177 | }, 178 | { 179 | "start": 203, 180 | "end": 235 181 | }, 182 | { 183 | "start": 243, 184 | "end": 275 185 | }, 186 | { 187 | "start": 283, 188 | "end": 308 189 | }, 190 | { 191 | "start": 316, 192 | "end": 351 193 | }, 194 | { 195 | "start": 359, 196 | "end": 402 197 | }, 198 | { 199 | "start": 410, 200 | "end": 446 201 | }, 202 | { 203 | "start": 454, 204 | "end": 465 205 | } 206 | ] 207 | }, 208 | "end": { 209 | "line": 11, 210 | "column": 22 211 | } 212 | }, 213 | "prop": "prefix-${prop}", 214 | "value": "value" 215 | } 216 | ], 217 | "source": { 218 | "start": { 219 | "line": 10, 220 | "column": 1 221 | }, 222 | "input": { 223 | "file": "tpl-decl.mjs", 224 | "quasis": [ 225 | { 226 | "start": 116, 227 | "end": 125 228 | }, 229 | { 230 | "start": 132, 231 | "end": 164 232 | }, 233 | { 234 | "start": 171, 235 | "end": 196 236 | }, 237 | { 238 | "start": 203, 239 | "end": 235 240 | }, 241 | { 242 | "start": 243, 243 | "end": 275 244 | }, 245 | { 246 | "start": 283, 247 | "end": 308 248 | }, 249 | { 250 | "start": 316, 251 | "end": 351 252 | }, 253 | { 254 | "start": 359, 255 | "end": 402 256 | }, 257 | { 258 | "start": 410, 259 | "end": 446 260 | }, 261 | { 262 | "start": 454, 263 | "end": 465 264 | } 265 | ] 266 | }, 267 | "end": { 268 | "line": 12, 269 | "column": 1 270 | } 271 | }, 272 | "selector": "prop prefix" 273 | }, 274 | { 275 | "raws": { 276 | "before": "\n", 277 | "between": " ", 278 | "semicolon": false, 279 | "after": "\n" 280 | }, 281 | "type": "rule", 282 | "nodes": [ 283 | { 284 | "raws": { 285 | "before": "\n\t", 286 | "between": ": " 287 | }, 288 | "type": "decl", 289 | "source": { 290 | "start": { 291 | "line": 14, 292 | "column": 2 293 | }, 294 | "input": { 295 | "file": "tpl-decl.mjs", 296 | "quasis": [ 297 | { 298 | "start": 116, 299 | "end": 125 300 | }, 301 | { 302 | "start": 132, 303 | "end": 164 304 | }, 305 | { 306 | "start": 171, 307 | "end": 196 308 | }, 309 | { 310 | "start": 203, 311 | "end": 235 312 | }, 313 | { 314 | "start": 243, 315 | "end": 275 316 | }, 317 | { 318 | "start": 283, 319 | "end": 308 320 | }, 321 | { 322 | "start": 316, 323 | "end": 351 324 | }, 325 | { 326 | "start": 359, 327 | "end": 402 328 | }, 329 | { 330 | "start": 410, 331 | "end": 446 332 | }, 333 | { 334 | "start": 454, 335 | "end": 465 336 | } 337 | ] 338 | }, 339 | "end": { 340 | "line": 14, 341 | "column": 22 342 | } 343 | }, 344 | "prop": "${prop}-suffix", 345 | "value": "value" 346 | } 347 | ], 348 | "source": { 349 | "start": { 350 | "line": 13, 351 | "column": 1 352 | }, 353 | "input": { 354 | "file": "tpl-decl.mjs", 355 | "quasis": [ 356 | { 357 | "start": 116, 358 | "end": 125 359 | }, 360 | { 361 | "start": 132, 362 | "end": 164 363 | }, 364 | { 365 | "start": 171, 366 | "end": 196 367 | }, 368 | { 369 | "start": 203, 370 | "end": 235 371 | }, 372 | { 373 | "start": 243, 374 | "end": 275 375 | }, 376 | { 377 | "start": 283, 378 | "end": 308 379 | }, 380 | { 381 | "start": 316, 382 | "end": 351 383 | }, 384 | { 385 | "start": 359, 386 | "end": 402 387 | }, 388 | { 389 | "start": 410, 390 | "end": 446 391 | }, 392 | { 393 | "start": 454, 394 | "end": 465 395 | } 396 | ] 397 | }, 398 | "end": { 399 | "line": 15, 400 | "column": 1 401 | } 402 | }, 403 | "selector": "prop suffix" 404 | }, 405 | { 406 | "raws": { 407 | "before": "\n", 408 | "between": " ", 409 | "semicolon": false, 410 | "after": "\n" 411 | }, 412 | "type": "rule", 413 | "nodes": [ 414 | { 415 | "raws": { 416 | "before": "\n\t", 417 | "between": ": " 418 | }, 419 | "type": "decl", 420 | "source": { 421 | "start": { 422 | "line": 17, 423 | "column": 2 424 | }, 425 | "input": { 426 | "file": "tpl-decl.mjs", 427 | "quasis": [ 428 | { 429 | "start": 116, 430 | "end": 125 431 | }, 432 | { 433 | "start": 132, 434 | "end": 164 435 | }, 436 | { 437 | "start": 171, 438 | "end": 196 439 | }, 440 | { 441 | "start": 203, 442 | "end": 235 443 | }, 444 | { 445 | "start": 243, 446 | "end": 275 447 | }, 448 | { 449 | "start": 283, 450 | "end": 308 451 | }, 452 | { 453 | "start": 316, 454 | "end": 351 455 | }, 456 | { 457 | "start": 359, 458 | "end": 402 459 | }, 460 | { 461 | "start": 410, 462 | "end": 446 463 | }, 464 | { 465 | "start": 454, 466 | "end": 465 467 | } 468 | ] 469 | }, 470 | "end": { 471 | "line": 17, 472 | "column": 15 473 | } 474 | }, 475 | "prop": "prop", 476 | "value": "${value}" 477 | } 478 | ], 479 | "source": { 480 | "start": { 481 | "line": 16, 482 | "column": 1 483 | }, 484 | "input": { 485 | "file": "tpl-decl.mjs", 486 | "quasis": [ 487 | { 488 | "start": 116, 489 | "end": 125 490 | }, 491 | { 492 | "start": 132, 493 | "end": 164 494 | }, 495 | { 496 | "start": 171, 497 | "end": 196 498 | }, 499 | { 500 | "start": 203, 501 | "end": 235 502 | }, 503 | { 504 | "start": 243, 505 | "end": 275 506 | }, 507 | { 508 | "start": 283, 509 | "end": 308 510 | }, 511 | { 512 | "start": 316, 513 | "end": 351 514 | }, 515 | { 516 | "start": 359, 517 | "end": 402 518 | }, 519 | { 520 | "start": 410, 521 | "end": 446 522 | }, 523 | { 524 | "start": 454, 525 | "end": 465 526 | } 527 | ] 528 | }, 529 | "end": { 530 | "line": 18, 531 | "column": 1 532 | } 533 | }, 534 | "selector": "value" 535 | }, 536 | { 537 | "raws": { 538 | "before": "\n", 539 | "between": " ", 540 | "semicolon": false, 541 | "after": "\n" 542 | }, 543 | "type": "rule", 544 | "nodes": [ 545 | { 546 | "raws": { 547 | "before": "\n\t", 548 | "between": ": " 549 | }, 550 | "type": "decl", 551 | "source": { 552 | "start": { 553 | "line": 20, 554 | "column": 2 555 | }, 556 | "input": { 557 | "file": "tpl-decl.mjs", 558 | "quasis": [ 559 | { 560 | "start": 116, 561 | "end": 125 562 | }, 563 | { 564 | "start": 132, 565 | "end": 164 566 | }, 567 | { 568 | "start": 171, 569 | "end": 196 570 | }, 571 | { 572 | "start": 203, 573 | "end": 235 574 | }, 575 | { 576 | "start": 243, 577 | "end": 275 578 | }, 579 | { 580 | "start": 283, 581 | "end": 308 582 | }, 583 | { 584 | "start": 316, 585 | "end": 351 586 | }, 587 | { 588 | "start": 359, 589 | "end": 402 590 | }, 591 | { 592 | "start": 410, 593 | "end": 446 594 | }, 595 | { 596 | "start": 454, 597 | "end": 465 598 | } 599 | ] 600 | }, 601 | "end": { 602 | "line": 20, 603 | "column": 22 604 | } 605 | }, 606 | "prop": "prop", 607 | "value": "prefix-${value}" 608 | } 609 | ], 610 | "source": { 611 | "start": { 612 | "line": 19, 613 | "column": 1 614 | }, 615 | "input": { 616 | "file": "tpl-decl.mjs", 617 | "quasis": [ 618 | { 619 | "start": 116, 620 | "end": 125 621 | }, 622 | { 623 | "start": 132, 624 | "end": 164 625 | }, 626 | { 627 | "start": 171, 628 | "end": 196 629 | }, 630 | { 631 | "start": 203, 632 | "end": 235 633 | }, 634 | { 635 | "start": 243, 636 | "end": 275 637 | }, 638 | { 639 | "start": 283, 640 | "end": 308 641 | }, 642 | { 643 | "start": 316, 644 | "end": 351 645 | }, 646 | { 647 | "start": 359, 648 | "end": 402 649 | }, 650 | { 651 | "start": 410, 652 | "end": 446 653 | }, 654 | { 655 | "start": 454, 656 | "end": 465 657 | } 658 | ] 659 | }, 660 | "end": { 661 | "line": 21, 662 | "column": 1 663 | } 664 | }, 665 | "selector": "value prefix" 666 | }, 667 | { 668 | "raws": { 669 | "before": "\n", 670 | "between": " ", 671 | "semicolon": false, 672 | "after": "\n" 673 | }, 674 | "type": "rule", 675 | "nodes": [ 676 | { 677 | "raws": { 678 | "before": "\n\t", 679 | "between": ": " 680 | }, 681 | "type": "decl", 682 | "source": { 683 | "start": { 684 | "line": 23, 685 | "column": 2 686 | }, 687 | "input": { 688 | "file": "tpl-decl.mjs", 689 | "quasis": [ 690 | { 691 | "start": 116, 692 | "end": 125 693 | }, 694 | { 695 | "start": 132, 696 | "end": 164 697 | }, 698 | { 699 | "start": 171, 700 | "end": 196 701 | }, 702 | { 703 | "start": 203, 704 | "end": 235 705 | }, 706 | { 707 | "start": 243, 708 | "end": 275 709 | }, 710 | { 711 | "start": 283, 712 | "end": 308 713 | }, 714 | { 715 | "start": 316, 716 | "end": 351 717 | }, 718 | { 719 | "start": 359, 720 | "end": 402 721 | }, 722 | { 723 | "start": 410, 724 | "end": 446 725 | }, 726 | { 727 | "start": 454, 728 | "end": 465 729 | } 730 | ] 731 | }, 732 | "end": { 733 | "line": 23, 734 | "column": 22 735 | } 736 | }, 737 | "prop": "prop", 738 | "value": "${value}-suffix" 739 | } 740 | ], 741 | "source": { 742 | "start": { 743 | "line": 22, 744 | "column": 1 745 | }, 746 | "input": { 747 | "file": "tpl-decl.mjs", 748 | "quasis": [ 749 | { 750 | "start": 116, 751 | "end": 125 752 | }, 753 | { 754 | "start": 132, 755 | "end": 164 756 | }, 757 | { 758 | "start": 171, 759 | "end": 196 760 | }, 761 | { 762 | "start": 203, 763 | "end": 235 764 | }, 765 | { 766 | "start": 243, 767 | "end": 275 768 | }, 769 | { 770 | "start": 283, 771 | "end": 308 772 | }, 773 | { 774 | "start": 316, 775 | "end": 351 776 | }, 777 | { 778 | "start": 359, 779 | "end": 402 780 | }, 781 | { 782 | "start": 410, 783 | "end": 446 784 | }, 785 | { 786 | "start": 454, 787 | "end": 465 788 | } 789 | ] 790 | }, 791 | "end": { 792 | "line": 24, 793 | "column": 1 794 | } 795 | }, 796 | "selector": "value suffix" 797 | }, 798 | { 799 | "raws": { 800 | "before": "\n", 801 | "between": " ", 802 | "semicolon": true, 803 | "after": "\n" 804 | }, 805 | "type": "rule", 806 | "nodes": [ 807 | { 808 | "raws": { 809 | "before": "\n\t", 810 | "between": ": " 811 | }, 812 | "type": "decl", 813 | "source": { 814 | "start": { 815 | "line": 26, 816 | "column": 2 817 | }, 818 | "input": { 819 | "file": "tpl-decl.mjs", 820 | "quasis": [ 821 | { 822 | "start": 116, 823 | "end": 125 824 | }, 825 | { 826 | "start": 132, 827 | "end": 164 828 | }, 829 | { 830 | "start": 171, 831 | "end": 196 832 | }, 833 | { 834 | "start": 203, 835 | "end": 235 836 | }, 837 | { 838 | "start": 243, 839 | "end": 275 840 | }, 841 | { 842 | "start": 283, 843 | "end": 308 844 | }, 845 | { 846 | "start": 316, 847 | "end": 351 848 | }, 849 | { 850 | "start": 359, 851 | "end": 402 852 | }, 853 | { 854 | "start": 410, 855 | "end": 446 856 | }, 857 | { 858 | "start": 454, 859 | "end": 465 860 | } 861 | ] 862 | }, 863 | "end": { 864 | "line": 26, 865 | "column": 16 866 | } 867 | }, 868 | "prop": "prop", 869 | "value": "${value}" 870 | } 871 | ], 872 | "source": { 873 | "start": { 874 | "line": 25, 875 | "column": 1 876 | }, 877 | "input": { 878 | "file": "tpl-decl.mjs", 879 | "quasis": [ 880 | { 881 | "start": 116, 882 | "end": 125 883 | }, 884 | { 885 | "start": 132, 886 | "end": 164 887 | }, 888 | { 889 | "start": 171, 890 | "end": 196 891 | }, 892 | { 893 | "start": 203, 894 | "end": 235 895 | }, 896 | { 897 | "start": 243, 898 | "end": 275 899 | }, 900 | { 901 | "start": 283, 902 | "end": 308 903 | }, 904 | { 905 | "start": 316, 906 | "end": 351 907 | }, 908 | { 909 | "start": 359, 910 | "end": 402 911 | }, 912 | { 913 | "start": 410, 914 | "end": 446 915 | }, 916 | { 917 | "start": 454, 918 | "end": 465 919 | } 920 | ] 921 | }, 922 | "end": { 923 | "line": 27, 924 | "column": 1 925 | } 926 | }, 927 | "selector": "value semicolon" 928 | }, 929 | { 930 | "raws": { 931 | "before": "\n", 932 | "between": " ", 933 | "semicolon": true, 934 | "after": "\n" 935 | }, 936 | "type": "rule", 937 | "nodes": [ 938 | { 939 | "raws": { 940 | "before": "\n\t", 941 | "between": ": " 942 | }, 943 | "type": "decl", 944 | "source": { 945 | "start": { 946 | "line": 29, 947 | "column": 2 948 | }, 949 | "input": { 950 | "file": "tpl-decl.mjs", 951 | "quasis": [ 952 | { 953 | "start": 116, 954 | "end": 125 955 | }, 956 | { 957 | "start": 132, 958 | "end": 164 959 | }, 960 | { 961 | "start": 171, 962 | "end": 196 963 | }, 964 | { 965 | "start": 203, 966 | "end": 235 967 | }, 968 | { 969 | "start": 243, 970 | "end": 275 971 | }, 972 | { 973 | "start": 283, 974 | "end": 308 975 | }, 976 | { 977 | "start": 316, 978 | "end": 351 979 | }, 980 | { 981 | "start": 359, 982 | "end": 402 983 | }, 984 | { 985 | "start": 410, 986 | "end": 446 987 | }, 988 | { 989 | "start": 454, 990 | "end": 465 991 | } 992 | ] 993 | }, 994 | "end": { 995 | "line": 29, 996 | "column": 23 997 | } 998 | }, 999 | "prop": "prop", 1000 | "value": "prefix-${value}" 1001 | } 1002 | ], 1003 | "source": { 1004 | "start": { 1005 | "line": 28, 1006 | "column": 1 1007 | }, 1008 | "input": { 1009 | "file": "tpl-decl.mjs", 1010 | "quasis": [ 1011 | { 1012 | "start": 116, 1013 | "end": 125 1014 | }, 1015 | { 1016 | "start": 132, 1017 | "end": 164 1018 | }, 1019 | { 1020 | "start": 171, 1021 | "end": 196 1022 | }, 1023 | { 1024 | "start": 203, 1025 | "end": 235 1026 | }, 1027 | { 1028 | "start": 243, 1029 | "end": 275 1030 | }, 1031 | { 1032 | "start": 283, 1033 | "end": 308 1034 | }, 1035 | { 1036 | "start": 316, 1037 | "end": 351 1038 | }, 1039 | { 1040 | "start": 359, 1041 | "end": 402 1042 | }, 1043 | { 1044 | "start": 410, 1045 | "end": 446 1046 | }, 1047 | { 1048 | "start": 454, 1049 | "end": 465 1050 | } 1051 | ] 1052 | }, 1053 | "end": { 1054 | "line": 30, 1055 | "column": 1 1056 | } 1057 | }, 1058 | "selector": "value prefix semicolon" 1059 | }, 1060 | { 1061 | "raws": { 1062 | "before": "\n", 1063 | "between": " ", 1064 | "semicolon": true, 1065 | "after": "\n" 1066 | }, 1067 | "type": "rule", 1068 | "nodes": [ 1069 | { 1070 | "raws": { 1071 | "before": "\n\t", 1072 | "between": ": " 1073 | }, 1074 | "type": "decl", 1075 | "source": { 1076 | "start": { 1077 | "line": 32, 1078 | "column": 2 1079 | }, 1080 | "input": { 1081 | "file": "tpl-decl.mjs", 1082 | "quasis": [ 1083 | { 1084 | "start": 116, 1085 | "end": 125 1086 | }, 1087 | { 1088 | "start": 132, 1089 | "end": 164 1090 | }, 1091 | { 1092 | "start": 171, 1093 | "end": 196 1094 | }, 1095 | { 1096 | "start": 203, 1097 | "end": 235 1098 | }, 1099 | { 1100 | "start": 243, 1101 | "end": 275 1102 | }, 1103 | { 1104 | "start": 283, 1105 | "end": 308 1106 | }, 1107 | { 1108 | "start": 316, 1109 | "end": 351 1110 | }, 1111 | { 1112 | "start": 359, 1113 | "end": 402 1114 | }, 1115 | { 1116 | "start": 410, 1117 | "end": 446 1118 | }, 1119 | { 1120 | "start": 454, 1121 | "end": 465 1122 | } 1123 | ] 1124 | }, 1125 | "end": { 1126 | "line": 32, 1127 | "column": 23 1128 | } 1129 | }, 1130 | "prop": "prop", 1131 | "value": "${value}-suffix" 1132 | } 1133 | ], 1134 | "source": { 1135 | "start": { 1136 | "line": 31, 1137 | "column": 1 1138 | }, 1139 | "input": { 1140 | "file": "tpl-decl.mjs", 1141 | "quasis": [ 1142 | { 1143 | "start": 116, 1144 | "end": 125 1145 | }, 1146 | { 1147 | "start": 132, 1148 | "end": 164 1149 | }, 1150 | { 1151 | "start": 171, 1152 | "end": 196 1153 | }, 1154 | { 1155 | "start": 203, 1156 | "end": 235 1157 | }, 1158 | { 1159 | "start": 243, 1160 | "end": 275 1161 | }, 1162 | { 1163 | "start": 283, 1164 | "end": 308 1165 | }, 1166 | { 1167 | "start": 316, 1168 | "end": 351 1169 | }, 1170 | { 1171 | "start": 359, 1172 | "end": 402 1173 | }, 1174 | { 1175 | "start": 410, 1176 | "end": 446 1177 | }, 1178 | { 1179 | "start": 454, 1180 | "end": 465 1181 | } 1182 | ] 1183 | }, 1184 | "end": { 1185 | "line": 33, 1186 | "column": 1 1187 | } 1188 | }, 1189 | "selector": "value suffix semicolon" 1190 | } 1191 | ], 1192 | "source": { 1193 | "input": { 1194 | "file": "tpl-decl.mjs", 1195 | "quasis": [ 1196 | { 1197 | "start": 116, 1198 | "end": 125 1199 | }, 1200 | { 1201 | "start": 132, 1202 | "end": 164 1203 | }, 1204 | { 1205 | "start": 171, 1206 | "end": 196 1207 | }, 1208 | { 1209 | "start": 203, 1210 | "end": 235 1211 | }, 1212 | { 1213 | "start": 243, 1214 | "end": 275 1215 | }, 1216 | { 1217 | "start": 283, 1218 | "end": 308 1219 | }, 1220 | { 1221 | "start": 316, 1222 | "end": 351 1223 | }, 1224 | { 1225 | "start": 359, 1226 | "end": 402 1227 | }, 1228 | { 1229 | "start": 410, 1230 | "end": 446 1231 | }, 1232 | { 1233 | "start": 454, 1234 | "end": 465 1235 | } 1236 | ] 1237 | }, 1238 | "start": { 1239 | "line": 6, 1240 | "column": 31 1241 | }, 1242 | "inline": false, 1243 | "lang": "template-literal", 1244 | "syntax": {} 1245 | } 1246 | } 1247 | ], 1248 | "source": { 1249 | "input": { 1250 | "file": "tpl-decl.mjs" 1251 | }, 1252 | "start": { 1253 | "line": 1, 1254 | "column": 1 1255 | }, 1256 | "lang": "jsx" 1257 | } 1258 | } 1259 | -------------------------------------------------------------------------------- /test/fixtures/tpl-in-tpl.mjs: -------------------------------------------------------------------------------- 1 | import styled from "styled-components"; 2 | 3 | const color = "#ddd"; 4 | 5 | export const Row = styled.div` 6 | border-bottom: ${(props) => (props.border ? `1px solid ${color}` : "0")}; 7 | `; 8 | -------------------------------------------------------------------------------- /test/fixtures/tpl-in-tpl.mjs.json: -------------------------------------------------------------------------------- 1 | { 2 | "raws": {}, 3 | "type": "root", 4 | "nodes": [ 5 | { 6 | "raws": { 7 | "semicolon": true, 8 | "after": "\n" 9 | }, 10 | "type": "root", 11 | "nodes": [ 12 | { 13 | "raws": { 14 | "before": "\n\t", 15 | "between": ": " 16 | }, 17 | "type": "decl", 18 | "source": { 19 | "start": { 20 | "line": 6, 21 | "column": 2 22 | }, 23 | "input": { 24 | "file": "tpl-in-tpl.mjs", 25 | "quasis": [ 26 | { 27 | "start": 94, 28 | "end": 111 29 | }, 30 | { 31 | "start": 168, 32 | "end": 170 33 | } 34 | ] 35 | }, 36 | "end": { 37 | "line": 6, 38 | "column": 74 39 | } 40 | }, 41 | "prop": "border-bottom", 42 | "value": "${(props) => (props.border ? `1px solid ${color}` : \"0\")}" 43 | } 44 | ], 45 | "source": { 46 | "input": { 47 | "file": "tpl-in-tpl.mjs", 48 | "quasis": [ 49 | { 50 | "start": 94, 51 | "end": 111 52 | }, 53 | { 54 | "start": 168, 55 | "end": 170 56 | } 57 | ] 58 | }, 59 | "start": { 60 | "line": 5, 61 | "column": 31 62 | }, 63 | "inline": false, 64 | "lang": "template-literal", 65 | "syntax": {} 66 | } 67 | } 68 | ], 69 | "source": { 70 | "input": { 71 | "file": "tpl-in-tpl.mjs" 72 | }, 73 | "start": { 74 | "line": 1, 75 | "column": 1 76 | }, 77 | "lang": "jsx" 78 | } 79 | } 80 | -------------------------------------------------------------------------------- /test/fixtures/tpl-selector.mjs: -------------------------------------------------------------------------------- 1 | import styled from "styled-components"; 2 | 3 | const selector = "div"; 4 | 5 | export const Row = styled.div` 6 | ${selector} a { 7 | display: block 8 | } 9 | a ${selector} { 10 | display: block 11 | } 12 | ${selector} 13 | a { 14 | display: block 15 | } 16 | a 17 | ${selector} { 18 | display: block 19 | } 20 | ${selector}, 21 | a { 22 | display: block 23 | } 24 | a, 25 | ${selector} { 26 | display: block 27 | } 28 | `; 29 | -------------------------------------------------------------------------------- /test/fixtures/tpl-selector.mjs.json: -------------------------------------------------------------------------------- 1 | { 2 | "raws": {}, 3 | "type": "root", 4 | "nodes": [ 5 | { 6 | "raws": { 7 | "semicolon": false, 8 | "after": "\n" 9 | }, 10 | "type": "root", 11 | "nodes": [ 12 | { 13 | "raws": { 14 | "before": "\n\t", 15 | "between": " ", 16 | "semicolon": false, 17 | "after": "\n\t" 18 | }, 19 | "type": "rule", 20 | "nodes": [ 21 | { 22 | "raws": { 23 | "before": "\n\t\t", 24 | "between": ": " 25 | }, 26 | "type": "decl", 27 | "source": { 28 | "start": { 29 | "line": 7, 30 | "column": 3 31 | }, 32 | "input": { 33 | "file": "tpl-selector.mjs", 34 | "quasis": [ 35 | { 36 | "start": 96, 37 | "end": 98 38 | }, 39 | { 40 | "start": 109, 41 | "end": 137 42 | }, 43 | { 44 | "start": 148, 45 | "end": 172 46 | }, 47 | { 48 | "start": 183, 49 | "end": 213 50 | }, 51 | { 52 | "start": 224, 53 | "end": 248 54 | }, 55 | { 56 | "start": 259, 57 | "end": 291 58 | }, 59 | { 60 | "start": 302, 61 | "end": 325 62 | } 63 | ] 64 | }, 65 | "end": { 66 | "line": 7, 67 | "column": 16 68 | } 69 | }, 70 | "prop": "display", 71 | "value": "block" 72 | } 73 | ], 74 | "source": { 75 | "start": { 76 | "line": 6, 77 | "column": 2 78 | }, 79 | "input": { 80 | "file": "tpl-selector.mjs", 81 | "quasis": [ 82 | { 83 | "start": 96, 84 | "end": 98 85 | }, 86 | { 87 | "start": 109, 88 | "end": 137 89 | }, 90 | { 91 | "start": 148, 92 | "end": 172 93 | }, 94 | { 95 | "start": 183, 96 | "end": 213 97 | }, 98 | { 99 | "start": 224, 100 | "end": 248 101 | }, 102 | { 103 | "start": 259, 104 | "end": 291 105 | }, 106 | { 107 | "start": 302, 108 | "end": 325 109 | } 110 | ] 111 | }, 112 | "end": { 113 | "line": 8, 114 | "column": 2 115 | } 116 | }, 117 | "selector": "${selector} a" 118 | }, 119 | { 120 | "raws": { 121 | "before": "\n\t", 122 | "between": " ", 123 | "semicolon": false, 124 | "after": "\n\t" 125 | }, 126 | "type": "rule", 127 | "nodes": [ 128 | { 129 | "raws": { 130 | "before": "\n\t\t", 131 | "between": ": " 132 | }, 133 | "type": "decl", 134 | "source": { 135 | "start": { 136 | "line": 10, 137 | "column": 3 138 | }, 139 | "input": { 140 | "file": "tpl-selector.mjs", 141 | "quasis": [ 142 | { 143 | "start": 96, 144 | "end": 98 145 | }, 146 | { 147 | "start": 109, 148 | "end": 137 149 | }, 150 | { 151 | "start": 148, 152 | "end": 172 153 | }, 154 | { 155 | "start": 183, 156 | "end": 213 157 | }, 158 | { 159 | "start": 224, 160 | "end": 248 161 | }, 162 | { 163 | "start": 259, 164 | "end": 291 165 | }, 166 | { 167 | "start": 302, 168 | "end": 325 169 | } 170 | ] 171 | }, 172 | "end": { 173 | "line": 10, 174 | "column": 16 175 | } 176 | }, 177 | "prop": "display", 178 | "value": "block" 179 | } 180 | ], 181 | "source": { 182 | "start": { 183 | "line": 9, 184 | "column": 2 185 | }, 186 | "input": { 187 | "file": "tpl-selector.mjs", 188 | "quasis": [ 189 | { 190 | "start": 96, 191 | "end": 98 192 | }, 193 | { 194 | "start": 109, 195 | "end": 137 196 | }, 197 | { 198 | "start": 148, 199 | "end": 172 200 | }, 201 | { 202 | "start": 183, 203 | "end": 213 204 | }, 205 | { 206 | "start": 224, 207 | "end": 248 208 | }, 209 | { 210 | "start": 259, 211 | "end": 291 212 | }, 213 | { 214 | "start": 302, 215 | "end": 325 216 | } 217 | ] 218 | }, 219 | "end": { 220 | "line": 11, 221 | "column": 2 222 | } 223 | }, 224 | "selector": "a ${selector}" 225 | }, 226 | { 227 | "raws": { 228 | "before": "\n\t", 229 | "between": " ", 230 | "semicolon": false, 231 | "after": "\n\t" 232 | }, 233 | "type": "rule", 234 | "nodes": [ 235 | { 236 | "raws": { 237 | "before": "\n\t\t", 238 | "between": ": " 239 | }, 240 | "type": "decl", 241 | "source": { 242 | "start": { 243 | "line": 14, 244 | "column": 3 245 | }, 246 | "input": { 247 | "file": "tpl-selector.mjs", 248 | "quasis": [ 249 | { 250 | "start": 96, 251 | "end": 98 252 | }, 253 | { 254 | "start": 109, 255 | "end": 137 256 | }, 257 | { 258 | "start": 148, 259 | "end": 172 260 | }, 261 | { 262 | "start": 183, 263 | "end": 213 264 | }, 265 | { 266 | "start": 224, 267 | "end": 248 268 | }, 269 | { 270 | "start": 259, 271 | "end": 291 272 | }, 273 | { 274 | "start": 302, 275 | "end": 325 276 | } 277 | ] 278 | }, 279 | "end": { 280 | "line": 14, 281 | "column": 16 282 | } 283 | }, 284 | "prop": "display", 285 | "value": "block" 286 | } 287 | ], 288 | "source": { 289 | "start": { 290 | "line": 12, 291 | "column": 2 292 | }, 293 | "input": { 294 | "file": "tpl-selector.mjs", 295 | "quasis": [ 296 | { 297 | "start": 96, 298 | "end": 98 299 | }, 300 | { 301 | "start": 109, 302 | "end": 137 303 | }, 304 | { 305 | "start": 148, 306 | "end": 172 307 | }, 308 | { 309 | "start": 183, 310 | "end": 213 311 | }, 312 | { 313 | "start": 224, 314 | "end": 248 315 | }, 316 | { 317 | "start": 259, 318 | "end": 291 319 | }, 320 | { 321 | "start": 302, 322 | "end": 325 323 | } 324 | ] 325 | }, 326 | "end": { 327 | "line": 15, 328 | "column": 2 329 | } 330 | }, 331 | "selector": "${selector}\n\ta" 332 | }, 333 | { 334 | "raws": { 335 | "before": "\n\t", 336 | "between": " ", 337 | "semicolon": false, 338 | "after": "\n\t" 339 | }, 340 | "type": "rule", 341 | "nodes": [ 342 | { 343 | "raws": { 344 | "before": "\n\t\t", 345 | "between": ": " 346 | }, 347 | "type": "decl", 348 | "source": { 349 | "start": { 350 | "line": 18, 351 | "column": 3 352 | }, 353 | "input": { 354 | "file": "tpl-selector.mjs", 355 | "quasis": [ 356 | { 357 | "start": 96, 358 | "end": 98 359 | }, 360 | { 361 | "start": 109, 362 | "end": 137 363 | }, 364 | { 365 | "start": 148, 366 | "end": 172 367 | }, 368 | { 369 | "start": 183, 370 | "end": 213 371 | }, 372 | { 373 | "start": 224, 374 | "end": 248 375 | }, 376 | { 377 | "start": 259, 378 | "end": 291 379 | }, 380 | { 381 | "start": 302, 382 | "end": 325 383 | } 384 | ] 385 | }, 386 | "end": { 387 | "line": 18, 388 | "column": 16 389 | } 390 | }, 391 | "prop": "display", 392 | "value": "block" 393 | } 394 | ], 395 | "source": { 396 | "start": { 397 | "line": 16, 398 | "column": 2 399 | }, 400 | "input": { 401 | "file": "tpl-selector.mjs", 402 | "quasis": [ 403 | { 404 | "start": 96, 405 | "end": 98 406 | }, 407 | { 408 | "start": 109, 409 | "end": 137 410 | }, 411 | { 412 | "start": 148, 413 | "end": 172 414 | }, 415 | { 416 | "start": 183, 417 | "end": 213 418 | }, 419 | { 420 | "start": 224, 421 | "end": 248 422 | }, 423 | { 424 | "start": 259, 425 | "end": 291 426 | }, 427 | { 428 | "start": 302, 429 | "end": 325 430 | } 431 | ] 432 | }, 433 | "end": { 434 | "line": 19, 435 | "column": 2 436 | } 437 | }, 438 | "selector": "a\n\t${selector}" 439 | }, 440 | { 441 | "raws": { 442 | "before": "\n\t", 443 | "between": " ", 444 | "semicolon": false, 445 | "after": "\n\t" 446 | }, 447 | "type": "rule", 448 | "nodes": [ 449 | { 450 | "raws": { 451 | "before": "\n\t\t", 452 | "between": ": " 453 | }, 454 | "type": "decl", 455 | "source": { 456 | "start": { 457 | "line": 22, 458 | "column": 3 459 | }, 460 | "input": { 461 | "file": "tpl-selector.mjs", 462 | "quasis": [ 463 | { 464 | "start": 96, 465 | "end": 98 466 | }, 467 | { 468 | "start": 109, 469 | "end": 137 470 | }, 471 | { 472 | "start": 148, 473 | "end": 172 474 | }, 475 | { 476 | "start": 183, 477 | "end": 213 478 | }, 479 | { 480 | "start": 224, 481 | "end": 248 482 | }, 483 | { 484 | "start": 259, 485 | "end": 291 486 | }, 487 | { 488 | "start": 302, 489 | "end": 325 490 | } 491 | ] 492 | }, 493 | "end": { 494 | "line": 22, 495 | "column": 16 496 | } 497 | }, 498 | "prop": "display", 499 | "value": "block" 500 | } 501 | ], 502 | "source": { 503 | "start": { 504 | "line": 20, 505 | "column": 2 506 | }, 507 | "input": { 508 | "file": "tpl-selector.mjs", 509 | "quasis": [ 510 | { 511 | "start": 96, 512 | "end": 98 513 | }, 514 | { 515 | "start": 109, 516 | "end": 137 517 | }, 518 | { 519 | "start": 148, 520 | "end": 172 521 | }, 522 | { 523 | "start": 183, 524 | "end": 213 525 | }, 526 | { 527 | "start": 224, 528 | "end": 248 529 | }, 530 | { 531 | "start": 259, 532 | "end": 291 533 | }, 534 | { 535 | "start": 302, 536 | "end": 325 537 | } 538 | ] 539 | }, 540 | "end": { 541 | "line": 23, 542 | "column": 2 543 | } 544 | }, 545 | "selector": "${selector},\n\ta" 546 | }, 547 | { 548 | "raws": { 549 | "before": "\n\t", 550 | "between": " ", 551 | "semicolon": false, 552 | "after": "\n\t" 553 | }, 554 | "type": "rule", 555 | "nodes": [ 556 | { 557 | "raws": { 558 | "before": "\n\t\t", 559 | "between": ": " 560 | }, 561 | "type": "decl", 562 | "source": { 563 | "start": { 564 | "line": 26, 565 | "column": 3 566 | }, 567 | "input": { 568 | "file": "tpl-selector.mjs", 569 | "quasis": [ 570 | { 571 | "start": 96, 572 | "end": 98 573 | }, 574 | { 575 | "start": 109, 576 | "end": 137 577 | }, 578 | { 579 | "start": 148, 580 | "end": 172 581 | }, 582 | { 583 | "start": 183, 584 | "end": 213 585 | }, 586 | { 587 | "start": 224, 588 | "end": 248 589 | }, 590 | { 591 | "start": 259, 592 | "end": 291 593 | }, 594 | { 595 | "start": 302, 596 | "end": 325 597 | } 598 | ] 599 | }, 600 | "end": { 601 | "line": 26, 602 | "column": 16 603 | } 604 | }, 605 | "prop": "display", 606 | "value": "block" 607 | } 608 | ], 609 | "source": { 610 | "start": { 611 | "line": 24, 612 | "column": 2 613 | }, 614 | "input": { 615 | "file": "tpl-selector.mjs", 616 | "quasis": [ 617 | { 618 | "start": 96, 619 | "end": 98 620 | }, 621 | { 622 | "start": 109, 623 | "end": 137 624 | }, 625 | { 626 | "start": 148, 627 | "end": 172 628 | }, 629 | { 630 | "start": 183, 631 | "end": 213 632 | }, 633 | { 634 | "start": 224, 635 | "end": 248 636 | }, 637 | { 638 | "start": 259, 639 | "end": 291 640 | }, 641 | { 642 | "start": 302, 643 | "end": 325 644 | } 645 | ] 646 | }, 647 | "end": { 648 | "line": 27, 649 | "column": 2 650 | } 651 | }, 652 | "selector": "a,\n\t${selector}" 653 | } 654 | ], 655 | "source": { 656 | "input": { 657 | "file": "tpl-selector.mjs", 658 | "quasis": [ 659 | { 660 | "start": 96, 661 | "end": 98 662 | }, 663 | { 664 | "start": 109, 665 | "end": 137 666 | }, 667 | { 668 | "start": 148, 669 | "end": 172 670 | }, 671 | { 672 | "start": 183, 673 | "end": 213 674 | }, 675 | { 676 | "start": 224, 677 | "end": 248 678 | }, 679 | { 680 | "start": 259, 681 | "end": 291 682 | }, 683 | { 684 | "start": 302, 685 | "end": 325 686 | } 687 | ] 688 | }, 689 | "start": { 690 | "line": 5, 691 | "column": 31 692 | }, 693 | "inline": false, 694 | "lang": "template-literal", 695 | "syntax": {} 696 | } 697 | } 698 | ], 699 | "source": { 700 | "input": { 701 | "file": "tpl-selector.mjs" 702 | }, 703 | "start": { 704 | "line": 1, 705 | "column": 1 706 | }, 707 | "lang": "jsx" 708 | } 709 | } 710 | -------------------------------------------------------------------------------- /test/fixtures/tpl-special.mjs: -------------------------------------------------------------------------------- 1 | import styled from "styled-components"; 2 | 3 | const img = "/images/logo.png"; 4 | 5 | export const Row = styled.div` 6 | img[${img}] { 7 | background-image: url(${img}); 8 | } 9 | img[att="${img}"] { 10 | background-image: url("${img}"); 11 | } 12 | `; 13 | -------------------------------------------------------------------------------- /test/fixtures/tpl-special.mjs.json: -------------------------------------------------------------------------------- 1 | { 2 | "raws": {}, 3 | "type": "root", 4 | "nodes": [ 5 | { 6 | "raws": { 7 | "semicolon": false, 8 | "after": "\n" 9 | }, 10 | "type": "root", 11 | "nodes": [ 12 | { 13 | "raws": { 14 | "before": "\n", 15 | "between": " ", 16 | "semicolon": true, 17 | "after": "\n" 18 | }, 19 | "type": "rule", 20 | "nodes": [ 21 | { 22 | "raws": { 23 | "before": "\n\t", 24 | "between": ": " 25 | }, 26 | "type": "decl", 27 | "source": { 28 | "start": { 29 | "line": 7, 30 | "column": 2 31 | }, 32 | "input": { 33 | "file": "tpl-special.mjs", 34 | "quasis": [ 35 | { 36 | "start": 104, 37 | "end": 109 38 | }, 39 | { 40 | "start": 115, 41 | "end": 142 42 | }, 43 | { 44 | "start": 148, 45 | "end": 162 46 | }, 47 | { 48 | "start": 168, 49 | "end": 197 50 | }, 51 | { 52 | "start": 203, 53 | "end": 209 54 | } 55 | ] 56 | }, 57 | "end": { 58 | "line": 7, 59 | "column": 31 60 | } 61 | }, 62 | "prop": "background-image", 63 | "value": "url(${img})" 64 | } 65 | ], 66 | "source": { 67 | "start": { 68 | "line": 6, 69 | "column": 1 70 | }, 71 | "input": { 72 | "file": "tpl-special.mjs", 73 | "quasis": [ 74 | { 75 | "start": 104, 76 | "end": 109 77 | }, 78 | { 79 | "start": 115, 80 | "end": 142 81 | }, 82 | { 83 | "start": 148, 84 | "end": 162 85 | }, 86 | { 87 | "start": 168, 88 | "end": 197 89 | }, 90 | { 91 | "start": 203, 92 | "end": 209 93 | } 94 | ] 95 | }, 96 | "end": { 97 | "line": 8, 98 | "column": 1 99 | } 100 | }, 101 | "selector": "img[${img}]" 102 | }, 103 | { 104 | "raws": { 105 | "before": "\n", 106 | "between": " ", 107 | "semicolon": true, 108 | "after": "\n" 109 | }, 110 | "type": "rule", 111 | "nodes": [ 112 | { 113 | "raws": { 114 | "before": "\n\t", 115 | "between": ": " 116 | }, 117 | "type": "decl", 118 | "source": { 119 | "start": { 120 | "line": 10, 121 | "column": 2 122 | }, 123 | "input": { 124 | "file": "tpl-special.mjs", 125 | "quasis": [ 126 | { 127 | "start": 104, 128 | "end": 109 129 | }, 130 | { 131 | "start": 115, 132 | "end": 142 133 | }, 134 | { 135 | "start": 148, 136 | "end": 162 137 | }, 138 | { 139 | "start": 168, 140 | "end": 197 141 | }, 142 | { 143 | "start": 203, 144 | "end": 209 145 | } 146 | ] 147 | }, 148 | "end": { 149 | "line": 10, 150 | "column": 33 151 | } 152 | }, 153 | "prop": "background-image", 154 | "value": "url(\"${img}\")" 155 | } 156 | ], 157 | "source": { 158 | "start": { 159 | "line": 9, 160 | "column": 1 161 | }, 162 | "input": { 163 | "file": "tpl-special.mjs", 164 | "quasis": [ 165 | { 166 | "start": 104, 167 | "end": 109 168 | }, 169 | { 170 | "start": 115, 171 | "end": 142 172 | }, 173 | { 174 | "start": 148, 175 | "end": 162 176 | }, 177 | { 178 | "start": 168, 179 | "end": 197 180 | }, 181 | { 182 | "start": 203, 183 | "end": 209 184 | } 185 | ] 186 | }, 187 | "end": { 188 | "line": 11, 189 | "column": 1 190 | } 191 | }, 192 | "selector": "img[att=\"${img}\"]" 193 | } 194 | ], 195 | "source": { 196 | "input": { 197 | "file": "tpl-special.mjs", 198 | "quasis": [ 199 | { 200 | "start": 104, 201 | "end": 109 202 | }, 203 | { 204 | "start": 115, 205 | "end": 142 206 | }, 207 | { 208 | "start": 148, 209 | "end": 162 210 | }, 211 | { 212 | "start": 168, 213 | "end": 197 214 | }, 215 | { 216 | "start": 203, 217 | "end": 209 218 | } 219 | ] 220 | }, 221 | "start": { 222 | "line": 5, 223 | "column": 31 224 | }, 225 | "inline": false, 226 | "lang": "template-literal", 227 | "syntax": {} 228 | } 229 | } 230 | ], 231 | "source": { 232 | "input": { 233 | "file": "tpl-special.mjs" 234 | }, 235 | "start": { 236 | "line": 1, 237 | "column": 1 238 | }, 239 | "lang": "jsx" 240 | } 241 | } 242 | -------------------------------------------------------------------------------- /test/glamorous.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | const expect = require("chai").expect; 3 | const syntax = require("../"); 4 | const fs = require("fs"); 5 | 6 | describe("javascript tests", () => { 7 | it("glamorous", () => { 8 | const filename = require.resolve("./fixtures/glamorous.jsx"); 9 | let code = fs.readFileSync(filename); 10 | 11 | const document = syntax.parse(code, { 12 | from: filename, 13 | }); 14 | 15 | code = code.toString(); 16 | 17 | expect(document.toString(syntax)).to.equal(code); 18 | expect(document.nodes).to.lengthOf(5); 19 | 20 | document.nodes.forEach(root => { 21 | expect(root.source).to.haveOwnProperty("input"); 22 | 23 | expect(code).to.includes(root.source.input.css); 24 | expect(root.source.input.css.length).lessThan(code.length); 25 | expect(root.source).to.haveOwnProperty("start").to.haveOwnProperty("line").to.greaterThan(1); 26 | 27 | root.walk(node => { 28 | expect(node).to.haveOwnProperty("source"); 29 | 30 | expect(node.source).to.haveOwnProperty("input").to.haveOwnProperty("css").equal(root.source.input.css); 31 | 32 | expect(node.source).to.haveOwnProperty("start").to.haveOwnProperty("line"); 33 | expect(node.source).to.haveOwnProperty("end").to.haveOwnProperty("line"); 34 | }); 35 | }); 36 | }); 37 | }); 38 | -------------------------------------------------------------------------------- /test/literals.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | const expect = require("chai").expect; 3 | const syntax = require("../"); 4 | const fs = require("fs"); 5 | 6 | describe("template literals", () => { 7 | it("template literals inside template literals", () => { 8 | const file = require.resolve("./fixtures/tpl-in-tpl.mjs"); 9 | let code = fs.readFileSync(file); 10 | 11 | const document = syntax.parse(code, { 12 | from: file, 13 | }); 14 | 15 | code = code.toString(); 16 | expect(document.toString()).to.equal(code); 17 | expect(document.source).to.haveOwnProperty("lang", "jsx"); 18 | 19 | expect(document.nodes).to.have.lengthOf(1); 20 | expect(document.first.nodes).to.have.lengthOf(1); 21 | 22 | document.first.nodes.forEach((decl) => { 23 | expect(decl).have.property("type", "decl"); 24 | expect(decl).have.property("prop", "border-bottom"); 25 | expect(decl).have.property("value", "${(props) => (props.border ? `1px solid ${color}` : \"0\")}"); 26 | }); 27 | }); 28 | 29 | it("multiline arrow function", () => { 30 | const file = require.resolve("./fixtures/multiline-arrow-function.mjs"); 31 | let code = fs.readFileSync(file); 32 | 33 | const document = syntax.parse(code, { 34 | from: file, 35 | }); 36 | 37 | code = code.toString(); 38 | expect(document.toString()).to.equal(code); 39 | expect(document.source).to.haveOwnProperty("lang", "jsx"); 40 | 41 | expect(document.nodes).to.have.lengthOf(1); 42 | expect(document.first.nodes).to.have.lengthOf(1); 43 | 44 | document.first.nodes.forEach((decl) => { 45 | expect(decl).have.property("type", "decl"); 46 | expect(decl).have.property("prop", "color"); 47 | expect(decl).have.property( 48 | "value", 49 | [ 50 | "${(props) =>", 51 | "(props.status === \"signed\" && \"red\") ||", 52 | "\"blue\"}", 53 | ].join("\n\t\t") 54 | ); 55 | }); 56 | }); 57 | 58 | it("interpolation as the only content of a component", () => { 59 | const file = require.resolve("./fixtures/interpolation-content.mjs"); 60 | let code = fs.readFileSync(file); 61 | 62 | const document = syntax.parse(code, { 63 | from: file, 64 | }); 65 | 66 | code = code.toString(); 67 | expect(document.toString()).to.equal(code); 68 | expect(document.source).to.haveOwnProperty("lang", "jsx"); 69 | 70 | expect(document.nodes).to.have.lengthOf(9); 71 | document.nodes.forEach((root, i) => { 72 | switch (i) { 73 | case 0: { 74 | expect(root.nodes).to.have.lengthOf(1); 75 | root.nodes.forEach(decl => { 76 | expect(decl).have.property("type", "decl"); 77 | expect(decl).have.property("prop", "display"); 78 | expect(decl).have.property("value", "inline-block"); 79 | }); 80 | return; 81 | } 82 | case 1: 83 | case 2: { 84 | expect(root.nodes).to.have.lengthOf(2); 85 | expect(root.first).have.property("type", "literal"); 86 | expect(root.first).have.property("text", "${buttonStyles}"); 87 | expect(root.last).have.property("type", "decl"); 88 | expect(root.last).have.property("prop", "color"); 89 | expect(root.last).have.property("value", "red"); 90 | return; 91 | } 92 | case 3: 93 | case 4: { 94 | expect(root.nodes).to.have.lengthOf(2); 95 | expect(root.first).have.property("type", "decl"); 96 | expect(root.first).have.property("prop", "color"); 97 | expect(root.first).have.property("value", "red"); 98 | expect(root.last).have.property("type", "literal"); 99 | expect(root.last).have.property("text", "${buttonStyles}"); 100 | } 101 | } 102 | }); 103 | }); 104 | 105 | it("selector", () => { 106 | const file = require.resolve("./fixtures/tpl-selector.mjs"); 107 | let code = fs.readFileSync(file); 108 | 109 | const document = syntax.parse(code, { 110 | from: file, 111 | }); 112 | 113 | code = code.toString(); 114 | expect(document.toString()).to.equal(code); 115 | expect(document.source).to.haveOwnProperty("lang", "jsx"); 116 | 117 | expect(document.nodes).to.have.lengthOf(1); 118 | expect(document.first.nodes).to.have.lengthOf(6); 119 | document.first.nodes.forEach(rule => { 120 | expect(rule).to.have.property("type", "rule"); 121 | expect(rule).to.have.property("selector"); 122 | expect(rule.selector).to.match(/(?:^|\s)\$\{selector\}(?=,|\s|$)/); 123 | }); 124 | }); 125 | 126 | describe("decl", () => { 127 | const file = require.resolve("./fixtures/tpl-decl.mjs"); 128 | const code = fs.readFileSync(file); 129 | 130 | syntax.parse(code, { 131 | from: file, 132 | }).first.nodes.forEach(rule => { 133 | it(rule.selector, () => { 134 | expect(rule.nodes).to.have.lengthOf(1); 135 | const decl = rule.first; 136 | expect(decl).to.have.property( 137 | "prop", 138 | /\bprop\b/.test(rule.selector) 139 | ? `${/\bprefix\b/.test(rule.selector) ? "prefix-" : ""}\${prop}${/\bsuffix\b/.test(rule.selector) ? "-suffix" : ""}` 140 | : "prop" 141 | ); 142 | expect(decl).to.have.property( 143 | "value", 144 | /\bvalue\b/.test(rule.selector) 145 | ? `${/\bprefix\b/.test(rule.selector) ? "prefix-" : ""}\${value}${/\bsuffix\b/.test(rule.selector) ? "-suffix" : ""}` 146 | : "value" 147 | ); 148 | }); 149 | }); 150 | }); 151 | 152 | it("non-literals", () => { 153 | const file = require.resolve("./fixtures/tpl-special.mjs"); 154 | let code = fs.readFileSync(file); 155 | 156 | const document = syntax.parse(code, { 157 | from: file, 158 | }); 159 | 160 | code = code.toString(); 161 | expect(document.toString()).to.equal(code); 162 | expect(document.source).to.haveOwnProperty("lang", "jsx"); 163 | 164 | document.walk(node => { 165 | expect(node).to.have.property("type"); 166 | expect(node.type).to.not.equal("literal"); 167 | }); 168 | }); 169 | 170 | describe("template-safe-parse", () => { 171 | [ 172 | "./fixtures/tpl-in-tpl.mjs", 173 | "./fixtures/multiline-arrow-function.mjs", 174 | "./fixtures/interpolation-content.mjs", 175 | "./fixtures/tpl-selector.mjs", 176 | "./fixtures/tpl-decl.mjs", 177 | "./fixtures/tpl-special.mjs", 178 | ].map(file => { 179 | it(file, () => { 180 | file = require.resolve(file); 181 | const code = fs.readFileSync(file); 182 | syntax({ 183 | css: "safe-parser", 184 | }).parse(code, { 185 | from: "styled-safe-parse.js", 186 | }); 187 | }); 188 | }); 189 | }); 190 | }); 191 | -------------------------------------------------------------------------------- /test/non-style.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | const spawnSync = require("child_process").spawnSync; 3 | const fs = require("fs"); 4 | const files = spawnSync("git", ["ls-files"], { encoding: "utf8" }).stdout.match(/^.+\.js$/gm); 5 | const syntax = require("../"); 6 | const expect = require("chai").expect; 7 | 8 | describe("not throw error for non-style js file", () => { 9 | files.forEach(file => { 10 | it(file, () => { 11 | const code = fs.readFileSync(file); 12 | const document = syntax.parse(code, { 13 | from: file, 14 | }); 15 | expect(document.source).to.haveOwnProperty("lang", "jsx"); 16 | expect(document.toString()).to.equal(code.toString()); 17 | }); 18 | }); 19 | }); 20 | -------------------------------------------------------------------------------- /test/react-native.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | const expect = require("chai").expect; 3 | const syntax = require("../"); 4 | const fs = require("fs"); 5 | 6 | describe("react-native", () => { 7 | it("StyleSheet", () => { 8 | const filename = require.resolve("./fixtures/react-native.mjs"); 9 | let code = fs.readFileSync(filename); 10 | 11 | const document = syntax.parse(code, { 12 | from: filename, 13 | }); 14 | 15 | code = code.toString(); 16 | 17 | expect(document.toString(syntax)).to.equal(code); 18 | expect(document.nodes).to.lengthOf(1); 19 | expect(document.first.nodes).to.lengthOf(1); 20 | expect(document.first.first.nodes).to.lengthOf(2); 21 | expect(document.first.first.first).to.haveOwnProperty("type", "rule"); 22 | expect(document.first.first.first).to.haveOwnProperty("selector", "box"); 23 | expect(document.first.first.last).to.haveOwnProperty("type", "rule"); 24 | expect(document.first.first.last).to.haveOwnProperty("selector", "text"); 25 | 26 | document.nodes.forEach(root => { 27 | expect(root.source).to.haveOwnProperty("input"); 28 | 29 | expect(code).to.includes(root.source.input.css); 30 | expect(root.source.input.css.length).lessThan(code.length); 31 | expect(root.source).to.haveOwnProperty("start").to.haveOwnProperty("line").to.greaterThan(1); 32 | 33 | root.walk(node => { 34 | expect(node).to.haveOwnProperty("source"); 35 | 36 | expect(node.source).to.haveOwnProperty("input").to.haveOwnProperty("css").equal(root.source.input.css); 37 | 38 | expect(node.source).to.haveOwnProperty("start").to.haveOwnProperty("line"); 39 | expect(node.source).to.haveOwnProperty("end").to.haveOwnProperty("line"); 40 | }); 41 | }); 42 | }); 43 | }); 44 | -------------------------------------------------------------------------------- /test/react.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | const expect = require("chai").expect; 3 | const syntax = require("../"); 4 | 5 | describe("react", () => { 6 | it("first line indentation handle", () => { 7 | const code = ` 8 | export default ; 15 | `; 16 | 17 | const document = syntax.parse(code, { 18 | from: "before.js", 19 | }); 20 | 21 | expect(document.toString(syntax)).to.equal(code); 22 | expect(document.nodes).to.lengthOf(1); 23 | expect(document.first.source.input.css).to.match(/^\s+\{/); 24 | expect(document.first.source.start.column).to.equal(1); 25 | expect(document.first.raws.beforeStart).to.match(/\n$/); 26 | expect(document.first.first.raws.before).to.match(/^\s+$/); 27 | expect(document.first.first.source.start.column).to.greaterThan(1); 28 | }); 29 | }); 30 | -------------------------------------------------------------------------------- /test/styled-components.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | const expect = require("chai").expect; 3 | const syntax = require("../"); 4 | const fs = require("fs"); 5 | 6 | describe("styled-components", () => { 7 | it("basic", () => { 8 | const file = require.resolve("./fixtures/styled-components"); 9 | let code = fs.readFileSync(file); 10 | 11 | const document = syntax.parse(code, { 12 | from: file, 13 | }); 14 | 15 | code = code.toString(); 16 | expect(document.toString()).to.equal(code); 17 | expect(document.source).to.haveOwnProperty("lang", "jsx"); 18 | 19 | expect(document.nodes).to.have.lengthOf(1); 20 | expect(document.first.nodes).to.have.lengthOf(8); 21 | 22 | const lines = code.match(/^.+$/gm).slice(3).map(line => (line.replace(/^\s*(.+?);?\s*$/, "$1"))); 23 | document.first.nodes.forEach((decl, i) => { 24 | if (i) { 25 | expect(decl).to.have.property("type", "decl"); 26 | } else { 27 | expect(decl).to.have.property("type", "comment"); 28 | } 29 | expect(decl.toString()).to.equal(lines[i]); 30 | }); 31 | }); 32 | 33 | it("empty template literal", () => { 34 | const code = [ 35 | "function test() {", 36 | " alert`debug`", 37 | " return ``;", 38 | "}", 39 | "", 40 | ].join("\n"); 41 | const document = syntax.parse(code, { 42 | from: "empty_template_literal.js", 43 | }); 44 | expect(document.toString()).to.equal(code); 45 | expect(document.source).to.haveOwnProperty("lang", "jsx"); 46 | expect(document.nodes).to.have.lengthOf(0); 47 | }); 48 | 49 | it("skip javascript syntax error", () => { 50 | const code = "\\`"; 51 | const document = syntax.parse(code, { 52 | from: "syntax_error.js", 53 | }); 54 | expect(document.toString()).to.equal(code); 55 | expect(document.source).to.haveOwnProperty("lang", "jsx"); 56 | expect(document.nodes).to.have.lengthOf(0); 57 | }); 58 | 59 | it("skip @babel/traverse error", () => { 60 | const code = "let a;let a"; 61 | const document = syntax.parse(code, { 62 | from: "traverse_error.js", 63 | }); 64 | expect(document.toString()).to.equal(code); 65 | expect(document.source).to.haveOwnProperty("lang", "jsx"); 66 | expect(document.nodes).to.have.lengthOf(0); 67 | }); 68 | 69 | it("illegal template literal", () => { 70 | const code = [ 71 | "const styled = require(\"styled-components\");", 72 | "styled.div`$\n{display: block}\n${g} {}`", 73 | ].join("\n"); 74 | const document = syntax.parse(code, { 75 | from: "illegal_template_literal.js", 76 | }); 77 | expect(document.toString()).to.equal(code); 78 | expect(document.source).to.haveOwnProperty("lang", "jsx"); 79 | expect(document.nodes).to.have.lengthOf(1); 80 | expect(document.first.nodes).to.have.lengthOf(2); 81 | expect(document.first.first).have.property("type", "rule"); 82 | expect(document.first.first).have.property("selector", "$"); 83 | expect(document.last.last).have.property("type", "rule"); 84 | expect(document.last.last).have.property("selector", "${g}"); 85 | }); 86 | 87 | it("styled.img", () => { 88 | const code = [ 89 | "const styled = require(\"styled-components\");", 90 | "const Image1 = styled.img.attrs({ src: 'url' })`", 91 | " bad-selector {", 92 | " color: red;", 93 | " }", 94 | "`;", 95 | ].join("\n"); 96 | const root = syntax.parse(code, { 97 | from: "styled.img.js", 98 | }); 99 | expect(root.toString()).to.equal(code); 100 | }); 101 | 102 | it("throw CSS syntax error", () => { 103 | const code = [ 104 | "const styled = require(\"styled-components\");", 105 | "styled.div`a{`;", 106 | ].join("\n"); 107 | 108 | expect(() => { 109 | syntax.parse(code, { 110 | from: "css_syntax_error.js", 111 | }); 112 | }).to.throw("css_syntax_error.js:2:12: Unclosed block"); 113 | }); 114 | 115 | it("not skip empty template literal", () => { 116 | const code = [ 117 | "const styled = require(\"styled-components\");", 118 | "styled.div``;", 119 | ].join("\n"); 120 | const root = syntax.parse(code, { 121 | from: "empty_template_literal.js", 122 | }); 123 | expect(root.toString()).to.equal(code); 124 | expect(root.nodes).to.have.lengthOf(1); 125 | }); 126 | 127 | it("fix CSS syntax error", () => { 128 | const code = [ 129 | "const styled = require(\"styled-components\");", 130 | "styled.div`a{`;", 131 | ].join("\n"); 132 | const document = syntax({ 133 | css: "safe-parser", 134 | }).parse(code, { 135 | from: "postcss-safe-parser.js", 136 | }); 137 | expect(document.toString()).to.equal([ 138 | "const styled = require(\"styled-components\");", 139 | "styled.div`a{}`;", 140 | ].join("\n")); 141 | expect(document.source).to.haveOwnProperty("lang", "jsx"); 142 | expect(document.nodes).to.have.lengthOf(1); 143 | expect(document.first.nodes).to.have.lengthOf(1); 144 | expect(document.first.first).have.property("type", "rule"); 145 | expect(document.first.first).have.property("selector", "a"); 146 | }); 147 | 148 | it("fix styled syntax error", () => { 149 | const code = [ 150 | "const styled = require(\"styled-components\");", 151 | "styled.div`${ a } {`", 152 | ].join("\n"); 153 | const document = syntax({ 154 | css: "safe-parser", 155 | }).parse(code, { 156 | from: "styled-safe-parse.js", 157 | }); 158 | expect(document.toString()).to.equal([ 159 | "const styled = require(\"styled-components\");", 160 | "styled.div`${ a } {}`", 161 | ].join("\n")); 162 | expect(document.source).to.haveOwnProperty("lang", "jsx"); 163 | expect(document.nodes).to.have.lengthOf(1); 164 | expect(document.first.nodes).to.have.lengthOf(1); 165 | expect(document.first.first).have.property("type", "rule"); 166 | expect(document.first.first).have.property("selector", "${ a }"); 167 | }); 168 | 169 | it("template literal in prop", () => { 170 | const code = [ 171 | "const styled = require(\"styled-components\");", 172 | "styled.div`margin-${/* sc-custom 'left' */ rtlSwitch}: 12.5px;`", 173 | ].join("\n"); 174 | const document = syntax.parse(code, { 175 | from: "template_literal_in_prop.js", 176 | }); 177 | expect(document.toString()).to.equal(code); 178 | expect(document.source).to.haveOwnProperty("lang", "jsx"); 179 | expect(document.nodes).to.have.lengthOf(1); 180 | expect(document.first.first).to.haveOwnProperty("prop", "margin-${/* sc-custom 'left' */ rtlSwitch}"); 181 | }); 182 | 183 | it("lazy assignment", () => { 184 | const code = [ 185 | "let myDiv;", 186 | "myDiv = require(\"styled-components\").div;", 187 | "myDiv`a{}`;", 188 | ].join("\n"); 189 | const document = syntax.parse(code, { 190 | from: "lazy_assign.js", 191 | }); 192 | expect(document.toString()).to.equal(code); 193 | expect(document.source).to.haveOwnProperty("lang", "jsx"); 194 | expect(document.nodes).to.have.lengthOf(1); 195 | }); 196 | 197 | it("lazy assignment without init", () => { 198 | const code = [ 199 | "myDiv = require(\"styled-components\").div;", 200 | "myDiv`a{}`;", 201 | ].join("\n"); 202 | const document = syntax.parse(code, { 203 | from: "lazy_assign_no_init.js", 204 | }); 205 | expect(document.toString()).to.equal(code); 206 | expect(document.source).to.haveOwnProperty("lang", "jsx"); 207 | expect(document.nodes).to.have.lengthOf(1); 208 | }); 209 | 210 | it("array destructuring assignment", () => { 211 | const code = [ 212 | "const [", 213 | "\tstyledDiv,", 214 | "\t...c", 215 | "] = require(\"styled-components\");", 216 | "styledDiv`a{}`;", 217 | ].join("\n"); 218 | const document = syntax.parse(code, { 219 | from: "arr_destructuring.js", 220 | }); 221 | expect(document.toString()).to.equal(code); 222 | expect(document.source).to.haveOwnProperty("lang", "jsx"); 223 | expect(document.nodes).to.have.lengthOf(1); 224 | }); 225 | 226 | it("object destructuring assignment", () => { 227 | const code = [ 228 | "const {", 229 | "\t// commit", 230 | "\t['div']: styledDiv,", 231 | "\ta,", 232 | "\t...styled", 233 | "} = require(\"styled-components\");", 234 | "styledDiv`a{}`;", 235 | "styled.div`a{}`;", 236 | "a`a{}`;", 237 | ].join("\n"); 238 | const document = syntax.parse(code, { 239 | from: "obj_destructuring.js", 240 | }); 241 | expect(document.toString()).to.equal(code); 242 | expect(document.source).to.haveOwnProperty("lang", "jsx"); 243 | expect(document.nodes).to.have.lengthOf(3); 244 | }); 245 | }); 246 | -------------------------------------------------------------------------------- /test/supports.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | const fs = require("fs"); 3 | const path = require("path"); 4 | const expect = require("chai").expect; 5 | const syntax = require("../"); 6 | 7 | function clean (node) { 8 | if (node.raws) { 9 | delete node.raws.node; 10 | delete node.raws.beforeStart; 11 | delete node.raws.afterEnd; 12 | } 13 | if (node.source) { 14 | delete node.source.opts; 15 | delete node.source.input.css; 16 | delete node.source.input.hasBOM; 17 | node.source.input.file = path.basename(node.source.input.file); 18 | } 19 | delete node.indexes; 20 | delete node.lastEach; 21 | delete node.rawCache; 22 | delete node.document; 23 | if (node.nodes) { 24 | node.nodes = node.nodes.map(clean); 25 | }; 26 | return node; 27 | } 28 | 29 | describe("should support for each CSS in JS package", () => { 30 | [ 31 | "emotion-10.jsx", 32 | "glamorous.jsx", 33 | "interpolation-content.mjs", 34 | "jsx.jsx", 35 | "lit-css.mjs", 36 | "react-emotion.jsx", 37 | "react-native.mjs", 38 | "styled-components.js", 39 | "styled-opts.mjs", 40 | "styled-props.jsx", 41 | "tpl-decl.mjs", 42 | "tpl-in-tpl.mjs", 43 | "tpl-selector.mjs", 44 | "tpl-special.mjs", 45 | "material-ui.jsx", 46 | ].forEach(file => { 47 | it(file, () => { 48 | file = require.resolve("./fixtures/" + file); 49 | const code = fs.readFileSync(file); 50 | const document = syntax.parse(code, { 51 | from: file, 52 | }); 53 | expect(document.source).to.haveOwnProperty("lang", "jsx"); 54 | expect(document.toString()).to.equal(code.toString()); 55 | expect(document.nodes.length).to.greaterThan(0); 56 | const parsed = JSON.stringify(clean(document), 0, "\t"); 57 | // fs.writeFileSync(file + ".json", parsed + "\n"); 58 | expect(parsed).to.equal(fs.readFileSync(file + ".json", "utf8").trim()); 59 | }); 60 | }); 61 | }); 62 | -------------------------------------------------------------------------------- /un-camel-case.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | function unCamelCase (str) { 3 | return str.replace(/[\w-]+/g, (s) => ( 4 | /^[A-Z]?[a-z]*(?:[A-Z][a-z]*)+$/.test(s) 5 | ? s.replace( 6 | /[A-Z]/g, 7 | s => "-" + s.toLowerCase() 8 | ).replace( 9 | /^(o|ms|moz|khtml|epub|(\w+-?)*webkit)(?=-)/i, 10 | "-$1" 11 | ) 12 | : s 13 | )); 14 | } 15 | 16 | module.exports = unCamelCase; 17 | --------------------------------------------------------------------------------