@cssdoc/config 0.2.0 → 0.3.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.
@@ -6,11 +6,15 @@
6
6
  "type": "object",
7
7
  "additionalProperties": false,
8
8
  "properties": {
9
- "$schema": { "type": "string" },
9
+ "$schema": {
10
+ "type": "string"
11
+ },
10
12
  "extends": {
11
13
  "description": "Paths to other cssdoc.json files (or packages) to inherit tag definitions from.",
12
14
  "type": "array",
13
- "items": { "type": "string" }
15
+ "items": {
16
+ "type": "string"
17
+ }
14
18
  },
15
19
  "noStandardTags": {
16
20
  "description": "Disable every built-in standard tag; only tagDefinitions remain supported.",
@@ -24,32 +28,93 @@
24
28
  "additionalProperties": false,
25
29
  "required": ["tagName", "syntaxKind"],
26
30
  "properties": {
27
- "tagName": { "type": "string", "pattern": "^@?[a-zA-Z][a-zA-Z0-9-]*$" },
28
- "syntaxKind": { "enum": ["record", "block", "modifier", "inline"] },
29
- "allowMultiple": { "type": "boolean" },
30
- "recordKind": { "enum": ["component", "utility", "rule", "declaration"] },
31
- "aliasFor": { "type": "string" }
31
+ "tagName": {
32
+ "type": "string",
33
+ "pattern": "^@?[a-zA-Z][a-zA-Z0-9-]*$"
34
+ },
35
+ "syntaxKind": {
36
+ "enum": ["record", "block", "modifier", "inline"]
37
+ },
38
+ "allowMultiple": {
39
+ "type": "boolean"
40
+ },
41
+ "recordKind": {
42
+ "enum": ["component", "utility", "rule", "declaration"]
43
+ },
44
+ "aliasFor": {
45
+ "type": "string"
46
+ }
32
47
  }
33
48
  }
34
49
  },
35
50
  "supportForTags": {
36
51
  "description": "Enable or disable specific tags by name.",
37
52
  "type": "object",
38
- "additionalProperties": { "type": "boolean" }
53
+ "additionalProperties": {
54
+ "type": "boolean"
55
+ }
39
56
  },
40
57
  "modifierConvention": {
41
58
  "description": "How modifier classes are spelled: a preset name (bem, rscss, bare) or a custom convention.",
42
59
  "oneOf": [
43
- { "enum": ["bem", "rscss", "bare"] },
60
+ {
61
+ "enum": ["bem", "rscss", "bare"]
62
+ },
44
63
  {
45
64
  "type": "object",
46
65
  "additionalProperties": false,
47
66
  "required": ["structure", "separator"],
48
67
  "properties": {
49
- "structure": { "enum": ["chained", "suffix", "attribute"] },
50
- "separator": { "type": "string" },
51
- "propValue": { "type": "boolean" },
52
- "propValueSeparator": { "type": "string" }
68
+ "structure": {
69
+ "enum": ["chained", "suffix", "attribute"]
70
+ },
71
+ "separator": {
72
+ "oneOf": [
73
+ {
74
+ "type": "string"
75
+ },
76
+ {
77
+ "type": "array",
78
+ "items": {
79
+ "type": "string"
80
+ }
81
+ }
82
+ ]
83
+ },
84
+ "elementSeparator": {
85
+ "description": "BEM-style element delimiter (e.g. \"__\"); matched classes become parts.",
86
+ "oneOf": [
87
+ {
88
+ "type": "string"
89
+ },
90
+ {
91
+ "type": "array",
92
+ "items": {
93
+ "type": "string"
94
+ }
95
+ }
96
+ ]
97
+ },
98
+ "statePrefixes": {
99
+ "description": "Class prefixes that mark a state (e.g. [\"is-\",\"has-\"]).",
100
+ "type": "array",
101
+ "items": {
102
+ "type": "string"
103
+ }
104
+ },
105
+ "statePseudoClasses": {
106
+ "description": "Native pseudo-classes (without the colon) recognized as states, e.g. [\"disabled\",\"checked\"]. Overrides the built-in default set.",
107
+ "type": "array",
108
+ "items": {
109
+ "type": "string"
110
+ }
111
+ },
112
+ "propValue": {
113
+ "type": "boolean"
114
+ },
115
+ "propValueSeparator": {
116
+ "type": "string"
117
+ }
53
118
  }
54
119
  }
55
120
  ]
@@ -57,7 +122,29 @@
57
122
  "rules": {
58
123
  "description": "Per-rule severity overrides (off/warn/error).",
59
124
  "type": "object",
60
- "additionalProperties": { "enum": ["off", "warn", "error"] }
125
+ "additionalProperties": {
126
+ "enum": ["off", "warn", "error"]
127
+ }
128
+ },
129
+ "naming": {
130
+ "description": "Enforce a name case on class names: a preset (pascalCase, camelCase, lowercase) or a custom regex.",
131
+ "type": "object",
132
+ "additionalProperties": false,
133
+ "properties": {
134
+ "component": {
135
+ "type": "string"
136
+ },
137
+ "part": {
138
+ "type": "string"
139
+ }
140
+ }
141
+ },
142
+ "structureIgnore": {
143
+ "description": "Class names to exempt from the structure-unknown-selector rule — legitimately-external classes (utilities, cross-component refs) referenced in @structure. Literal names or simple globs where * matches any run of characters (e.g. util-*).",
144
+ "type": "array",
145
+ "items": {
146
+ "type": "string"
147
+ }
61
148
  }
62
149
  }
