Skip to content

Commit 1e48882

Browse files
committedApr 19, 2018
feat: Require the :scope pseudo for root states.
- Buggy getBlockNode removed from block-intermediates - block-intermediates made a private module of BlockParser - Unrelated: Fix webpack plugin file watcher for auto rebuilds
1 parent e0c369b commit 1e48882

30 files changed

+285
-349
lines changed
 

Diff for: ‎ARCHITECTURE.md

+1-1
Original file line numberDiff line numberDiff line change
@@ -175,7 +175,7 @@ We can easily conceptualize the `RewriteMapping` data for each element in develo
175175
```javascript
176176
// For Element 1:
177177
// - `.class-0` is always applied
178-
// - [state|active] is *only* applied when `isActive` is true
178+
// - `:scope[state|active]` is *only* applied when `isActive` is true
179179
const el1Classes = [
180180
"block__class-0",
181181
isActive && "block__class-0--active"

Diff for: ‎packages/@css-blocks/core/src/Analyzer/validations/property-conflict-validator.ts

+1
Original file line numberDiff line numberDiff line change
@@ -55,6 +55,7 @@ function evaluate(obj: Style, propToRules: PropMap, conflicts: ConflictMap) {
5555
// If these styles are from the same block, abort!
5656
if (other.style.block === self.style.block) { continue; }
5757

58+
5859
// Get the declarations for this specific property.
5960
let selfDecl = self.declarations.get(prop);
6061
let otherDecl = other.declarations.get(prop);

Diff for: ‎packages/@css-blocks/core/src/BlockCompiler/ConflictResolver.ts

+16-5
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,9 @@
11
import { assertNever } from "@opticss/util";
22
import { CompoundSelector, ParsedSelector, parseSelector, postcss, postcssSelectorParser as selectorParser } from "opticss";
33

4-
import { getBlockNode } from "../BlockParser";
4+
import { isRootNode, isClassNode, isAttributeNode, toAttrToken } from "../BlockParser";
55
import { RESOLVE_RE } from "../BlockSyntax";
6-
import { Block, Style } from "../BlockTree";
6+
import { Block, BlockClass, Style } from "../BlockTree";
77
import { ResolvedConfiguration } from "../configuration";
88
import * as errors from "../errors";
99
import { QueryKeySelector } from "../query";
@@ -88,11 +88,22 @@ export class ConflictResolver {
8888
let parsedSelectors = block.getParsedSelectors(rule);
8989
parsedSelectors.forEach((sel) => {
9090
let key = sel.key;
91+
let obj: Style | null = null;
92+
let container: BlockClass | null;
9193

9294
// Fetch the associated `Style`. If does not exist (ex: malformed selector), skip.
93-
let blockNode = getBlockNode(key);
94-
if (!blockNode) { return; }
95-
let obj: Style | null = block.nodeAndTypeToStyle(blockNode);
95+
for (let node of key.nodes) {
96+
if (isRootNode(node)) {
97+
container = obj = block.rootClass;
98+
}
99+
if (isClassNode(node)) {
100+
container = obj = block.getClass(node.value);
101+
}
102+
else if (isAttributeNode(node)) {
103+
obj = container!.getAttributeValue(toAttrToken(node));
104+
}
105+
}
106+
96107
if (!obj) { return; }
97108

98109
// Fetch the set of Style conflicts. If the Style has already

Diff for: ‎packages/@css-blocks/core/src/BlockParser/features/construct-block.ts

+29-52
Original file line numberDiff line numberDiff line change
@@ -1,15 +1,13 @@
1-
import { assertNever } from "@opticss/util";
21
import { CompoundSelector, ParsedSelector, postcss, postcssSelectorParser as selectorParser } from "opticss";
32

4-
import { Block, Style } from "../../BlockTree";
3+
import { Block, BlockClass, Style } from "../../BlockTree";
54
import * as errors from "../../errors";
65
import { selectorSourceLocation as loc, sourceLocation } from "../../SourceLocation";
76
import {
87
BlockNodeAndType,
98
BlockType,
109
NodeAndType,
1110
blockTypeName,
12-
getBlockNode,
1311
isAttributeNode,
1412
isClassLevelObject,
1513
isClassNode,
@@ -71,53 +69,32 @@ export async function constructBlock(root: postcss.Root, block: Block, file: str
7169
// For each `CompoundSelector` in this rule, configure the `Block` object
7270
// depending on the BlockType.
7371
while (currentCompoundSel) {
72+
7473
let isKey = (keySel === currentCompoundSel);
75-
let obj = getBlockNode(currentCompoundSel);
76-
if (obj) {
77-
switch (obj.blockType) {
78-
79-
// If type `block`, track all property concerns on the block object
80-
// itself, excluding any inheritance properties. Make sure to
81-
// process any inheritance properties present in this ruleset.
82-
case BlockType.root:
83-
if (!isKey) { break; }
84-
styleRuleTuples.add([block.rootClass, rule]);
85-
break;
86-
87-
// If a local attribute selector, ensure the attribute is registered with
88-
// the parent block and track add all property concerns from this
89-
// ruleset. If a foreign attribute, do nothing (validation happened earlier).
90-
case BlockType.attribute:
91-
if (obj.blockName) { break; }
92-
let attr = block.rootClass.ensureAttributeValue(toAttrToken(obj.node));
93-
if (!isKey) { break; }
94-
styleRuleTuples.add([attr, rule]);
95-
break;
96-
97-
// If a class selector, ensure this class is registered with the
98-
// parent block and track all property concerns from this ruleset.
99-
case BlockType.class:
100-
let blockClass = block.ensureClass(obj.node.value);
101-
if (!isKey) { break; }
102-
styleRuleTuples.add([blockClass, rule]);
103-
break;
104-
105-
// If a classAttribute selector, ensure the class is registered with
106-
// the parent block, and the attribute is registered with this class.
107-
// Track all property concerns from this ruleset.
108-
case BlockType.classAttribute:
109-
let classNode = obj.node.prev();
110-
let classObj = block.ensureClass(classNode.value!);
111-
let classAttr = classObj.ensureAttributeValue(toAttrToken(obj.node));
112-
if (!isKey) { break; }
113-
styleRuleTuples.add([classAttr, rule]);
114-
break;
115-
default:
116-
assertNever(obj);
74+
let blockClass: BlockClass | undefined = undefined;
75+
let foundStyles: Style[] = [];
76+
77+
for (let node of currentCompoundSel.nodes) {
78+
if (isRootNode(node)){
79+
blockClass = block.rootClass;
80+
}
81+
else if (isClassNode(node)){
82+
blockClass = block.ensureClass(node.value);
83+
}
84+
else if (isAttributeNode(node)){
85+
// The fact that a base class exists for all state selectors is validated elsewhere
86+
foundStyles.push(blockClass!.ensureAttributeValue(toAttrToken(node)));
11787
}
88+
11889
}
90+
// If we haven't found any terminating states, we're targeting the discovered Block class.
91+
if (blockClass && !foundStyles.length) { foundStyles.push(blockClass); }
92+
93+
// If this is the key selector, save this ruleset on the created style.
94+
if (isKey) {
95+
foundStyles.map(s => styleRuleTuples.add([s, rule]));
96+
}
11997

120-
// Move on to the next compound selector.
12198
currentCompoundSel = currentCompoundSel.next && currentCompoundSel.next.selector;
12299
}
123100
});
@@ -325,14 +302,14 @@ function assertBlockObject(block: Block, sel: CompoundSelector, rule: postcss.Ru
325302
);
326303
}
327304
if (!found) {
328-
found = { node: n, blockType: BlockType.attribute };
329-
} else if (found.blockType === BlockType.class) {
330-
found = { node: n, blockType: BlockType.classAttribute };
331-
} else if (found.blockType === BlockType.root) {
332305
throw new errors.InvalidBlockSyntax(
333-
`It's redundant to specify a state with an explicit .root: ${rule.selector}`,
334-
loc(file, rule, found.node),
306+
`States without an explicit :scope or class selector are not yet supported: ${rule.selector}`,
307+
loc(file, rule, n),
335308
);
309+
} else if (found.blockType === BlockType.class || found.blockType === BlockType.classAttribute) {
310+
found = { node: n, blockType: BlockType.classAttribute };
311+
} else if (found.blockType === BlockType.root || found.blockType === BlockType.attribute) {
312+
found = { node: n, blockType: BlockType.attribute };
336313
}
337314
}
338315

