@markuplint/ml-config 4.6.2 → 4.7.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.
package/CHANGELOG.md CHANGED
@@ -3,6 +3,14 @@
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.7.0](https://github.com/markuplint/markuplint/compare/@markuplint/ml-config@4.6.2...@markuplint/ml-config@4.7.0) (2024-05-28)
7
+
8
+ ### Features
9
+
10
+ - **ml-config:** `pretenders` field accepts new structure ([3f9f5e8](https://github.com/markuplint/markuplint/commit/3f9f5e8ffdb7fa2526e842559b871ec6414de190))
11
+ - **ml-config:** add test for merging `pretenders` structure ([fac00d7](https://github.com/markuplint/markuplint/commit/fac00d7ce4c709757097b8ce8c2e40813af0461c))
12
+ - **ml-config:** improve merging for new `pretenders` structure ([fa7d990](https://github.com/markuplint/markuplint/commit/fa7d990c91e7843f6928fd48dc1ff41b7b446402))
13
+
6
14
  ## [4.6.2](https://github.com/markuplint/markuplint/compare/@markuplint/ml-config@4.6.1...@markuplint/ml-config@4.6.2) (2024-05-12)
7
15
 
8
16
  **Note:** Version bump only for package @markuplint/ml-config
@@ -1,4 +1,4 @@
1
- import type { Config, AnyRule, AnyRuleV2 } from './types.js';
1
+ import type { Config, AnyRule, AnyRuleV2, OptimizedConfig } from './types.js';
2
2
  import type { Nullable } from '@markuplint/shared';
3
- export declare function mergeConfig(a: Config, b: Config): Config;
3
+ export declare function mergeConfig(a: Config, b?: Config): OptimizedConfig;
4
4
  export declare function mergeRule(a: Nullable<AnyRule | AnyRuleV2>, b: AnyRule | AnyRuleV2): AnyRule;
@@ -1,24 +1,37 @@
1
1
  import deepmerge from 'deepmerge';
2
2
  import { deleteUndefProp, cleanOptions, isRuleConfigValue } from './utils.js';
3
3
  export function mergeConfig(a, b) {
4
+ const deleteExtendsProp = !!b;
5
+ b = b ?? {};
4
6
  const config = {
5
7
  ...a,
6
8
  ...b,
7
- plugins: concatArray(a.plugins, b.plugins, true, 'name'),
9
+ plugins: concatArray(a.plugins, b.plugins, true, 'name')?.map(plugin => {
10
+ if (typeof plugin === 'string') {
11
+ return {
12
+ name: plugin,
13
+ };
14
+ }
15
+ return plugin;
16
+ }),
8
17
  parser: mergeObject(a.parser, b.parser),
9
18
  parserOptions: mergeObject(a.parserOptions, b.parserOptions),
10
19
  specs: mergeObject(a.specs, b.specs),
11
20
  excludeFiles: concatArray(a.excludeFiles, b.excludeFiles, true),
21
+ pretenders: mergePretenders(a.pretenders, b.pretenders),
12
22
  rules: mergeRules(
13
23
  // TODO: Deep merge
14
24
  a.rules, b.rules),
15
25
  nodeRules: concatArray(a.nodeRules, b.nodeRules),
16
26
  childNodeRules: concatArray(a.childNodeRules, b.childNodeRules),
17
27
  overrideMode: b.overrideMode ?? a.overrideMode,
18
- overrides: mergeObject(a.overrides, b.overrides),
19
- // delete all
20
- extends: undefined,
28
+ overrides: mergeOverrides(a.overrides, b.overrides),
29
+ extends: concatArray(toReadonlyArray(a.extends), toReadonlyArray(b.extends)),
21
30
  };
31
+ if (deleteExtendsProp) {
32
+ // @ts-ignore
33
+ delete config.extends;
34
+ }
22
35
  deleteUndefProp(config);
23
36
  return config;
24
37
  }
@@ -62,6 +75,47 @@ export function mergeRule(a, b) {
62
75
  deleteUndefProp(res);
63
76
  return res;
64
77
  }
78
+ function mergePretenders(a, b) {
79
+ if (!a && !b) {
80
+ return;
81
+ }
82
+ const aDetails = a ? convertPretenersToDetails(a) : undefined;
83
+ const bDetails = b ? convertPretenersToDetails(b) : undefined;
84
+ const details = mergeObject(aDetails, bDetails) ?? {};
85
+ deleteUndefProp(details);
86
+ return details;
87
+ }
88
+ function convertPretenersToDetails(pretenders) {
89
+ if (isReadonlyArray(pretenders)) {
90
+ return {
91
+ data: pretenders,
92
+ };
93
+ }
94
+ return pretenders;
95
+ }
96
+ function mergeOverrides(a = {}, b = {}) {
97
+ const keys = new Set();
98
+ for (const key of Object.keys(a))
99
+ keys.add(key);
100
+ for (const key of Object.keys(b))
101
+ keys.add(key);
102
+ if (keys.size === 0) {
103
+ return;
104
+ }
105
+ const result = {};
106
+ for (const key of keys) {
107
+ const config = mergeConfig(a[key] ?? {}, b[key] ?? {});
108
+ // @ts-ignore
109
+ delete config.$schema;
110
+ // @ts-ignore
111
+ delete config.extends;
112
+ // @ts-ignore
113
+ delete config.overrides;
114
+ deleteUndefProp(config);
115
+ result[key] = config;
116
+ }
117
+ return result;
118
+ }
65
119
  function mergeObject(a, b) {
66
120
  if (a == null) {
67
121
  return b ?? undefined;
@@ -162,3 +216,23 @@ function optimizeRule(rule) {
162
216
  }
163
217
  return cleanOptions(rule);
164
218
  }
219
+ function toReadonlyArray(value) {
220
+ if (value == null) {
221
+ return [];
222
+ }
223
+ return isReadonlyArray(value) ? value : [value];
224
+ }
225
+ /**
226
+ * Checks if a value is a readonly array.
227
+ *
228
+ * If the array is readonly, it passes the type check.
229
+ * However, it saves the type because using ESLint warns `@typescript-eslint/prefer-readonly-parameter-types`.
230
+ *
231
+ * @param value - The value to check.
232
+ * @returns `true` if the value is a readonly array, `false` otherwise.
233
+ * @template T - The type of elements in the array.
234
+ * @template X - The type of the value if it's not an array.
235
+ */
236
+ function isReadonlyArray(value) {
237
+ return Array.isArray(value);
238
+ }
package/lib/types.d.ts CHANGED
@@ -10,7 +10,7 @@ export type Config = {
10
10
  readonly parserOptions?: ParserOptions;
11
11
  readonly specs?: SpecConfig;
12
12
  readonly excludeFiles?: readonly string[];
13
- readonly pretenders?: readonly Pretender[];
13
+ readonly pretenders?: readonly Pretender[] | PretenderDetails;
14
14
  readonly rules?: Rules;
15
15
  readonly nodeRules?: readonly NodeRule[];
16
16
  readonly childNodeRules?: readonly ChildNodeRule[];
@@ -24,10 +24,18 @@ export type PlainData = Nullable<PrimitiveScalar> | readonly PlainData[] | {
24
24
  export type NonNullablePlainData = PrimitiveScalar | readonly NonNullablePlainData[] | {
25
25
  readonly [key: string]: NonNullablePlainData;
26
26
  };
27
- export type OverrideConfig = Omit<Config, '$schema' | 'extends' | 'overrideMode' | 'overrides'>;
27
+ type NoInherit = '$schema' | 'extends' | 'overrideMode' | 'overrides';
28
+ export type OverrideConfig = Omit<Config, NoInherit>;
29
+ export type OptimizedConfig = Omit<Config, '$schema' | 'extends' | 'plugins' | 'pretenders' | 'overrides'> & {
30
+ readonly extends?: readonly string[];
31
+ readonly plugins?: readonly PluginConfig[];
32
+ readonly pretenders?: PretenderDetails;
33
+ readonly overrides?: Readonly<Record<string, OptimizedOverrideConfig>>;
34
+ };
35
+ export type OptimizedOverrideConfig = Omit<OptimizedConfig, NoInherit>;
28
36
  export type PluginConfig = {
29
37
  readonly name: string;
30
- readonly settings: Readonly<Record<string, NonNullablePlainData>>;
38
+ readonly settings?: Readonly<Record<string, NonNullablePlainData>>;
31
39
  };
32
40
  export type ParserConfig = {
33
41
  readonly [extensionPattern: string]: string;
@@ -35,6 +43,17 @@ export type ParserConfig = {
35
43
  export type SpecConfig = {
36
44
  readonly [extensionPattern: string]: string;
37
45
  };
46
+ export type PretenderDetails = {
47
+ /**
48
+ * @experimental
49
+ */
50
+ readonly files?: readonly string[];
51
+ /**
52
+ * @experimental
53
+ */
54
+ readonly imports?: readonly string[];
55
+ readonly data?: readonly Pretender[];
56
+ };
38
57
  export type PretenderFileData = {
39
58
  readonly version: string;
40
59
  readonly data: readonly Pretender[];
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@markuplint/ml-config",
3
- "version": "4.6.2",
3
+ "version": "4.7.0",
4
4
  "description": "JSON Schema and TypeScript types of markuplint configure JSON",
5
5
  "repository": "git@github.com:markuplint/markuplint.git",
6
6
  "author": "Yusuke Hirao <yusukehirao@me.com>",
@@ -24,14 +24,14 @@
24
24
  "clean": "tsc --build --clean"
25
25
  },
26
26
  "dependencies": {
27
- "@markuplint/ml-ast": "4.3.1",
28
- "@markuplint/selector": "4.6.2",
29
- "@markuplint/shared": "4.4.0",
27
+ "@markuplint/ml-ast": "4.4.0",
28
+ "@markuplint/selector": "4.6.3",
29
+ "@markuplint/shared": "4.4.1",
30
30
  "@types/mustache": "4.2.5",
31
31
  "deepmerge": "4.3.1",
32
32
  "is-plain-object": "5.0.0",
33
33
  "mustache": "4.2.0",
34
- "type-fest": "4.18.2"
34
+ "type-fest": "4.18.3"
35
35
  },
36
- "gitHead": "ca7dc6bf40eac4f2813e492878f889eb77751a70"
36
+ "gitHead": "bf70c41b1d2497e85b73c9ecd5551eb522e6bdfc"
37
37
  }