@markuplint/rules 3.0.0-dev.54 → 3.0.0-dev.96
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/lib/attr-check.d.ts +1 -1
- package/lib/attr-check.js +12 -0
- package/lib/helpers.d.ts +1 -1
- package/lib/index.js +4 -0
- package/lib/invalid-attr/index.js +7 -0
- package/lib/no-empty-palpable-content/index.js +8 -0
- package/lib/no-refer-to-non-existent-id/index.d.ts +1 -0
- package/lib/no-refer-to-non-existent-id/index.js +67 -2
- package/lib/placeholder-label-option/index.d.ts +2 -0
- package/lib/placeholder-label-option/index.js +83 -0
- package/lib/require-datetime/index.d.ts +6 -0
- package/lib/require-datetime/index.js +34 -0
- package/lib/require-datetime/types.d.ts +10 -0
- package/lib/require-datetime/types.js +2 -0
- package/lib/require-datetime/utils.d.ts +11 -0
- package/lib/require-datetime/utils.js +134 -0
- package/package.json +8 -6
- package/schema.json +6 -0
package/lib/attr-check.d.ts
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import type { Translator } from '@markuplint/i18n';
|
|
2
2
|
import type { Attribute as AttrSpec, AttributeType } from '@markuplint/ml-spec';
|
|
3
3
|
type Invalid = {
|
|
4
|
-
invalidType: 'non-existent' | 'invalid-value';
|
|
4
|
+
invalidType: 'non-existent' | 'invalid-value' | 'disallowed-attr';
|
|
5
5
|
message: string;
|
|
6
6
|
loc?: Loc;
|
|
7
7
|
};
|
package/lib/attr-check.js
CHANGED
|
@@ -22,6 +22,12 @@ function attrCheck(t, name, value, isCustomRule, spec) {
|
|
|
22
22
|
// Ignore checking because ARIA attributes are check on another rule
|
|
23
23
|
return false;
|
|
24
24
|
}
|
|
25
|
+
// @see https://www.w3.org/TR/adapt/
|
|
26
|
+
// It is an experimental
|
|
27
|
+
if (/^adapt-.+$/.test(name)) {
|
|
28
|
+
// Ignore checking because "adapt-*" attribute is any type
|
|
29
|
+
return false;
|
|
30
|
+
}
|
|
25
31
|
}
|
|
26
32
|
// Existence
|
|
27
33
|
if (!spec) {
|
|
@@ -41,6 +47,12 @@ function attrCheck(t, name, value, isCustomRule, spec) {
|
|
|
41
47
|
t('Did you mean "{0*}"?', spec.name),
|
|
42
48
|
};
|
|
43
49
|
}
|
|
50
|
+
if (spec.noUse) {
|
|
51
|
+
return {
|
|
52
|
+
invalidType: 'disallowed-attr',
|
|
53
|
+
message: t('{0} is {1:c}', t('the "{0*}" {1}', name, 'attribute'), 'disallowed'),
|
|
54
|
+
};
|
|
55
|
+
}
|
|
44
56
|
const types = Array.isArray(spec.type) ? spec.type : [spec.type];
|
|
45
57
|
const invalidList = types.map(type => {
|
|
46
58
|
if (!type) {
|
package/lib/helpers.d.ts
CHANGED
|
@@ -21,7 +21,7 @@ export declare function match(needle: string, pattern: string): boolean;
|
|
|
21
21
|
*/
|
|
22
22
|
export declare const rePCENChar: string;
|
|
23
23
|
export declare function isValidAttr(t: Translator, name: string, value: string, isDynamicValue: boolean, node: Element<any, any>, attrSpecs: Attribute[], log?: Log): false | {
|
|
24
|
-
invalidType: "non-existent" | "invalid-value";
|
|
24
|
+
invalidType: "non-existent" | "invalid-value" | "disallowed-attr";
|
|
25
25
|
message: string;
|
|
26
26
|
loc?: {
|
|
27
27
|
raw: string;
|
package/lib/index.js
CHANGED
|
@@ -24,7 +24,9 @@ const no_hard_code_id_1 = tslib_1.__importDefault(require("./no-hard-code-id"));
|
|
|
24
24
|
const no_refer_to_non_existent_id_1 = tslib_1.__importDefault(require("./no-refer-to-non-existent-id"));
|
|
25
25
|
const no_use_event_handler_attr_1 = tslib_1.__importDefault(require("./no-use-event-handler-attr"));
|
|
26
26
|
const permitted_contents_1 = tslib_1.__importDefault(require("./permitted-contents"));
|
|
27
|
+
const placeholder_label_option_1 = tslib_1.__importDefault(require("./placeholder-label-option"));
|
|
27
28
|
const require_accessible_name_1 = tslib_1.__importDefault(require("./require-accessible-name"));
|
|
29
|
+
const require_datetime_1 = tslib_1.__importDefault(require("./require-datetime"));
|
|
28
30
|
const required_attr_1 = tslib_1.__importDefault(require("./required-attr"));
|
|
29
31
|
const required_element_1 = tslib_1.__importDefault(require("./required-element"));
|
|
30
32
|
const required_h1_1 = tslib_1.__importDefault(require("./required-h1"));
|
|
@@ -54,7 +56,9 @@ exports.default = {
|
|
|
54
56
|
'no-refer-to-non-existent-id': no_refer_to_non_existent_id_1.default,
|
|
55
57
|
'no-use-event-handler-attr': no_use_event_handler_attr_1.default,
|
|
56
58
|
'permitted-contents': permitted_contents_1.default,
|
|
59
|
+
'placeholder-label-option': placeholder_label_option_1.default,
|
|
57
60
|
'require-accessible-name': require_accessible_name_1.default,
|
|
61
|
+
'require-datetime': require_datetime_1.default,
|
|
58
62
|
'required-attr': required_attr_1.default,
|
|
59
63
|
'required-element': required_element_1.default,
|
|
60
64
|
'required-h1': required_h1_1.default,
|
|
@@ -77,6 +77,13 @@ exports.default = (0, ml_core_1.createRule)({
|
|
|
77
77
|
}
|
|
78
78
|
if (invalid) {
|
|
79
79
|
switch (invalid.invalidType) {
|
|
80
|
+
case 'disallowed-attr': {
|
|
81
|
+
report({
|
|
82
|
+
scope: attr,
|
|
83
|
+
message: invalid.message,
|
|
84
|
+
});
|
|
85
|
+
break;
|
|
86
|
+
}
|
|
80
87
|
case 'invalid-value': {
|
|
81
88
|
if (attr.isDynamicValue) {
|
|
82
89
|
break;
|
|
@@ -10,6 +10,14 @@ exports.default = (0, ml_core_1.createRule)({
|
|
|
10
10
|
},
|
|
11
11
|
async verify({ document, report, t }) {
|
|
12
12
|
await document.walkOn('Element', el => {
|
|
13
|
+
/**
|
|
14
|
+
* Exception
|
|
15
|
+
*
|
|
16
|
+
* - The `textarea` element is possibly empty because a user inputs it.
|
|
17
|
+
*/
|
|
18
|
+
if (el.localName === 'textarea') {
|
|
19
|
+
return;
|
|
20
|
+
}
|
|
13
21
|
if (!(0, ml_spec_1.isPalpableElement)(el, el.ownerMLDocument.specs, {
|
|
14
22
|
extendsSvg: false,
|
|
15
23
|
extendsExposableElements: el.rule.options.extendsExposableElements,
|
|
@@ -1,15 +1,20 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
3
|
const ml_core_1 = require("@markuplint/ml-core");
|
|
4
|
+
const HYPERLINK_SELECTOR = 'a[href], area[href]';
|
|
4
5
|
exports.default = (0, ml_core_1.createRule)({
|
|
5
6
|
defaultOptions: {
|
|
6
7
|
ariaVersion: '1.2',
|
|
8
|
+
fragmentRefersNameAttr: false,
|
|
7
9
|
},
|
|
8
10
|
async verify({ document, report, t }) {
|
|
9
11
|
const idList = new Set();
|
|
12
|
+
const nameList = new Set();
|
|
10
13
|
let hasDynamicId = false;
|
|
11
|
-
|
|
12
|
-
|
|
14
|
+
let hasDynamicName = false;
|
|
15
|
+
document.querySelectorAll('[id]').forEach(el => {
|
|
16
|
+
const attr = el.getAttributeNode('id');
|
|
17
|
+
if (!attr) {
|
|
13
18
|
return;
|
|
14
19
|
}
|
|
15
20
|
if (attr.isDynamicValue) {
|
|
@@ -22,6 +27,18 @@ exports.default = (0, ml_core_1.createRule)({
|
|
|
22
27
|
if (hasDynamicId) {
|
|
23
28
|
return;
|
|
24
29
|
}
|
|
30
|
+
document.querySelectorAll('[name]').forEach(el => {
|
|
31
|
+
const attr = el.getAttributeNode('name');
|
|
32
|
+
if (!attr) {
|
|
33
|
+
return;
|
|
34
|
+
}
|
|
35
|
+
if (attr.isDynamicValue) {
|
|
36
|
+
hasDynamicName = true;
|
|
37
|
+
}
|
|
38
|
+
if (attr.valueType !== 'code') {
|
|
39
|
+
nameList.add(attr.value);
|
|
40
|
+
}
|
|
41
|
+
});
|
|
25
42
|
await document.walkOn('Attr', attr => {
|
|
26
43
|
var _a, _b, _c, _d, _e, _f, _g, _h, _j, _k, _l, _m;
|
|
27
44
|
const attrSpec = (0, ml_core_1.getAttrSpecs)(attr.ownerElement, document.specs);
|
|
@@ -100,5 +117,53 @@ exports.default = (0, ml_core_1.createRule)({
|
|
|
100
117
|
}
|
|
101
118
|
}
|
|
102
119
|
});
|
|
120
|
+
/**
|
|
121
|
+
* @see https://html.spec.whatwg.org/multipage/browsing-the-web.html#scrolling-to-a-fragment
|
|
122
|
+
*/
|
|
123
|
+
await document.walkOn('Element', el => {
|
|
124
|
+
var _a, _b, _c, _d;
|
|
125
|
+
if (el.rule.options.fragmentRefersNameAttr && hasDynamicName) {
|
|
126
|
+
return;
|
|
127
|
+
}
|
|
128
|
+
if (!el.matches(HYPERLINK_SELECTOR)) {
|
|
129
|
+
return;
|
|
130
|
+
}
|
|
131
|
+
const href = el.getAttributeNode('href');
|
|
132
|
+
if (!href) {
|
|
133
|
+
return;
|
|
134
|
+
}
|
|
135
|
+
const rawFragment = (_a = href.value.match(/^#(.+)/)) === null || _a === void 0 ? void 0 : _a[1];
|
|
136
|
+
if (rawFragment == null) {
|
|
137
|
+
return;
|
|
138
|
+
}
|
|
139
|
+
const decodedFragment = decode(rawFragment);
|
|
140
|
+
// > 2. If fragment is the empty string, then return the special value top of the document.
|
|
141
|
+
// >
|
|
142
|
+
// > 9. If decodedFragment is an ASCII case-insensitive match for the string top, then return the top of the document.
|
|
143
|
+
if (decodedFragment === '' || /^top$/i.test(decodedFragment)) {
|
|
144
|
+
return;
|
|
145
|
+
}
|
|
146
|
+
if (!idList.has(decodedFragment) &&
|
|
147
|
+
(el.rule.options.fragmentRefersNameAttr ? !nameList.has(decodedFragment) : true)) {
|
|
148
|
+
report({
|
|
149
|
+
scope: href,
|
|
150
|
+
line: (_b = href.valueNode) === null || _b === void 0 ? void 0 : _b.startLine,
|
|
151
|
+
col: (_c = href.valueNode) === null || _c === void 0 ? void 0 : _c.startCol,
|
|
152
|
+
raw: (_d = href.valueNode) === null || _d === void 0 ? void 0 : _d.raw,
|
|
153
|
+
message: t('Missing {0}', t('"{0*}" ID', decodedFragment)),
|
|
154
|
+
});
|
|
155
|
+
}
|
|
156
|
+
});
|
|
103
157
|
},
|
|
104
158
|
});
|
|
159
|
+
function decode(fragment) {
|
|
160
|
+
try {
|
|
161
|
+
return decodeURI(fragment);
|
|
162
|
+
}
|
|
163
|
+
catch (e) {
|
|
164
|
+
if (e instanceof URIError) {
|
|
165
|
+
return fragment;
|
|
166
|
+
}
|
|
167
|
+
throw e;
|
|
168
|
+
}
|
|
169
|
+
}
|
|
@@ -0,0 +1,83 @@
|
|
|
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
|
+
verify({ document, report, t }) {
|
|
6
|
+
document.querySelectorAll('select').forEach(select => {
|
|
7
|
+
if (hasPlaceholderLabelOption(select)) {
|
|
8
|
+
return;
|
|
9
|
+
}
|
|
10
|
+
if (!needPlaceholderLabelOption(select)) {
|
|
11
|
+
return;
|
|
12
|
+
}
|
|
13
|
+
report({
|
|
14
|
+
scope: select,
|
|
15
|
+
message: t('need {0}', t('the {0}', 'placeholder label option')),
|
|
16
|
+
});
|
|
17
|
+
});
|
|
18
|
+
},
|
|
19
|
+
});
|
|
20
|
+
/**
|
|
21
|
+
* > If a select element has a required attribute specified,
|
|
22
|
+
* > does not have a multiple attribute specified,
|
|
23
|
+
* > and has a display size of 1,
|
|
24
|
+
* > then the select element must have a placeholder label option.
|
|
25
|
+
*
|
|
26
|
+
* @param select
|
|
27
|
+
* @returns
|
|
28
|
+
*/
|
|
29
|
+
function needPlaceholderLabelOption(select) {
|
|
30
|
+
const hasRequired = select.hasAttribute('required');
|
|
31
|
+
if (!hasRequired) {
|
|
32
|
+
return false;
|
|
33
|
+
}
|
|
34
|
+
const hasMultiple = select.hasAttribute('multiple');
|
|
35
|
+
if (hasMultiple) {
|
|
36
|
+
return false;
|
|
37
|
+
}
|
|
38
|
+
const size = select.getAttribute('size') || '1';
|
|
39
|
+
if (size !== '1') {
|
|
40
|
+
return false;
|
|
41
|
+
}
|
|
42
|
+
return true;
|
|
43
|
+
}
|
|
44
|
+
/**
|
|
45
|
+
* > If a select element has a required attribute specified,
|
|
46
|
+
* > does not have a multiple attribute specified,
|
|
47
|
+
* > and has a display size of 1;
|
|
48
|
+
* > and if the value of the first option element
|
|
49
|
+
* > in the select element's list of options (if any) is the empty string,
|
|
50
|
+
* > and that option element's parent node is the select element (and not an optgroup element),
|
|
51
|
+
* > then that option is the select element's **placeholder label option**.
|
|
52
|
+
*
|
|
53
|
+
* @param select
|
|
54
|
+
* @returns
|
|
55
|
+
*/
|
|
56
|
+
function hasPlaceholderLabelOption(select) {
|
|
57
|
+
var _a;
|
|
58
|
+
// > has a required attribute specified
|
|
59
|
+
if (!select.hasAttribute('required')) {
|
|
60
|
+
return false;
|
|
61
|
+
}
|
|
62
|
+
// > does not have a multiple attribute specified
|
|
63
|
+
if (select.hasAttribute('multiple')) {
|
|
64
|
+
return false;
|
|
65
|
+
}
|
|
66
|
+
// > has a display size of 1
|
|
67
|
+
const size = select.getAttribute('size') || '1';
|
|
68
|
+
if (size !== '1') {
|
|
69
|
+
return false;
|
|
70
|
+
}
|
|
71
|
+
// > in the select element's list of options (if any) is the empty string
|
|
72
|
+
const firstOption = select.querySelector('option');
|
|
73
|
+
if (!firstOption) {
|
|
74
|
+
// if any
|
|
75
|
+
return true;
|
|
76
|
+
}
|
|
77
|
+
// > that option element's parent node is the select element (and not an optgroup element)
|
|
78
|
+
if (((_a = firstOption.parentElement) === null || _a === void 0 ? void 0 : _a.localName) === 'optgroup') {
|
|
79
|
+
return false;
|
|
80
|
+
}
|
|
81
|
+
const value = firstOption.getAttribute('value');
|
|
82
|
+
return value === '' || value === null;
|
|
83
|
+
}
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
const ml_core_1 = require("@markuplint/ml-core");
|
|
4
|
+
const types_1 = require("@markuplint/types");
|
|
5
|
+
const utils_1 = require("./utils");
|
|
6
|
+
exports.default = (0, ml_core_1.createRule)({
|
|
7
|
+
defaultOptions: {
|
|
8
|
+
langs: undefined,
|
|
9
|
+
},
|
|
10
|
+
verify({ document, report, t }) {
|
|
11
|
+
document.querySelectorAll('time:not([datetime])').forEach(time => {
|
|
12
|
+
if (time.hasMutableChildren()) {
|
|
13
|
+
return;
|
|
14
|
+
}
|
|
15
|
+
const content = time.textContent.trim();
|
|
16
|
+
const result = (0, types_1.check)(content, 'DateTime');
|
|
17
|
+
if (result.matched) {
|
|
18
|
+
return;
|
|
19
|
+
}
|
|
20
|
+
const candidate = (0, utils_1.getCandidateDatetimeString)(content, time.rule.options.langs);
|
|
21
|
+
if (candidate) {
|
|
22
|
+
report({
|
|
23
|
+
scope: time,
|
|
24
|
+
message: t('need {0*}', `datetime="${candidate}"`),
|
|
25
|
+
});
|
|
26
|
+
return;
|
|
27
|
+
}
|
|
28
|
+
report({
|
|
29
|
+
scope: time,
|
|
30
|
+
message: t('need {0}', t('the "{0*}" {1}', 'datetime', 'attribute')),
|
|
31
|
+
});
|
|
32
|
+
});
|
|
33
|
+
},
|
|
34
|
+
});
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
export type DateTimeKey = 'year' | 'month' | 'day' | 'hour' | 'minute' | 'second' | 'ms';
|
|
2
|
+
export type DateTimeData = Partial<Record<DateTimeKey, number>>;
|
|
3
|
+
export type DateTime = {
|
|
4
|
+
datetime: DateTimeData;
|
|
5
|
+
zone?: number;
|
|
6
|
+
};
|
|
7
|
+
/**
|
|
8
|
+
* @see https://github.com/wanasit/chrono#locales
|
|
9
|
+
*/
|
|
10
|
+
export type Lang = 'en' | 'ja' | 'fr' | 'nl' | 'ru' | 'de' | 'pt' | 'zh';
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import type { DateTime, Lang } from './types';
|
|
2
|
+
/**
|
|
3
|
+
* Datetime-ish text to a datetime data
|
|
4
|
+
*
|
|
5
|
+
* @param content
|
|
6
|
+
* @param langs
|
|
7
|
+
* @param base Reference date for a test
|
|
8
|
+
* @returns
|
|
9
|
+
*/
|
|
10
|
+
export declare function parseADatetime(content: string, langs: Lang[], base?: Date): DateTime | null;
|
|
11
|
+
export declare function getCandidateDatetimeString(content: string, langs?: Lang[]): string | null;
|
|
@@ -0,0 +1,134 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.getCandidateDatetimeString = exports.parseADatetime = void 0;
|
|
4
|
+
const tslib_1 = require("tslib");
|
|
5
|
+
const chrono = tslib_1.__importStar(require("chrono-node"));
|
|
6
|
+
const defaultLangs = ['en', 'ja', 'fr', 'nl', 'ru', 'de', 'pt', 'zh'];
|
|
7
|
+
/**
|
|
8
|
+
* Datetime-ish text to a datetime data
|
|
9
|
+
*
|
|
10
|
+
* @param content
|
|
11
|
+
* @param langs
|
|
12
|
+
* @param base Reference date for a test
|
|
13
|
+
* @returns
|
|
14
|
+
*/
|
|
15
|
+
function parseADatetime(content, langs, base) {
|
|
16
|
+
const date = parseTryMultipleLangs(content, langs, base);
|
|
17
|
+
if (!date) {
|
|
18
|
+
return null;
|
|
19
|
+
}
|
|
20
|
+
const data = {};
|
|
21
|
+
if (date.isCertain('year')) {
|
|
22
|
+
data.year = date.get('year');
|
|
23
|
+
}
|
|
24
|
+
if (date.isCertain('month')) {
|
|
25
|
+
data.month = date.get('month');
|
|
26
|
+
}
|
|
27
|
+
if (date.isCertain('day')) {
|
|
28
|
+
data.day = date.get('day');
|
|
29
|
+
}
|
|
30
|
+
if (date.isCertain('hour')) {
|
|
31
|
+
data.hour = date.get('hour');
|
|
32
|
+
}
|
|
33
|
+
if (date.isCertain('hour')) {
|
|
34
|
+
data.minute = date.get('minute') || 0;
|
|
35
|
+
}
|
|
36
|
+
if (date.isCertain('second')) {
|
|
37
|
+
data.second = date.get('second');
|
|
38
|
+
}
|
|
39
|
+
if (date.isCertain('millisecond')) {
|
|
40
|
+
data.ms = date.get('millisecond');
|
|
41
|
+
}
|
|
42
|
+
const datetime = {
|
|
43
|
+
datetime: data,
|
|
44
|
+
};
|
|
45
|
+
if (date.isCertain('timezoneOffset')) {
|
|
46
|
+
datetime.zone = date.get('timezoneOffset');
|
|
47
|
+
}
|
|
48
|
+
return datetime;
|
|
49
|
+
}
|
|
50
|
+
exports.parseADatetime = parseADatetime;
|
|
51
|
+
function getCandidateDatetimeString(content, langs = defaultLangs) {
|
|
52
|
+
const date = parseADatetime(content, langs);
|
|
53
|
+
if (!date) {
|
|
54
|
+
return null;
|
|
55
|
+
}
|
|
56
|
+
let datetimeStr = toDatetimeString(date.datetime);
|
|
57
|
+
if (!datetimeStr) {
|
|
58
|
+
return null;
|
|
59
|
+
}
|
|
60
|
+
if (date.zone) {
|
|
61
|
+
const plusMinus = date.zone < 0 ? '-' : '+';
|
|
62
|
+
const hour = Math.floor(Math.abs(date.zone) / 60);
|
|
63
|
+
const minute = Math.abs(date.zone) % 60;
|
|
64
|
+
datetimeStr += `${plusMinus}${f(hour, 2)}${f(minute, 2)}`;
|
|
65
|
+
}
|
|
66
|
+
return datetimeStr;
|
|
67
|
+
}
|
|
68
|
+
exports.getCandidateDatetimeString = getCandidateDatetimeString;
|
|
69
|
+
function toDatetimeString(date) {
|
|
70
|
+
if (only(date, ['year', 'month'])) {
|
|
71
|
+
return `${f(date.year, 4)}-${f(date.month, 2)}`;
|
|
72
|
+
}
|
|
73
|
+
if (only(date, ['year', 'month', 'day'])) {
|
|
74
|
+
return `${f(date.year, 4)}-${f(date.month, 2)}-${f(date.day, 2)}`;
|
|
75
|
+
}
|
|
76
|
+
if (only(date, ['month', 'day'])) {
|
|
77
|
+
return `${f(date.month, 2)}-${f(date.day, 2)}`;
|
|
78
|
+
}
|
|
79
|
+
if (only(date, ['hour', 'minute'])) {
|
|
80
|
+
return `${f(date.hour, 2)}:${f(date.minute, 2)}`;
|
|
81
|
+
}
|
|
82
|
+
if (only(date, ['hour', 'minute', 'second'])) {
|
|
83
|
+
return `${f(date.hour, 2)}:${f(date.minute, 2)}:${f(date.second, 2)}`;
|
|
84
|
+
}
|
|
85
|
+
if (only(date, ['hour', 'minute', 'second', 'ms'])) {
|
|
86
|
+
return `${f(date.hour, 2)}:${f(date.minute, 2)}:${f(date.second, 2)}.${date.ms}`;
|
|
87
|
+
}
|
|
88
|
+
if (only(date, ['year', 'month', 'day', 'hour', 'minute'])) {
|
|
89
|
+
return `${f(date.year, 4)}-${f(date.month, 2)}-${f(date.day, 2)}T${f(date.hour, 2)}:${f(date.minute, 2)}`;
|
|
90
|
+
}
|
|
91
|
+
if (only(date, ['year', 'month', 'day', 'hour', 'minute', 'second'])) {
|
|
92
|
+
return `${f(date.year, 4)}-${f(date.month, 2)}-${f(date.day, 2)}T${f(date.hour, 2)}:${f(date.minute, 2)}:${f(date.second, 2)}`;
|
|
93
|
+
}
|
|
94
|
+
if (only(date, ['year', 'month', 'day', 'hour', 'minute', 'second', 'ms'])) {
|
|
95
|
+
return `${f(date.year, 4)}-${f(date.month, 2)}-${f(date.day, 2)}T${f(date.hour, 2)}:${f(date.minute, 2)}:${f(date.second, 2)}.${date.ms}`;
|
|
96
|
+
}
|
|
97
|
+
return null;
|
|
98
|
+
}
|
|
99
|
+
function parseTryMultipleLangs(content, langs, base) {
|
|
100
|
+
for (const lang of langs) {
|
|
101
|
+
// eslint-disable-next-line import/namespace
|
|
102
|
+
const results = chrono[lang].casual.parse(content, base);
|
|
103
|
+
// Is not multiple datetime contents
|
|
104
|
+
if (results.length < 1) {
|
|
105
|
+
continue;
|
|
106
|
+
}
|
|
107
|
+
const result = results[0];
|
|
108
|
+
// Is not a range or period
|
|
109
|
+
if (result.end) {
|
|
110
|
+
continue;
|
|
111
|
+
}
|
|
112
|
+
return result.start;
|
|
113
|
+
}
|
|
114
|
+
return null;
|
|
115
|
+
}
|
|
116
|
+
function only(date, keys) {
|
|
117
|
+
const list = Object.keys(date);
|
|
118
|
+
for (const exists of list) {
|
|
119
|
+
if (!keys.includes(exists)) {
|
|
120
|
+
return false;
|
|
121
|
+
}
|
|
122
|
+
}
|
|
123
|
+
return true;
|
|
124
|
+
}
|
|
125
|
+
/**
|
|
126
|
+
* Formatter
|
|
127
|
+
*
|
|
128
|
+
* @param n
|
|
129
|
+
* @param pad zero padding
|
|
130
|
+
* @returns
|
|
131
|
+
*/
|
|
132
|
+
function f(n, pad) {
|
|
133
|
+
return n.toString(10).padStart(pad, '0');
|
|
134
|
+
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@markuplint/rules",
|
|
3
|
-
"version": "3.0.0-dev.
|
|
3
|
+
"version": "3.0.0-dev.96+3b9f1720",
|
|
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,11 +20,13 @@
|
|
|
20
20
|
"./lib/permitted-contents/debug.js": "./lib/permitted-contents/debug.browser.js"
|
|
21
21
|
},
|
|
22
22
|
"dependencies": {
|
|
23
|
-
"@markuplint/html-spec": "3.0.0-dev.
|
|
24
|
-
"@markuplint/ml-core": "3.0.0-dev.
|
|
25
|
-
"@markuplint/ml-spec": "3.0.0-dev.
|
|
26
|
-
"@markuplint/types": "3.0.0-dev.
|
|
23
|
+
"@markuplint/html-spec": "3.0.0-dev.96+3b9f1720",
|
|
24
|
+
"@markuplint/ml-core": "3.0.0-dev.96+3b9f1720",
|
|
25
|
+
"@markuplint/ml-spec": "3.0.0-dev.96+3b9f1720",
|
|
26
|
+
"@markuplint/types": "3.0.0-dev.96+3b9f1720",
|
|
27
27
|
"@ungap/structured-clone": "^1.0.1",
|
|
28
|
+
"ansi-colors": "^4.1.3",
|
|
29
|
+
"chrono-node": "^2.5.0",
|
|
28
30
|
"debug": "^4.3.4",
|
|
29
31
|
"html-entities": "^2.3.3",
|
|
30
32
|
"tslib": "^2.4.1"
|
|
@@ -32,5 +34,5 @@
|
|
|
32
34
|
"devDependencies": {
|
|
33
35
|
"@types/debug": "^4.1.7"
|
|
34
36
|
},
|
|
35
|
-
"gitHead": "
|
|
37
|
+
"gitHead": "3b9f17205d7754b29edf790bdbbf5e4931ba27a2"
|
|
36
38
|
}
|
package/schema.json
CHANGED
|
@@ -75,9 +75,15 @@
|
|
|
75
75
|
"permitted-contents": {
|
|
76
76
|
"$ref": "https://raw.githubusercontent.com/markuplint/markuplint/main/packages/%40markuplint/rules/src/permitted-contents/schema.json"
|
|
77
77
|
},
|
|
78
|
+
"placeholder-label-option": {
|
|
79
|
+
"$ref": "https://raw.githubusercontent.com/markuplint/markuplint/main/packages/%40markuplint/rules/src/placeholder-label-option/schema.json"
|
|
80
|
+
},
|
|
78
81
|
"require-accessible-name": {
|
|
79
82
|
"$ref": "https://raw.githubusercontent.com/markuplint/markuplint/main/packages/%40markuplint/rules/src/require-accessible-name/schema.json"
|
|
80
83
|
},
|
|
84
|
+
"require-datetime": {
|
|
85
|
+
"$ref": "https://raw.githubusercontent.com/markuplint/markuplint/main/packages/%40markuplint/rules/src/require-datetime/schema.json"
|
|
86
|
+
},
|
|
81
87
|
"required-attr": {
|
|
82
88
|
"$ref": "https://raw.githubusercontent.com/markuplint/markuplint/main/packages/%40markuplint/rules/src/required-attr/schema.json"
|
|
83
89
|
},
|