Diff for: ‎packages/@css-blocks/core/src/BlockSyntax/BlockPath.ts

+5-2
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,7 @@ interface ClassToken {
2020
export interface IAttrToken {
2121
namespace?: string;
2222
name: string;
23-
value?: string;
23+
value: string;
2424
}
2525

2626
export interface AttrToken extends IAttrToken {
@@ -146,6 +146,8 @@ export class BlockPath {
146146
*/
147147
private addToken(token: Partial<Token>, isUserProvided: boolean): void {
148148

149+
if (!token.type) { return; }
150+
149151
// Final validation of incoming data. Blocks may have no name. State attribute must have a namespace.
150152
if (!isBlock(token) && !hasName(token)) { this.throw(ERRORS.noname); }
151153
if (isAttribute(token) && !isValidNamespace(token)) { this.throw(ERRORS.namespace); }
@@ -179,7 +181,7 @@ export class BlockPath {
179181
let char,
180182
working = "",
181183
walker = this.walker,
182-
token: Partial<Token> = { type: "block" };
184+
token: Partial<Token> | undefined = { type: "block" };
183185

184186
while (char = walker.next()) {
185187

@@ -193,6 +195,7 @@ export class BlockPath {
193195
if (working === ROOT_CLASS) {
194196
this.addToken({ type: "class", name: ROOT_CLASS }, true);
195197
working = "";
198+
token = {};
196199
break;
197200
}
198201
else {

Diff for: ‎packages/@css-blocks/core/src/BlockTree/Attribute.ts

+1-1
Original file line numberDiff line numberDiff line change
@@ -139,7 +139,7 @@ export class Attribute extends Inheritable<Attribute, Block, BlockClass, AttrVal
139139
* @returns The Attribute's attribute selector.
140140
*/
141141
asSource(value?: string): string {
142-
return (this.blockClass.isRoot ? "" : this.blockClass.asSource()) + this.unqualifiedSource(value);
142+
return this.blockClass.asSource() + this.unqualifiedSource(value);
143143
}
144144

145145
/**

Diff for: ‎packages/@css-blocks/core/src/BlockTree/Block.ts

+5-38
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import { MultiMap, ObjectDictionary, assertNever } from "@opticss/util";
1+
import { MultiMap, ObjectDictionary } from "@opticss/util";
22
import { whatever } from "@opticss/util";
33
import {
44
CompoundSelector,
@@ -8,12 +8,7 @@ import {
88
postcssSelectorParser as selectorParser,
99
} from "opticss";
1010

11-
import {
12-
BlockType,
13-
NodeAndType,
14-
isAttributeNode,
15-
isClassNode,
16-
} from "../BlockParser";
11+
import { isAttributeNode, isClassNode } from "../BlockParser";
1712
import { isRootNode, toAttrToken } from "../BlockParser";
1813
import { BlockPath, CLASS_NAME_IDENT, ROOT_CLASS } from "../BlockSyntax";
1914
import { ResolvedConfiguration } from "../configuration";
@@ -279,34 +274,8 @@ export class Block
279274
return map;
280275
}
281276

282-
/**
283-
* Fetch a the cached `Style` from `Block` given `NodeAndType`.
284-
* @param obj The `NodeAndType` object to use for `Style` lookup.
285-
*/
286-
nodeAndTypeToStyle(obj: NodeAndType): Styles | null {
287-
switch (obj.blockType) {
288-
case BlockType.root:
289-
return this.rootClass;
290-
case BlockType.attribute:
291-
return this.rootClass.getAttributeValue(toAttrToken(obj.node));
292-
case BlockType.class:
293-
return this.getClass(obj.node.value);
294-
case BlockType.classAttribute:
295-
let classNode = obj.node.prev();
296-
let classObj = this.getClass(classNode.value!);
297-
if (classObj) {
298-
return classObj.getAttributeValue(toAttrToken(obj.node));
299-
}
300-
return null;
301-
default:
302-
return assertNever(obj);
303-
}
304-
}
305-
306277
nodeAsStyle(node: selectorParser.Node): [Styles, number] | null {
307-
if (isRootNode(node)) {
308-
return [this.rootClass, 0];
309-
} else if (selectorParser.isTag(node)) {
278+
if (selectorParser.isTag(node)) {
310279
let otherBlock = this.getReferencedBlock(node.value);
311280
if (otherBlock) {
312281
let next = node.next();
@@ -341,11 +310,9 @@ export class Block
341310
} else {
342311
return null;
343312
}
344-
} else if (selectorParser.isClassName(node)) {
313+
} else if (selectorParser.isClassName(node) || isRootNode(node)) {
345314
let klass = this.getClass(node.value);
346-
if (klass === null) {
347-
return null;
348-
}
315+
if (klass === null) { return null; }
349316
let next = node.next();
350317
if (next && isAttributeNode(next)) {
351318
let attr = klass.getAttributeValue(toAttrToken(next));

Diff for: ‎packages/@css-blocks/core/src/BlockTree/BlockClass.ts

+2-4
Original file line numberDiff line numberDiff line change
@@ -109,9 +109,8 @@ export class BlockClass extends Style<BlockClass, Block, Block, Attribute> {
109109
*/
110110
public getAttributeValue(token: AttrToken | string): AttrValue | null {
111111
token = ensureToken(token);
112-
let value = token.value || ATTR_PRESENT;
113112
let attr = this.getAttribute(token);
114-
return attr ? attr.getValue(value) || null : null;
113+
return attr ? attr.getValue(token.value) || null : null;
115114
}
116115

117116
/**
@@ -136,8 +135,7 @@ export class BlockClass extends Style<BlockClass, Block, Block, Attribute> {
136135
*/
137136
public ensureAttributeValue(token: AttrToken | string): AttrValue {
138137
token = ensureToken(token);
139-
let value = token.value || ATTR_PRESENT;
140-
return this.ensureAttribute(token).ensureValue(value);
138+
return this.ensureAttribute(token).ensureValue(token.value);
141139
}
142140

143141
/**

Diff for: ‎packages/@css-blocks/core/test/attribute-container-test.ts

+8-8
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,7 @@ export class AttributeContainerTest extends BEMProcessor {
2727
let filename = "foo/bar/a-block.css";
2828
imports.registerSource(
2929
filename,
30-
`[state|large] { font-size: 20px; }
30+
`:scope[state|large] { font-size: 20px; }
3131
.foo { float: left; }
3232
.foo[state|small] { font-size: 5px; }`,
3333
);
@@ -52,9 +52,9 @@ export class AttributeContainerTest extends BEMProcessor {
5252
let filename = "foo/bar/a-block.css";
5353
imports.registerSource(
5454
filename,
55-
`[state|size=large] { font-size: 20px; }
56-
[state|size=small] { font-size: 10px; }
57-
[state|active] { color: red; }
55+
`:scope[state|size=large] { font-size: 20px; }
56+
:scope[state|size=small] { font-size: 10px; }
57+
:scope[state|active] { color: red; }
5858
.foo[state|mode=collapsed] { display: none; }
5959
.foo[state|mode=minimized] { display: block; max-height: 100px; }
6060
.foo[state|mode=expanded] { display: block; }`,
@@ -83,9 +83,9 @@ export class AttributeContainerTest extends BEMProcessor {
8383
let filename = "foo/bar/sub-block.block.css";
8484
imports.registerSource(
8585
"foo/bar/base-block.block.css",
86-
`[state|size=large] { font-size: 20px; }
87-
[state|size=small] { font-size: 10px; }
88-
[state|active] { color: red; }
86+
`:scope[state|size=large] { font-size: 20px; }
87+
:scope[state|size=small] { font-size: 10px; }
88+
:scope[state|active] { color: red; }
8989
.foo[state|mode=collapsed] { display: none; }
9090
.foo[state|mode=minimized] { display: block; max-height: 100px; }
9191
.foo[state|mode=expanded] { display: block; }`,
@@ -94,7 +94,7 @@ export class AttributeContainerTest extends BEMProcessor {
9494
filename,
9595
`@block-reference base-block from "base-block.block.css";
9696
:scope { extends: base-block; }
97-
[state|size=tiny] { font-size: 6px; }
97+
:scope[state|size=tiny] { font-size: 6px; }
9898
.foo[state|mode=minimized] { display: block; max-height: 200px; }`,
9999
);
100100

Diff for: ‎packages/@css-blocks/core/test/block-factory-test.ts

+1-1
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,7 @@ export class BlockFactoryTests extends BEMProcessor {
2626
imports.registerSource(
2727
baseFilename,
2828
`:scope { color: purple; }
29-
[state|large] { font-size: 20px; }
29+
:scope[state|large] { font-size: 20px; }
3030
.foo { float: left; }
3131
.foo[state|small] { font-size: 5px; }`,
3232
);

Diff for: ‎packages/@css-blocks/core/test/block-inheritance-test.ts

+2-2
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@ export class BlockInheritance extends BEMProcessor {
1111
imports.registerSource(
1212
"foo/bar/base.css",
1313
`:scope { color: purple; }
14-
[state|large] { font-size: 20px; }
14+
:scope[state|large] { font-size: 20px; }
1515
.foo { float: left; }
1616
.foo[state|small] { font-size: 5px; }`,
1717
);
@@ -37,7 +37,7 @@ export class BlockInheritance extends BEMProcessor {
3737
" .b[state|small] => .inherits__b--small\n" +
3838
" .foo => .base__foo .inherits__foo\n" +
3939
" .foo[state|small] => .base__foo--small\n" +
40-
" [state|large] => .base--large */\n",
40+
" :scope[state|large] => .base--large */\n",
4141
);
4242
});
4343
}

0 commit comments

Comments
 (0)
Please sign in to comment.