63
150
  }
package/dist/index.d.mts CHANGED
@@ -3,8 +3,18 @@ import { CssDocConfiguration, CssDocTagDefinition, ModifierConventionInput } fro
3
3
  //#region src/CssDocConfigFile.d.ts
4
4
  /** A per-rule severity override, as spelled in `cssdoc.json`. */
5
5
  type RuleSeverityOverride = "off" | "warn" | "error";
6
- /** The conventional file name loaders look for. */
7
- declare const CSSDOC_CONFIG_FILENAME = "cssdoc.json";
6
+ /** The `naming` block: a name case (preset or custom regex) per class kind, as spelled in `cssdoc.json`. */
7
+ interface NamingOverride {
8
+ component?: string;
9
+ part?: string;
10
+ }
11
+ /**
12
+ * The conventional file names loaders look for, in preference order. Both are parsed the same way
13
+ * (JSON with comments); `.jsonc` just makes the comments explicit.
14
+ */
15
+ declare const CSSDOC_CONFIG_FILENAMES: readonly ["cssdoc.json", "cssdoc.jsonc"];
16
+ /** The primary config file name (`cssdoc.json`). See {@link CSSDOC_CONFIG_FILENAMES} for all names. */
17
+ declare const CSSDOC_CONFIG_FILENAME: "cssdoc.json";
8
18
  /** A loaded `cssdoc.json` (plus the files it `extends`), ready to configure a parser. */
