@markuplint/ml-core 2.0.0-dev.26 → 2.0.0-rc.5

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.
Files changed (36) hide show
  1. package/lib/convert-ruleset.d.ts +1 -1
  2. package/lib/convert-ruleset.js +1 -1
  3. package/lib/debug.js +8 -2
  4. package/lib/index.d.ts +1 -0
  5. package/lib/index.js +3 -1
  6. package/lib/ml-core.d.ts +2 -1
  7. package/lib/ml-core.js +25 -16
  8. package/lib/ml-dom/document.d.ts +5 -2
  9. package/lib/ml-dom/document.js +99 -51
  10. package/lib/ml-dom/helper/match-selector.d.ts +12 -1
  11. package/lib/ml-dom/helper/match-selector.js +108 -90
  12. package/lib/ml-dom/helper/selector.d.ts +5 -3
  13. package/lib/ml-dom/helper/selector.js +275 -79
  14. package/lib/ml-dom/helper/walkers.js +1 -1
  15. package/lib/ml-dom/rule-mapper.d.ts +16 -0
  16. package/lib/ml-dom/rule-mapper.js +57 -0
  17. package/lib/ml-dom/tokens/abstract-element.d.ts +1 -0
  18. package/lib/ml-dom/tokens/abstract-element.js +31 -6
  19. package/lib/ml-dom/tokens/attribute.d.ts +1 -0
  20. package/lib/ml-dom/tokens/attribute.js +7 -0
  21. package/lib/ml-dom/tokens/node.d.ts +8 -7
  22. package/lib/ml-dom/tokens/node.js +21 -13
  23. package/lib/ml-dom/tokens/preprocessor-specific-attribute.d.ts +1 -0
  24. package/lib/ml-dom/tokens/preprocessor-specific-attribute.js +3 -0
  25. package/lib/ml-dom/tokens/token.d.ts +0 -2
  26. package/lib/ml-dom/tokens/token.js +0 -1
  27. package/lib/ml-rule/ml-rule.d.ts +5 -2
  28. package/lib/ml-rule/ml-rule.js +19 -3
  29. package/lib/ml-rule/types.d.ts +3 -3
  30. package/lib/test/index.d.ts +8 -5
  31. package/lib/test/index.js +7 -7
  32. package/markuplint-recommended.json +150 -11
  33. package/package.json +7 -7
  34. package/tsconfig.tsbuildinfo +1 -1
  35. package/lib/create-rule.d.ts +0 -4
  36. package/lib/create-rule.js +0 -8
@@ -1,3 +1,3 @@
1
1
  import type { Config } from '@markuplint/ml-config';
2
2
  import Ruleset from './ruleset';
3
- export declare function convertRuleset(config: Config): Ruleset;
3
+ export declare function convertRuleset(config?: Config): Ruleset;
@@ -3,7 +3,7 @@ Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.convertRuleset = void 0;
4
4
  const tslib_1 = require("tslib");
5
5
  const ruleset_1 = (0, tslib_1.__importDefault)(require("./ruleset"));
