@markuplint/rules 3.10.0 → 3.12.0

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.
@@ -2,14 +2,14 @@ import type { Translator } from '@markuplint/i18n';
2
2
  import type { Attribute as AttrSpec, AttributeType } from '@markuplint/ml-spec';
3
3
  import type { ReadonlyDeep } from 'type-fest';
4
4
  type Invalid = {
5
- invalidType: 'non-existent' | 'invalid-value' | 'disallowed-attr';
6
- message: string;
7
- loc?: Loc;
5
+ invalidType: 'non-existent' | 'invalid-value' | 'disallowed-attr';
6
+ message: string;
7
+ loc?: Loc;
8
8
  };
9
9
  type Loc = {
10
- raw: string;
11
- line: number;
12
- col: number;
10
+ raw: string;
11
+ line: number;
12
+ col: number;
13
13
  };
14
14
  /**
15
15
  * Use in rules `invalid-attr` and `wai-aria`
@@ -19,17 +19,6 @@ type Loc = {
19
19
  * @param isCustomRule
20
20
  * @param spec
21
21
  */
22
- export declare function attrCheck(
23
- t: Translator,
24
- name: string,
25
- value: string,
26
- isCustomRule: boolean,
27
- spec?: AttrSpec,
28
- ): Invalid | false;
29
- export declare function valueCheck(
30
- t: Translator,
31
- name: string,
32
- value: string,
33
- type: ReadonlyDeep<AttributeType>,
34
- ): [string, Loc] | false;
22
+ export declare function attrCheck(t: Translator, name: string, value: string, isCustomRule: boolean, spec?: AttrSpec): Invalid | false;
23
+ export declare function valueCheck(t: Translator, name: string, value: string, type: ReadonlyDeep<AttributeType>): [string, Loc] | false;
35
24
  export {};
@@ -43,7 +43,7 @@ exports.default = (0, ml_core_1.createRule)({
43
43
  if (!('scope' in targetNode && 'line' in targetNode && targetNode.line != null)) {
44
44
  continue;
45
45
  }
46
- const escapedText = targetNode.raw.replace(/&(?:[a-z]+|#[0-9]+|x[0-9]);/gi, $0 => '*'.repeat($0.length));
46
+ const escapedText = targetNode.raw.replace(/&(?:[a-z]+|#[0-9]+|#x[0-9a-f]+);/gi, $0 => '*'.repeat($0.length));
47
47
  (0, ml_core_1.getLocationFromChars)(defaultChars, escapedText, targetNode.line, targetNode.col).forEach(location => {
48
48
  report({
49
49
  scope: targetNode.scope,
@@ -29,7 +29,7 @@ export declare function mergeHints(a: Readonly<Hints>, b: Readonly<Hints>): Part
29
29
  need?: string | undefined;
30
30
  }> | undefined;
31
31
  max?: number | undefined;
32
- not?: import("packages/@markuplint/ml-core/lib").DocumentType<import("./types").TagRule[], import("./types").Options> | import("packages/@markuplint/ml-core/lib/ml-dom/node/character-data").MLCharacterData<import("./types").TagRule[], import("./types").Options, import("packages/@markuplint/ml-ast/lib").MLASTAbstractNode> | import("packages/@markuplint/ml-core/lib").Element<import("./types").TagRule[], import("./types").Options> | import("packages/@markuplint/ml-core/lib").Block<import("./types").TagRule[], import("./types").Options> | undefined;
32
+ not?: ChildNode | undefined;
33
33
  transparent?: Element | undefined;
34
34
  }>;
35
35
  export declare function cleanObject<T extends Object>(object: T): Partial<T>;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@markuplint/rules",
3
- "version": "3.10.0",
3
+ "version": "3.12.0",
4
4
  "description": "Built-in rules of markuplint",
5
5
  "repository": "git@github.com:markuplint/markuplint.git",
6
6
  "author": "Yusuke Hirao <yusukehirao@me.com>",
@@ -20,19 +20,19 @@
20
20
  "./lib/permitted-contents/debug.js": "./lib/permitted-contents/debug.browser.js"
21
21
  },
22
22
  "dependencies": {
23
- "@markuplint/html-spec": "3.9.0",
24
- "@markuplint/ml-core": "3.10.0",
25
- "@markuplint/ml-spec": "3.9.0",
26
- "@markuplint/selector": "3.9.0",
27
- "@markuplint/shared": "3.6.0",
28
- "@markuplint/types": "3.7.0",
29
- "@types/debug": "^4.1.7",
23
+ "@markuplint/html-spec": "3.11.0",
24
+ "@markuplint/ml-core": "3.12.0",
25
+ "@markuplint/ml-spec": "3.11.0",
26
+ "@markuplint/selector": "3.11.0",
27
+ "@markuplint/shared": "3.8.0",
28
+ "@markuplint/types": "3.9.0",
29
+ "@types/debug": "^4.1.8",
30
30
  "@ungap/structured-clone": "^1.2.0",
31
31
  "ansi-colors": "^4.1.3",
32
32
  "chrono-node": "^2.6.3",
33
33
  "debug": "^4.3.4",
34
- "tslib": "^2.5.0",
35
- "type-fest": "^3.10.0"
34
+ "tslib": "^2.6.0",
35
+ "type-fest": "^3.12.0"
36
36
  },
37
- "gitHead": "af370797bfc887e5a5a2ff57fbaa8392ac98ead2"
37
+ "gitHead": "4ab20276db48a6acb29a923ea8666890ca853442"
38
38
  }
@@ -55,3 +55,18 @@ test('in EJS', async () => {
55
55
  });
56
56
  expect(violations.length).toBe(0);
57
57
  });
58
+
59
+ describe('Issues', () => {
60
+ test('#1074', async () => {
61
+ const { violations } = await mlRuleTest(rule, '<span>&#9660;</span><span>&#x25BC;</span><span>&x25BC;</span>');
62
+ expect(violations).toStrictEqual([
63
+ {
64
+ severity: 'error',
65
+ message: 'Illegal characters must escape in character reference',
66
+ line: 1,
67
+ col: 48,
68
+ raw: '&',
69
+ },
70
+ ]);
71
+ });
72
+ });
@@ -33,16 +33,3 @@ test('deprecated', async () => {
33
33
  },
34
34
  ]);
35
35
  });
36
-
37
- test('svg', async () => {
38
- const { violations } = await mlRuleTest(rule, '<svg><altGlyph>text</altGlyph></svg>');
39
- expect(violations).toStrictEqual([
40
- {
41
- severity: 'error',
42
- message: 'The "altGlyph" element is obsolete',
43
- line: 1,
44
- col: 6,
45
- raw: '<altGlyph>',
46
- },
47
- ]);
48
- });
@@ -1629,4 +1629,13 @@ ol(itemscope itemtype="https://schema.org/BreadcrumbList")
1629
1629
  ).violations,
