markuplint 4.18.3 → 4.18.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.
- package/ARCHITECTURE.ja.md +1 -1
- package/ARCHITECTURE.md +1 -1
- package/CHANGELOG.md +11 -0
- package/SKILL.md +1 -1
- package/docs/maintenance.ja.md +3 -1
- package/docs/maintenance.md +3 -1
- package/lib/api/ml-engine.d.ts +15 -0
- package/lib/api/ml-engine.js +53 -22
- package/lib/api/types.d.ts +9 -0
- package/package.json +11 -11
package/ARCHITECTURE.ja.md
CHANGED
|
@@ -170,7 +170,7 @@ flowchart TD
|
|
|
170
170
|
2. options.configFile -- 明示的な設定ファイルパス(--config フラグ)
|
|
171
171
|
3. ConfigProvider.search()-- ファイル位置からの自動探索(--no-search-config でなければ)
|
|
172
172
|
4. options.defaultConfig -- フォールバック設定
|
|
173
|
-
5. markuplint:recommended --
|
|
173
|
+
5. markuplint:recommended -- 設定が一切見つからない場合のデフォルト(`configFile` も指定されていない場合)
|
|
174
174
|
```
|
|
175
175
|
|
|
176
176
|
これらは `ConfigProvider.resolve()` で結合され、`@markuplint/ml-config` の `mergeConfig()` を使用して全レイヤーをマージします。
|
package/ARCHITECTURE.md
CHANGED
|
@@ -170,7 +170,7 @@ The `resolveConfig()` method resolves configuration from multiple sources with t
|
|
|
170
170
|
2. options.configFile -- Explicit config file path (--config flag)
|
|
171
171
|
3. ConfigProvider.search()-- Auto-discovery from file location (unless --no-search-config)
|
|
172
172
|
4. options.defaultConfig -- Fallback config
|
|
173
|
-
5. markuplint:recommended -- Default when no config is found at all
|
|
173
|
+
5. markuplint:recommended -- Default when no config is found at all (and no `configFile` is given)
|
|
174
174
|
```
|
|
175
175
|
|
|
176
176
|
These are combined via `ConfigProvider.resolve()` which merges all layers using `@markuplint/ml-config`'s `mergeConfig()`.
|
package/CHANGELOG.md
CHANGED
|
@@ -3,6 +3,17 @@
|
|
|
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
|
+
## [4.18.5](https://github.com/markuplint/markuplint/compare/v4.18.4...v4.18.5) (2026-10-27)
|
|
7
|
+
|
|
8
|
+
### Bug Fixes
|
|
9
|
+
|
|
10
|
+
- **markuplint:** do not search for a configuration file when noSearchConfig is set ([1163e16](https://github.com/markuplint/markuplint/commit/1163e16afc265b8f2fc05345f6ceed6d3232a105)), closes [#4409](https://github.com/markuplint/markuplint/issues/4409)
|
|
11
|
+
- **markuplint:** keep inline configurations when the configuration is resolved again ([b0d5d0b](https://github.com/markuplint/markuplint/commit/b0d5d0bb449c695e563678cd5c3719c109edad7a)), closes [#4415](https://github.com/markuplint/markuplint/issues/4415)
|
|
12
|
+
|
|
13
|
+
## [4.18.4](https://github.com/markuplint/markuplint/compare/v4.18.3...v4.18.4) (2026-10-22)
|
|
14
|
+
|
|
15
|
+
**Note:** Version bump only for package markuplint
|
|
16
|
+
|
|
6
17
|
## [4.18.3](https://github.com/markuplint/markuplint/compare/v4.18.2...v4.18.3) (2026-05-10)
|
|
7
18
|
|
|
8
19
|
**Note:** Version bump only for package markuplint
|
package/SKILL.md
CHANGED
|
@@ -88,7 +88,7 @@ Change how MLEngine resolves and merges configuration. Follow recipe #3 in `docs
|
|
|
88
88
|
|
|
89
89
|
1. Read `src/api/ml-engine.ts` and locate `resolveConfig()`
|
|
90
90
|
2. Current priority: options.config -> configFile -> search -> defaultConfig -> recommended
|
|
91
|
-
3. Understand `ConfigProvider` methods: `search()`, `set()`, `resolve()` (from `@markuplint/file-resolver`)
|
|
91
|
+
3. Understand `ConfigProvider` methods: `search()`, `set()`, `resolve()`, `invalidate()`, `runExclusive()` (from `@markuplint/file-resolver`); `resolve()` with `cache` `false` clears the store, including what `set()` has just registered
|
|
92
92
|
|
|
93
93
|
### Step 2: Modify the logic
|
|
94
94
|
|
package/docs/maintenance.ja.md
CHANGED
|
@@ -119,7 +119,9 @@ expect(violations).toStrictEqual([
|
|
|
119
119
|
3. `@markuplint/file-resolver` の `ConfigProvider` を理解:
|
|
120
120
|
- `set(config)` で設定を登録しキーを返す
|
|
121
121
|
- `search(file)` でターゲットに最も近い設定ファイルを検索
|
|
122
|
-
- `resolve(file, keys, cache)`
|
|
122
|
+
- `resolve(file, keys, cache)` で全設定レイヤーをマージ。`cache` が `false` のときは最初にストアを消すため、直前に `set()` で登録した設定も消える
|
|
123
|
+
- `invalidate()` でストアとキャッシュを消す。インライン設定を残したまま読み直すには、`set()` の前に呼び、`cache` を `true` にして `resolve()` する(`resolveConfig(false)` がそうしている)
|
|
124
|
+
- `runExclusive(fn)` で `invalidate()` → `set()` / `search()` → `resolve()` の一連を、重なる呼び出しに割り込まれずに実行する
|
|
123
125
|
4. イベント発行(`this.emit('config', ...)`)を保持しつつ `resolveConfig()` を変更
|
|
124
126
|
5. 新しい API オプションを追加する場合、`src/api/types.ts` の `APIOptions` を更新
|
|
125
127
|
6. `src/api/ml-engine.spec.ts` にテストを追加
|
package/docs/maintenance.md
CHANGED
|
@@ -119,7 +119,9 @@ expect(violations).toStrictEqual([
|
|
|
119
119
|
3. Understand `ConfigProvider` from `@markuplint/file-resolver`:
|
|
120
120
|
- `set(config)` registers a config and returns a key
|
|
121
121
|
- `search(file)` finds the nearest config file for a target
|
|
122
|
-
- `resolve(file, keys, cache)` merges all config layers
|
|
122
|
+
- `resolve(file, keys, cache)` merges all config layers; with `cache` `false` it first clears the store, which also discards what `set()` has just registered
|
|
123
|
+
- `invalidate()` clears the store and the caches; call it before `set()`, then `resolve()` with `cache` `true`, for a re-read that keeps the inline configs (`resolveConfig(false)` does)
|
|
124
|
+
- `runExclusive(fn)` runs a whole `invalidate()` → `set()` / `search()` → `resolve()` sequence without an overlapping call interleaving
|
|
123
125
|
4. Make changes to `resolveConfig()`, preserving the event emissions (`this.emit('config', ...)`)
|
|
124
126
|
5. If adding new API options, update `APIOptions` in `src/api/types.ts`
|
|
125
127
|
6. Add tests in `src/api/ml-engine.spec.ts`
|
package/lib/api/ml-engine.d.ts
CHANGED
|
@@ -74,6 +74,21 @@ export declare class MLEngine extends Emitter<MLEngineEventMap> {
|
|
|
74
74
|
private i18n;
|
|
75
75
|
private onChange;
|
|
76
76
|
private provide;
|
|
77
|
+
/**
|
|
78
|
+
* Resolves the configuration for the target file: the configuration found by searching
|
|
79
|
+
* upward from the file (else `defaultConfig`), then `configFile`, then `config`.
|
|
80
|
+
* `markuplint:recommended` is used only when none of them is given.
|
|
81
|
+
*
|
|
82
|
+
* With `noSearchConfig` the search is not run at all, because it throws for a
|
|
83
|
+
* configuration file that cannot be loaded anywhere up to the file system root.
|
|
84
|
+
* `defaultConfig` still applies. `configFile` does not stop the search.
|
|
85
|
+
*
|
|
86
|
+
* `cache` set to `false` reads every configuration file again, and the inline configurations
|
|
87
|
+
* (`defaultConfig`, `config` and the `markuplint:recommended` fallback) are kept (#4415).
|
|
88
|
+
*
|
|
89
|
+
* @param cache - Whether to reuse cached configuration; `false` reads the configuration files again
|
|
90
|
+
* @returns The merged configuration with the plugins, files and errors collected on the way
|
|
91
|
+
*/
|
|
77
92
|
resolveConfig(cache: boolean): Promise<ConfigSet>;
|
|
78
93
|
private resolveParser;
|
|
79
94
|
private resolvePretenders;
|
package/lib/api/ml-engine.js
CHANGED
|
@@ -294,31 +294,62 @@ export class MLEngine extends Emitter {
|
|
|
294
294
|
configErrors: configSet.errs,
|
|
295
295
|
};
|
|
296
296
|
}
|
|
297
|
+
/**
|
|
298
|
+
* Resolves the configuration for the target file: the configuration found by searching
|
|
299
|
+
* upward from the file (else `defaultConfig`), then `configFile`, then `config`.
|
|
300
|
+
* `markuplint:recommended` is used only when none of them is given.
|
|
301
|
+
*
|
|
302
|
+
* With `noSearchConfig` the search is not run at all, because it throws for a
|
|
303
|
+
* configuration file that cannot be loaded anywhere up to the file system root.
|
|
304
|
+
* `defaultConfig` still applies. `configFile` does not stop the search.
|
|
305
|
+
*
|
|
306
|
+
* `cache` set to `false` reads every configuration file again, and the inline configurations
|
|
307
|
+
* (`defaultConfig`, `config` and the `markuplint:recommended` fallback) are kept (#4415).
|
|
308
|
+
*
|
|
309
|
+
* @param cache - Whether to reuse cached configuration; `false` reads the configuration files again
|
|
310
|
+
* @returns The merged configuration with the plugins, files and errors collected on the way
|
|
311
|
+
*/
|
|
297
312
|
async resolveConfig(cache) {
|
|
298
313
|
this.emit('log', 'resolveConfig', JSON.stringify(__classPrivateFieldGet(this, _MLEngine_configProvider, "f"), null, 2));
|
|
299
314
|
configLog('configProvider: %s', __classPrivateFieldGet(this, _MLEngine_configProvider, "f"));
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
this
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
//
|
|
316
|
-
//
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
315
|
+
// The whole invalidate → set → search → resolve sequence runs as one exclusive unit: an
|
|
316
|
+
// overlapping call on this engine's provider (a watch-triggered `provide(false)` while
|
|
317
|
+
// `exec()` or `--show-config` resolves) must not clear the configurations registered below
|
|
318
|
+
// before `resolve()` has used them (#4415).
|
|
319
|
+
const configSet = await __classPrivateFieldGet(this, _MLEngine_configProvider, "f").runExclusive(async () => {
|
|
320
|
+
if (!cache) {
|
|
321
|
+
// Before any `set()` below. `resolve()` with `cache` set to `false` would clear the store
|
|
322
|
+
// itself, after the inline configurations were registered, and then fail to load their
|
|
323
|
+
// keys as file paths (#4415). Everything is cleared here instead, and `resolve()` is
|
|
324
|
+
// called with `cache` as `true`: what was cleared is read again.
|
|
325
|
+
__classPrivateFieldGet(this, _MLEngine_configProvider, "f").invalidate();
|
|
326
|
+
}
|
|
327
|
+
const defaultConfigKey = __classPrivateFieldGet(this, _MLEngine_options, "f")?.defaultConfig && __classPrivateFieldGet(this, _MLEngine_configProvider, "f").set(mergeConfig(__classPrivateFieldGet(this, _MLEngine_options, "f")?.defaultConfig));
|
|
328
|
+
configLog('defaultConfigKey: %s', defaultConfigKey ?? 'N/A');
|
|
329
|
+
this.emit('log', 'defaultConfigKey', defaultConfigKey ?? 'N/A');
|
|
330
|
+
// Not searching at all (#4409), rather than discarding the result: the search throws for a
|
|
331
|
+
// configuration file that cannot be loaded anywhere up to the file system root.
|
|
332
|
+
const noSearch = !!__classPrivateFieldGet(this, _MLEngine_options, "f")?.noSearchConfig;
|
|
333
|
+
const targetConfig = noSearch ? null : await __classPrivateFieldGet(this, _MLEngine_configProvider, "f").search(__classPrivateFieldGet(this, _MLEngine_file, "f"));
|
|
334
|
+
this.emit('log', 'targetConfig', noSearch ? 'skipped (noSearchConfig)' : (targetConfig ?? 'N/A'));
|
|
335
|
+
// The configuration that stands in for the target's own: the search result, else `defaultConfig`.
|
|
336
|
+
const configFilePathsFromTarget = targetConfig ?? defaultConfigKey ?? null;
|
|
337
|
+
configLog('configFilePathsFromTarget: %s', configFilePathsFromTarget ?? 'N/A');
|
|
338
|
+
this.emit('log', 'configFilePathsFromTarget', configFilePathsFromTarget ?? 'N/A');
|
|
339
|
+
const configKey = __classPrivateFieldGet(this, _MLEngine_options, "f")?.config && __classPrivateFieldGet(this, _MLEngine_configProvider, "f").set(mergeConfig(__classPrivateFieldGet(this, _MLEngine_options, "f").config));
|
|
340
|
+
configLog('option.config: %s', configKey ?? 'N/A');
|
|
341
|
+
this.emit('log', 'option.config', configKey ?? 'N/A');
|
|
342
|
+
let defaultRecommended = null;
|
|
343
|
+
if (!defaultConfigKey && !configFilePathsFromTarget && !configKey && !__classPrivateFieldGet(this, _MLEngine_options, "f")?.configFile) {
|
|
344
|
+
// No configured
|
|
345
|
+
// Default: set recommended
|
|
346
|
+
// Not with `configFile`: the preset is merged after it and would override its rules (#4415).
|
|
347
|
+
defaultRecommended = __classPrivateFieldGet(this, _MLEngine_configProvider, "f").set({ extends: ['markuplint:recommended'] });
|
|
348
|
+
}
|
|
349
|
+
configLog('defaultRecommended: %s', defaultRecommended ?? 'N/A');
|
|
350
|
+
this.emit('log', 'defaultRecommended', defaultRecommended ?? 'N/A');
|
|
351
|
+
return __classPrivateFieldGet(this, _MLEngine_configProvider, "f").resolve(__classPrivateFieldGet(this, _MLEngine_file, "f"), [configFilePathsFromTarget, __classPrivateFieldGet(this, _MLEngine_options, "f")?.configFile, configKey, defaultRecommended], true);
|
|
352
|
+
});
|
|
322
353
|
this.emit('config', __classPrivateFieldGet(this, _MLEngine_file, "f").path, configSet);
|
|
323
354
|
if (__classPrivateFieldGet(this, _MLEngine_options, "f")?.watch) {
|
|
324
355
|
// It doesn't watch the main HTML file because it may is watched and managed by a language server or text editor or more.
|
package/lib/api/types.d.ts
CHANGED
|
@@ -6,9 +6,18 @@ import type { AnyMLRule, MLSchema, Ruleset } from '@markuplint/ml-core';
|
|
|
6
6
|
* Options for the markuplint API, controlling configuration, locale, rules, and behavior.
|
|
7
7
|
*/
|
|
8
8
|
export type APIOptions = {
|
|
9
|
+
/**
|
|
10
|
+
* The path of a configuration file. When it is given, `markuplint:recommended` is not added
|
|
11
|
+
* after it as the fallback, so the file's own rules are not overridden (#4415).
|
|
12
|
+
*/
|
|
9
13
|
readonly configFile?: string;
|
|
10
14
|
readonly config?: Config;
|
|
11
15
|
readonly defaultConfig?: Config;
|
|
16
|
+
/**
|
|
17
|
+
* Do not search for a configuration file from the target file. No configuration file is
|
|
18
|
+
* read for the search, so a malformed one cannot affect the run. `defaultConfig`, `config`
|
|
19
|
+
* and `configFile` still apply; `configFile` alone does not disable the search.
|
|
20
|
+
*/
|
|
12
21
|
readonly noSearchConfig?: boolean;
|
|
13
22
|
readonly locale?: string;
|
|
14
23
|
readonly fix?: boolean;
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "markuplint",
|
|
3
|
-
"version": "4.18.
|
|
3
|
+
"version": "4.18.5",
|
|
4
4
|
"description": "An HTML linter for all markup developers",
|
|
5
5
|
"author": "Yusuke Hirao",
|
|
6
6
|
"license": "MIT",
|
|
@@ -24,15 +24,15 @@
|
|
|
24
24
|
},
|
|
25
25
|
"dependencies": {
|
|
26
26
|
"@markuplint/cli-utils": "4.18.0",
|
|
27
|
-
"@markuplint/file-resolver": "4.18.
|
|
28
|
-
"@markuplint/html-parser": "4.18.
|
|
29
|
-
"@markuplint/html-spec": "4.18.
|
|
27
|
+
"@markuplint/file-resolver": "4.18.5",
|
|
28
|
+
"@markuplint/html-parser": "4.18.4",
|
|
29
|
+
"@markuplint/html-spec": "4.18.4",
|
|
30
30
|
"@markuplint/i18n": "4.18.0",
|
|
31
|
-
"@markuplint/ml-ast": "4.18.
|
|
32
|
-
"@markuplint/ml-config": "4.18.
|
|
33
|
-
"@markuplint/ml-core": "4.18.
|
|
34
|
-
"@markuplint/ml-spec": "4.18.
|
|
35
|
-
"@markuplint/rules": "4.18.
|
|
31
|
+
"@markuplint/ml-ast": "4.18.4",
|
|
32
|
+
"@markuplint/ml-config": "4.18.4",
|
|
33
|
+
"@markuplint/ml-core": "4.18.4",
|
|
34
|
+
"@markuplint/ml-spec": "4.18.4",
|
|
35
|
+
"@markuplint/rules": "4.18.4",
|
|
36
36
|
"@markuplint/shared": "4.18.0",
|
|
37
37
|
"@types/debug": "4.1.13",
|
|
38
38
|
"chokidar": "5.0.0",
|
|
@@ -41,7 +41,7 @@
|
|
|
41
41
|
"os-locale": "6.0.2",
|
|
42
42
|
"strict-event-emitter": "0.5.1",
|
|
43
43
|
"strip-ansi": "7.2.0",
|
|
44
|
-
"type-fest": "5.
|
|
44
|
+
"type-fest": "5.10.0"
|
|
45
45
|
},
|
|
46
|
-
"gitHead": "
|
|
46
|
+
"gitHead": "e7307cd5bdb771ac91e723c4c8869499190f2465"
|
|
47
47
|
}
|