Skip to content

Commit 29cc368

Browse files
ramithachriseppstein
authored andcommitted
feat: Getting rid of more thrown errors.
1 parent a3eee56 commit 29cc368

9 files changed

+82
-65
lines changed

packages/@css-blocks/core/src/BlockParser/BlockFactory.ts

+2-1
Original file line numberDiff line numberDiff line change
@@ -77,7 +77,8 @@ export class BlockFactory {
7777
* @returns The Block object promise.
7878
*/
7979
parse(root: postcss.Root, identifier: string, name: string): Promise<Block> {
80-
// this function is referenced only in tests. Use parseSync if we need to catch errors
80+
// this function is referenced only in tests. Use parseSync if we need to
81+
// catch errors
8182
return this.promises[identifier] = this.parser.parse(root, identifier, name);
8283
}
8384

packages/@css-blocks/core/src/BlockParser/BlockParser.ts

+1
Original file line numberDiff line numberDiff line change
@@ -104,6 +104,7 @@ export class BlockParser {
104104

105105
// Return our fully constructed block.
106106
debug(` - Complete`);
107+
107108
return block;
108109
}
109110

packages/@css-blocks/core/src/BlockParser/features/assert-foreign-global-attribute.ts

+12-8
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,10 @@
1-
import { postcss, postcssSelectorParser as selectorParser } from "opticss";
1+
import { ParsedSelector, postcss, postcssSelectorParser as selectorParser } from "opticss";
22

33
import { ROOT_CLASS } from "../../BlockSyntax";
44
import { Block } from "../../BlockTree";
55
import { Configuration } from "../../configuration";
66
import * as errors from "../../errors";
7-
import { selectorSourceRange as range } from "../../SourceLocation";
7+
import { selectorSourceRange as range, sourceRange } from "../../SourceLocation";
88
import { isAttributeNode, toAttrToken } from "../block-intermediates";
99

1010
/**
@@ -17,8 +17,13 @@ import { isAttributeNode, toAttrToken } from "../block-intermediates";
1717
export async function assertForeignGlobalAttribute(configuration: Configuration, root: postcss.Root, block: Block, file: string) {
1818

1919
root.walkRules((rule) => {
20-
21-
let parsedSelectors = block.getParsedSelectors(rule);
20+
let parsedSelectors: ParsedSelector[];
21+
try {
22+
parsedSelectors = block.getParsedSelectors(rule);
23+
} catch (e) {
24+
block.addError(new errors.InvalidBlockSyntax(e.message, sourceRange(configuration, block.stylesheet, file, rule)));
25+
parsedSelectors = [];
26+
}
2227

2328
parsedSelectors.forEach((iSel) => {
2429

@@ -57,18 +62,17 @@ export async function assertForeignGlobalAttribute(configuration: Configuration,
5762
// If state referenced does not exist on external block, throw
5863
let otherAttr = otherBlock.rootClass.getAttributeValue(toAttrToken(node));
5964
if (!otherAttr) {
60-
throw new errors.InvalidBlockSyntax(
65+
block.addError(new errors.InvalidBlockSyntax(
6166
`No state ${node.toString()} found in : ${rule.selector}`,
62-
range(configuration, block.stylesheet, file, rule, node));
67+
range(configuration, block.stylesheet, file, rule, node)));
6368
}
6469

6570
// If external state is not set as global, throw.
66-
if (!otherAttr.isGlobal) {
71+
else if (!otherAttr.isGlobal) {
6772
throw new errors.InvalidBlockSyntax(
6873
`${node.toString()} is not global: ${rule.selector}`,
6974
range(configuration, block.stylesheet, file, rule, node));
7075
}
71-
7276
}
7377
});
7478
});

packages/@css-blocks/core/src/BlockParser/features/construct-block.ts

+33-27
Original file line numberDiff line numberDiff line change
@@ -42,9 +42,13 @@ function shouldBeParsedAsBlockSelector(rule: postcss.Rule): boolean {
4242
**/
4343
function getParsedSelectors(configuration: Configuration, block: Block, rule: postcss.Rule, file: string): ParsedSelector[] {
4444
let res;
45-
try { res = block.getParsedSelectors(rule); }
46-
catch (e) { throw new errors.InvalidBlockSyntax(e.message, sourceRange(configuration, block.stylesheet, file, rule)); }
47-
return res;
45+
try {
46+
res = block.getParsedSelectors(rule);
47+
}
48+
catch (e) {
49+
block.addError(new errors.InvalidBlockSyntax(e.message, sourceRange(configuration, block.stylesheet, file, rule)));
50+
}
51+
return res || [];
4852
}
4953

5054
export async function constructBlock(configuration: Configuration, root: postcss.Root, block: Block, file: string): Promise<Block> {
@@ -114,10 +118,10 @@ function addStyleRules(configuration: Configuration, block: Block, rule: postcss
114118
decl.value.split(/\s+/).map(alias => {
115119
let cleanedAlias = stripQuotes(alias);
116120
if (!CLASS_NAME_IDENT.test(cleanedAlias)) {
117-
throw new errors.InvalidBlockSyntax(
121+
block.addError(new errors.InvalidBlockSyntax(
118122
`Illegal block-alias. "${alias}" is not a legal CSS identifier.`,
119123
sourceRange(configuration, block.stylesheet, file, rule),
120-
);
124+
));
121125
}
122126
aliases.add(cleanedAlias);
123127
});
@@ -297,10 +301,10 @@ function assertBlockObject(configuration: Configuration, block: Block, sel: Comp
297301
};
298302
} else {
299303
if (found.blockType === BlockType.class || found.blockType === BlockType.classAttribute) {
300-
throw new errors.InvalidBlockSyntax(
304+
block.addError(new errors.InvalidBlockSyntax(
301305
`${n} cannot be on the same element as ${found.node}: ${rule.selector}`,
302306
range(configuration, block.stylesheet, file, rule, sel.nodes[0]),
303-
);
307+
));
304308
}
305309
}
306310
}
@@ -316,10 +320,10 @@ function assertBlockObject(configuration: Configuration, block: Block, sel: Comp
316320
));
317321
}
318322
if (n.attribute === "scope") {
319-
throw new errors.InvalidBlockSyntax(
323+
block.addError(new errors.InvalidBlockSyntax(
320324
`A state cannot be named 'scope'.`,
321325
range(configuration, block.stylesheet, file, rule, n),
322-
);
326+
));
323327
}
324328
if (!found) {
325329
block.addError(new errors.InvalidBlockSyntax(
@@ -330,14 +334,15 @@ function assertBlockObject(configuration: Configuration, block: Block, sel: Comp
330334
found = { node: n, blockType: BlockType.classAttribute };
331335
} else if (found.blockType === BlockType.root || found.blockType === BlockType.attribute) {
332336
if (n.namespace === true) {
333-
throw new errors.InvalidBlockSyntax(
337+
block.addError(new errors.InvalidBlockSyntax(
334338
`The "any namespace" selector is not supported: ${rule.selector}`,
335339
range(configuration, block.stylesheet, file, rule, n),
336-
);
340+
));
341+
} else {
342+
// XXX this is where we drop the ref to the other attribute nodes,
343+
// XXX potentially causing the interface to not be fully discovered
344+
found = { node: n, blockType: BlockType.attribute, blockName: n.namespace };
337345
}
338-
// XXX this is where we drop the ref to the other attribute nodes,
339-
// XXX potentially causing the interface to not be fully discovered
340-
found = { node: n, blockType: BlockType.attribute, blockName: n.namespace };
341346
}
342347
}
343348

@@ -389,19 +394,20 @@ function assertBlockObject(configuration: Configuration, block: Block, sel: Comp
389394
}
390395
let external = block.getReferencedBlock(blockName);
391396
if (!external) {
392-
throw new errors.InvalidBlockSyntax(`A block named "${blockName}" does not exist in this context.`,
393-
range(configuration, block.stylesheet, file, rule, sel.nodes[0]));
394-
}
395-
let globalStates = external.rootClass.allAttributeValues().filter((a) => a.isGlobal);
396-
if (!globalStates.length) {
397-
throw new errors.InvalidBlockSyntax(
398-
`External Block '${blockName}' has no global states.`,
399-
range(configuration, block.stylesheet, file, rule, sel.nodes[0]));
400-
}
401-
if (result.blockType !== BlockType.attribute) {
402-
throw new errors.InvalidBlockSyntax(
403-
`Missing global state selector on external Block '${blockName}'. Did you mean one of: ${globalStates.map((s) => s.asSource()).join(" ")}`,
404-
range(configuration, block.stylesheet, file, rule, sel.nodes[0]));
397+
block.addError(new errors.InvalidBlockSyntax(`A block named "${blockName}" does not exist in this context.`,
398+
range(configuration, block.stylesheet, file, rule, sel.nodes[0])));
399+
} else {
400+
let globalStates = external.rootClass.allAttributeValues().filter((a) => a.isGlobal);
401+
if (!globalStates.length) {
402+
block.addError(new errors.InvalidBlockSyntax(
403+
`External Block '${blockName}' has no global states.`,
404+
range(configuration, block.stylesheet, file, rule, sel.nodes[0])));
405+
}
406+
if (result.blockType !== BlockType.attribute) {
407+
block.addError(new errors.InvalidBlockSyntax(
408+
`Missing global state selector on external Block '${blockName}'. Did you mean one of: ${globalStates.map((s) => s.asSource()).join(" ")}`,
409+
range(configuration, block.stylesheet, file, rule, sel.nodes[0])));
410+
}
405411
}
406412
return result;
407413
}

packages/@css-blocks/core/test/BlockParser/block-attribute-test.ts

+5-5
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
// import { assert } from "chai";
22
import { suite, test } from "mocha-typescript";
33

4-
import { assertError } from "../util/assertError";
4+
import { assertMultipleErrors } from "../util/assertError";
55
import { BEMProcessor } from "../util/BEMProcessor";
66
import { MockImportRegistry } from "../util/MockImportRegistry";
77

@@ -16,9 +16,9 @@ export class BlockAttributesTest extends BEMProcessor {
1616
let filename = "foo/bar/test-block.css";
1717
let inputCSS = `:scope[scope] { color: red; }`;
1818

19-
return assertError(
20-
InvalidBlockSyntax,
21-
`A state cannot be named 'scope'. (foo/bar/test-block.css:1:7)`,
22-
this.process(filename, inputCSS, {importer: imports.importer()}));
19+
return assertMultipleErrors([{
20+
type: InvalidBlockSyntax,
21+
message: `A state cannot be named 'scope'. (foo/bar/test-block.css:1:7)`,
22+
}], this.process(filename, inputCSS, {importer: imports.importer()}));
2323
}
2424
}