1630
1630
  ).toStrictEqual([]);
1631
1631
  });
1632
+
1633
+ test('#1078', async () => {
1634
+ expect(
1635
+ (await mlRuleTest(rule, '<script src="foo.js" referrerpolicy="no-referrer"></script>')).violations,
1636
+ ).toStrictEqual([]);
1637
+ expect(
1638
+ (await mlRuleTest(rule, '<img src="foo.png" referrerpolicy="no-referrer"></img>')).violations,
1639
+ ).toStrictEqual([]);
1640
+ });
1632
1641
  });
@@ -1368,4 +1368,28 @@ describe('Issues', () => {
1368
1368
  .violations,
1369
1369
  ).toStrictEqual([]);
1370
1370
  });
1371
+
1372
+ test('#1046', async () => {
1373
+ const sourceCode = '<span><div></div></span>';
1374
+ expect(
1375
+ (
1376
+ await mlRuleTest(rule, sourceCode, {
1377
+ rule: {
1378
+ severity: 'warning',
1379
+ },
1380
+ parser: {
1381
+ '.*': '@markuplint/jsx-parser',
1382
+ },
1383
+ })
1384
+ ).violations,
1385
+ ).toStrictEqual([
1386
+ {
1387
+ severity: 'warning',
1388
+ line: 1,
1389
+ col: 7,
1390
+ message: 'The "div" element is not allowed in the "span" element in this context',
1391
+ raw: '<div>',
1392
+ },
1393
+ ]);
1394
+ });
1371
1395
  });
