├── .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 | [](https://www.npmjs.com/package/postcss-jsx)
5 | [](https://travis-ci.org/gucong3000/postcss-jsx)
6 | [](https://travis-ci.org/gucong3000/postcss-syntaxes)
7 | [](https://codecov.io/gh/gucong3000/postcss-jsx)
8 | [](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
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 |
--------------------------------------------------------------------------------