packages/@css-blocks/core/test/BlockParser/native-pseudos-test.ts

+3-3
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,7 @@ export class DisallowedPseudos extends BEMProcessor {
1717
message: "The :not() pseudoclass cannot be used: .another-class:not([foo])" +
1818
" (foo/bar/illegal-not-pseudoclass.css:2:35)",
1919
}],
20-
this.process(filename, inputCSS));
20+
this.process(filename, inputCSS));
2121
}
2222

2323
@test "disallows the :matches() pseudoclass."() {
@@ -28,7 +28,7 @@ export class DisallowedPseudos extends BEMProcessor {
2828
type: InvalidBlockSyntax,
2929
message: "The :matches() pseudoclass cannot be used: .another-class:matches([foo])" +
3030
" (foo/bar/illegal-not-pseudoclass.css:2:35)"}],
31-
this.process(filename, inputCSS));
31+
this.process(filename, inputCSS));
3232
}
3333

3434
@test "disallows pseudos not attached to a block object."() {
@@ -38,6 +38,6 @@ export class DisallowedPseudos extends BEMProcessor {
3838
type: InvalidBlockSyntax,
3939
message: "Missing block object in selector component ':hover': :scope :hover" +
4040
" (foo/bar/illegal-class-combinator.css:1:8)"}],
41-
this.process(filename, inputCSS));
41+
this.process(filename, inputCSS));
4242
}
4343
}