@@ -392,22 +392,7 @@ describe('Issues', () => {
392
392
  test('#592', async () => {
393
393
  expect(
394
394
  (await mlRuleTest(rule, '<svg aria-label="i-have-name"><path /><rect><path /></rect></svg>')).violations,
395
- ).toStrictEqual([
396
- {
397
- col: 31,
398
- line: 1,
399
- message: 'Require accessible name',
400
- raw: '<path />',
401
- severity: 'error',
402
- },
403
- {
404
- col: 39,
405
- line: 1,
406
- message: 'Require accessible name',
407
- raw: '<rect>',
408
- severity: 'error',
409
- },
410
- ]);
395
+ ).toStrictEqual([]);
411
396
  expect(
412
397
  (await mlRuleTest(rule, '<svg role="img" aria-label="i-have-name"><path /><rect><path /></rect></svg>'))
413
398
  .violations,
@@ -419,15 +404,7 @@ describe('Issues', () => {
419
404
  '<svg aria-label="i-have-name"><path aria-label="i-have-name" /><rect><path /></rect></svg>',
420
405
  )
421
406
  ).violations,
422
- ).toStrictEqual([
423
- {
424
- col: 64,
425
- line: 1,
426
- message: 'Require accessible name',
427
- raw: '<rect>',
428
- severity: 'error',
429
- },
430
- ]);
407
+ ).toStrictEqual([]);
431
408
  expect(
432
409
  (
433
410
  await mlRuleTest(
@@ -443,4 +420,25 @@ describe('Issues', () => {
443
420
  expect((await mlRuleTest(rule, '<dialog></dialog>')).violations.length).toBe(1);
444
421
  expect((await mlRuleTest(rule, '<div role="dialog"></div>')).violations.length).toBe(1);
445
422
  });
423
+
424
+ test('#1018', async () => {
425
+ expect(
426
+ (
427
+ await mlRuleTest(rule, '<button><slot /></button>', {
428
+ parser: {
429
+ '.*': '@markuplint/svelte-parser',
430
+ },
431
+ })
432
+ ).violations.length,
433
+ ).toBe(0);
434
+ expect(
435
+ (
436
+ await mlRuleTest(rule, '<button></button>', {
437
+ parser: {
438
+ '.*': '@markuplint/svelte-parser',
439
+ },
440
+ })
441
+ ).violations.length,
442
+ ).toBe(1);
443
+ });
446
444
  });
@@ -1170,4 +1170,34 @@ describe('Issues', () => {
1170
1170
  },
1171
1171
  ]);
1172
1172
  });
1173
+
1174
+ test('#1084', async () => {
1175
+ const { violations } = await mlRuleTest(
1176
+ rule,
1177
+ `
1178
+ <select multiple aria-label="some label text">
1179
+ <option>foo</option>
1180
+ <option>bar</option>
1181
+ </select>
1182
+
1183
+ <select aria-label="some label text">
1184
+ <option>foo</option>
1185
+ <option>bar</option>
1186
+ </select>
1187
+
1188
+ <select aria-label="some label text">
1189
+ <option>foo</option>
1190
+ <optgroup label="group">
1191
+ <option>bar</option>
1192
+ </optgroup>
1193
+ </select>
1194
+
1195
+ <datalist>
1196
+ <option>foo</option>
1197
+ <option>bar</option>
1198
+ </datalist>
1199
+ `,
1200
+ );
1201
+ expect(violations).toStrictEqual([]);
1202
+ });
1173
1203
  });
