Skip to content
This repository was archived by the owner on Feb 9, 2023. It is now read-only.

Commit ef86196

Browse files
committed
Add an unstable_subtitute option that replaces all literal expressions with a sass-like dummy value
1 parent e37d564 commit ef86196

File tree

5 files changed

+148
-0
lines changed

5 files changed

+148
-0
lines changed

substitute-javascript.js

Lines changed: 71 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,71 @@
1+
'use strict';
2+
3+
const literalsKey = Symbol('Literals');
4+
const replacementsKey = Symbol('Replaced properties');
5+
6+
function addSubstitution(item, key, literals) {
7+
item[replacementsKey] = item[replacementsKey] || [];
8+
9+
literals.forEach((literal) => {
10+
const position = item[key].indexOf(literal);
11+
12+
if (position !== -1) {
13+
const substitution = '$dummyValue' + position;
14+
15+
item[replacementsKey].push({
16+
key,
17+
original: literal,
18+
substitution,
19+
});
20+
21+
item[key] = item[key].replace(literal, substitution);
22+
}
23+
});
24+
}
25+
26+
exports.addSubstitutions = (node, css, opts) => {
27+
if (typeof node.walk !== 'function') return;
28+
29+
if (css && opts) {
30+
const offset = opts.quasis[0].start;
31+
32+
node[literalsKey] = [];
33+
34+
opts.expressions.forEach(({ start, end }) => {
35+
node[literalsKey].push('${' + css.substring(start - offset, end - offset) + '}');
36+
});
37+
}
38+
39+
if (!node[literalsKey]) return;
40+
41+
node.walk((item) => {
42+
if (item.type === 'atrule') {
43+
addSubstitution(item, 'name', node[literalsKey]);
44+
addSubstitution(item, 'params', node[literalsKey]);
45+
} else if (item.type === 'rule') {
46+
addSubstitution(item, 'selector', node[literalsKey]);
47+
} else if (item.type === 'decl') {
48+
addSubstitution(item, 'prop', node[literalsKey]);
49+
addSubstitution(item, 'value', node[literalsKey]);
50+
}
51+
});
52+
};
53+
54+
exports.removeSubstitutions = (node) => {
55+
if (typeof node.walk !== 'function') return;
56+
57+
node.walk((item) => {
58+
if (!item[replacementsKey]) {
59+
return;
60+
}
61+
62+
item[replacementsKey].forEach((replacement) => {
63+
item[replacement.key] = item[replacement.key].replace(
64+
replacement.substitution,
65+
replacement.original,
66+
);
67+
});
68+
69+
delete item[replacementsKey];
70+
});
71+
};

template-parse.js

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
'use strict';
22

33
const Input = require('postcss/lib/input');
4+
const substitutions = require('./substitute-javascript');
45
const TemplateParser = require('./template-parser');
56

67
function templateParse(css, opts) {
@@ -13,6 +14,11 @@ function templateParse(css, opts) {
1314

1415
parser.parse();
1516

17+
if (opts.syntax.config.unstable_substitute) {
18+
parser.root.unstable_substitute = true;
19+
substitutions.addSubstitutions(parser.root, css, opts);
20+
}
21+
1622
return parser.root;
1723
}
1824

template-safe-parse.js

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
'use strict';
22

33
const Input = require('postcss/lib/input');
4+
const substitutions = require('./substitute-javascript');
45
const TemplateSafeParser = require('./template-safe-parser');
56

67
function templateSafeParse(css, opts) {
@@ -13,6 +14,11 @@ function templateSafeParse(css, opts) {
1314

1415
parser.parse();
1516

17+
if (opts.syntax.config.unstable_substitute) {
18+
parser.root.unstable_substitute = true;
19+
substitutions.addSubstitutions(parser.root, css, opts);
20+
}
21+
1622
return parser.root;
1723
}
1824

template-stringify.js

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,18 @@
11
'use strict';
22

3+
const substitutions = require('./substitute-javascript');
34
const TemplateStringifier = require('./template-stringifier');
45

56
module.exports = function TemplateStringify(node, builder) {
7+
if (node.unstable_substitute) {
8+
substitutions.removeSubstitutions(node);
9+
}
10+
611
const str = new TemplateStringifier(builder);
712

813
str.stringify(node);
14+
15+
if (node.unstable_substitute) {
16+
substitutions.addSubstitutions(node);
17+
}
918
};

test/styled-components.js

Lines changed: 56 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -323,4 +323,60 @@ describe('styled-components', () => {
323323
expect(document.source).toHaveProperty('lang', 'jsx');
324324
expect(document.nodes).toHaveLength(3);
325325
});
326+
327+
it('re-writes various stuff to substitutes', () => {
328+
const code = `
329+
import styled from 'styled-components';
330+
331+
const C = styled.div\`
332+
@media (min-width: \${t => t.test}) {
333+
color: \${p => p.width};
334+
\${prop}: green;
335+
background-\${prop}: green;
336+
\${decl};
337+
}
338+
\`;
339+
`;
340+
341+
const document = syntax({ unstable_substitute: true }).parse(code);
342+
343+
expect(document.source).toHaveProperty('lang', 'jsx');
344+
345+
expect(document.nodes).toHaveLength(1);
346+
347+
expect(document).toMatchObject({
348+
nodes: [
349+
{
350+
nodes: [
351+
{
352+
type: 'atrule',
353+
params: '(min-width: $dummyValue12)',
354+
nodes: [
355+
{
356+
type: 'decl',
357+
prop: 'color',
358+
value: '$dummyValue0',
359+
},
360+
{
361+
type: 'decl',
362+
prop: '$dummyValue0',
363+
value: 'green',
364+
},
365+
{
366+
type: 'decl',
367+
prop: 'background-$dummyValue11',
368+
value: 'green',
369+
},
370+
{
371+
type: 'literal',
372+
},
373+
],
374+
},
375+
],
376+
},
377+
],
378+
});
379+
380+
expect(document.toString()).toBe(code);
381+
});
326382
});

0 commit comments

Comments
 (0)