9
19
  declare class CssDocConfigFile {
10
20
  /** The absolute path the file was loaded from (or would be, when not found). */
@@ -28,6 +38,16 @@ declare class CssDocConfigFile {
28
38
  * resolved severities — pass these to `resolveRuleSeverities` in `@cssdoc/providers`.
29
39
  */
30
40
  readonly ruleSeverities: Readonly<Record<string, RuleSeverityOverride>>;
41
+ /**
42
+ * The name-case conventions, merged across the `extends` chain (this file wins). Pass to
43
+ * `resolveNaming` in `@cssdoc/providers`.
44
+ */
45
+ readonly naming: Readonly<NamingOverride>;
46
+ /**
47
+ * Class names exempt from the `structure-unknown-selector` rule, merged (union) across the `extends`
48
+ * chain. Pass to `lintModel` in `@cssdoc/providers`.
49
+ */
50
+ readonly structureIgnore: readonly string[];
31
51
  private constructor();
32
52
  /** Whether this file — or any file it extends — reported an error. */
33
53
  get hasErrors(): boolean;
@@ -142,7 +162,39 @@ declare const cssDocSchema: {
142
162
  readonly enum: readonly ["chained", "suffix", "attribute"];
143
163
  };
144
164
  readonly separator: {
145
- readonly type: "string";
165
+ readonly oneOf: readonly [{
166
+ readonly type: "string";
167
+ }, {
168
+ readonly type: "array";
169
+ readonly items: {
170
+ readonly type: "string";
171
+ };
172
+ }];
173
+ };
174
+ readonly elementSeparator: {
175
+ readonly description: "BEM-style element delimiter (e.g. \"__\"); matched classes become parts.";
176
+ readonly oneOf: readonly [{
177
+ readonly type: "string";
178
+ }, {
179
+ readonly type: "array";
180
+ readonly items: {
181
+ readonly type: "string";
182
+ };
183
+ }];
184
+ };
185
+ readonly statePrefixes: {
186
+ readonly description: "Class prefixes that mark a state (e.g. [\"is-\",\"has-\"]).";
187
+ readonly type: "array";
188
+ readonly items: {
189
+ readonly type: "string";
190
+ };
191
+ };
192
+ readonly statePseudoClasses: {
193
+ readonly description: "Native pseudo-classes (without the colon) recognized as states, e.g. [\"disabled\",\"checked\"]. Overrides the built-in default set.";
194
+ readonly type: "array";
195
+ readonly items: {
196
+ readonly type: "string";
197
+ };
146
198
  };
147
199
  readonly propValue: {
148
200
  readonly type: "boolean";
@@ -160,7 +212,27 @@ declare const cssDocSchema: {
160
212
  readonly enum: readonly ["off", "warn", "error"];
161
213
  };
162
214
  };
215
+ readonly naming: {
216
+ readonly description: "Enforce a name case on class names: a preset (pascalCase, camelCase, lowercase) or a custom regex.";
217
+ readonly type: "object";
218
+ readonly additionalProperties: false;
219
+ readonly properties: {
220
+ readonly component: {
221
+ readonly type: "string";
222
+ };
223
+ readonly part: {
224
+ readonly type: "string";
225
+ };
226
+ };
227
+ };
228
+ readonly structureIgnore: {
229
+ readonly description: "Class names to exempt from the structure-unknown-selector rule — legitimately-external classes (utilities, cross-component refs) referenced in @structure. Literal names or simple globs where * matches any run of characters (e.g. util-*).";
230
+ readonly type: "array";
231
+ readonly items: {
232
+ readonly type: "string";
233
+ };
234
+ };
163
235
  };
164
236
  };
165
237
  //#endregion
166
- export { CSSDOC_CONFIG_FILENAME, CssDocConfigFile, cssDocSchema };
238
+ export { CSSDOC_CONFIG_FILENAME, CSSDOC_CONFIG_FILENAMES, CssDocConfigFile, cssDocSchema };
package/dist/index.mjs CHANGED
@@ -2,6 +2,7 @@ import { createRequire } from "node:module";
2
2
  import { existsSync, readFileSync } from "node:fs";
3
3
  import { dirname, parse, resolve } from "node:path";
4
4
  import { Ajv } from "ajv";
5
+ import { parse as parse$1, printParseErrorCode } from "jsonc-parser";
5
6
  import { CssDocConfiguration, CssDocTagDefinition } from "@cssdoc/core";
6
7
  //#region src/schema.ts
7
8
  /**
@@ -80,7 +81,27 @@ const cssDocSchema = {
80
81
  "suffix",
81
82
  "attribute"
82
83
  ] },
83
- separator: { type: "string" },
84
+ separator: { oneOf: [{ type: "string" }, {
85
+ type: "array",
86
+ items: { type: "string" }
87
+ }] },
88
+ elementSeparator: {
89
+ description: "BEM-style element delimiter (e.g. \"__\"); matched classes become parts.",
90
+ oneOf: [{ type: "string" }, {
91
+ type: "array",
92
+ items: { type: "string" }
93
+ }]
94
+ },
95
+ statePrefixes: {
96
+ description: "Class prefixes that mark a state (e.g. [\"is-\",\"has-\"]).",
97
+ type: "array",
98
+ items: { type: "string" }
99
+ },
100
+ statePseudoClasses: {
101
+ description: "Native pseudo-classes (without the colon) recognized as states, e.g. [\"disabled\",\"checked\"]. Overrides the built-in default set.",
102
+ type: "array",
103
+ items: { type: "string" }
104
+ },
84
105
  propValue: { type: "boolean" },
85
106
  propValueSeparator: { type: "string" }
86
107
  }
@@ -94,6 +115,20 @@ const cssDocSchema = {
94
115
  "warn",
95
116
  "error"
96
117
  ] }
118
+ },
119
+ naming: {
120
+ description: "Enforce a name case on class names: a preset (pascalCase, camelCase, lowercase) or a custom regex.",
121
+ type: "object",
122
+ additionalProperties: false,
123
+ properties: {
124
+ component: { type: "string" },
125
+ part: { type: "string" }
126
+ }
127
+ },
128
+ structureIgnore: {
129
+ description: "Class names to exempt from the structure-unknown-selector rule — legitimately-external classes (utilities, cross-component refs) referenced in @structure. Literal names or simple globs where * matches any run of characters (e.g. util-*).",
130
+ type: "array",
131
+ items: { type: "string" }
97
132
  }
98
133
  }
99
134
  };
@@ -109,8 +144,13 @@ const cssDocSchema = {
109
144
  * @module
110
145
  */