packages/@css-blocks/core/test/BlockParser/selector-validation-test.ts

+16-15
Original file line numberDiff line numberDiff line change
@@ -40,10 +40,13 @@ export class StraightJacket extends BEMProcessor {
4040
let filename = "foo/bar/test-state.css";
4141
let inputCSS = `:scope {color: #111;}
4242
:scope[state:asdf=foo] { transform: scale(2); }`;
43-
return assertError(
44-
InvalidBlockSyntax,
45-
'Unexpected ":" found. (foo/bar/test-state.css:2:21)',
46-
this.process(filename, inputCSS));
43+
return assertMultipleErrors([{
44+
type: InvalidBlockSyntax,
45+
message: 'Unexpected ":" found. (foo/bar/test-state.css:2:21)',
46+
}, {
47+
type: InvalidBlockSyntax,
48+
message: 'Unexpected ":" found. (foo/bar/test-state.css:2:21)'}],
49+
this.process(filename, inputCSS));
4750
}
4851

4952
@test "cannot combine two different states"() {
@@ -142,30 +145,28 @@ export class StraightJacket extends BEMProcessor {
142145
let inputCSS = `div[foo] { display: block; }`;
143146
return assertMultipleErrors([{
144147
type: InvalidBlockSyntax,
145-
message: "Tag name selectors are not allowed: div[foo] (foo/bar/illegal.css:1:1)"
148+
message: "Tag name selectors are not allowed: div[foo] (foo/bar/illegal.css:1:1)",
146149
},
147-
{
150+
{
148151
type: InvalidBlockSyntax,
149152
message: "States without an explicit :scope or class selector are not supported: div[foo] (foo/bar/illegal.css:1:4)"},
150-
{
153+
{
151154
type: InvalidBlockSyntax,
152-
message: "Missing block object in selector component 'div[foo]': div[foo] (foo/bar/illegal.css:1:1)"
153-
}], this.process(filename, inputCSS));
155+
message: "Missing block object in selector component 'div[foo]': div[foo] (foo/bar/illegal.css:1:1)",
156+
}], this.process(filename, inputCSS));
154157
}
155158

