@brnshkr/config 0.0.1-beta.3 → 0.0.1-beta.4

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 (37) hide show
  1. package/README.md +174 -226
  2. package/conf/.gitignore.dist +15 -0
  3. package/conf/Makefile +3778 -0
  4. package/conf/Makefile.dist +13 -0
  5. package/conf/bunfig.dist.toml +5 -0
  6. package/conf/commitlint.dist.mjs +1 -0
  7. package/conf/editorconfig.dist +19 -0
  8. package/conf/launch.dist.json +31 -0
  9. package/conf/markdownlint.dist.mjs +1 -0
  10. package/conf/spelling/defaults.json +185 -0
  11. package/conf/tsconfig.dist.json +3 -0
  12. package/conf/tsconfig.json +31 -27
  13. package/conf/vitest.dist.mjs +1 -0
  14. package/conf/vscode-css-custom-data.dist.json +95 -0
  15. package/conf/vscode-extensions.dist.json +21 -0
  16. package/conf/vscode-settings.dist.jsonc +338 -0
  17. package/dist/commitlint/index.d.mts +72 -0
  18. package/dist/commitlint/index.mjs +239 -0
  19. package/dist/eslint/index.d.mts +3890 -1387
  20. package/dist/eslint/index.mjs +2662 -471
  21. package/dist/markdownlint/index.d.mts +2304 -0
  22. package/dist/markdownlint/index.mjs +440 -0
  23. package/dist/shared.mjs +406 -64
  24. package/dist/spelling/index.d.mts +30 -0
  25. package/dist/spelling/index.mjs +2 -0
  26. package/dist/spelling/spelling.test.d.mts +1 -0
  27. package/dist/spelling/spelling.test.mjs +12 -0
  28. package/dist/stylelint/index.d.mts +47 -9
  29. package/dist/stylelint/index.mjs +145 -47
  30. package/dist/vitest/index.d.mts +73 -0
  31. package/dist/vitest/index.mjs +181 -0
  32. package/package.json +192 -105
  33. package/conf/tsconfig.json.example +0 -3
  34. package/dist/scripts/eslint.mjs +0 -16
  35. package/dist/scripts/stylelint.mjs +0 -31
  36. /package/conf/{eslint.config.mjs.example → eslint.dist.mjs} +0 -0
  37. /package/conf/{stylelint.config.mjs.example → stylelint.dist.mjs} +0 -0
package/dist/shared.mjs CHANGED
@@ -1,36 +1,52 @@
1
1
  import { isPackageExists } from "local-pkg";
2
+ import fs from "node:fs";
3
+ import path from "node:path";
4
+ import { execFileSync } from "node:child_process";
2
5
  //#region ../package.json
3
6
  var name = "@brnshkr/config";
4
- var version = "0.0.1-beta.3";
7
+ var version = "0.0.1-beta.4";
5
8
  //#endregion
6
9
  //#region ../src/js/shared/utils/package-json.ts
7
- const packageOrganizationInternal = name.split("/").at(0)?.replace(/^@/v, "");
10
+ /**
11
+ * @internal @brnshkr/config
12
+ */
13
+ const packageOrganizationInternal = name.split("/", 1).at(0)?.replace(/^@/v, "");
8
14
  if (packageOrganizationInternal === void 0 || packageOrganizationInternal.length === 0) throw new Error("Failed to read package organization from package.json file.");
9
15
  const packageOrganization = packageOrganizationInternal;
10
16
  const packageOrganizationUpper = packageOrganizationInternal.toUpperCase();
11
17
  //#endregion
12
18
  //#region ../src/js/shared/utils/log.ts