111
146
  const validateSchema = new Ajv({ allErrors: true }).compile(cssDocSchema);
112
- /** The conventional file name loaders look for. */
113
- const CSSDOC_CONFIG_FILENAME = "cssdoc.json";
147
+ /**
148
+ * The conventional file names loaders look for, in preference order. Both are parsed the same way
149
+ * (JSON with comments); `.jsonc` just makes the comments explicit.
150
+ */
151
+ const CSSDOC_CONFIG_FILENAMES = ["cssdoc.json", "cssdoc.jsonc"];
152
+ /** The primary config file name (`cssdoc.json`). See {@link CSSDOC_CONFIG_FILENAMES} for all names. */
153
+ const CSSDOC_CONFIG_FILENAME = CSSDOC_CONFIG_FILENAMES[0];
114
154
  /** A loaded `cssdoc.json` (plus the files it `extends`), ready to configure a parser. */
115
155
  var CssDocConfigFile = class CssDocConfigFile {
116
156
  /** The absolute path the file was loaded from (or would be, when not found). */
@@ -134,6 +174,16 @@ var CssDocConfigFile = class CssDocConfigFile {
134
174
  * resolved severities — pass these to `resolveRuleSeverities` in `@cssdoc/providers`.
135
175
  */
136
176
  ruleSeverities;
177
+ /**
178
+ * The name-case conventions, merged across the `extends` chain (this file wins). Pass to
179
+ * `resolveNaming` in `@cssdoc/providers`.
180
+ */
181
+ naming;
182
+ /**
183
+ * Class names exempt from the `structure-unknown-selector` rule, merged (union) across the `extends`
184
+ * chain. Pass to `lintModel` in `@cssdoc/providers`.
185
+ */
186
+ structureIgnore;
137
187
  constructor(init) {
138
188
  this.filePath = init.filePath;
139
189
  this.fileNotFound = init.fileNotFound;
@@ -144,9 +194,19 @@ var CssDocConfigFile = class CssDocConfigFile {
144
194
  this.extendsFiles = init.extendsFiles;
145
195
  this.modifierConvention = init.modifierConvention;
146
196
  const severities = {};
147
- for (const extended of init.extendsFiles) Object.assign(severities, extended.ruleSeverities);
197
+ const naming = {};
198
+ const structureIgnore = /* @__PURE__ */ new Set();
199
+ for (const extended of init.extendsFiles) {
200
+ Object.assign(severities, extended.ruleSeverities);
201
+ Object.assign(naming, extended.naming);
202
+ for (const g of extended.structureIgnore) structureIgnore.add(g);
203
+ }
148
204
  Object.assign(severities, init.rules);
205
+ Object.assign(naming, init.naming);
206
+ for (const g of init.structureIgnore) structureIgnore.add(g);
149
207
  this.ruleSeverities = severities;
208
+ this.naming = naming;
209
+ this.structureIgnore = [...structureIgnore];
150
210
  }
151
211
  /** Whether this file — or any file it extends — reported an error. */
152
212
  get hasErrors() {
@@ -202,8 +262,10 @@ var CssDocConfigFile = class CssDocConfigFile {
202
262
  let current = resolve(folderPath);
203
263
  const root = parse(current).root;
204
264
  for (;;) {
205
- const candidate = resolve(current, CSSDOC_CONFIG_FILENAME);
206
- if (existsSync(candidate)) return CssDocConfigFile._loadFile(candidate, /* @__PURE__ */ new Set());
265
+ for (const name of CSSDOC_CONFIG_FILENAMES) {
266
+ const candidate = resolve(current, name);
267
+ if (existsSync(candidate)) return CssDocConfigFile._loadFile(candidate, /* @__PURE__ */ new Set());
268
+ }
207
269
  if (current === root) break;
208
270
  current = dirname(current);
209
271
  }
@@ -215,7 +277,9 @@ var CssDocConfigFile = class CssDocConfigFile {
215
277
  tagDefinitions: [],
216
278
  supportForTags: /* @__PURE__ */ new Map(),
217
279
  extendsFiles: [],
218
- rules: {}
280
+ rules: {},
281
+ naming: {},
282
+ structureIgnore: []
219
283
  });
220
284
  }
221
285
  static _loadFile(filePath, visited) {
@@ -228,7 +292,9 @@ var CssDocConfigFile = class CssDocConfigFile {
228
292
  tagDefinitions: [],
229
293
  supportForTags: /* @__PURE__ */ new Map(),
230
294
  extendsFiles: [],
231
- rules: {}
295
+ rules: {},
296
+ naming: {},
297
+ structureIgnore: []
232
298
  });
233
299
  if (visited.has(filePath)) {
234
300
  messages.push("Circular extends reference; skipped.");
@@ -236,11 +302,11 @@ var CssDocConfigFile = class CssDocConfigFile {
236
302
  }
237
303
  visited.add(filePath);
238
304
  if (!existsSync(filePath)) return empty(true);
239
- let raw;
240
- try {
241
- raw = JSON.parse(readFileSync(filePath, "utf8"));
242
- } catch (error) {
243
- messages.push(`Invalid JSON: ${error.message}`);
305
+ const parseErrors = [];
306
+ const raw = parse$1(readFileSync(filePath, "utf8"), parseErrors, { allowTrailingComma: true });
307
+ if (parseErrors.length > 0 || raw === void 0) {
308
+ for (const err of parseErrors) messages.push(`Invalid JSON at offset ${err.offset}: ${printParseErrorCode(err.error)}`);
309
+ if (parseErrors.length === 0) messages.push("Invalid JSON: empty or unparseable config.");
244
310
  return empty(false);
245
311
  }
246
312
  if (!validateSchema(raw)) {
@@ -274,9 +340,11 @@ var CssDocConfigFile = class CssDocConfigFile {
274
340
  supportForTags: new Map(Object.entries(raw.supportForTags ?? {})),
275
341
  extendsFiles,
276
342
  modifierConvention: raw.modifierConvention,
277
- rules: raw.rules ?? {}
343
+ rules: raw.rules ?? {},
344
+ naming: raw.naming ?? {},
345
+ structureIgnore: raw.structureIgnore ?? []
278
346
  });
279
347
  }
280
348
  };
281
349
  //#endregion
282
- export { CSSDOC_CONFIG_FILENAME, CssDocConfigFile, cssDocSchema };
350
+ export { CSSDOC_CONFIG_FILENAME, CSSDOC_CONFIG_FILENAMES, CssDocConfigFile, cssDocSchema };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@cssdoc/config",
3
- "version": "0.2.0",
3
+ "version": "0.3.0",
4
4
  "description": "Load a cssdoc.json configuration file (custom tags, extends) into an @cssdoc/core CssDocConfiguration — TSDoc-config, for CSS.",
5
5
  "keywords": [
6
6
  "config",
@@ -31,7 +31,8 @@
31
31
  },
32
32
  "dependencies": {
33
33
  "ajv": "^8.20.0",
34
- "@cssdoc/core": "0.2.0"
34
+ "jsonc-parser": "^3.3.1",
35
+ "@cssdoc/core": "0.3.0"
35
36
  },
36
37
  "devDependencies": {
37
38
  "@types/node": "^24.13.3",