@markuplint/ml-spec 5.6.0 → 5.6.1
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/CHANGELOG.md +7 -0
- package/lib/types/index.d.ts +50 -3
- package/lib/utils/directive-resolver.d.ts +11 -0
- package/lib/utils/directive-resolver.js +31 -0
- package/package.json +3 -3
package/CHANGELOG.md
CHANGED
|
@@ -3,6 +3,13 @@
|
|
|
3
3
|
All notable changes to this project will be documented in this file.
|
|
4
4
|
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
|
|
5
5
|
|
|
6
|
+
## [5.6.1](https://github.com/markuplint/markuplint/compare/v5.6.0...v5.6.1) (2026-11-13)
|
|
7
|
+
|
|
8
|
+
### Bug Fixes
|
|
9
|
+
|
|
10
|
+
* **ml-spec:** let a directive pattern declare the key of its repeat ([df95b4c](https://github.com/markuplint/markuplint/commit/df95b4cd56753fa4c665991f6bc091e4c0620225))
|
|
11
|
+
|
|
12
|
+
|
|
6
13
|
# [5.6.0](https://github.com/markuplint/markuplint/compare/v5.5.0...v5.6.0) (2026-11-12)
|
|
7
14
|
|
|
8
15
|
### Bug Fixes
|
package/lib/types/index.d.ts
CHANGED
|
@@ -155,11 +155,58 @@ export type DirectivePattern = {
|
|
|
155
155
|
*/
|
|
156
156
|
readonly valueType?: 'string' | 'number' | 'boolean' | 'code';
|
|
157
157
|
/**
|
|
158
|
-
* If true, the attribute
|
|
159
|
-
* Can also be a string array of potentialName values for which
|
|
160
|
-
*
|
|
158
|
+
* If true, the attribute is not a repeat of an attribute of the same name (`:class` beside `class`,
|
|
159
|
+
* which Vue merges). Can also be a string array of potentialName values for which this holds
|
|
160
|
+
* (e.g., ['class', 'style']).
|
|
161
|
+
*
|
|
162
|
+
* It is not whether the attribute may be written twice: that is told by {@link DirectivePattern.duplicateKey}
|
|
163
|
+
* (without it, the same spelling of a name that is not `true` is a repeat, and `true` is never one).
|
|
161
164
|
*/
|
|
162
165
|
readonly isDuplicatable?: true | readonly string[];
|
|
166
|
+
/**
|
|
167
|
+
* The key under which the framework counts this directive as written twice, as a template that takes the
|
|
168
|
+
* captures of `pattern` (`$1`, `$2`, …) as they are spelled. Two attributes of an element with the same key
|
|
169
|
+
* are a repeat, whatever name the directive resolves to (`potentialName`): a framework tells a repeat by
|
|
170
|
+
* what it keeps of the name, and a directive is not an attribute of the DOM.
|
|
171
|
+
*
|
|
172
|
+
* Without it, a pattern is keyed by the name as it is written (how a framework that rejects only the
|
|
173
|
+
* same spelling of a directive tells a repeat), except a pattern with `isDuplicatable: true`, which is
|
|
174
|
+
* never a repeat (a framework that takes any number of the same directive).
|
|
175
|
+
*
|
|
176
|
+
* @example "$1" -- the key of `:view-box.camel` is its argument, `view-box` (with `duplicateKeyModifiers`, `viewBox`)
|
|
177
|
+
*/
|
|
178
|
+
readonly duplicateKey?: string;
|
|
179
|
+
/**
|
|
180
|
+
* Modifiers of the directive that change {@link DirectivePattern.duplicateKey}: the modifiers are those
|
|
181
|
+
* written after the name, and a framework gives two spellings of a name that differ in them the same key
|
|
182
|
+
* (the order of them, a modifier that it does not know) or a different one (a modifier that changes the
|
|
183
|
+
* name that is bound). Only the modifiers listed here count, and a modifier is matched as it is spelled.
|
|
184
|
+
*/
|
|
185
|
+
readonly duplicateKeyModifiers?: {
|
|
186
|
+
/**
|
|
187
|
+
* The capture group of `pattern` that holds the modifiers, joined with `.` and starting with it (`.prop.camel`).
|
|
188
|
+
*/
|
|
189
|
+
readonly group: number;
|
|
190
|
+
/**
|
|
191
|
+
* The modifiers that turn the template into camelCase before the prefixes are put in front of it.
|
|
192
|
+
*/
|
|
193
|
+
readonly camelize?: readonly string[];
|
|
194
|
+
/**
|
|
195
|
+
* The prefix that a modifier puts in front of the key. They are put in the order listed, so that
|
|
196
|
+
* the first of the listed modifiers that is written is the outermost.
|
|
197
|
+
*/
|
|
198
|
+
readonly prefixes?: readonly {
|
|
199
|
+
readonly modifier: string;
|
|
200
|
+
readonly prefix: string;
|
|
201
|
+
}[];
|
|
202
|
+
/**
|
|
203
|
+
* When no prefix is put in front of the key, it is the name of an attribute (`:id.foo` binds `id`), so
|
|
204
|
+
* the element folds its case as it does for any attribute and it is a repeat of that attribute
|
|
205
|
+
* written as such (`:id`). A key with a prefix is a property or a namespaced name, which
|
|
206
|
+
* keeps its case (`.id` and `.ID` are two).
|
|
207
|
+
*/
|
|
208
|
+
readonly foldedWithoutPrefix?: true;
|
|
209
|
+
};
|
|
163
210
|
};
|
|
164
211
|
/**
|
|
165
212
|
* Reference URLs
|
|
@@ -22,6 +22,17 @@ export type DirectiveResolution = {
|
|
|
22
22
|
readonly expressionLanguage?: ExpressionLanguage;
|
|
23
23
|
readonly valueType?: 'string' | 'number' | 'boolean' | 'code';
|
|
24
24
|
readonly isDuplicatable?: boolean;
|
|
25
|
+
/**
|
|
26
|
+
* Present only when the matched pattern decides it ({@link DirectivePattern.duplicateKey}): the key
|
|
27
|
+
* under which the directive counts as written twice, or `null` when it never does (a pattern that is
|
|
28
|
+
* `isDuplicatable: true` and declares no key). Absent, the key is the name as it is written.
|
|
29
|
+
*/
|
|
30
|
+
readonly duplicateKey?: string | null;
|
|
31
|
+
/**
|
|
32
|
+
* Present only when the key is the name of an attribute that the element folds
|
|
33
|
+
* ({@link DirectivePattern.duplicateKeyModifiers}'s `foldedWithoutPrefix`) and no prefix is in front of it.
|
|
34
|
+
*/
|
|
35
|
+
readonly duplicateKeyFolds?: true;
|
|
25
36
|
};
|
|
26
37
|
/**
|
|
27
38
|
* Resolves an attribute name against a list of compiled directive patterns.
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { camelize } from '@markuplint/shared';
|
|
1
2
|
const cache = new WeakMap();
|
|
2
3
|
/**
|
|
3
4
|
* Compiles directive patterns into RegExp objects, caching them
|
|
@@ -14,6 +15,33 @@ export function compileDirectivePatterns(patterns) {
|
|
|
14
15
|
}
|
|
15
16
|
return compiled;
|
|
16
17
|
}
|
|
18
|
+
/**
|
|
19
|
+
* The key of an attribute that a pattern gives, as {@link DirectivePattern.duplicateKey} says.
|
|
20
|
+
*/
|
|
21
|
+
function resolveDuplicateKey(
|
|
22
|
+
// eslint-disable-next-line @typescript-eslint/prefer-readonly-parameter-types
|
|
23
|
+
pattern, match) {
|
|
24
|
+
if (pattern.duplicateKey == null) {
|
|
25
|
+
return { key: pattern.isDuplicatable === true ? null : undefined, folds: false };
|
|
26
|
+
}
|
|
27
|
+
const modifiers = pattern.duplicateKeyModifiers;
|
|
28
|
+
const written = new Set((modifiers ? (match[modifiers.group] ?? '') : '').split('.').filter(Boolean));
|
|
29
|
+
// Only what the directive writes is camelized (`transformBind` of Vue does so to the argument), not the
|
|
30
|
+
// text that the template puts around it
|
|
31
|
+
const camelized = modifiers?.camelize?.some(modifier => written.has(modifier)) === true;
|
|
32
|
+
const key = pattern.duplicateKey.replaceAll(/\$(\d+)/g, (_, idx) => {
|
|
33
|
+
const capture = match[Number(idx)] ?? '';
|
|
34
|
+
return camelized ? camelize(capture) : capture;
|
|
35
|
+
});
|
|
36
|
+
if (!modifiers) {
|
|
37
|
+
return { key, folds: false };
|
|
38
|
+
}
|
|
39
|
+
const prefix = (modifiers.prefixes ?? [])
|
|
40
|
+
.filter(({ modifier }) => written.has(modifier))
|
|
41
|
+
.map(({ prefix }) => prefix)
|
|
42
|
+
.join('');
|
|
43
|
+
return { key: prefix + key, folds: modifiers.foldedWithoutPrefix === true && prefix === '' };
|
|
44
|
+
}
|
|
17
45
|
/**
|
|
18
46
|
* Resolves an attribute name against a list of compiled directive patterns.
|
|
19
47
|
* Returns the first match's resolution, or null if no pattern matches.
|
|
@@ -38,6 +66,7 @@ compiledPatterns) {
|
|
|
38
66
|
else if (Array.isArray(pattern.isDuplicatable) && potentialName) {
|
|
39
67
|
isDuplicatable = pattern.isDuplicatable.includes(potentialName);
|
|
40
68
|
}
|
|
69
|
+
const duplicateKey = resolveDuplicateKey(pattern, match);
|
|
41
70
|
return {
|
|
42
71
|
potentialName,
|
|
43
72
|
isDirective: pattern.isDirective,
|
|
@@ -45,6 +74,8 @@ compiledPatterns) {
|
|
|
45
74
|
valueType: pattern.valueType,
|
|
46
75
|
isDuplicatable,
|
|
47
76
|
...(pattern.expressionLanguage ? { expressionLanguage: pattern.expressionLanguage } : {}),
|
|
77
|
+
...(duplicateKey.key === undefined ? {} : { duplicateKey: duplicateKey.key }),
|
|
78
|
+
...(duplicateKey.folds ? { duplicateKeyFolds: true } : {}),
|
|
48
79
|
};
|
|
49
80
|
}
|
|
50
81
|
return null;
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@markuplint/ml-spec",
|
|
3
|
-
"version": "5.6.
|
|
3
|
+
"version": "5.6.1",
|
|
4
4
|
"description": "Types and schema that specs of the Markup languages for markuplint",
|
|
5
5
|
"repository": {
|
|
6
6
|
"type": "git",
|
|
@@ -40,8 +40,8 @@
|
|
|
40
40
|
"type-fest": "^5.10.0"
|
|
41
41
|
},
|
|
42
42
|
"devDependencies": {
|
|
43
|
-
"@markuplint/test-tools": "5.6.
|
|
43
|
+
"@markuplint/test-tools": "5.6.1",
|
|
44
44
|
"json-schema-to-typescript": "16.0.0"
|
|
45
45
|
},
|
|
46
|
-
"gitHead": "
|
|
46
|
+
"gitHead": "a9245162df3699c4880469f493aaaf6103343d83"
|
|
47
47
|
}
|