Skip to content

Commit bf50fad

Browse files
committed
Explicitly annotate GenericAtRule as having optional nodes
1 parent f38dbb0 commit bf50fad

File tree

3 files changed

+197
-172
lines changed

3 files changed

+197
-172
lines changed

pkg/sass-parser/lib/src/statement/container.test.ts

+194-170
Original file line numberDiff line numberDiff line change
@@ -6,183 +6,207 @@ import * as postcss from 'postcss';
66

77
import {GenericAtRule, Root, Rule} from '../..';
88

9-
let root: Root;
109
describe('a container node', () => {
11-
beforeEach(() => {
12-
root = new Root();
13-
});
14-
15-
describe('can add', () => {
16-
it('a single Sass node', () => {
17-
const rule = new Rule({selector: '.foo'});
18-
root.append(rule);
19-
expect(root.nodes).toEqual([rule]);
20-
expect(rule.parent).toBe(root);
21-
});
22-
23-
it('a list of Sass nodes', () => {
24-
const rule1 = new Rule({selector: '.foo'});
25-
const rule2 = new Rule({selector: '.bar'});
26-
root.append([rule1, rule2]);
27-
expect(root.nodes).toEqual([rule1, rule2]);
28-
expect(rule1.parent).toBe(root);
29-
expect(rule2.parent).toBe(root);
30-
});
31-
32-
it('a Sass root node', () => {
33-
const rule1 = new Rule({selector: '.foo'});
34-
const rule2 = new Rule({selector: '.bar'});
35-
const otherRoot = new Root({nodes: [rule1, rule2]});
36-
root.append(otherRoot);
37-
expect(root.nodes[0]).toBeInstanceOf(Rule);
38-
expect(root.nodes[0]).toHaveInterpolation(
39-
'selectorInterpolation',
40-
'.foo',
41-
);
42-
expect(root.nodes[1]).toBeInstanceOf(Rule);
43-
expect(root.nodes[1]).toHaveInterpolation(
44-
'selectorInterpolation',
45-
'.bar',
46-
);
47-
expect(root.nodes[0].parent).toBe(root);
48-
expect(root.nodes[1].parent).toBe(root);
49-
expect(rule1.parent).toBeUndefined();
50-
expect(rule2.parent).toBeUndefined();
10+
describe('with nodes', () => {
11+
let root: Root;
12+
beforeEach(() => {
13+
root = new Root();
5114
});
5215

53-
it('a PostCSS rule node', () => {
54-
const node = postcss.parse('.foo {}').nodes[0];
55-
root.append(node);
56-
expect(root.nodes[0]).toBeInstanceOf(Rule);
57-
expect(root.nodes[0]).toHaveInterpolation(
58-
'selectorInterpolation',
59-
'.foo',
60-
);
61-
expect(root.nodes[0].parent).toBe(root);
62-
expect(root.nodes[0].source).toBe(node.source);
63-
expect(node.parent).toBeUndefined();
64-
});
65-
66-
it('a PostCSS at-rule node', () => {
67-
const node = postcss.parse('@foo bar').nodes[0];
68-
root.append(node);
69-
expect(root.nodes[0]).toBeInstanceOf(GenericAtRule);
70-
expect(root.nodes[0]).toHaveInterpolation('nameInterpolation', 'foo');
71-
expect(root.nodes[0]).toHaveInterpolation('paramsInterpolation', 'bar');
72-
expect(root.nodes[0].parent).toBe(root);
73-
expect(root.nodes[0].source).toBe(node.source);
74-
expect(node.parent).toBeUndefined();
75-
});
76-
77-
it('a list of PostCSS nodes', () => {
78-
const rule1 = new postcss.Rule({selector: '.foo'});
79-
const rule2 = new postcss.Rule({selector: '.bar'});
80-
root.append([rule1, rule2]);
81-
expect(root.nodes[0]).toBeInstanceOf(Rule);
82-
expect(root.nodes[0]).toHaveInterpolation(
83-
'selectorInterpolation',
84-
'.foo',
85-
);
86-
expect(root.nodes[1]).toBeInstanceOf(Rule);
87-
expect(root.nodes[1]).toHaveInterpolation(
88-
'selectorInterpolation',
89-
'.bar',
90-
);
91-
expect(root.nodes[0].parent).toBe(root);
92-
expect(root.nodes[1].parent).toBe(root);
93-
expect(rule1.parent).toBeUndefined();
94-
expect(rule2.parent).toBeUndefined();
95-
});
96-
97-
it('a PostCSS root node', () => {
98-
const rule1 = new postcss.Rule({selector: '.foo'});
99-
const rule2 = new postcss.Rule({selector: '.bar'});
100-
const otherRoot = new postcss.Root({nodes: [rule1, rule2]});
101-
root.append(otherRoot);
102-
expect(root.nodes[0]).toBeInstanceOf(Rule);
103-
expect(root.nodes[0]).toHaveInterpolation(
104-
'selectorInterpolation',
105-
'.foo',
106-
);
107-
expect(root.nodes[1]).toBeInstanceOf(Rule);
108-
expect(root.nodes[1]).toHaveInterpolation(
109-
'selectorInterpolation',
110-
'.bar',
111-
);
112-
expect(root.nodes[0].parent).toBe(root);
113-
expect(root.nodes[1].parent).toBe(root);
114-
expect(rule1.parent).toBeUndefined();
115-
expect(rule2.parent).toBeUndefined();
116-
});
117-
118-
it("a single Sass node's properties", () => {
119-
root.append({selectorInterpolation: '.foo'});
120-
expect(root.nodes[0]).toBeInstanceOf(Rule);
121-
expect(root.nodes[0]).toHaveInterpolation(
122-
'selectorInterpolation',
123-
'.foo',
124-
);
125-
expect(root.nodes[0].parent).toBe(root);
126-
});
127-
128-
it("a single PostCSS node's properties", () => {
129-
root.append({selector: '.foo'});
130-
expect(root.nodes[0]).toBeInstanceOf(Rule);
131-
expect(root.nodes[0]).toHaveInterpolation(
132-
'selectorInterpolation',
133-
'.foo',
134-
);
135-
expect(root.nodes[0].parent).toBe(root);
136-
});
137-
138-
it('a list of properties', () => {
139-
root.append(
140-
{selectorInterpolation: '.foo'},
141-
{selectorInterpolation: '.bar'},
142-
);
143-
expect(root.nodes[0]).toBeInstanceOf(Rule);
144-
expect(root.nodes[0]).toHaveInterpolation(
145-
'selectorInterpolation',
146-
'.foo',
147-
);
148-
expect(root.nodes[1]).toBeInstanceOf(Rule);
149-
expect(root.nodes[1]).toHaveInterpolation(
150-
'selectorInterpolation',
151-
'.bar',
152-
);
153-
expect(root.nodes[0].parent).toBe(root);
154-
expect(root.nodes[1].parent).toBe(root);
155-
});
156-
157-
it('a plain CSS string', () => {
158-
root.append('.foo {}');
159-
expect(root.nodes[0]).toBeInstanceOf(Rule);
160-
expect(root.nodes[0]).toHaveInterpolation(
161-
'selectorInterpolation',
162-
'.foo',
163-
);
164-
expect(root.nodes[0].parent).toBe(root);
16+
describe('can add', () => {
17+
it('a single Sass node', () => {
18+
const rule = new Rule({selector: '.foo'});
19+
root.append(rule);
20+
expect(root.nodes).toEqual([rule]);
21+
expect(rule.parent).toBe(root);
22+
});
23+
24+
it('a list of Sass nodes', () => {
25+
const rule1 = new Rule({selector: '.foo'});
26+
const rule2 = new Rule({selector: '.bar'});
27+
root.append([rule1, rule2]);
28+
expect(root.nodes).toEqual([rule1, rule2]);
29+
expect(rule1.parent).toBe(root);
30+
expect(rule2.parent).toBe(root);
31+
});
32+
33+
it('a Sass root node', () => {
34+
const rule1 = new Rule({selector: '.foo'});
35+
const rule2 = new Rule({selector: '.bar'});
36+
const otherRoot = new Root({nodes: [rule1, rule2]});
37+
root.append(otherRoot);
38+
expect(root.nodes[0]).toBeInstanceOf(Rule);
39+
expect(root.nodes[0]).toHaveInterpolation(
40+
'selectorInterpolation',
41+
'.foo',
42+
);
43+
expect(root.nodes[1]).toBeInstanceOf(Rule);
44+
expect(root.nodes[1]).toHaveInterpolation(
45+
'selectorInterpolation',
46+
'.bar',
47+
);
48+
expect(root.nodes[0].parent).toBe(root);
49+
expect(root.nodes[1].parent).toBe(root);
50+
expect(rule1.parent).toBeUndefined();
51+
expect(rule2.parent).toBeUndefined();
52+
});
53+
54+
it('a PostCSS rule node', () => {
55+
const node = postcss.parse('.foo {}').nodes[0];
56+
root.append(node);
57+
expect(root.nodes[0]).toBeInstanceOf(Rule);
58+
expect(root.nodes[0]).toHaveInterpolation(
59+
'selectorInterpolation',
60+
'.foo',
61+
);
62+
expect(root.nodes[0].parent).toBe(root);
63+
expect(root.nodes[0].source).toBe(node.source);
64+
expect(node.parent).toBeUndefined();
65+
});
66+
67+
it('a PostCSS at-rule node', () => {
68+
const node = postcss.parse('@foo bar').nodes[0];
69+
root.append(node);
70+
expect(root.nodes[0]).toBeInstanceOf(GenericAtRule);
71+
expect(root.nodes[0]).toHaveInterpolation('nameInterpolation', 'foo');
72+
expect(root.nodes[0]).toHaveInterpolation('paramsInterpolation', 'bar');
73+
expect(root.nodes[0].parent).toBe(root);
74+
expect(root.nodes[0].source).toBe(node.source);
75+
expect(node.parent).toBeUndefined();
76+
});
77+
78+
it('a list of PostCSS nodes', () => {
79+
const rule1 = new postcss.Rule({selector: '.foo'});
80+
const rule2 = new postcss.Rule({selector: '.bar'});
81+
root.append([rule1, rule2]);
82+
expect(root.nodes[0]).toBeInstanceOf(Rule);
83+
expect(root.nodes[0]).toHaveInterpolation(
84+
'selectorInterpolation',
85+
'.foo',
86+
);
87+
expect(root.nodes[1]).toBeInstanceOf(Rule);
88+
expect(root.nodes[1]).toHaveInterpolation(
89+
'selectorInterpolation',
90+
'.bar',
91+
);
92+
expect(root.nodes[0].parent).toBe(root);
93+
expect(root.nodes[1].parent).toBe(root);
94+
expect(rule1.parent).toBeUndefined();
95+
expect(rule2.parent).toBeUndefined();
96+
});
97+
98+
it('a PostCSS root node', () => {
99+
const rule1 = new postcss.Rule({selector: '.foo'});
100+
const rule2 = new postcss.Rule({selector: '.bar'});
101+
const otherRoot = new postcss.Root({nodes: [rule1, rule2]});
102+
root.append(otherRoot);
103+
expect(root.nodes[0]).toBeInstanceOf(Rule);
104+
expect(root.nodes[0]).toHaveInterpolation(
105+
'selectorInterpolation',
106+
'.foo',
107+
);
108+
expect(root.nodes[1]).toBeInstanceOf(Rule);
109+
expect(root.nodes[1]).toHaveInterpolation(
110+
'selectorInterpolation',
111+
'.bar',
112+
);
113+
expect(root.nodes[0].parent).toBe(root);
114+
expect(root.nodes[1].parent).toBe(root);
115+
expect(rule1.parent).toBeUndefined();
116+
expect(rule2.parent).toBeUndefined();
117+
});
118+
119+
it("a single Sass node's properties", () => {
120+
root.append({selectorInterpolation: '.foo'});
121+
expect(root.nodes[0]).toBeInstanceOf(Rule);
122+
expect(root.nodes[0]).toHaveInterpolation(
123+
'selectorInterpolation',
124+
'.foo',
125+
);
126+
expect(root.nodes[0].parent).toBe(root);
127+
});
128+
129+
it("a single PostCSS node's properties", () => {
130+
root.append({selector: '.foo'});
131+
expect(root.nodes[0]).toBeInstanceOf(Rule);
132+
expect(root.nodes[0]).toHaveInterpolation(
133+
'selectorInterpolation',
134+
'.foo',
135+
);
136+
expect(root.nodes[0].parent).toBe(root);
137+
});
138+
139+
it('a list of properties', () => {
140+
root.append(
141+
{selectorInterpolation: '.foo'},
142+
{selectorInterpolation: '.bar'},
143+
);
144+
expect(root.nodes[0]).toBeInstanceOf(Rule);
145+
expect(root.nodes[0]).toHaveInterpolation(
146+
'selectorInterpolation',
147+
'.foo',
148+
);
149+
expect(root.nodes[1]).toBeInstanceOf(Rule);
150+
expect(root.nodes[1]).toHaveInterpolation(
151+
'selectorInterpolation',
152+
'.bar',
153+
);
154+
expect(root.nodes[0].parent).toBe(root);
155+
expect(root.nodes[1].parent).toBe(root);
156+
});
157+
158+
it('a plain CSS string', () => {
159+
root.append('.foo {}');
160+
expect(root.nodes[0]).toBeInstanceOf(Rule);
161+
expect(root.nodes[0]).toHaveInterpolation(
162+
'selectorInterpolation',
163+
'.foo',
164+
);
165+
expect(root.nodes[0].parent).toBe(root);
166+
});
167+
168+
it('a list of plain CSS strings', () => {
169+
root.append(['.foo {}', '.bar {}']);
170+
expect(root.nodes[0]).toBeInstanceOf(Rule);
171+
expect(root.nodes[0]).toHaveInterpolation(
172+
'selectorInterpolation',
173+
'.foo',
174+
);
175+
expect(root.nodes[1]).toBeInstanceOf(Rule);
176+
expect(root.nodes[1]).toHaveInterpolation(
177+
'selectorInterpolation',
178+
'.bar',
179+
);
180+
expect(root.nodes[0].parent).toBe(root);
181+
expect(root.nodes[1].parent).toBe(root);
182+
});
183+
184+
it('undefined', () => {
185+
root.append(undefined);
186+
expect(root.nodes).toHaveLength(0);
187+
});
165188
});
189+
});
166190

167-
it('a list of plain CSS strings', () => {
168-
root.append(['.foo {}', '.bar {}']);
169-
expect(root.nodes[0]).toBeInstanceOf(Rule);
170-
expect(root.nodes[0]).toHaveInterpolation(
171-
'selectorInterpolation',
172-
'.foo',
173-
);
174-
expect(root.nodes[1]).toBeInstanceOf(Rule);
175-
expect(root.nodes[1]).toHaveInterpolation(
176-
'selectorInterpolation',
177-
'.bar',
178-
);
179-
expect(root.nodes[0].parent).toBe(root);
180-
expect(root.nodes[1].parent).toBe(root);
191+
describe('without nodes', () => {
192+
let rule: GenericAtRule;
193+
beforeEach(() => {
194+
rule = new GenericAtRule({name: 'foo'});
181195
});
182196

183-
it('undefined', () => {
184-
root.append(undefined);
185-
expect(root.nodes).toHaveLength(0);
197+
describe('can add', () => {
198+
it('a node', () => {
199+
rule.append('@bar');
200+
expect(rule.nodes).not.toBeUndefined();
201+
expect(rule.nodes![0]).toBeInstanceOf(GenericAtRule);
202+
expect(rule.nodes![0]).toHaveInterpolation('nameInterpolation', 'bar');
203+
});
204+
205+
it('undefined', () => {
206+
rule.append(undefined);
207+
expect(rule.nodes).not.toBeUndefined();
208+
expect(rule.nodes).toHaveLength(0);
209+
});
186210
});
187211
});
188212
});

pkg/sass-parser/lib/src/statement/generic-at-rule.ts

+2-2
Original file line numberDiff line numberDiff line change
@@ -72,7 +72,7 @@ export class GenericAtRule
7272
readonly sassType = 'atrule' as const;
7373
declare parent: StatementWithChildren | undefined;
7474
declare raws: GenericAtRuleRaws;
75-
declare nodes: ChildNode[];
75+
declare nodes: ChildNode[] | undefined;
7676

7777
get name(): string {
7878
return this.nameInterpolation.toString();
@@ -207,7 +207,7 @@ export class GenericAtRule
207207

208208
/** @hidden */
209209
normalize(node: NewNode, sample?: postcss.Node): ChildNode[] {
210-
return normalize(this, node, sample);
210+
return normalize(this as StatementWithChildren, node, sample);
211211
}
212212
}
213213

0 commit comments

Comments
 (0)