Skip to content

Commit ae1be31

Browse files
committed
fix: add more info to attr value / statement validity
1 parent e4df379 commit ae1be31

3 files changed

Lines changed: 48 additions & 22 deletions

File tree

.changeset/eleven-chicken-share.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
---
2+
"htmljs-parser": patch
3+
---
4+
5+
Expose extra information about statement and attr value validity.

src/__tests__/validate.test.ts

Lines changed: 24 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -4,69 +4,77 @@ import { isValidAttrValue, isValidStatement } from "..";
44
describe("validation helpers", () => {
55
describe("isValidStatement", () => {
66
it("accepts single-line expressions", () => {
7-
assert.equal(isValidStatement("foo + bar"), true);
7+
assert.equal(isValidStatement("foo + bar"), 2);
88
});
99

1010
it("accepts indented continuation lines", () => {
11-
assert.equal(isValidStatement("foo\n + bar"), true);
11+
assert.equal(isValidStatement("foo\n + bar"), 1);
1212
});
1313

1414
it("rejects unindented continuation lines", () => {
15-
assert.equal(isValidStatement("foo\nbar"), false);
15+
assert.equal(isValidStatement("foo\nbar"), 0);
1616
});
1717

1818
it("accepts indented ternary continuation", () => {
19-
assert.equal(isValidStatement("foo ?\n bar : baz"), true);
19+
assert.equal(isValidStatement("foo ?\n bar : baz"), 2);
2020
});
2121

2222
it("rejects unterminated groups", () => {
23-
assert.equal(isValidStatement("(foo"), false);
23+
assert.equal(isValidStatement("(foo"), 0);
2424
});
2525

2626
it("rejects mismatched closing groups", () => {
27-
assert.equal(isValidStatement(")"), false);
27+
assert.equal(isValidStatement(")"), 0);
2828
});
2929
});
3030

3131
describe("isValidAttrValue", () => {
3232
it("accepts html attr values with operators", () => {
33-
assert.equal(isValidAttrValue("foo + bar", false), true);
33+
assert.equal(isValidAttrValue("foo + bar", false), 2);
3434
});
3535

3636
it("accepts html attr values containing =>", () => {
37-
assert.equal(isValidAttrValue("foo=>bar", false), true);
37+
assert.equal(isValidAttrValue("foo=>bar", false), 2);
3838
});
3939

4040
it("rejects html attr values terminated by >", () => {
41-
assert.equal(isValidAttrValue("foo >", false), false);
41+
assert.equal(isValidAttrValue("foo >", false), 0);
4242
});
4343

4444
it("accepts concise attr values with >", () => {
45-
assert.equal(isValidAttrValue("foo > bar", true), true);
45+
assert.equal(isValidAttrValue("foo > bar", true), 2);
4646
});
4747

4848
it("rejects html attr values terminated by commas", () => {
49-
assert.equal(isValidAttrValue("foo, bar", false), false);
49+
assert.equal(isValidAttrValue("foo, bar", false), 0);
5050
});
5151

5252
it("accepts html attr values containing semicolons", () => {
53-
assert.equal(isValidAttrValue("foo;", false), true);
53+
assert.equal(isValidAttrValue("foo;", false), 2);
5454
});
5555

5656
it("rejects concise attr values terminated by semicolons", () => {
57-
assert.equal(isValidAttrValue("foo;", true), false);
57+
assert.equal(isValidAttrValue("foo;", true), 0);
5858
});
5959

6060
it("accepts html attr values with decrement operator", () => {
61-
assert.equal(isValidAttrValue("foo --", false), true);
61+
assert.equal(isValidAttrValue("foo --", false), 2);
6262
});
6363

6464
it("rejects concise attr values with decrement operator", () => {
65-
assert.equal(isValidAttrValue("foo --", true), false);
65+
assert.equal(isValidAttrValue("foo --", true), 0);
6666
});
6767

6868
it("rejects attr values separated only by whitespace", () => {
69-
assert.equal(isValidAttrValue("foo bar", false), false);
69+
assert.equal(isValidAttrValue("foo bar", false), 0);
70+
});
71+
72+
it("accepts continued multiline logical expression", () => {
73+
assert.equal(isValidAttrValue("a &&\nb", true), 1);
74+
});
75+
76+
it("accepts continued multiline enclosed logical expression", () => {
77+
assert.equal(isValidAttrValue("a && (\nb\n)", true), 2);
7078
});
7179
});
7280
});

src/util/validators.ts

Lines changed: 19 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -28,7 +28,13 @@ const ROOT_RANGE = {
2828
end: 0,
2929
};
3030

31-
export function isValidStatement(code: string): boolean {
31+
export enum Validity {
32+
invalid,
33+
valid,
34+
enclosed,
35+
}
36+
37+
export function isValidStatement(code: string): Validity {
3238
return isValid(code, true, prepareStatement);
3339
}
3440

@@ -38,7 +44,7 @@ function prepareStatement(expr: STATE.ExpressionMeta) {
3844
expr.consumeIndentedContent = true;
3945
}
4046

41-
export function isValidAttrValue(code: string, concise: boolean): boolean {
47+
export function isValidAttrValue(code: string, concise: boolean): Validity {
4248
return isValid(code, concise, prepareAttrValue);
4349
}
4450

@@ -54,7 +60,7 @@ function isValid(
5460
data: string,
5561
concise: boolean,
5662
prepare: (expr: STATE.ExpressionMeta, concise: boolean) => void,
57-
) {
63+
): Validity {
5864
const parser = new Parser({});
5965
const maxPos = (parser.maxPos = data.length);
6066
parser.pos = 0;
@@ -68,18 +74,21 @@ function isValid(
6874
parser.activeState = ROOT_STATE;
6975
parser.activeRange = ROOT_RANGE;
7076
const expr = parser.enterState(STATE.EXPRESSION);
77+
let isEnclosed = true;
7178
prepare(expr, concise);
7279

7380
while (parser.pos < maxPos) {
7481
const code = data.charCodeAt(parser.pos);
7582

7683
if (code === CODE.NEWLINE) {
84+
if (isEnclosed && !expr.groupStack.length) isEnclosed = false;
7785
parser.forward = 1;
7886
parser.activeState.eol.call(parser, 1, parser.activeRange);
7987
} else if (
8088
code === CODE.CARRIAGE_RETURN &&
8189
data.charCodeAt(parser.pos + 1) === CODE.NEWLINE
8290
) {
91+
if (isEnclosed && !expr.groupStack.length) isEnclosed = false;
8392
parser.forward = 2;
8493
parser.activeState.eol.call(parser, 2, parser.activeRange);
8594
} else {
@@ -88,15 +97,19 @@ function isValid(
8897
}
8998

9099
if (parser.activeRange === ROOT_RANGE) {
91-
return false;
100+
return Validity.invalid;
92101
}
93102

94103
parser.pos += parser.forward;
95104
}
96105

97-
return (
106+
if (
98107
parser.pos === maxPos &&
99108
parser.activeRange === expr &&
100109
!expr.groupStack.length
101-
);
110+
) {
111+
return isEnclosed ? Validity.enclosed : Validity.valid;
112+
}
113+
114+
return Validity.invalid;
102115
}

0 commit comments

Comments
 (0)