156159
@test "disallows stand-alone attribute selectors except for states."() {
157160
let filename = "foo/bar/illegal-class-combinator.css";
158161
let inputCSS = `:scope [href] { display: block; }`;
159-
return assertMultipleErrors(
160-
[{
162+
return assertMultipleErrors([{
161163
type: InvalidBlockSyntax,
162164
message: "States without an explicit :scope or class selector are not supported: " +
163165
":scope [href] (foo/bar/illegal-class-combinator.css:1:8)",
164-
}, {
166+
}, {
165167
type: InvalidBlockSyntax,
166168
message: "Missing block object in selector component '[href]': :scope [href] (foo/bar/illegal-class-combinator.css:1:8)",
167-
}],
168-
this.process(filename, inputCSS));
169+
}], this.process(filename, inputCSS));
169170
}
170171

171172
// It's possible we can relax this constraint, but I want to see
@@ -178,7 +179,7 @@ export class StraightJacket extends BEMProcessor {
178179
type: InvalidBlockSyntax,
179180
message: "Cannot select attributes in the `html` namespace: :scope[html|href] " +
180181
"(foo/bar/illegal-class-combinator.css:1:7)"}],
181-
this.process(filename, inputCSS));
182+
this.process(filename, inputCSS));
182183
}
183184

184185
@test "disallows a state before a class for the same element."() {

packages/@css-blocks/core/test/global-states-test.ts

+8-5
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
import { assert } from "chai";
22
import { suite, test } from "mocha-typescript";
33

4-
import { assertError } from "./util/assertError";
4+
import { assertError, assertMultipleErrors } from "./util/assertError";
55
import { BEMProcessor } from "./util/BEMProcessor";
66
import cssBlocks = require("./util/postcss-helper");
77
import { setupImporting } from "./util/setupImporting";
@@ -51,10 +51,13 @@ export class BlockInheritance extends BEMProcessor {
5151
border: none;
5252
}`;
5353

54-
return assertError(
55-
cssBlocks.InvalidBlockSyntax,
56-
"External Block 'app' has no global states. (widget.block.css:2:21)",
57-
this.process(filename, inputCSS, config));
54+
return assertMultipleErrors([{
55+
type: cssBlocks.InvalidBlockSyntax,
56+
message: "External Block 'app' has no global states. (widget.block.css:2:21)",
57+
}, {
58+
type: cssBlocks.InvalidBlockSyntax,
59+
message: "No state [app|is-not-loading] found in : :scope[app|is-not-loading] .b (widget.block.css:2:27)",
60+
}], this.process(filename, inputCSS, config));
5861
}
5962
@test "Can't use non-global states"() {
6063
let { imports, config } = setupImporting();

packages/@css-blocks/core/test/util/assertError.ts

+2-1
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,7 @@ export function assertMultipleErrors(errors: ErrorTypeMessage[], promise: postcs
4040
}
4141

4242
export function assertMultipleErrorsWithoutPromise(multipleErrorsError: MultipleCssBlockErrors, errors: ErrorTypeMessage[]) {
43+
console.log(multipleErrorsError);
4344
if (multipleErrorsError.errors.length !== errors.length) {
4445
console.log(multipleErrorsError.errors.length, errors.length);
4546
console.log(multipleErrorsError.errors);
@@ -49,4 +50,4 @@ export function assertMultipleErrorsWithoutPromise(multipleErrorsError: Multiple
4950
assert(error.type.name, typeof multipleErrorsError.errors[idx]);
5051
assert.deepEqual(multipleErrorsError.errors[idx].message.split(error.type.prefix + ":")[1].trim(), error.message);
5152
});
52-
}
53+
}

0 commit comments

Comments
 (0)