@markuplint/types 3.0.0-alpha.0 → 3.0.0-alpha.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +1 -1
- package/lib/css-syntax.spec.js +1 -1
- package/lib/defs.js +4 -4
- package/lib/get-candidate.d.ts +3 -0
- package/lib/{get-candicate.js → get-candidate.js} +8 -8
- package/lib/list.spec.js +4 -4
- package/lib/number.js +4 -4
- package/lib/primitive/is-uint.d.ts +1 -1
- package/lib/primitive/is-uint.js +4 -4
- package/lib/token/token-collection.d.ts +21 -21
- package/lib/token/token-collection.js +101 -101
- package/lib/token/token-collection.spec.js +2 -2
- package/lib/token/token.d.ts +22 -22
- package/lib/token/token.js +36 -36
- package/lib/types.d.ts +2 -2
- package/lib/types.schema.d.ts +3 -0
- package/lib/whatwg/check-autocomplete.js +55 -15
- package/lib/whatwg/check-autocomplete.spec.js +19 -10
- package/lib/whatwg/check-datetime/datetime-tokens.d.ts +1 -1
- package/lib/whatwg/check-datetime/datetime-tokens.js +16 -16
- package/lib/whatwg/check-datetime/global-date-and-time-string.js +3 -3
- package/lib/whatwg/check-datetime/local-date-and-time-string.js +2 -2
- package/lib/whatwg/check-datetime/time-string.js +1 -1
- package/lib/whatwg/check-datetime/time-zone-offset-string.js +4 -4
- package/lib/whatwg/check-mime-type.js +1 -1
- package/lib/whatwg/check-mime-type.spec.js +3 -3
- package/package.json +4 -4
- package/src/css-syntax.spec.ts +1 -1
- package/src/defs.ts +4 -4
- package/src/{get-candicate.ts → get-candidate.ts} +6 -6
- package/src/list.spec.ts +4 -4
- package/src/number.ts +4 -4
- package/src/primitive/is-uint.ts +4 -4
- package/src/token/token-collection.spec.ts +2 -2
- package/src/token/token-collection.ts +113 -113
- package/src/token/token.ts +58 -58
- package/src/types.schema.ts +3 -0
- package/src/types.ts +2 -2
- package/src/whatwg/check-autocomplete.spec.ts +19 -10
- package/src/whatwg/check-autocomplete.ts +71 -15
- package/src/whatwg/check-datetime/datetime-tokens.ts +18 -18
- package/src/whatwg/check-datetime/global-date-and-time-string.ts +3 -3
- package/src/whatwg/check-datetime/local-date-and-time-string.ts +4 -4
- package/src/whatwg/check-datetime/time-string.ts +2 -2
- package/src/whatwg/check-datetime/time-zone-offset-string.ts +4 -4
- package/src/whatwg/check-mime-type.spec.ts +3 -3
- package/src/whatwg/check-mime-type.ts +1 -1
- package/tsconfig.tsbuildinfo +1 -1
- package/lib/get-candicate.d.ts +0 -3
package/src/types.ts
CHANGED
|
@@ -20,7 +20,7 @@ export type UnmatchedResultOptions = {
|
|
|
20
20
|
partName?: string;
|
|
21
21
|
expects?: Expect[];
|
|
22
22
|
extra?: Expect;
|
|
23
|
-
|
|
23
|
+
candidate?: string;
|
|
24
24
|
};
|
|
25
25
|
|
|
26
26
|
export type UnmatchedResultReason =
|
|
@@ -64,7 +64,7 @@ export type MatchedResult = {
|
|
|
64
64
|
};
|
|
65
65
|
|
|
66
66
|
export type Expect = {
|
|
67
|
-
type: 'const' | 'format' | 'syntax' | '
|
|
67
|
+
type: 'const' | 'format' | 'syntax' | 'regexp' | 'common';
|
|
68
68
|
value: string;
|
|
69
69
|
};
|
|
70
70
|
|
|
@@ -2,28 +2,37 @@ import { checkAutoComplete } from './check-autocomplete';
|
|
|
2
2
|
|
|
3
3
|
const check = checkAutoComplete();
|
|
4
4
|
|
|
5
|
-
test('
|
|
5
|
+
test('basic', () => {
|
|
6
6
|
expect(check('on').matched).toBe(true);
|
|
7
|
+
expect(check('on webauthn').matched).toBe(false);
|
|
7
8
|
expect(check('off').matched).toBe(true);
|
|
9
|
+
expect(check('off webauthn').matched).toBe(false);
|
|
8
10
|
expect(check('name').matched).toBe(true);
|
|
9
11
|
expect(check('given-name').matched).toBe(true);
|
|
12
|
+
expect(check('given-name webauthn').matched).toBe(true);
|
|
13
|
+
expect(check('given-name webauthun').matched).toBe(false);
|
|
10
14
|
expect(check('section-').matched).toBe(true);
|
|
11
15
|
expect(check('section-foo').matched).toBe(true);
|
|
16
|
+
expect(check('section-foo webauthn').matched).toBe(true);
|
|
12
17
|
expect(check('section-foo name').matched).toBe(true);
|
|
18
|
+
expect(check('section-foo name webauthn').matched).toBe(true);
|
|
13
19
|
expect(check('section-foo shipping name').matched).toBe(true);
|
|
14
20
|
expect(check('section-foo billing name').matched).toBe(true);
|
|
15
21
|
expect(check('section-foo billing home').matched).toBe(true);
|
|
16
22
|
expect(check('section-foo billing work').matched).toBe(true);
|
|
17
23
|
expect(check('section-foo billing home tel').matched).toBe(true);
|
|
18
24
|
expect(check('section-foo billing work email').matched).toBe(true);
|
|
25
|
+
expect(check('section-foo billing work email webauthn').matched).toBe(true);
|
|
19
26
|
expect(check('shipping name').matched).toBe(true);
|
|
20
27
|
expect(check('billing name').matched).toBe(true);
|
|
21
28
|
expect(check('billing home').matched).toBe(true);
|
|
22
29
|
expect(check('billing work').matched).toBe(true);
|
|
23
30
|
expect(check('billing home tel').matched).toBe(true);
|
|
24
31
|
expect(check('billing work email').matched).toBe(true);
|
|
32
|
+
expect(check('billing work email webauthn').matched).toBe(true);
|
|
25
33
|
expect(check('home tel').matched).toBe(true);
|
|
26
34
|
expect(check('work email').matched).toBe(true);
|
|
35
|
+
expect(check('work email webauthn').matched).toBe(true);
|
|
27
36
|
});
|
|
28
37
|
|
|
29
38
|
test('unexpected-token', () => {
|
|
@@ -40,7 +49,7 @@ test('unexpected-token', () => {
|
|
|
40
49
|
{ type: 'const', value: 'shipping' },
|
|
41
50
|
{ type: 'common', value: 'autofill field name' },
|
|
42
51
|
],
|
|
43
|
-
|
|
52
|
+
candidate: undefined,
|
|
44
53
|
ref: 'https://html.spec.whatwg.org/multipage/form-control-infrastructure.html#autofill-field',
|
|
45
54
|
});
|
|
46
55
|
});
|
|
@@ -58,7 +67,7 @@ test('unexpected-token', () => {
|
|
|
58
67
|
{ type: 'common', value: 'autofill named group' },
|
|
59
68
|
{ type: 'common', value: 'autofill field name' },
|
|
60
69
|
],
|
|
61
|
-
|
|
70
|
+
candidate: undefined,
|
|
62
71
|
ref: 'https://html.spec.whatwg.org/multipage/form-control-infrastructure.html#autofill-field',
|
|
63
72
|
});
|
|
64
73
|
});
|
|
@@ -77,7 +86,7 @@ test('unexpected-token', () => {
|
|
|
77
86
|
{ type: 'const', value: 'shipping' },
|
|
78
87
|
{ type: 'common', value: 'autofill field name' },
|
|
79
88
|
],
|
|
80
|
-
|
|
89
|
+
candidate: 'name',
|
|
81
90
|
ref: 'https://html.spec.whatwg.org/multipage/form-control-infrastructure.html#autofill-field',
|
|
82
91
|
});
|
|
83
92
|
expect(check('section-foo shipping neme')).toStrictEqual({
|
|
@@ -96,12 +105,12 @@ test('unexpected-token', () => {
|
|
|
96
105
|
{ type: 'const', value: 'fax' },
|
|
97
106
|
{ type: 'const', value: 'pager' },
|
|
98
107
|
],
|
|
99
|
-
|
|
108
|
+
candidate: 'name',
|
|
100
109
|
ref: 'https://html.spec.whatwg.org/multipage/form-control-infrastructure.html#autofill-field',
|
|
101
110
|
});
|
|
102
|
-
expect(check('section-foo shipping home
|
|
111
|
+
expect(check('section-foo shipping home name')).toStrictEqual({
|
|
103
112
|
matched: false,
|
|
104
|
-
raw: '
|
|
113
|
+
raw: 'name',
|
|
105
114
|
offset: 26,
|
|
106
115
|
length: 4,
|
|
107
116
|
line: 1,
|
|
@@ -119,7 +128,7 @@ test('unexpected-token', () => {
|
|
|
119
128
|
{ type: 'const', value: 'email' },
|
|
120
129
|
{ type: 'const', value: 'impp' },
|
|
121
130
|
],
|
|
122
|
-
|
|
131
|
+
candidate: undefined,
|
|
123
132
|
ref: 'https://html.spec.whatwg.org/multipage/form-control-infrastructure.html#autofilling-form-controls:-the-autocomplete-attribute:attr-fe-autocomplete-tel',
|
|
124
133
|
});
|
|
125
134
|
expect(check('section-foo shipping home emall')).toStrictEqual({
|
|
@@ -142,7 +151,7 @@ test('unexpected-token', () => {
|
|
|
142
151
|
{ type: 'const', value: 'email' },
|
|
143
152
|
{ type: 'const', value: 'impp' },
|
|
144
153
|
],
|
|
145
|
-
|
|
154
|
+
candidate: 'email',
|
|
146
155
|
ref: 'https://html.spec.whatwg.org/multipage/form-control-infrastructure.html#autofilling-form-controls:-the-autocomplete-attribute:attr-fe-autocomplete-tel',
|
|
147
156
|
});
|
|
148
157
|
});
|
|
@@ -372,7 +381,7 @@ test('typo', () => {
|
|
|
372
381
|
{ type: 'common', value: 'autofill named group' },
|
|
373
382
|
{ type: 'common', value: 'autofill field name' },
|
|
374
383
|
],
|
|
375
|
-
|
|
384
|
+
candidate: 'section-foo',
|
|
376
385
|
ref: 'https://html.spec.whatwg.org/multipage/form-control-infrastructure.html#autofill-field',
|
|
377
386
|
});
|
|
378
387
|
});
|
|
@@ -1,10 +1,13 @@
|
|
|
1
1
|
import type { Expect } from '..';
|
|
2
2
|
import type { CustomSyntaxChecker } from '../types';
|
|
3
3
|
|
|
4
|
-
import {
|
|
4
|
+
import { log } from '../debug';
|
|
5
|
+
import { getCandidate } from '../get-candidate';
|
|
5
6
|
import { matched } from '../match-result';
|
|
6
7
|
import { TokenCollection } from '../token';
|
|
7
8
|
|
|
9
|
+
const acLog = log.extend('autocomplete');
|
|
10
|
+
|
|
8
11
|
/**
|
|
9
12
|
* https://html.spec.whatwg.org/multipage/form-control-infrastructure.html#attr-fe-autocomplete-section
|
|
10
13
|
*/
|
|
@@ -86,7 +89,12 @@ const contactableFieldNames = [
|
|
|
86
89
|
'impp',
|
|
87
90
|
];
|
|
88
91
|
|
|
89
|
-
|
|
92
|
+
/**
|
|
93
|
+
* @see https://html.spec.whatwg.org/multipage/form-control-infrastructure.html#attr-fe-autocomplete-webauthn
|
|
94
|
+
*/
|
|
95
|
+
const webauthnFieldNames = ['webauthn'];
|
|
96
|
+
|
|
97
|
+
const URL_AUTOCOMPLETE = 'https://html.spec.whatwg.org/multipage/form-control-infrastructure.html#attr-fe-autocomplete';
|
|
90
98
|
const URL_ON_OFF =
|
|
91
99
|
'https://html.spec.whatwg.org/multipage/form-control-infrastructure.html#autofilling-form-controls:-the-autocomplete-attribute:attr-fe-autocomplete-on-2';
|
|
92
100
|
const URL_NAMED_GROUP =
|
|
@@ -120,14 +128,15 @@ export const checkAutoComplete: CustomSyntaxChecker = () => value => {
|
|
|
120
128
|
value: 'autocomplete',
|
|
121
129
|
},
|
|
122
130
|
],
|
|
123
|
-
ref:
|
|
131
|
+
ref: URL_AUTOCOMPLETE,
|
|
124
132
|
});
|
|
125
133
|
if (!listingChecked.matched) {
|
|
134
|
+
acLog('Unmatch: %s', listingChecked.reason);
|
|
126
135
|
return listingChecked;
|
|
127
136
|
}
|
|
128
137
|
|
|
129
|
-
const
|
|
130
|
-
const headAndTail1 =
|
|
138
|
+
const identTokens = tokens.getIdentTokens();
|
|
139
|
+
const headAndTail1 = identTokens.headAndTail();
|
|
131
140
|
let { head, tail } = headAndTail1;
|
|
132
141
|
if (!head) {
|
|
133
142
|
// Never
|
|
@@ -141,6 +150,7 @@ export const checkAutoComplete: CustomSyntaxChecker = () => value => {
|
|
|
141
150
|
// > (i.e. the "on" and "off" keywords are not allowed).
|
|
142
151
|
if (head.match(['on', 'off'], true)) {
|
|
143
152
|
if (tail.length) {
|
|
153
|
+
acLog('[Unmatched ("%s")] Unexpected pair with "on" or "off": "%s"', value, tail.value);
|
|
144
154
|
return tail[0].unmatched({
|
|
145
155
|
reason: 'extra-token',
|
|
146
156
|
expects: [
|
|
@@ -163,6 +173,7 @@ export const checkAutoComplete: CustomSyntaxChecker = () => value => {
|
|
|
163
173
|
hasNamedGroup = true;
|
|
164
174
|
const sectionToken = tail.search(namedGroup);
|
|
165
175
|
if (sectionToken) {
|
|
176
|
+
acLog('[Unmatched ("%s")] Deprecated in autofill named group: "%s"', value, sectionToken.value);
|
|
166
177
|
return sectionToken.unmatched({
|
|
167
178
|
partName: 'autofill named group',
|
|
168
179
|
reason: 'duplicated',
|
|
@@ -188,6 +199,7 @@ export const checkAutoComplete: CustomSyntaxChecker = () => value => {
|
|
|
188
199
|
hasPartOfAddress = true;
|
|
189
200
|
const partToken = tail.search(partOfAddress);
|
|
190
201
|
if (partToken) {
|
|
202
|
+
acLog('[Unmatched ("%s")] Duplicated values: "%s"', value, partToken.value);
|
|
191
203
|
return partToken.unmatched({
|
|
192
204
|
reason: 'duplicated',
|
|
193
205
|
expects: [
|
|
@@ -219,19 +231,37 @@ export const checkAutoComplete: CustomSyntaxChecker = () => value => {
|
|
|
219
231
|
}
|
|
220
232
|
|
|
221
233
|
if (!contactableFiledToken.match(contactableFieldNames, true)) {
|
|
222
|
-
const
|
|
234
|
+
const candidate = getCandidate(contactableFiledToken.value, contactableFieldNames);
|
|
235
|
+
acLog('[Unmatched ("%s")] Unexpected token: "%s"', value, contactableFiledToken.value);
|
|
223
236
|
return contactableFiledToken.unmatched({
|
|
224
237
|
reason: 'unexpected-token',
|
|
225
238
|
expects: contactableFieldNames.slice().map(token => ({
|
|
226
239
|
type: 'const' as const,
|
|
227
240
|
value: token,
|
|
228
241
|
})),
|
|
229
|
-
|
|
242
|
+
candidate,
|
|
230
243
|
ref: URL_CONTACTABLE_FIELD,
|
|
231
244
|
});
|
|
232
245
|
}
|
|
233
246
|
|
|
234
247
|
if (tail[1]) {
|
|
248
|
+
if (tail[1].match(webauthnFieldNames)) {
|
|
249
|
+
return matched();
|
|
250
|
+
}
|
|
251
|
+
|
|
252
|
+
const candidate = getCandidate(tail[1].value, webauthnFieldNames);
|
|
253
|
+
|
|
254
|
+
if (candidate) {
|
|
255
|
+
acLog(
|
|
256
|
+
'[Unmatched ("%s")] Unnecessarily token: "%s", Do you mean "%s"? ',
|
|
257
|
+
value,
|
|
258
|
+
tail[1].value,
|
|
259
|
+
candidate,
|
|
260
|
+
);
|
|
261
|
+
} else {
|
|
262
|
+
acLog('[Unmatched ("%s")] Unnecessarily token: "%s"', value, tail[1].value);
|
|
263
|
+
}
|
|
264
|
+
|
|
235
265
|
return tail[1].unmatched({
|
|
236
266
|
reason: 'extra-token',
|
|
237
267
|
expects: [
|
|
@@ -249,6 +279,23 @@ export const checkAutoComplete: CustomSyntaxChecker = () => value => {
|
|
|
249
279
|
|
|
250
280
|
if (head.match([...autofillFieldNames, ...contactableFieldNames], true)) {
|
|
251
281
|
if (tail.length) {
|
|
282
|
+
if (tail[0].match(webauthnFieldNames)) {
|
|
283
|
+
return matched();
|
|
284
|
+
}
|
|
285
|
+
|
|
286
|
+
const candidate = getCandidate(tail[0].value, webauthnFieldNames);
|
|
287
|
+
|
|
288
|
+
if (candidate) {
|
|
289
|
+
acLog(
|
|
290
|
+
'[Unmatched ("%s")] Unnecessarily token: "%s", Do you mean "%s"? ',
|
|
291
|
+
value,
|
|
292
|
+
tail[0].value,
|
|
293
|
+
candidate,
|
|
294
|
+
);
|
|
295
|
+
} else {
|
|
296
|
+
acLog('[Unmatched ("%s")] Unnecessarily token: "%s"', value, tail[0].value);
|
|
297
|
+
}
|
|
298
|
+
|
|
252
299
|
return tail[0].unmatched({
|
|
253
300
|
reason: 'extra-token',
|
|
254
301
|
expects: [
|
|
@@ -264,13 +311,17 @@ export const checkAutoComplete: CustomSyntaxChecker = () => value => {
|
|
|
264
311
|
return matched();
|
|
265
312
|
}
|
|
266
313
|
|
|
314
|
+
if (head.match(webauthnFieldNames)) {
|
|
315
|
+
return matched();
|
|
316
|
+
}
|
|
317
|
+
|
|
267
318
|
const expects: Expect[] = [
|
|
268
319
|
{
|
|
269
320
|
type: 'common',
|
|
270
321
|
value: 'autofill field name',
|
|
271
322
|
},
|
|
272
323
|
];
|
|
273
|
-
let
|
|
324
|
+
let candidate: string | undefined;
|
|
274
325
|
|
|
275
326
|
if (!hasNamedGroup) {
|
|
276
327
|
expects.unshift({
|
|
@@ -280,9 +331,9 @@ export const checkAutoComplete: CustomSyntaxChecker = () => value => {
|
|
|
280
331
|
|
|
281
332
|
// Potentially typo a named group
|
|
282
333
|
const [prefix, namedGroupStr] = head.value.split('-');
|
|
283
|
-
const
|
|
284
|
-
if (
|
|
285
|
-
|
|
334
|
+
const candidatePrefix = getCandidate(prefix, 'section');
|
|
335
|
+
if (candidatePrefix) {
|
|
336
|
+
candidate = `${candidatePrefix}-${namedGroupStr || ''}`;
|
|
286
337
|
}
|
|
287
338
|
} else if (!hasPartOfAddress) {
|
|
288
339
|
expects.unshift(
|
|
@@ -295,7 +346,7 @@ export const checkAutoComplete: CustomSyntaxChecker = () => value => {
|
|
|
295
346
|
})),
|
|
296
347
|
);
|
|
297
348
|
|
|
298
|
-
|
|
349
|
+
candidate = getCandidate(
|
|
299
350
|
head.value,
|
|
300
351
|
partOfAddress,
|
|
301
352
|
autofillFieldNames,
|
|
@@ -310,15 +361,20 @@ export const checkAutoComplete: CustomSyntaxChecker = () => value => {
|
|
|
310
361
|
})),
|
|
311
362
|
);
|
|
312
363
|
|
|
313
|
-
|
|
364
|
+
candidate = getCandidate(head.value, autofillFieldNames, contactingTokens, contactableFieldNames);
|
|
314
365
|
}
|
|
315
366
|
|
|
316
|
-
|
|
367
|
+
candidate = candidate || getCandidate(head.value, autofillFieldNames);
|
|
317
368
|
|
|
369
|
+
if (candidate) {
|
|
370
|
+
acLog('[Unmatched ("%s")] Unexpected token: "%s", Do you mean "%s"? ', value, head.value, candidate);
|
|
371
|
+
} else {
|
|
372
|
+
acLog('[Unmatched ("%s")] Unexpected token: "%s"', value, head.value);
|
|
373
|
+
}
|
|
318
374
|
return head.unmatched({
|
|
319
375
|
reason: 'unexpected-token',
|
|
320
376
|
expects,
|
|
321
|
-
|
|
377
|
+
candidate,
|
|
322
378
|
ref: URL_AUTOFILL_FIELD,
|
|
323
379
|
});
|
|
324
380
|
};
|
|
@@ -13,9 +13,9 @@ export const datetimeTokenCheck: Record<
|
|
|
13
13
|
| 'secondFractionalPart'
|
|
14
14
|
| 'week'
|
|
15
15
|
| 'hyphen'
|
|
16
|
-
| '
|
|
16
|
+
| 'colon'
|
|
17
17
|
| 'extra'
|
|
18
|
-
| '
|
|
18
|
+
| 'colonOrEnd'
|
|
19
19
|
| 'decimalPointOrEnd'
|
|
20
20
|
| 'localDateTimeSeparator'
|
|
21
21
|
| 'normalizedlocalDateTimeSeparator'
|
|
@@ -391,19 +391,19 @@ export const datetimeTokenCheck: Record<
|
|
|
391
391
|
/**
|
|
392
392
|
* > A U+003A COLON character (:)
|
|
393
393
|
*/
|
|
394
|
-
|
|
395
|
-
log('Parsing Datetime
|
|
394
|
+
colon(colon) {
|
|
395
|
+
log('Parsing Datetime colon: "%s"', colon?.value);
|
|
396
396
|
|
|
397
|
-
if (!
|
|
397
|
+
if (!colon || !colon.value) {
|
|
398
398
|
return unmatched('', 'missing-token', {
|
|
399
|
-
expects: [{ type: 'common', value: '
|
|
399
|
+
expects: [{ type: 'common', value: 'colon' }],
|
|
400
400
|
partName: 'time',
|
|
401
401
|
});
|
|
402
402
|
}
|
|
403
|
-
if (!
|
|
404
|
-
return
|
|
403
|
+
if (!colon.match(':')) {
|
|
404
|
+
return colon.unmatched({
|
|
405
405
|
reason: 'unexpected-token',
|
|
406
|
-
expects: [{ type: 'common', value: '
|
|
406
|
+
expects: [{ type: 'common', value: 'colon' }],
|
|
407
407
|
partName: 'time',
|
|
408
408
|
});
|
|
409
409
|
}
|
|
@@ -414,15 +414,15 @@ export const datetimeTokenCheck: Record<
|
|
|
414
414
|
* or
|
|
415
415
|
* End of token
|
|
416
416
|
*/
|
|
417
|
-
|
|
418
|
-
if (!
|
|
417
|
+
colonOrEnd(colon) {
|
|
418
|
+
if (!colon || !colon.value) {
|
|
419
419
|
return matched();
|
|
420
420
|
}
|
|
421
421
|
|
|
422
|
-
if (!
|
|
423
|
-
return
|
|
422
|
+
if (!colon.match(':')) {
|
|
423
|
+
return colon.unmatched({
|
|
424
424
|
reason: 'unexpected-token',
|
|
425
|
-
expects: [{ type: 'common', value: '
|
|
425
|
+
expects: [{ type: 'common', value: 'colon' }],
|
|
426
426
|
partName: 'time',
|
|
427
427
|
});
|
|
428
428
|
}
|
|
@@ -498,8 +498,8 @@ export const datetimeTokenCheck: Record<
|
|
|
498
498
|
* > a U+002D HYPHEN-MINUS character (-),
|
|
499
499
|
* > representing the sign of the time-zone offset
|
|
500
500
|
*/
|
|
501
|
-
plusOrMinusSign(
|
|
502
|
-
if (!
|
|
501
|
+
plusOrMinusSign(colon) {
|
|
502
|
+
if (!colon) {
|
|
503
503
|
return unmatched('', 'missing-token', {
|
|
504
504
|
expects: [
|
|
505
505
|
{ type: 'const', value: '+' },
|
|
@@ -508,8 +508,8 @@ export const datetimeTokenCheck: Record<
|
|
|
508
508
|
partName: 'time-zone',
|
|
509
509
|
});
|
|
510
510
|
}
|
|
511
|
-
if (!
|
|
512
|
-
return
|
|
511
|
+
if (!colon.match(['+', '-'])) {
|
|
512
|
+
return colon.unmatched({
|
|
513
513
|
reason: 'unexpected-token',
|
|
514
514
|
expects: [
|
|
515
515
|
{ type: 'const', value: '+' },
|
|
@@ -49,7 +49,7 @@ export const checkGlobalDateAndTimeString: CustomSyntaxChecker = () =>
|
|
|
49
49
|
datetimeTokenCheck.date,
|
|
50
50
|
datetimeTokenCheck.localDateTimeSeparator,
|
|
51
51
|
datetimeTokenCheck.hour,
|
|
52
|
-
datetimeTokenCheck.
|
|
52
|
+
datetimeTokenCheck.colon,
|
|
53
53
|
datetimeTokenCheck.minute,
|
|
54
54
|
second => {
|
|
55
55
|
if (!second || !second.value) {
|
|
@@ -58,10 +58,10 @@ export const checkGlobalDateAndTimeString: CustomSyntaxChecker = () =>
|
|
|
58
58
|
|
|
59
59
|
const secondTokens = TokenCollection.fromPatterns(second, [/:?/, /[0-9]*/, /\.?/, /[0-9]*/]);
|
|
60
60
|
|
|
61
|
-
log('
|
|
61
|
+
log('Second Part: "%s" => %O', secondTokens.value, secondTokens);
|
|
62
62
|
|
|
63
63
|
const res = secondTokens.eachCheck(
|
|
64
|
-
datetimeTokenCheck.
|
|
64
|
+
datetimeTokenCheck.colon,
|
|
65
65
|
datetimeTokenCheck.second,
|
|
66
66
|
datetimeTokenCheck.decimalPointOrEnd,
|
|
67
67
|
datetimeTokenCheck.secondFractionalPart,
|
|
@@ -51,9 +51,9 @@ export const checkLocalDateAndTimeString: CustomSyntaxChecker = () =>
|
|
|
51
51
|
datetimeTokenCheck.date,
|
|
52
52
|
datetimeTokenCheck.localDateTimeSeparator,
|
|
53
53
|
datetimeTokenCheck.hour,
|
|
54
|
-
datetimeTokenCheck.
|
|
54
|
+
datetimeTokenCheck.colon,
|
|
55
55
|
datetimeTokenCheck.minute,
|
|
56
|
-
datetimeTokenCheck.
|
|
56
|
+
datetimeTokenCheck.colonOrEnd,
|
|
57
57
|
datetimeTokenCheck.second,
|
|
58
58
|
datetimeTokenCheck.decimalPointOrEnd,
|
|
59
59
|
datetimeTokenCheck.secondFractionalPart,
|
|
@@ -113,9 +113,9 @@ export const checkNormalizedLocalDateAndTimeString: CustomSyntaxChecker = () =>
|
|
|
113
113
|
datetimeTokenCheck.date,
|
|
114
114
|
datetimeTokenCheck.normalizedlocalDateTimeSeparator,
|
|
115
115
|
datetimeTokenCheck.hour,
|
|
116
|
-
datetimeTokenCheck.
|
|
116
|
+
datetimeTokenCheck.colon,
|
|
117
117
|
datetimeTokenCheck.minute,
|
|
118
|
-
datetimeTokenCheck.
|
|
118
|
+
datetimeTokenCheck.colonOrEnd,
|
|
119
119
|
datetimeTokenCheck.second,
|
|
120
120
|
datetimeTokenCheck.decimalPointOrEnd,
|
|
121
121
|
datetimeTokenCheck.secondFractionalPart,
|
|
@@ -33,9 +33,9 @@ export const checkTimeString: CustomSyntaxChecker = () =>
|
|
|
33
33
|
|
|
34
34
|
const res = tokens.eachCheck(
|
|
35
35
|
datetimeTokenCheck.hour,
|
|
36
|
-
datetimeTokenCheck.
|
|
36
|
+
datetimeTokenCheck.colon,
|
|
37
37
|
datetimeTokenCheck.minute,
|
|
38
|
-
datetimeTokenCheck.
|
|
38
|
+
datetimeTokenCheck.colonOrEnd,
|
|
39
39
|
datetimeTokenCheck.second,
|
|
40
40
|
datetimeTokenCheck.decimalPointOrEnd,
|
|
41
41
|
datetimeTokenCheck.secondFractionalPart,
|
|
@@ -68,13 +68,13 @@ export function parseTimeZone(zone: string | Token) {
|
|
|
68
68
|
}
|
|
69
69
|
},
|
|
70
70
|
datetimeTokenCheck.hour,
|
|
71
|
-
|
|
72
|
-
if (!
|
|
71
|
+
colon => {
|
|
72
|
+
if (!colon || !colon.value) {
|
|
73
73
|
return;
|
|
74
74
|
}
|
|
75
75
|
|
|
76
|
-
if (!
|
|
77
|
-
return
|
|
76
|
+
if (!colon.match(':')) {
|
|
77
|
+
return colon.unmatched({
|
|
78
78
|
reason: 'unexpected-token',
|
|
79
79
|
expects: [{ type: 'const', value: ':' }],
|
|
80
80
|
partName: 'time-zone',
|
|
@@ -21,7 +21,7 @@ test('excrescence-token', () => {
|
|
|
21
21
|
ref: null,
|
|
22
22
|
});
|
|
23
23
|
expect(check('x/y;')).toStrictEqual({
|
|
24
|
-
|
|
24
|
+
candidate: 'x/y',
|
|
25
25
|
column: 4,
|
|
26
26
|
expects: [{ type: 'format', value: 'MIME Type' }],
|
|
27
27
|
length: 1,
|
|
@@ -33,7 +33,7 @@ test('excrescence-token', () => {
|
|
|
33
33
|
ref: null,
|
|
34
34
|
});
|
|
35
35
|
expect(checkNoParam('x/y;')).toStrictEqual({
|
|
36
|
-
|
|
36
|
+
candidate: 'x/y',
|
|
37
37
|
column: 4,
|
|
38
38
|
expects: [{ type: 'format', value: 'MIME Type with no parameters' }],
|
|
39
39
|
length: 1,
|
|
@@ -45,7 +45,7 @@ test('excrescence-token', () => {
|
|
|
45
45
|
ref: null,
|
|
46
46
|
});
|
|
47
47
|
expect(checkNoParam('x/y;a=b;c=D;E="F"')).toStrictEqual({
|
|
48
|
-
|
|
48
|
+
candidate: 'x/y',
|
|
49
49
|
column: 4,
|
|
50
50
|
expects: [{ type: 'format', value: 'MIME Type with no parameters' }],
|
|
51
51
|
length: 14,
|
|
@@ -39,7 +39,7 @@ export const checkMIMEType: CustomSyntaxChecker<{
|
|
|
39
39
|
return new Token(extraToken, mimeType.essence.length, value).unmatched({
|
|
40
40
|
reason: 'extra-token',
|
|
41
41
|
expects: expects(withoutParameters),
|
|
42
|
-
|
|
42
|
+
candidate: mimeType.essence,
|
|
43
43
|
});
|
|
44
44
|
}
|
|
45
45
|
return unmatched(value, 'syntax-error', { expects: expects(withoutParameters) });
|