@@ -1,15 +0,0 @@
1
- import type { Options } from '../types';
2
- import type { ElementChecker } from '@markuplint/ml-core';
3
- import type { ARIAProperty, ARIARole } from '@markuplint/ml-spec';
4
- export declare const checkingRequiredProp: ElementChecker<
5
- boolean,
6
- Options,
7
- {
8
- role?:
9
- | (ARIARole & {
10
- isImplicit?: boolean;
11
- })
12
- | null;
13
- propSpecs: readonly ARIAProperty[];
14
- }
15
- >;
@@ -1,27 +0,0 @@
1
- "use strict";
2
- Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.checkingRequiredProp = void 0;
4
- const checkingRequiredProp = ({ el, role, propSpecs }) => t => {
5
- var _a;
6
- if (!role) {
7
- return;
8
- }
9
- if (role.isImplicit) {
10
- return;
11
- }
12
- const requiredProps = role.ownedProperties.filter(s => s.required).map(s => s.name);
13
- for (const requiredProp of requiredProps) {
14
- const has = el.attributes.some(attr => {
15
- const attrName = attr.name.toLowerCase();
16
- return attrName === requiredProp;
17
- });
18
- if (!has) {
19
- const propSpec = propSpecs.find(p => p.name === requiredProp);
20
- return {
21
- scope: el,
22
- message: t('{0:c} on {1}', t('Require {0}', t('the "{0*}" {1}', requiredProp, `ARIA ${(_a = propSpec === null || propSpec === void 0 ? void 0 : propSpec.type) !== null && _a !== void 0 ? _a : 'property'}`)), t('the "{0*}" {1}', role.name, 'role')),
23
- };
24
- }
25
- }
26
- };
27
- exports.checkingRequiredProp = checkingRequiredProp;
@@ -1,15 +0,0 @@
1
- import type { Options } from '../types';
2
- import type { ElementChecker } from '@markuplint/ml-core';
3
- import type { ARIAProperty, ARIARole } from '@markuplint/ml-spec';
4
- export declare const checkingRequiredProp: ElementChecker<
5
- boolean,
6
- Options,
7
- {
8
- role?:
9
- | (ARIARole & {
10
- isImplicit?: boolean;
11
- })
12
- | null;
13
- propSpecs: readonly ARIAProperty[];
14
- }
15
- >;
@@ -1,27 +0,0 @@
1
- "use strict";
2
- Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.checkingRequiredProp = void 0;
4
- const checkingRequiredProp = ({ el, role, propSpecs }) => t => {
5
- var _a;
6
- if (!role) {
7
- return;
8
- }
9
- if (role.isImplicit) {
10
- return;
11
- }
12
- const requiredProps = role.ownedProperties.filter(s => s.required).map(s => s.name);
13
- for (const requiredProp of requiredProps) {
14
- const has = el.attributes.some(attr => {
15
- const attrName = attr.name.toLowerCase();
16
- return attrName === requiredProp;
17
- });
18
- if (!has) {
19
- const propSpec = propSpecs.find(p => p.name === requiredProp);
20
- return {
21
- scope: el,
22
- message: t('{0:c} on {1}', t('Require {0}', t('the "{0*}" {1}', requiredProp, `ARIA ${(_a = propSpec === null || propSpec === void 0 ? void 0 : propSpec.type) !== null && _a !== void 0 ? _a : 'property'}`)), t('the "{0*}" {1}', role.name, 'role')),
23
- };
24
- }
25
- }
26
- };
27
- exports.checkingRequiredProp = checkingRequiredProp;
@@ -1,15 +0,0 @@
1
- import type { Options } from '../types';
2
- import type { ElementChecker } from '@markuplint/ml-core';
3
- import type { ARIAProperty, ARIARole } from '@markuplint/ml-spec';
4
- export declare const checkingUnadoptedExplicitRoles: ElementChecker<
5
- boolean,
6
- Options,
7
- {
8
- role?:
9
- | (ARIARole & {
10
- isImplicit?: boolean;
11
- })
12
- | null;
13
- propSpecs: readonly ARIAProperty[];
14
- }
15
- >;
@@ -1,17 +0,0 @@
1
- "use strict";
2
- Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.checkingUnadoptedExplicitRoles = void 0;
4
- const checkingUnadoptedExplicitRoles = ({ el, role, propSpecs }) => t => {
5
- if (!role) {
6
- return;
7
- }
8
- if (role.name) {
9
- console.log(el.localName, role.name);
10
- return {
11
- scope: el,
12
- message: '',
13
- };
14
- }
15
- return;
16
- };
17
- exports.checkingUnadoptedExplicitRoles = checkingUnadoptedExplicitRoles;
@@ -1,2 +0,0 @@
1
- declare const _default: Readonly<import('@markuplint/ml-core').RuleSeed<boolean, null>>;
2
- export default _default;
package/lib/xxx/index.js DELETED
@@ -1,32 +0,0 @@
1
- "use strict";
2
- Object.defineProperty(exports, "__esModule", { value: true });
3
- const ml_core_1 = require("@markuplint/ml-core");
4
- exports.default = (0, ml_core_1.createRule)({
5
- defaultValue: true,
6
- defaultOptions: null,
7
- async verify({ document, report, t }) {
8
- // Element
9
- await document.walkOn("Element", (el) => {
10
- const raw = el.raw.trim();
11
- if (/./i.test(raw)) {
12
- report({
13
- scope: el,
14
- message: t("It is {0}", "issue"),
15
- });
16
- }
17
- });
18
- // Attribute
19
- await document.walkOn("Attr", (attr) => {
20
- var _a, _b, _c;
21
- if (/./i.test(attr.name)) {
22
- report({
23
- scope: attr,
24
- line: (_a = attr.nameNode) === null || _a === void 0 ? void 0 : _a.startLine,
25
- col: (_b = attr.nameNode) === null || _b === void 0 ? void 0 : _b.startCol,
26
- raw: (_c = attr.nameNode) === null || _c === void 0 ? void 0 : _c.raw,
27
- message: t("It is {0}", "issue"),
28
- });
29
- }
30
- });
31
- },
32
- });