6
- function convertRuleset(config) {
6
+ function convertRuleset(config = {}) {
7
7
  return new ruleset_1.default(config);
8
8
  }
9
9
  exports.convertRuleset = convertRuleset;
package/lib/debug.js CHANGED
@@ -3,10 +3,16 @@ Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.enableDebug = exports.log = void 0;
4
4
  const tslib_1 = require("tslib");
5
5
  const debug_1 = (0, tslib_1.__importDefault)(require("debug"));
6
+ const CLI_NS = 'markuplint-cli';
6
7
  exports.log = (0, debug_1.default)('ml-core');
7
8
  function enableDebug() {
8
- if (!debug_1.default.enabled('ml-core')) {
9
- debug_1.default.enable('ml-core*');
9
+ if (!exports.log.enabled) {
10
+ debug_1.default.enable(`${exports.log.namespace}*`);
11
+ (0, exports.log)(`Debug enable: ${exports.log.namespace}`);
12
+ if (!debug_1.default.enabled(CLI_NS)) {
13
+ debug_1.default.enable(`${exports.log.namespace}*,${CLI_NS}*`);
14
+ (0, exports.log)(`Debug enable: ${exports.log.namespace}, ${CLI_NS}`);
15
+ }
10
16
  }
11
17
  }
12
18
  exports.enableDebug = enableDebug;
package/lib/index.d.ts CHANGED
@@ -1,4 +1,5 @@
1
1
  export { RuleInfo, RuleConfig, RuleConfigValue } from '@markuplint/ml-config';
2
+ export { getAttrSpecs } from '@markuplint/ml-spec';
2
3
  export * from './convert-ruleset';
3
4
  export * from './ml-core';
4
5
  export * from './ml-rule';
package/lib/index.js CHANGED
@@ -1,7 +1,9 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.enableDebug = exports.getIndent = exports.Ruleset = exports.MLParseError = void 0;
3
+ exports.enableDebug = exports.getIndent = exports.Ruleset = exports.MLParseError = exports.getAttrSpecs = void 0;
4
4
  const tslib_1 = require("tslib");
5
+ var ml_spec_1 = require("@markuplint/ml-spec");
6
+ Object.defineProperty(exports, "getAttrSpecs", { enumerable: true, get: function () { return ml_spec_1.getAttrSpecs; } });
5
7
  (0, tslib_1.__exportStar)(require("./convert-ruleset"), exports);
6
8
  (0, tslib_1.__exportStar)(require("./ml-core"), exports);
7
9
  (0, tslib_1.__exportStar)(require("./ml-rule"), exports);
package/lib/ml-core.d.ts CHANGED
@@ -5,10 +5,11 @@ import MLParseError from './ml-error/ml-parse-error';
5
5
  export declare type MLCoreParams = {
6
6
  sourceCode: string;
7
7
  filename: string;
8
+ debug?: boolean;
8
9
  } & MLFabric;
9
10
  export declare class MLCore {
10
11
  #private;
11
- constructor({ parser, sourceCode, ruleset, rules, locale, schemas, parserOptions, filename }: MLCoreParams);
12
+ constructor({ parser, sourceCode, ruleset, rules, locale, schemas, parserOptions, filename, debug }: MLCoreParams);
12
13
  get document(): MLParseError | Document<RuleConfigValue, unknown>;
13
14
  verify(fix?: boolean): Promise<Violation[]>;
14
15
  setCode(sourceCode: string): void;
package/lib/ml-core.js CHANGED
@@ -6,9 +6,10 @@ const tslib_1 = require("tslib");
6
6
  const debug_1 = require("./debug");
7
7
  const ml_dom_1 = require("./ml-dom");
8
8
  const ml_parse_error_1 = (0, tslib_1.__importDefault)(require("./ml-error/ml-parse-error"));
9
+ const _1 = require(".");
9
10
  const resultLog = debug_1.log.extend('result');
10
11
  class MLCore {
11
- constructor({ parser, sourceCode, ruleset, rules, locale, schemas, parserOptions, filename }) {
12
+ constructor({ parser, sourceCode, ruleset, rules, locale, schemas, parserOptions, filename, debug }) {
12
13
  var _a, _b, _c;
13
14
  _MLCore_filename.set(this, void 0);
14
15
  _MLCore_document.set(this, void 0);
@@ -33,6 +34,9 @@ class MLCore {
33
34
  (0, tslib_1.__classPrivateFieldSet)(this, _MLCore_ast, (0, tslib_1.__classPrivateFieldGet)(this, _MLCore_parser, "f").parse((0, tslib_1.__classPrivateFieldGet)(this, _MLCore_sourceCode, "f"), 0, 0, 0, (0, tslib_1.__classPrivateFieldGet)(this, _MLCore_ignoreFrontMatter, "f")), "f");
34
35
  (0, tslib_1.__classPrivateFieldSet)(this, _MLCore_filename, filename, "f");
35
36
  (0, tslib_1.__classPrivateFieldSet)(this, _MLCore_rules, rules, "f");
37
+ if (debug) {
38
+ (0, _1.enableDebug)();
39
+ }
36
40
  this._createDocument();
37
41
  }
38
42
  get document() {
@@ -54,8 +58,8 @@ class MLCore {
54
58
  return violations;
55
59
  }
56
60
  for (const rule of (0, tslib_1.__classPrivateFieldGet)(this, _MLCore_rules, "f")) {
57
- const ruleInfo = rule.optimizeOption((0, tslib_1.__classPrivateFieldGet)(this, _MLCore_ruleset, "f").rules[rule.name] || false);
58
- if (ruleInfo.disabled) {
61
+ const ruleInfo = rule.getRuleInfo((0, tslib_1.__classPrivateFieldGet)(this, _MLCore_ruleset, "f"), rule.name);
62
+ if (ruleInfo.disabled && ruleInfo.nodeRules.length === 0 && ruleInfo.childNodeRules.length === 0) {
59
63
  continue;
60
64
  }
61
65
  (0, debug_1.log)('%s Rule: verify', rule.name);
@@ -81,18 +85,20 @@ class MLCore {
81
85
  }
82
86
  (0, debug_1.log)('%s Rule: verify end', rule.name);
83
87
  }
84
- const { e, w, i } = violations.reduce((c, v) => {
85
- if (v.severity === 'error')
86
- c.e += 1;
87
- if (v.severity === 'warning')
88
- c.w += 1;
89
- if (v.severity === 'info')
90
- c.i += 1;
91
- return c;
92
- }, { e: 0, w: 0, i: 0 });
93
- resultLog('Error: %d', e);
94
- resultLog('Warning: %d', w);
95
- resultLog('Info: %d', i);
88
+ if (resultLog.enabled) {
89
+ const { e, w, i } = violations.reduce((c, v) => {
90
+ if (v.severity === 'error')
91
+ c.e += 1;
92
+ if (v.severity === 'warning')
93
+ c.w += 1;
94
+ if (v.severity === 'info')
95
+ c.i += 1;
96
+ return c;
97
+ }, { e: 0, w: 0, i: 0 });
98
+ resultLog('Error: %d', e);
99
+ resultLog('Warning: %d', w);
100
+ resultLog('Info: %d', i);
101
+ }
96
102
  (0, debug_1.log)('verify: end');
97
103
  return violations;
98
104
  }
@@ -119,7 +125,10 @@ class MLCore {
119
125
  }
120
126
  _createDocument() {
121
127
  try {
122
- (0, tslib_1.__classPrivateFieldSet)(this, _MLCore_document, new ml_dom_1.Document((0, tslib_1.__classPrivateFieldGet)(this, _MLCore_ast, "f"), (0, tslib_1.__classPrivateFieldGet)(this, _MLCore_ruleset, "f"), (0, tslib_1.__classPrivateFieldGet)(this, _MLCore_schemas, "f"), (0, tslib_1.__classPrivateFieldGet)(this, _MLCore_filename, "f")), "f");
128
+ (0, tslib_1.__classPrivateFieldSet)(this, _MLCore_document, new ml_dom_1.Document((0, tslib_1.__classPrivateFieldGet)(this, _MLCore_ast, "f"), (0, tslib_1.__classPrivateFieldGet)(this, _MLCore_ruleset, "f"), (0, tslib_1.__classPrivateFieldGet)(this, _MLCore_schemas, "f"), {
129
+ filename: (0, tslib_1.__classPrivateFieldGet)(this, _MLCore_filename, "f"),
130
+ tagNameCaseSensitive: (0, tslib_1.__classPrivateFieldGet)(this, _MLCore_parser, "f").tagNameCaseSensitive,
131
+ }), "f");
123
132
  }
124
133
  catch (err) {
125
134
  if (err instanceof ml_parse_error_1.default) {
@@ -42,7 +42,10 @@ export default class MLDOMDocument<T extends RuleConfigValue, O = null> {
42
42
  * @param ast node list of markuplint AST
43
43
  * @param ruleset ruleset object
44
44
  */
45
- constructor(ast: MLASTDocument, ruleset: Ruleset, schemas: readonly [MLMLSpec, ...ExtendedSpec[]], filename?: string);
45
+ constructor(ast: MLASTDocument, ruleset: Ruleset, schemas: readonly [MLMLSpec, ...ExtendedSpec[]], options?: {
46
+ filename?: string;
47
+ tagNameCaseSensitive?: boolean;
48
+ });
46
49
  get doctype(): MLDOMDoctype<T, O> | null;
47
50
  get tree(): AnonymousNode<T, O>[];
48
51
  walk(walker: Walker<T, O>): Promise<void>;
@@ -60,5 +63,5 @@ export default class MLDOMDocument<T extends RuleConfigValue, O = null> {
60
63
  matchNodes(query: string): MLDOMElement<T, O>[];
61
64
  toString(): string;
62
65
  debugMap(): string[];
63
- private _init;
66
+ private _ruleMapping;
64
67
  }
@@ -9,9 +9,11 @@ const helper_1 = require("./helper");
9
9
  const debug_2 = require("./helper/debug");
10
10
  const match_selector_1 = require("./helper/match-selector");
11
11
  const walkers_1 = require("./helper/walkers");
12
+ const rule_mapper_1 = require("./rule-mapper");
12
13
  const tokens_1 = require("./tokens");
13
14
  const log = debug_1.log.extend('ml-dom');
14
15
  const docLog = log.extend('document');
16
+ const ruleLog = docLog.extend('rule');
15
17
  /**
16
18
  * markuplint DOM Document
17
19
  */
@@ -21,7 +23,7 @@ class MLDOMDocument {
21
23
  * @param ast node list of markuplint AST
22
24
  * @param ruleset ruleset object
23
25
  */
24
- constructor(ast, ruleset, schemas, filename) {
26
+ constructor(ast, ruleset, schemas, options) {
25
27
  /**
26
28
  *
27
29
  */
@@ -33,7 +35,7 @@ class MLDOMDocument {
33
35
  _MLDOMDocument_filename.set(this, void 0);
34
36
  this.isFragment = ast.isFragment;
35
37
  this.specs = (0, ml_spec_1.getSpec)(schemas);
36
- (0, tslib_1.__classPrivateFieldSet)(this, _MLDOMDocument_filename, filename, "f");
38
+ (0, tslib_1.__classPrivateFieldSet)(this, _MLDOMDocument_filename, options === null || options === void 0 ? void 0 : options.filename, "f");
37
39
  // console.log(ast.nodeList.map((n, i) => `${i}: ${n.uuid} "${n.raw.trim()}"(${n.type})`));
38
40
  this.nodeList = Object.freeze(ast.nodeList.map(astNode => {
39
41
  if (astNode.type === 'endtag') {
@@ -41,7 +43,7 @@ class MLDOMDocument {
41
43
  }
42
44
  return (0, helper_1.createNode)(astNode, this);
43
45
  }));
44
- this._init(ruleset);
46
+ this._ruleMapping(ruleset, options === null || options === void 0 ? void 0 : options.tagNameCaseSensitive);
45
47
  }
46
48
  /**
47
49
  * It could be used in rule, make sure it is immutable
@@ -122,78 +124,124 @@ class MLDOMDocument {
122
124
  debugMap() {
123
125
  return (0, debug_2.nodeListToDebugMaps)(this.nodeList, true);
124
126
  }
125
- _init(ruleset) {
126
- docLog('Initialize');
127
+ _ruleMapping(ruleset, tagNameCaseSensitive) {
128
+ docLog('Rule Mapping');
129
+ const ruleMapper = new rule_mapper_1.RuleMapper(this.nodeList);
127
130
  // add rules to node
128
131
  for (const node of this.nodeList) {
129
- docLog('Add rules to node <%s>', 'nodeName' in node ? node.nodeName : `#${node.type}`);
132
+ if (node.type === 'ElementCloseTag') {
133
+ continue;
134
+ }
135
+ if (docLog.enabled) {
136
+ docLog('Add rules to node <%s>', 'nodeName' in node ? node.nodeName : `#${node.type}`);
137
+ }
130
138
  // global rules
131
- for (const ruleName of Object.keys(ruleset.rules)) {
139
+ Object.keys(ruleset.rules).forEach(ruleName => {
132
140
  const rule = ruleset.rules[ruleName];
133
- node.rules[ruleName] = rule;
134
- }
135
- if (node.type !== 'Element' && node.type !== 'ElementCloseTag') {
141
+ ruleMapper.set(node, ruleName, {
142
+ from: 'rules',
143
+ specificity: [0, 0, 0],
144
+ rule,
145
+ });
146
+ });
147
+ if (node.type !== 'Element' && node.type !== 'OmittedElement' && node.type !== 'Text') {
136
148
  continue;
137
149
  }
138
- const selectorTarget = node.type === 'Element' ? node : node.startTag;
150
+ const selectorTarget = node.type === 'Element' || node.type === 'OmittedElement' ? node : null;
139
151
  // node specs and special rules for node by selector
140
- for (const nodeRule of ruleset.nodeRules) {
152
+ ruleset.nodeRules.forEach((nodeRule, i) => {
141
153
  if (!nodeRule.rules) {
142
- continue;
154
+ return;
143
155
  }
144
- const selector = nodeRule.selector || nodeRule.tagName || nodeRule.regexSelector;
145
- if (!selector) {
146
- continue;
156
+ if (!selectorTarget) {
157
+ return;
147
158
  }
148
- const matched = (0, match_selector_1.matchSelector)(selectorTarget, selector);
149
- if (!matched) {
150
- continue;
159
+ const selector = nodeRule.selector || nodeRule.regexSelector || nodeRule.tagName;
160
+ const matches =
161
+ /**
162
+ * Forward v1.x compatibility
163
+ */
164
+ nodeRule.tagName && /^#text$/i.test(nodeRule.tagName) && node.type === 'Text'
165
+ ? {
166
+ matched: true,
167
+ selector: '#text',
168
+ specificity: [0, 0, 0],
169
+ }
170
+ : /**
171
+ * v2.0.0 or later
172
+ */
173
+ (0, match_selector_1.matchSelector)(selectorTarget, selector, tagNameCaseSensitive);
174
+ if (!matches.matched) {
175
+ return;
176
+ }
177
+ if (docLog.enabled) {
178
+ docLog('Matched nodeRule: <%s>(%s)', 'nodeName' in node ? node.nodeName : node.type, matches.selector || '*');
151
179
  }
152
180
  const ruleList = Object.keys(nodeRule.rules);
153
- docLog('Matched nodeRule: <%s>(%s) <- Rules: %o', node.nodeName, matched.__node || 'No Selector', ruleList);
154
- // special rules
155
181
  for (const ruleName of ruleList) {
156
182
  const rule = nodeRule.rules[ruleName];
157
- const convertedRule = (0, ml_config_1.exchangeValueOnRule)(rule, matched);
183
+ const convertedRule = (0, ml_config_1.exchangeValueOnRule)(rule, matches.data || {});
158
184
  if (convertedRule === undefined) {
159
185
  continue;
160
186
  }
161
- node.rules[ruleName] = convertedRule;
187
+ const globalRule = ruleset.rules[ruleName];
188
+ const mergedRule = globalRule ? (0, ml_config_1.mergeRule)(globalRule, convertedRule) : convertedRule;
189
+ ruleLog('↑ nodeRule (%s): %O', ruleName, mergedRule);
190
+ ruleMapper.set(node, ruleName, {
191
+ from: 'nodeRules',
192
+ specificity: matches.specificity,
193
+ rule: mergedRule,
194
+ });
162
195
  }
163
- }
164
- }
165
- // overwrite rule to child node
166
- for (const nodeRule of ruleset.childNodeRules) {
167
- if (!nodeRule.rules) {
168
- break;
169
- }
170
- for (const ruleName of Object.keys(nodeRule.rules)) {
171
- const rule = nodeRule.rules[ruleName];
172
- for (const node of this.nodeList) {
173
- if (node.type !== 'Element') {
174
- continue;
196
+ });
197
+ // overwrite rule to child node
198
+ if (selectorTarget && ruleset.childNodeRules.length) {
199
+ const descendants = [];
200
+ (0, walkers_1.syncWalk)(selectorTarget.childNodes, childNode => {
201
+ descendants.push(childNode);
202
+ if (childNode.type === 'Element' && childNode.closeTag) {
203
+ descendants.push(childNode.closeTag);
204
+ }
205
+ });
206
+ const children = selectorTarget.childNodes;
207
+ ruleset.childNodeRules.forEach((nodeRule, i) => {
208
+ if (!nodeRule.rules) {
209
+ return;
210
+ }
211
+ const nodeRuleRules = nodeRule.rules;
212
+ const selector = nodeRule.selector || nodeRule.tagName || nodeRule.regexSelector;
213
+ if (!selector) {
214
+ return;
215
+ }
216
+ const matches = (0, match_selector_1.matchSelector)(selectorTarget, selector, tagNameCaseSensitive);
217
+ if (!matches.matched) {
218
+ return;
175
219
  }
176
- const matched = (0, match_selector_1.matchSelector)(node, nodeRule.selector || nodeRule.regexSelector);
177
- if (matched) {
178
- const convertedRule = (0, ml_config_1.exchangeValueOnRule)(rule, matched);
220
+ if (docLog.enabled) {
221
+ docLog('Matched childNodeRule: <%s>(%s), inheritance: %o', selectorTarget.nodeName, matches.selector || '*', !!nodeRule.inheritance);
222
+ }
223
+ const targetDescendants = nodeRule.inheritance ? descendants : children;
224
+ Object.keys(nodeRuleRules).forEach(ruleName => {
225
+ const rule = nodeRuleRules[ruleName];
226
+ const convertedRule = (0, ml_config_1.exchangeValueOnRule)(rule, matches.data || {});
179
227
  if (convertedRule === undefined) {
180
- continue;
228
+ return;
181
229
  }
182
- docLog('Matched childNodeRule: <%s>(%s) <- Rule: %s', node.nodeName, matched.__node || 'No Selector', ruleName);
183
- if (nodeRule.inheritance) {
184
- (0, walkers_1.syncWalk)(node.childNodes, childNode => {
185
- childNode.rules[ruleName] = convertedRule;
230
+ const globalRule = ruleset.rules[ruleName];
231
+ const mergedRule = globalRule ? (0, ml_config_1.mergeRule)(globalRule, convertedRule) : convertedRule;
232
+ ruleLog('↑ childNodeRule (%s): %O', ruleName, mergedRule);
233
+ targetDescendants.forEach(descendant => {
234
+ ruleMapper.set(descendant, ruleName, {
235
+ from: 'childNodeRules',
236
+ specificity: matches.specificity,
237
+ rule: mergedRule,
186
238
  });
187
- }
188
- else {
189
- for (const childNode of node.childNodes) {
190
- childNode.rules[ruleName] = convertedRule;
191
- }
192
- }
193
- }
194
- }
239
+ });
240
+ });
241
+ });
195
242
  }
196
243
  }
244
+ ruleMapper.apply();
197
245
  }
198
246
  }
199
247
  exports.default = MLDOMDocument;
@@ -1,5 +1,16 @@
1
1
  import type MLDOMAbstractElement from '../tokens/abstract-element';
2
+ import type { Specificity } from './selector';
2
3
  import type { RegexSelector } from '@markuplint/ml-config';
4
+ export declare type SelectorMatches = SelectorMatched | SelectorUnmatched;
5
+ declare type SelectorMatched = {
6
+ matched: true;
7
+ selector: string;
8
+ specificity: Specificity;
9
+ data?: Record<string, string>;
10
+ };
11
+ declare type SelectorUnmatched = {
12
+ matched: false;
13
+ };
3
14
  declare type TargetElement = MLDOMAbstractElement<any, any>;
4
- export declare function matchSelector(el: TargetElement, selector: string | RegexSelector | undefined): Record<string, string> | null;
15
+ export declare function matchSelector(el: TargetElement, selector: string | RegexSelector | undefined, tagNameCaseSensitive?: boolean): SelectorMatches;
5
16
  export {};