13
- const log = (type, message, context) => {
14
- console[type](`[${name}] ${message}`);
15
- if (context) console[type](context);
19
+ /**
20
+ * @internal @brnshkr/config
21
+ */
22
+ const log = (message) => {
23
+ console.error(`[${name}] ${message}`);
16
24
  };
17
25
  //#endregion
18
26
  //#region ../src/js/shared/utils/object.ts
27
+ const objectKeys = (object) => Object.keys(object);
28
+ const objectValues = (object) => Object.values(object);
19
29
  const objectEntries = (object) => Object.entries(object);
20
30
  const objectFromEntries = (entries) => Object.fromEntries(entries);
21
31
  const objectAssign = (target, source) => Object.assign(target, source);
32
+ const isPlainObject = (value) => {
33
+ if (typeof value !== "object" || !value) return false;
34
+ return (Object.getPrototypeOf(value) ?? Object.prototype) === Object.prototype;
35
+ };
22
36
  //#endregion
23
37
  //#region ../src/js/shared/utils/interop-import.ts
24
38
  const interopImport = async (thePackage) => {
25
39
  const resolved = await thePackage;
26
- return typeof resolved === "object" && resolved !== null && "default" in resolved ? resolved.default : resolved;
40
+ return typeof resolved === "object" && resolved && "default" in resolved ? resolved.default : resolved;
27
41
  };
28
42
  //#endregion
29
43
  //#region ../src/js/shared/utils/package-resolvers.ts
44
+ /**
45
+ * @internal @brnshkr/config
46
+ */
30
47
  const ESLINT_PACKAGES = {
31
48
  ESLINT_CSS: "@eslint/css",
32
- ESLINT_FLAT_CONFIG_GITIGNORE: "eslint-config-flat-gitignore",
33
- ESLINT_IMPORT_RESOVLER_TYPESCRIPT: "eslint-import-resolver-typescript",
49
+ ESLINT_IMPORT_RESOLVER_TYPESCRIPT: "eslint-import-resolver-typescript",
34
50
  ESLINT_JSON: "@eslint/json",
35
51
  ESLINT_MARKDOWN: "@eslint/markdown",
36
52
  ESLINT_MERGE_PROCESSORS: "eslint-merge-processors",
@@ -50,47 +66,74 @@ const ESLINT_PACKAGES = {
50
66
  ESLINT_PLUGIN_UNUSED_IMPORTS: "eslint-plugin-unused-imports",
51
67
  ESLINT_PLUGIN_YML: "eslint-plugin-yml",
52
68
  SVELTE: "svelte",
69
+ TAILWINDCSS: "tailwindcss",
53
70
  TYPESCRIPT: "typescript",
54
71
  TYPESCRIPT_ESLINT: "typescript-eslint",
55
72
  VITEST_ESLINT_PLUGIN: "@vitest/eslint-plugin"
56
73
  };
57
74
  const ESLINT_PACKAGE_RESOLVERS = {
58
- [ESLINT_PACKAGES.ESLINT_CSS]: async () => await interopImport(import("@eslint/css")),
59
- [ESLINT_PACKAGES.ESLINT_FLAT_CONFIG_GITIGNORE]: async () => await interopImport(import("eslint-config-flat-gitignore")),
60
- [ESLINT_PACKAGES.ESLINT_JSON]: async () => await interopImport(import("@eslint/json")),
61
- [ESLINT_PACKAGES.ESLINT_IMPORT_RESOVLER_TYPESCRIPT]: async () => await interopImport(import("eslint-import-resolver-typescript")),
62
- [ESLINT_PACKAGES.ESLINT_PLUGIN_ANTFU]: async () => await interopImport(import("eslint-plugin-antfu")),
63
- [ESLINT_PACKAGES.ESLINT_PLUGIN_ESLINT_COMMENTS]: async () => await interopImport(import("@eslint-community/eslint-plugin-eslint-comments")),
64
- [ESLINT_PACKAGES.ESLINT_PLUGIN_IMPORT_X]: async () => await interopImport(import("eslint-plugin-import-x")),
65
- [ESLINT_PACKAGES.ESLINT_PLUGIN_JSDOC]: async () => await interopImport(import("eslint-plugin-jsdoc")),
66
- [ESLINT_PACKAGES.ESLINT_PLUGIN_JSONC]: async () => await interopImport(import("eslint-plugin-jsonc")),
67
- [ESLINT_PACKAGES.ESLINT_PLUGIN_JSDOC_PROCESSOR]: async () => await interopImport(import("eslint-plugin-jsdoc/getJsdocProcessorPlugin.js")),
68
- [ESLINT_PACKAGES.ESLINT_MARKDOWN]: async () => await interopImport(import("@eslint/markdown")),
69
- [ESLINT_PACKAGES.ESLINT_MERGE_PROCESSORS]: async () => await interopImport(import("eslint-merge-processors")),
70
- [ESLINT_PACKAGES.ESLINT_PLUGIN_N]: async () => await interopImport(import("eslint-plugin-n")),
71
- [ESLINT_PACKAGES.ESLINT_PLUGIN_PERFECTIONIST]: async () => await interopImport(import("eslint-plugin-perfectionist")),
72
- [ESLINT_PACKAGES.ESLINT_PLUGIN_REGEXP]: async () => await interopImport(import("eslint-plugin-regexp")),
73
- [ESLINT_PACKAGES.ESLINT_PLUGIN_STYLISTIC]: async () => await interopImport(import("@stylistic/eslint-plugin")),
74
- [ESLINT_PACKAGES.ESLINT_PLUGIN_SVELTE]: async () => await interopImport(import("eslint-plugin-svelte")),
75
- [ESLINT_PACKAGES.ESLINT_PLUGIN_TOML]: async () => await interopImport(import("eslint-plugin-toml")),
76
- [ESLINT_PACKAGES.ESLINT_PLUGIN_UNICORN]: async () => await interopImport(import("eslint-plugin-unicorn")),
77
- [ESLINT_PACKAGES.ESLINT_PLUGIN_UNUSED_IMPORTS]: async () => await interopImport(import("eslint-plugin-unused-imports")),
78
- [ESLINT_PACKAGES.ESLINT_PLUGIN_YML]: async () => await interopImport(import("eslint-plugin-yml")),
75
+ [ESLINT_PACKAGES.ESLINT_CSS]: async () => interopImport(import("@eslint/css")),
76
+ [ESLINT_PACKAGES.ESLINT_JSON]: async () => interopImport(import("@eslint/json")),
77
+ [ESLINT_PACKAGES.ESLINT_IMPORT_RESOLVER_TYPESCRIPT]: async () => interopImport(import("eslint-import-resolver-typescript")),
78
+ [ESLINT_PACKAGES.ESLINT_PLUGIN_ANTFU]: async () => interopImport(import("eslint-plugin-antfu")),
79
+ [ESLINT_PACKAGES.ESLINT_PLUGIN_ESLINT_COMMENTS]: async () => interopImport(import("@eslint-community/eslint-plugin-eslint-comments")),
80
+ [ESLINT_PACKAGES.ESLINT_PLUGIN_IMPORT_X]: async () => interopImport(import("eslint-plugin-import-x")),
81
+ [ESLINT_PACKAGES.ESLINT_PLUGIN_JSDOC]: async () => interopImport(import("eslint-plugin-jsdoc")),
82
+ [ESLINT_PACKAGES.ESLINT_PLUGIN_JSONC]: async () => interopImport(import("eslint-plugin-jsonc")),
83
+ [ESLINT_PACKAGES.ESLINT_PLUGIN_JSDOC_PROCESSOR]: async () => interopImport(import("eslint-plugin-jsdoc/getJsdocProcessorPlugin.js")),
84
+ [ESLINT_PACKAGES.ESLINT_MARKDOWN]: async () => interopImport(import("@eslint/markdown")),
85
+ [ESLINT_PACKAGES.ESLINT_MERGE_PROCESSORS]: async () => interopImport(import("eslint-merge-processors")),
86
+ [ESLINT_PACKAGES.ESLINT_PLUGIN_N]: async () => interopImport(import("eslint-plugin-n")),
87
+ [ESLINT_PACKAGES.ESLINT_PLUGIN_PERFECTIONIST]: async () => interopImport(import("eslint-plugin-perfectionist")),
88
+ [ESLINT_PACKAGES.ESLINT_PLUGIN_REGEXP]: async () => interopImport(import("eslint-plugin-regexp")),
89
+ [ESLINT_PACKAGES.ESLINT_PLUGIN_STYLISTIC]: async () => interopImport(import("@stylistic/eslint-plugin")),
90
+ [ESLINT_PACKAGES.ESLINT_PLUGIN_SVELTE]: async () => interopImport(import("eslint-plugin-svelte")),
91
+ [ESLINT_PACKAGES.ESLINT_PLUGIN_TOML]: async () => interopImport(import("eslint-plugin-toml")),
92
+ [ESLINT_PACKAGES.ESLINT_PLUGIN_UNICORN]: async () => interopImport(import("eslint-plugin-unicorn")),
93
+ [ESLINT_PACKAGES.ESLINT_PLUGIN_UNUSED_IMPORTS]: async () => interopImport(import("eslint-plugin-unused-imports")),
94
+ [ESLINT_PACKAGES.ESLINT_PLUGIN_YML]: async () => interopImport(import("eslint-plugin-yml")),
79
95
  [ESLINT_PACKAGES.SVELTE]: () => isPackageExists(ESLINT_PACKAGES.SVELTE),
96
+ [ESLINT_PACKAGES.TAILWINDCSS]: () => isPackageExists(ESLINT_PACKAGES.TAILWINDCSS),
80
97
  [ESLINT_PACKAGES.TYPESCRIPT]: () => isPackageExists(ESLINT_PACKAGES.TYPESCRIPT),
81
- [ESLINT_PACKAGES.TYPESCRIPT_ESLINT]: async () => await interopImport(import("typescript-eslint")),
82
- [ESLINT_PACKAGES.VITEST_ESLINT_PLUGIN]: async () => await interopImport(import("@vitest/eslint-plugin"))
98
+ [ESLINT_PACKAGES.TYPESCRIPT_ESLINT]: async () => interopImport(import("typescript-eslint")),
99
+ [ESLINT_PACKAGES.VITEST_ESLINT_PLUGIN]: async () => interopImport(import("@vitest/eslint-plugin"))
100
+ };
101
+ const COMMITLINT_PACKAGES = {
102
+ COMMITLINT_CONFIG_CONVENTIONAL: "@commitlint/config-conventional",
103
+ COMMITLINT_PLUGIN_FUNCTION_RULES: "commitlint-plugin-function-rules",
104
+ COMMITLINT_PLUGIN_TENSE: "commitlint-plugin-tense"
105
+ };
106
+ const COMMITLINT_PACKAGE_RESOLVERS = {
107
+ [COMMITLINT_PACKAGES.COMMITLINT_CONFIG_CONVENTIONAL]: () => isPackageExists(COMMITLINT_PACKAGES.COMMITLINT_CONFIG_CONVENTIONAL),
108
+ [COMMITLINT_PACKAGES.COMMITLINT_PLUGIN_FUNCTION_RULES]: () => isPackageExists(COMMITLINT_PACKAGES.COMMITLINT_PLUGIN_FUNCTION_RULES),
109
+ [COMMITLINT_PACKAGES.COMMITLINT_PLUGIN_TENSE]: () => isPackageExists(COMMITLINT_PACKAGES.COMMITLINT_PLUGIN_TENSE)
110
+ };
111
+ const MARKDOWNLINT_PACKAGES = {
112
+ MARKDOWNLINT_GITHUB: "@github/markdownlint-github",
113
+ MARKDOWNLINT_RULES: "@hongminhee/markdownlint-rules",
114
+ MARKDOWNLINT_RULE_NO_TRAILING_SLASH_IN_LINKS: "markdownlint-rule-no-trailing-slash-in-links",
115
+ MARKDOWNLINT_RULE_RELATIVE_LINKS: "markdownlint-rule-relative-links",
116
+ MARKDOWNLINT_RULE_SEARCH_REPLACE: "markdownlint-rule-search-replace",
117
+ MARKDOWNLINT_RULE_TABLE_FORMAT: "markdownlint-rule-table-format"
118
+ };
119
+ const MARKDOWNLINT_PACKAGE_RESOLVERS = {
120
+ [MARKDOWNLINT_PACKAGES.MARKDOWNLINT_GITHUB]: () => isPackageExists(MARKDOWNLINT_PACKAGES.MARKDOWNLINT_GITHUB),
121
+ [MARKDOWNLINT_PACKAGES.MARKDOWNLINT_RULES]: () => isPackageExists(MARKDOWNLINT_PACKAGES.MARKDOWNLINT_RULES),
122
+ [MARKDOWNLINT_PACKAGES.MARKDOWNLINT_RULE_NO_TRAILING_SLASH_IN_LINKS]: () => isPackageExists(MARKDOWNLINT_PACKAGES.MARKDOWNLINT_RULE_NO_TRAILING_SLASH_IN_LINKS),
123
+ [MARKDOWNLINT_PACKAGES.MARKDOWNLINT_RULE_RELATIVE_LINKS]: () => isPackageExists(MARKDOWNLINT_PACKAGES.MARKDOWNLINT_RULE_RELATIVE_LINKS),
124
+ [MARKDOWNLINT_PACKAGES.MARKDOWNLINT_RULE_SEARCH_REPLACE]: () => isPackageExists(MARKDOWNLINT_PACKAGES.MARKDOWNLINT_RULE_SEARCH_REPLACE),
125
+ [MARKDOWNLINT_PACKAGES.MARKDOWNLINT_RULE_TABLE_FORMAT]: () => isPackageExists(MARKDOWNLINT_PACKAGES.MARKDOWNLINT_RULE_TABLE_FORMAT)
83
126
  };
84
127
  const STYLELINT_PACKAGES = {
85
128
  POSTCSS_HTML: "postcss-html",
86
129
  STYLELINT_CONFIG_CSS_MODULES: "stylelint-config-css-modules",
87
130
  STYLELINT_CONFIG_HTML: "stylelint-config-html",
88
131
  STYLELINT_CONFIG_RECESS_ORDER: "stylelint-config-recess-order",
132
+ STYLELINT_CONFIG_STANDARD_LESS: "stylelint-config-standard-less",
89
133
  STYLELINT_CONFIG_STANDARD_SCSS: "stylelint-config-standard-scss",
90
134
  STYLELINT_DECLARATION_STRICT_VALUE: "stylelint-declaration-strict-value",
91
135
  STYLELINT_ORDER: "stylelint-order",
92
136
  STYLELINT_PLUGIN_DEFENSIVE_CSS: "stylelint-plugin-defensive-css",
93
- STYLELINT_PLUGIN_LOGICAL_CSS: "stylelint-plugin-logical-css",
94
137
  STYLELINT_PLUGIN_USE_BASELINE: "stylelint-plugin-use-baseline",
95
138
  STYLELINT_USE_NESTING: "stylelint-use-nesting",
96
139
  STYLISTIC_STYLELINT_CONFIG: "@stylistic/stylelint-config"
@@ -100,51 +143,98 @@ const STYLELINT_PACKAGE_RESOLVERS = {
100
143
  [STYLELINT_PACKAGES.STYLELINT_CONFIG_CSS_MODULES]: () => isPackageExists(STYLELINT_PACKAGES.STYLELINT_CONFIG_CSS_MODULES),
101
144
  [STYLELINT_PACKAGES.STYLELINT_CONFIG_HTML]: () => isPackageExists(STYLELINT_PACKAGES.STYLELINT_CONFIG_HTML),
102
145
  [STYLELINT_PACKAGES.STYLELINT_CONFIG_RECESS_ORDER]: () => isPackageExists(STYLELINT_PACKAGES.STYLELINT_CONFIG_RECESS_ORDER),
146
+ [STYLELINT_PACKAGES.STYLELINT_CONFIG_STANDARD_LESS]: () => isPackageExists(STYLELINT_PACKAGES.STYLELINT_CONFIG_STANDARD_LESS),
103
147
  [STYLELINT_PACKAGES.STYLELINT_CONFIG_STANDARD_SCSS]: () => isPackageExists(STYLELINT_PACKAGES.STYLELINT_CONFIG_STANDARD_SCSS),
104
148
  [STYLELINT_PACKAGES.STYLELINT_DECLARATION_STRICT_VALUE]: () => isPackageExists(STYLELINT_PACKAGES.STYLELINT_DECLARATION_STRICT_VALUE),
105
149
  [STYLELINT_PACKAGES.STYLELINT_ORDER]: () => isPackageExists(STYLELINT_PACKAGES.STYLELINT_ORDER),
106
150
  [STYLELINT_PACKAGES.STYLELINT_PLUGIN_DEFENSIVE_CSS]: () => isPackageExists(STYLELINT_PACKAGES.STYLELINT_PLUGIN_DEFENSIVE_CSS),
107
- [STYLELINT_PACKAGES.STYLELINT_PLUGIN_LOGICAL_CSS]: () => isPackageExists(STYLELINT_PACKAGES.STYLELINT_PLUGIN_LOGICAL_CSS),
108
151
  [STYLELINT_PACKAGES.STYLELINT_PLUGIN_USE_BASELINE]: () => isPackageExists(STYLELINT_PACKAGES.STYLELINT_PLUGIN_USE_BASELINE),
109
152
  [STYLELINT_PACKAGES.STYLELINT_USE_NESTING]: () => isPackageExists(STYLELINT_PACKAGES.STYLELINT_USE_NESTING),
110
153
  [STYLELINT_PACKAGES.STYLISTIC_STYLELINT_CONFIG]: () => isPackageExists(STYLELINT_PACKAGES.STYLISTIC_STYLELINT_CONFIG)
111
154
  };
155
+ const VITEST_PACKAGES = {
156
+ HAPPY_DOM: "happy-dom",
157
+ JSDOM: "jsdom",
158
+ VITEST_UI: "@vitest/ui"
159
+ };
160
+ const VITEST_PACKAGE_RESOLVERS = {
161
+ [VITEST_PACKAGES.HAPPY_DOM]: () => isPackageExists(VITEST_PACKAGES.HAPPY_DOM),
162
+ [VITEST_PACKAGES.JSDOM]: () => isPackageExists(VITEST_PACKAGES.JSDOM),
163
+ [VITEST_PACKAGES.VITEST_UI]: () => isPackageExists(VITEST_PACKAGES.VITEST_UI)
164
+ };
112
165
  //#endregion
113
166
  //#region ../src/js/shared/utils/string.ts
167
+ /**
168
+ * @internal @brnshkr/config
169
+ */
114
170
  const joinAsQuotedList = (strings, type = "conjunction") => strings.length > 1 ? `"${strings.slice(0, -1).join("\", \"")}" ${type === "conjunction" ? "and" : "or"} "${strings.slice(-1).join("")}"` : {
115
171
  0: "",
116
172
  1: `"${strings[0] ?? ""}"`
117
173
  }[strings.length] ?? "";
118
174
  //#endregion
119
175
  //#region ../src/js/shared/utils/module.ts
176
+ /**
177
+ * @internal @brnshkr/config
178
+ */
120
179
  const PACKAGE_RESOLVERS = {
180
+ ...COMMITLINT_PACKAGE_RESOLVERS,
121
181
  ...ESLINT_PACKAGE_RESOLVERS,
122
- ...STYLELINT_PACKAGE_RESOLVERS
182
+ ...MARKDOWNLINT_PACKAGE_RESOLVERS,
183
+ ...STYLELINT_PACKAGE_RESOLVERS,
184
+ ...VITEST_PACKAGE_RESOLVERS
123
185
  };
124
186
  const warnMissingPackages = (moduleInfo, packages, type) => {
125
- log("error", `Failed resolving required dependencies for module "${moduleInfo.name}". Please install ${joinAsQuotedList([...packages], type === "requiredAny" ? "disjunction" : "conjunction")} or disable the ${moduleInfo.name} module in the config.`);
126
- log("log", `Run \`bun a -D -E ${packages.join(" ")}\` to install.`);
187
+ log(`Failed resolving required dependencies for module "${moduleInfo.name}". Please install ${joinAsQuotedList([...packages], type === "requiredAny" ? "disjunction" : "conjunction")} or disable the ${moduleInfo.name} module in the config.`);
188
+ log(`Run \`bun a -D -E ${packages.join(" ")}\` to install.`);
127
189
  };
128
190
  const packageCache = {};
191
+ const loadPackageAsynchronously = async (thePackage) => {
192
+ try {
193
+ return await PACKAGE_RESOLVERS[thePackage]();
194
+ } catch {
195
+ return false;
196
+ }
197
+ };
198
+ const loadPackageSynchronously = (thePackage) => {
199
+ try {
200
+ return PACKAGE_RESOLVERS[thePackage]();
201
+ } catch {
202
+ return false;
203
+ }
204
+ };
205
+ const collectPackagesAsynchronously = async (moduleInfo, type, packages) => {
206
+ const collectedPackages = [];
207
+ for (const thePackage of packages) {
208
+ const resolvedPackage = packageCache[thePackage] ?? await loadPackageAsynchronously(thePackage);
209
+ packageCache[thePackage] ??= resolvedPackage;
210
+ if (type === "requiredAll" && resolvedPackage === false) {
211
+ warnMissingPackages(moduleInfo, packages, type);
212
+ break;
213
+ }
214
+ collectedPackages.push(resolvedPackage === false ? void 0 : resolvedPackage);
215
+ }
216
+ return collectedPackages;
217
+ };
218
+ const collectPackagesSynchronously = (moduleInfo, type, packages) => {
219
+ const collectedPackages = [];
220
+ for (const thePackage of packages) {
221
+ const resolvedPackage = packageCache[thePackage] ?? loadPackageSynchronously(thePackage);
222
+ packageCache[thePackage] ??= resolvedPackage;
223
+ if (type === "requiredAll" && resolvedPackage === false) {
224
+ warnMissingPackages(moduleInfo, packages, type);
225
+ break;
226
+ }
227
+ collectedPackages.push(resolvedPackage === false ? void 0 : resolvedPackage);
228
+ }
229
+ return collectedPackages;
230
+ };
129
231
  const resolvePackagesSharedAsynchronously = async (moduleInfo, type) => {
130
232
  if (moduleInfo.packages === void 0) return {};
131
233
  const resolvedPackages = {};
132
234
  const packages = type === void 0 ? moduleInfo.packages : { [type]: moduleInfo.packages[type] };
133
235
  for (const [currentType, currentPackages] of objectEntries(packages)) {
134
236
  if (!currentPackages || currentPackages.length === 0) continue;
135
- resolvedPackages[currentType] ??= [];
136
- for (const currentPackage of currentPackages) try {
137
- const resolvedPackage = packageCache[currentPackage] ?? await PACKAGE_RESOLVERS[currentPackage]();
138
- packageCache[currentPackage] ??= resolvedPackage;
139
- if (resolvedPackage === false) throw new Error("Skip to catch block.");
140
- resolvedPackages[currentType].push(resolvedPackage);
141
- } catch {
142
- if (currentType === "requiredAll") {
143
- warnMissingPackages(moduleInfo, currentPackages, currentType);
144
- break;
145
- }
146
- resolvedPackages[currentType].push(void 0);
147
- }
237
+ resolvedPackages[currentType] = await collectPackagesAsynchronously(moduleInfo, currentType, currentPackages);
148
238
  if (currentType === "requiredAny" && resolvedPackages[currentType].filter((thePackage) => thePackage !== void 0).length === 0) warnMissingPackages(moduleInfo, currentPackages, currentType);
149
239
  }
150
240
  return type === void 0 ? resolvedPackages : resolvedPackages[type] ?? [];
@@ -155,19 +245,7 @@ const resolvePackagesSharedSynchronously = (moduleInfo, type) => {
155
245
  const packages = type === void 0 ? moduleInfo.packages : { [type]: moduleInfo.packages[type] };
156
246
  for (const [currentType, currentPackages] of objectEntries(packages)) {
157
247
  if (!currentPackages || currentPackages.length === 0) continue;
158
- resolvedPackages[currentType] ??= [];
159
- for (const currentPackage of currentPackages) try {
160
- const resolvedPackage = packageCache[currentPackage] ?? PACKAGE_RESOLVERS[currentPackage]();
161
- packageCache[currentPackage] ??= resolvedPackage;
162
- if (resolvedPackage === false) throw new Error("Skip to catch block.");
163
- resolvedPackages[currentType].push(resolvedPackage);
164
- } catch {
165
- if (currentType === "requiredAll") {
166
- warnMissingPackages(moduleInfo, currentPackages, currentType);
167
- break;
168
- }
169
- resolvedPackages[currentType].push(void 0);
170
- }
248
+ resolvedPackages[currentType] = collectPackagesSynchronously(moduleInfo, currentType, currentPackages);
171
249
  if (currentType === "requiredAny" && resolvedPackages[currentType].filter((thePackage) => thePackage !== void 0).length === 0) warnMissingPackages(moduleInfo, currentPackages, currentType);
172
250
  }
173
251
  return type === void 0 ? resolvedPackages : resolvedPackages[type] ?? [];
@@ -180,13 +258,31 @@ const isModuleEnabledByDefault = (moduleInfo) => {
180
258
  let isEnabled = false;
181
259
  for (const [currentType, currentPackages] of objectEntries(packages)) {
182
260
  if (currentType === "optional") continue;
183
- if (currentPackages && currentPackages.length > 0) isEnabled = currentType === "requiredAll" ? doAllPackagesExist(currentPackages) : doesAnyPackageExist(currentPackages);
261
+ if (currentPackages && currentPackages.length > 0) isEnabled = (currentType === "requiredAll" ? doAllPackagesExist : doesAnyPackageExist)(currentPackages);
184
262
  if (isEnabled) break;
185
263
  }
186
264
  return isEnabled;
187
265
  };
266
+ const createModuleState = () => {
267
+ const enabledStates = {};
268
+ return {
269
+ isModuleEnabled: (moduleInfo) => enabledStates[moduleInfo.name] ?? isModuleEnabledByDefault(moduleInfo),
270
+ setModuleEnabled: (moduleInfo, isEnabled) => {
271
+ enabledStates[moduleInfo.name] = isEnabled;
272
+ }
273
+ };
274
+ };
188
275
  //#endregion
189
276
  //#region ../src/js/shared/utils/globs.ts
277
+ /**
278
+ * @internal @brnshkr/config
279
+ */
280
+ const GLOB_TEST_FILES = [
281
+ "**/__tests__/**/*.?(c|m)[jt]s?(x)",
282
+ "**/*.spec.?(c|m)[jt]s?(x)",
283
+ "**/*.test.?(c|m)[jt]s?(x)"
284
+ ];
285
+ const GLOB_BENCHMARK_FILES = ["**/*.bench.?(c|m)[jt]s?(x)", "**/*.benchmark.?(c|m)[jt]s?(x)"];
190
286
  const GLOB_IGNORES = [
191
287
  "**/.cache/**",
192
288
  "**/.changeset/**",
@@ -195,6 +291,7 @@ const GLOB_IGNORES = [
195
291
  "**/.hg/store/**",
196
292
  "**/.history/**",
197
293
  "**/.idea/**",
294
+ "**/.local/**",
198
295
  "**/.next/**",
199
296
  "**/.nuxt/**",
200
297
  "**/.output/**",
@@ -204,7 +301,9 @@ const GLOB_IGNORES = [
204
301
  "**/.vercel/**",
205
302
  "**/.vite-inspect/**",
206
303
  "**/.vitepress/cache/**",
304
+ "**/.vitest/**",
207
305
  "**/.yarn/**",
306
+ "**/__generated__/**",
208
307
  "**/__snapshots__/**",
209
308
  "**/*.code-search",
210
309
  "**/*.example",
@@ -219,13 +318,256 @@ const GLOB_IGNORES = [
219
318
  "**/package-lock.json",
220
319
  "**/pnpm-lock.yaml",
221
320
  "**/temp/**",
321
+ "**/tests/**/[Ff]ixture?(s)/**",
222
322
  "**/tmp/**",
223
323
  "**/vendor/**",
224
324
  "**/vite.config.*.timestamp-*",
225
325
  "**/yarn.lock"
226
326
  ];
227
327
  //#endregion
328
+ //#region ../src/js/shared/utils/filesystem.ts
329
+ /**
330
+ * @internal @brnshkr/config
331
+ */
332
+ const toPosix = (value) => value.replaceAll("\\", "/");
333
+ const doesFileExist = (filePath) => {
334
+ try {
335
+ fs.accessSync(filePath, fs.constants.R_OK);
336
+ return true;
337
+ } catch {
338
+ return false;
339
+ }
340
+ };
341
+ const getMtime = (filePath) => {
342
+ try {
343
+ return fs.statSync(filePath).mtimeMs;
344
+ } catch {
345
+ return;
346
+ }
347
+ };
348
+ const readTextFile = (filePath) => {
349
+ try {
350
+ return fs.readFileSync(filePath, "utf-8");
351
+ } catch {
352
+ return;
353
+ }
354
+ };
355
+ const readJsonFile = (filePath) => {
356
+ const content = readTextFile(filePath);
357
+ if (content === void 0) return;
358
+ try {
359
+ return JSON.parse(content);
360
+ } catch {
361
+ return;
362
+ }
363
+ };
364
+ const readJsonObjectFile = (filePath) => {
365
+ const parsedContent = readJsonFile(filePath);
366
+ return isPlainObject(parsedContent) ? parsedContent : void 0;
367
+ };
368
+ const findNearestPackageJson = (startDirectory) => {
369
+ let current = path.resolve(startDirectory);
370
+ let parent = path.dirname(current);
371
+ while (current !== parent) {
372
+ const candidate = path.join(current, "package.json");
373
+ if (doesFileExist(candidate)) return candidate;
374
+ current = parent;
375
+ parent = path.dirname(current);
376
+ }
377
+ };
378
+ //#endregion
228
379
  //#region ../src/js/shared/utils/constants.ts
229
380
  const QUOTES = "single";
230
381
  //#endregion
231
- export { resolvePackagesSharedSynchronously as a, objectAssign as c, packageOrganization as d, packageOrganizationUpper as f, resolvePackagesSharedAsynchronously as i, objectEntries as l, GLOB_IGNORES as n, ESLINT_PACKAGES as o, version as p, isModuleEnabledByDefault as r, STYLELINT_PACKAGES as s, QUOTES as t, objectFromEntries as u };
382
+ //#region ../src/js/spelling/utils/files.ts
383
+ /**
384
+ * @internal @brnshkr/config/spelling
385
+ */
386
+ const COMMAND_TIMEOUT_MILLISECONDS = 6e4;
387
+ const listTrackedFiles = (rootDirectory) => {
388
+ return execFileSync("git", ["ls-files", "-z"], {
389
+ cwd: rootDirectory,
390
+ encoding: "utf-8",
391
+ maxBuffer: Infinity,
392
+ timeout: COMMAND_TIMEOUT_MILLISECONDS
393
+ }).split("\0").filter((filePath) => filePath !== "");
394
+ };
395
+ const isScannedPath = (filePath, scannedExtensions, scannedNames, ignoreExpressions) => {
396
+ if (ignoreExpressions.some((ignoreExpression) => ignoreExpression.test(filePath))) return false;
397
+ return scannedNames.has(path.basename(filePath)) || scannedExtensions.has(path.extname(filePath));
398
+ };
399
+ const collectFilePaths = (rootDirectory, settings) => {
400
+ const scannedExtensions = new Set(settings.fileExtensions);
401
+ const scannedNames = new Set(settings.fileNames);
402
+ const ignoreExpressions = settings.ignorePatterns.map((ignorePattern) => new RegExp(ignorePattern, "u"));
403
+ return listTrackedFiles(rootDirectory).filter((filePath) => isScannedPath(filePath, scannedExtensions, scannedNames, ignoreExpressions)).filter((filePath) => doesFileExist(path.join(rootDirectory, filePath)));
404
+ };
405
+ //#endregion
406
+ //#region ../src/js/spelling/utils/patterns.ts
407
+ /**
408
+ * @internal @brnshkr/config/spelling
409
+ */
410
+ const buildPatterns = (settings) => {
411
+ const stemSuffixGroup = `(?:${settings.stemSuffixes.join("|")})`;
412
+ return [...objectEntries(settings.britishSpellings).map(([britishSpelling, americanSpelling]) => ({
413
+ pattern: new RegExp(String.raw`\b${britishSpelling}\w*`, "giv"),
414
+ toAmericanSpelling: (word) => americanSpelling + word.slice(britishSpelling.length)
415
+ })), ...settings.britishStems.map((britishStem) => ({
416
+ pattern: new RegExp(String.raw`\b${britishStem}${stemSuffixGroup}\b`, "giv"),
417
+ toAmericanSpelling: (word) => `${britishStem.slice(0, -1)}z${word.slice(britishStem.length)}`
418
+ }))];
419
+ };
420
+ //#endregion
421
+ //#region ../src/js/spelling/utils/settings.ts
422
+ /**
423
+ * @internal @brnshkr/config/spelling
424
+ */
425
+ const EVERY_PATH$1 = "*";
426
+ const DEFAULTS_FILE = "defaults.json";
427
+ const LINE_SUFFIX_PATTERN = /^(?<text>.*):(?<lineNumbers>\d+(?:,\d+)*)$/v;
428
+ const findShippedDirectory = () => {
429
+ const packageJsonPath = findNearestPackageJson(import.meta.dirname);
430
+ if (packageJsonPath === void 0) throw new Error("Unable to locate the shipped defaults of \"@brnshkr/config\".");
431
+ return path.join(path.dirname(packageJsonPath), "conf", "spelling");
432
+ };
433
+ const readSettingsFile = (filePath) => {
434
+ const settings = readJsonObjectFile(filePath);
435
+ if (settings === void 0) throw new Error(`Unable to read "${filePath}".`);
436
+ return settings;
437
+ };
438
+ const readStringList = (settings, settingName) => {
439
+ const declaredSetting = settings[settingName];
440
+ return Array.isArray(declaredSetting) ? declaredSetting.filter((entry) => typeof entry === "string") : [];
441
+ };
442
+ const readStringMap = (settings, settingName) => {
443
+ const declaredSetting = settings[settingName];
444
+ const stringMap = {};
445
+ if (!isPlainObject(declaredSetting)) return stringMap;
446
+ for (const [settingKey, entry] of objectEntries(declaredSetting)) if (typeof entry === "string") stringMap[settingKey] = entry;
447
+ return stringMap;
448
+ };
449
+ const readStringListMap = (settings, settingName) => {
450
+ const declaredSetting = settings[settingName];
451
+ const stringListMap = {};
452
+ if (!isPlainObject(declaredSetting)) return stringListMap;
453
+ for (const settingKey of objectKeys(declaredSetting)) stringListMap[settingKey] = readStringList(declaredSetting, settingKey);
454
+ return stringListMap;
455
+ };
456
+ const toSettings = (settings) => ({
457
+ fileExtensions: readStringList(settings, "fileExtensions"),
458
+ fileNames: readStringList(settings, "fileNames"),
459
+ ignorePatterns: readStringList(settings, "ignorePatterns"),
460
+ britishSpellings: readStringMap(settings, "britishSpellings"),
461
+ britishStems: readStringList(settings, "britishStems"),
462
+ stemSuffixes: readStringList(settings, "stemSuffixes"),
463
+ allowlist: readStringListMap(settings, "allowlist")
464
+ });
465
+ const mergeLists = (shippedEntries, declaredEntries) => [.../* @__PURE__ */ new Set([...shippedEntries, ...declaredEntries])];
466
+ const mergeSettings = (shippedSettings, declaredSettings) => ({
467
+ fileExtensions: mergeLists(shippedSettings.fileExtensions, declaredSettings.fileExtensions),
468
+ fileNames: mergeLists(shippedSettings.fileNames, declaredSettings.fileNames),
469
+ ignorePatterns: mergeLists(shippedSettings.ignorePatterns, declaredSettings.ignorePatterns),
470
+ britishSpellings: {
471
+ ...shippedSettings.britishSpellings,
472
+ ...declaredSettings.britishSpellings
473
+ },
474
+ britishStems: mergeLists(shippedSettings.britishStems, declaredSettings.britishStems),
475
+ stemSuffixes: mergeLists(shippedSettings.stemSuffixes, declaredSettings.stemSuffixes),
476
+ allowlist: {
477
+ ...shippedSettings.allowlist,
478
+ ...declaredSettings.allowlist
479
+ }
480
+ });
481
+ const readSettings = (rootDirectory, configPath) => {
482
+ const shippedPath = path.join(findShippedDirectory(), DEFAULTS_FILE);
483
+ const shippedSettings = toSettings(readSettingsFile(shippedPath));
484
+ const settingsPath = path.resolve(rootDirectory, configPath);
485
+ return doesFileExist(settingsPath) ? mergeSettings(shippedSettings, toSettings(readSettingsFile(settingsPath))) : shippedSettings;
486
+ };
487
+ const parseAllowedLiterals = (declaredLiterals) => declaredLiterals.map((declaredLiteral) => {
488
+ const matchedGroups = LINE_SUFFIX_PATTERN.exec(declaredLiteral)?.groups;
489
+ return {
490
+ text: matchedGroups?.["text"] ?? declaredLiteral,
491
+ lineNumbers: matchedGroups === void 0 ? void 0 : (matchedGroups["lineNumbers"] ?? "").split(",").map(Number)
492
+ };
493
+ });
494
+ const isCoveredBy = (coveringCandidate, allowedLiteral) => {
495
+ if (coveringCandidate.text.toLowerCase() !== allowedLiteral.text.toLowerCase()) return false;
496
+ if (coveringCandidate.lineNumbers === void 0) return true;
497
+ return allowedLiteral.lineNumbers?.every((lineNumber) => coveringCandidate.lineNumbers?.includes(lineNumber) === true) ?? false;
498
+ };
499
+ const formatAllowedLiteral = ({ text, lineNumbers }) => lineNumbers === void 0 ? text : `${text}:${lineNumbers.join(",")}`;
500
+ const rejectCoveredLiterals = (allowlist) => {
501
+ const literalsAllowedEverywhere = allowlist[EVERY_PATH$1] ?? [];
502
+ for (const [allowedPath, allowedLiterals] of objectEntries(allowlist)) for (const [index, allowedLiteral] of allowedLiterals.entries()) {
503
+ const coveringLiteral = [...allowedPath === EVERY_PATH$1 ? [] : literalsAllowedEverywhere, ...allowedLiterals.slice(0, index)].find((coveringCandidate) => isCoveredBy(coveringCandidate, allowedLiteral));
504
+ if (coveringLiteral !== void 0) throw new Error(`Allowed word "${formatAllowedLiteral(allowedLiteral)}" under "${allowedPath}" is already covered by "${formatAllowedLiteral(coveringLiteral)}".`);
505
+ }
506
+ };
507
+ const readAllowlist = (settings) => {
508
+ const allowlist = {};
509
+ const allowlistEntries = objectEntries(settings.allowlist ?? {});
510
+ for (const [allowedPath, declaredLiterals] of allowlistEntries) allowlist[allowedPath] = parseAllowedLiterals(declaredLiterals);
511
+ rejectCoveredLiterals(allowlist);
512
+ return allowlist;
513
+ };
514
+ //#endregion
515
+ //#region ../src/js/spelling/index.ts
516
+ const EVERY_PATH = "*";
517
+ const DEFAULT_CONFIG_PATH = "conf/spelling.json";
518
+ const REGEX_METACHARACTERS = /[$\(\)*+.?\[\\\]^\{\|\}]/gv;
519
+ const escapeRegexLiteral = (value) => value.replaceAll(REGEX_METACHARACTERS, String.raw`\$&`);
520
+ const maskAllowedLiterals = (line, allowedLiterals, lineNumber) => {
521
+ let maskedLine = line;
522
+ for (const { text, lineNumbers } of allowedLiterals) if (lineNumbers === void 0 || lineNumbers.includes(lineNumber)) maskedLine = maskedLine.replaceAll(new RegExp(escapeRegexLiteral(text), "giu"), (matchedText) => ".".repeat(matchedText.length));
523
+ return maskedLine;
524
+ };
525
+ const compareCaseInsensitively = (firstValue, secondValue) => {
526
+ const firstLowercased = firstValue.toLowerCase();
527
+ const secondLowercased = secondValue.toLowerCase();
528
+ if (firstLowercased !== secondLowercased) return firstLowercased < secondLowercased ? -1 : 1;
529
+ return firstValue < secondValue ? -1 : Number(firstValue > secondValue);
530
+ };
531
+ const compareFindings = (firstFinding, secondFinding) => {
532
+ const pathOrder = compareCaseInsensitively(firstFinding.path, secondFinding.path);
533
+ if (pathOrder !== 0) return pathOrder;
534
+ return firstFinding.line === secondFinding.line ? compareCaseInsensitively(firstFinding.word, secondFinding.word) : firstFinding.line - secondFinding.line;
535
+ };
536
+ const findInFile = (fileContents, filePath, patterns, allowlist) => {
537
+ const allowedLiterals = [...allowlist[EVERY_PATH] ?? [], ...allowlist[filePath] ?? []];
538
+ const findings = [];
539
+ for (const [index, line] of fileContents.split("\n").entries()) {
540
+ const lineNumber = index + 1;
541
+ const maskedLine = maskAllowedLiterals(line, allowedLiterals, lineNumber);
542
+ for (const { pattern, toAmericanSpelling } of patterns) for (const [word] of maskedLine.matchAll(pattern)) findings.push({
543
+ path: filePath,
544
+ line: lineNumber,
545
+ word,
546
+ suggestion: toAmericanSpelling(word)
547
+ });
548
+ }
549
+ return findings;
550
+ };
551
+ /**
552
+ * Report every British spelling in a repository's tracked prose, docblocks and identifiers.
553
+ *
554
+ * @api
555
+ *
556
+ * @param options where to scan, which settings to read, and which files to look at
557
+ *
558
+ * @returns every finding, sorted by path, line and word
559
+ *
560
+ * @example
561
+ * ```js
562
+ * scan({ rootDirectory: process.cwd() });
563
+ * ```
564
+ */
565
+ const scan = (options) => {
566
+ const { rootDirectory = process.cwd(), configPath = DEFAULT_CONFIG_PATH, paths } = options ?? {};
567
+ const settings = readSettings(rootDirectory, configPath);
568
+ const allowlist = readAllowlist(settings);
569
+ const patterns = buildPatterns(settings);
570
+ return (paths ?? collectFilePaths(rootDirectory, settings)).map((filePath) => findInFile(readTextFile(path.join(rootDirectory, filePath)) ?? "", filePath, patterns, allowlist)).flat().toSorted(compareFindings);
571
+ };
572
+ //#endregion
573
+ export { name as A, objectAssign as C, objectValues as D, objectKeys as E, packageOrganization as O, isPlainObject as S, objectFromEntries as T, COMMITLINT_PACKAGES as _, getMtime as a, STYLELINT_PACKAGES as b, toPosix as c, GLOB_TEST_FILES as d, createModuleState as f, resolvePackagesSharedSynchronously as g, resolvePackagesSharedAsynchronously as h, findNearestPackageJson as i, version as j, packageOrganizationUpper as k, GLOB_BENCHMARK_FILES as l, isModuleEnabledByDefault as m, QUOTES as n, readJsonObjectFile as o, doAllPackagesExist as p, doesFileExist as r, readTextFile as s, scan as t, GLOB_IGNORES as u, ESLINT_PACKAGES as v, objectEntries as w, VITEST_PACKAGES as x, MARKDOWNLINT_PACKAGES as y };
@@ -0,0 +1,30 @@
1
+ //#region ../src/js/spelling/types/options.d.ts
2
+ interface SpellingOptions {
3
+ rootDirectory?: string;
4
+ configPath?: string;
5
+ paths?: string[];
6
+ }
7
+ interface SpellingFinding {
8
+ path: string;
9
+ line: number;
10
+ word: string;
11
+ suggestion: string;
12
+ }
13
+ //#endregion
14
+ //#region ../src/js/spelling/index.d.ts
15
+ /**
16
+ * Report every British spelling in a repository's tracked prose, docblocks and identifiers.
17
+ *
18
+ * @api
19
+ *
20
+ * @param options where to scan, which settings to read, and which files to look at
21
+ *
22
+ * @returns every finding, sorted by path, line and word
23
+ *
24
+ * @example
25
+ * ```js
26
+ * scan({ rootDirectory: process.cwd() });
27
+ * ```
28
+ */
29
+ export declare const scan: (options?: Partial<SpellingOptions>) => SpellingFinding[];
30
+ //#endregion
@@ -0,0 +1,2 @@
1
+ import { t as scan } from "../shared.mjs";
2
+ export { scan };
@@ -0,0 +1 @@
1
+ export {}
@@ -0,0 +1,12 @@
1
+ import { t as scan } from "../shared.mjs";
2
+ import { expect, test } from "vitest";
3
+ //#region ../src/js/spelling/spelling.test.ts
4
+ /**
5
+ * @internal @brnshkr/config/spelling
6
+ */
7
+ test("every tracked file is written in american english", () => {
8
+ const findings = scan().map(({ path, line, word, suggestion }) => `${path}:${String(line)} — "${word}", use "${suggestion}"`);
9
+ expect(findings).toStrictEqual([]);
10
+ });
11
+ //#endregion
12
+ export {};