Skip to content

Commit bc08be3

Browse files
authored
fix: manipulation of node structures, proper stringifying (shellscape#73)
* fix: manipulation of node structures, proper stringifying * docs: update Func params description * chore: clean up stale todo
1 parent 8106956 commit bc08be3

9 files changed

+69
-31
lines changed

docs/Func.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,7 @@ The name of the function.
1717
### `params`
1818
Type: `String`<br>
1919

20-
A `String` representation of the body of the function, between parenthesis, including the parenthesis characters. This value will be parsed and the result placed into the `nodes` property.
20+
A `String` representation of the body of the function, between parenthesis, including the parenthesis characters. This value will be parsed and the result placed into the `nodes` property. This value should be considered only a snapshot for reference. To manipulate function parameters, please leverage the `Container.nodes` property.
2121

2222
### `type`
2323
Type: `String`

lib/ValuesStringifier.js

Lines changed: 23 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,13 @@
11
const Stringifier = require('postcss/lib/stringifier');
22

33
module.exports = class LessStringifier extends Stringifier {
4-
basic(node) {
5-
const after = this.raw(node, 'after');
4+
basic(node, value) {
5+
const print = value || node.value;
6+
const after = node.raws.after ? this.raw(node, 'after') || '' : '';
7+
// NOTE: before is handled by postcss in stringifier.body
68

7-
this.builder(node.value, node, 'start');
8-
this.builder(after || '', node, 'end');
9+
this.builder(print, node, 'start');
10+
this.builder(after, node, 'end');
911
}
1012

1113
atword(...args) {
@@ -23,25 +25,30 @@ module.exports = class LessStringifier extends Stringifier {
2325
}
2426

2527
func(node) {
26-
const after = this.raw(node, 'after');
28+
const after = this.raw(node, 'after') || '';
2729

28-
this.builder(node.name + node.params, node, 'start');
29-
this.builder(after || '', node, 'end');
30+
this.builder(`${node.name}(`, node, 'start');
31+
32+
for (const child of node.nodes) {
33+
// since we're duplicating this.body here, we have to handle `before`
34+
// but we don't want the postcss default \n value, so check it's non-empty first
35+
const before = child.raws.before ? this.raw(child, 'before') : '';
36+
if (before) {
37+
this.builder(before);
38+
}
39+
this.stringify(child);
40+
}
41+
42+
this.builder(`)${after}`, node, 'end');
3043
}
3144

3245
interpolation(node) {
33-
const after = this.raw(node, 'after');
34-
35-
this.builder(node.prefix + node.params, node, 'start');
36-
this.builder(after || '', node, 'end');
46+
this.basic(node, node.prefix + node.params);
3747
}
3848

3949
numeric(node) {
40-
const start = node.value + node.unit;
41-
const after = this.raw(node, 'after');
42-
43-
this.builder(start, node, 'start');
44-
this.builder(after || '', node, 'end');
50+
const print = node.value + node.unit;
51+
this.basic(node, print);
4552
}
4653

4754
operator(node) {

lib/index.js

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -13,8 +13,6 @@ const Input = require('postcss/lib/input');
1313
const Parser = require('./ValuesParser');
1414
const Stringifier = require('./ValuesStringifier');
1515

16-
// TODO: walk methods for custom nodes
17-
1816
module.exports = {
1917
parse(css, options) {
2018
const input = new Input(css, options);

lib/nodes/Func.js

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -84,12 +84,17 @@ class Func extends Container {
8484
// use a new parser to parse the params of the function. recursion here makes for easier maint
8585
// we must require this here due to circular dependency resolution
8686
const { parse } = require('../'); // eslint-disable-line global-require
87-
const { nodes: children } = parse(params, opts);
87+
const root = parse(params, opts);
88+
const { nodes: children } = root;
8889

8990
// TODO: correct line and character position (should we just pad the input? probably easiest)
9091
for (const child of children) {
9192
node.push(child);
9293
}
94+
95+
if (root.raws.after) {
96+
node.last.raws.after = root.raws.after;
97+
}
9398
}
9499

95100
parser.end(lastToken);

test/integration.test.js

Lines changed: 18 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,8 @@
1313
*/
1414
const test = require('ava');
1515

16-
const { parse } = require('../lib');
16+
const { nodeToString, parse } = require('../lib');
17+
const Punctuation = require('../lib/nodes/Punctuation');
1718

1819
test('integration', (t) => {
1920
let root = parse(`normal normal 1em/1 'Source Sans Pro', serif`);
@@ -26,3 +27,19 @@ test('integration', (t) => {
2627
root = parse('1 / -1');
2728
t.is(root.nodes.length, 3);
2829
});
30+
31+
test('manipulation', (t) => {
32+
const source = 'rgb(100% 100% 100%)';
33+
const root = parse(source);
34+
const { first } = root;
35+
36+
let string = nodeToString(root);
37+
t.is(source, string);
38+
39+
first.nodes.splice(1, 0, new Punctuation({ value: ',', parent: first }));
40+
first.nodes.splice(3, 0, new Punctuation({ value: ',', parent: first }));
41+
42+
string = nodeToString(root);
43+
t.not(source, string);
44+
t.snapshot(string);
45+
});

test/snapshots/func.test.js.md

Lines changed: 10 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -182,7 +182,7 @@ Generated by [AVA](https://ava.li).
182182
Numeric {
183183
parent: [Circular],
184184
raws: {
185-
after: '',
185+
after: ' ',
186186
before: ' ',
187187
},
188188
source: {
@@ -344,7 +344,7 @@ Generated by [AVA](https://ava.li).
344344
Numeric {
345345
parent: [Circular],
346346
raws: {
347-
after: '',
347+
after: ' ',
348348
before: ' ',
349349
},
350350
source: {
@@ -1314,7 +1314,7 @@ Generated by [AVA](https://ava.li).
13141314
Numeric {
13151315
parent: [Circular],
13161316
raws: {
1317-
after: '',
1317+
after: ' ',
13181318
before: ' ',
13191319
},
13201320
source: {
@@ -1379,7 +1379,7 @@ Generated by [AVA](https://ava.li).
13791379
parent: [Circular],
13801380
quote: '"',
13811381
raws: {
1382-
after: '',
1382+
after: ' ',
13831383
before: ' ',
13841384
},
13851385
source: {
@@ -1443,7 +1443,7 @@ Generated by [AVA](https://ava.li).
14431443
parent: [Circular],
14441444
quote: '"',
14451445
raws: {
1446-
after: '',
1446+
after: ' ',
14471447
before: ' ',
14481448
},
14491449
source: {
@@ -1507,7 +1507,7 @@ Generated by [AVA](https://ava.li).
15071507
parent: [Circular],
15081508
quote: '\'',
15091509
raws: {
1510-
after: '',
1510+
after: ' ',
15111511
before: ' ',
15121512
},
15131513
source: {
@@ -1571,7 +1571,7 @@ Generated by [AVA](https://ava.li).
15711571
parent: [Circular],
15721572
quote: '\'',
15731573
raws: {
1574-
after: '',
1574+
after: ' ',
15751575
before: ' ',
15761576
},
15771577
source: {
@@ -1766,7 +1766,7 @@ Generated by [AVA](https://ava.li).
17661766
isVariable: false,
17671767
parent: [Circular],
17681768
raws: {
1769-
after: '',
1769+
after: ' ',
17701770
before: '',
17711771
},
17721772
source: {
@@ -1858,7 +1858,7 @@ Generated by [AVA](https://ava.li).
18581858
isVariable: false,
18591859
parent: [Circular],
18601860
raws: {
1861-
after: '',
1861+
after: ' ',
18621862
before: ' ',
18631863
},
18641864
source: {
@@ -1950,7 +1950,7 @@ Generated by [AVA](https://ava.li).
19501950
isVariable: false,
19511951
parent: [Circular],
19521952
raws: {
1953-
after: '',
1953+
after: ' ',
19541954
before: ' ',
19551955
},
19561956
source: {

test/snapshots/func.test.js.snap

87 Bytes
Binary file not shown.

test/snapshots/integration.test.js.md

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
# Snapshot report for `test/integration.test.js`
2+
3+
The actual snapshot is saved in `integration.test.js.snap`.
4+
5+
Generated by [AVA](https://ava.li).
6+
7+
## manipulation
8+
9+
> Snapshot 1
10+
11+
'rgb(100%, 100%, 100%)'
107 Bytes
Binary file not shown.

0 commit comments

Comments
 (0)