@brnshkr/config 0.0.1-beta.2 → 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 +177 -228
  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 +3897 -1383
  20. package/dist/eslint/index.mjs +2784 -419
  21. package/dist/markdownlint/index.d.mts +2304 -0
  22. package/dist/markdownlint/index.mjs +440 -0
  23. package/dist/shared.mjs +405 -65
  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 -29
  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,38 +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.2";
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
19
27
  const objectKeys = (object) => Object.keys(object);
28
+ const objectValues = (object) => Object.values(object);
20
29
  const objectEntries = (object) => Object.entries(object);
21
30
  const objectFromEntries = (entries) => Object.fromEntries(entries);
22
- const objectFreeze = (object) => Object.freeze(object);
23
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
+ };
24
36
  //#endregion
25
37
  //#region ../src/js/shared/utils/interop-import.ts
26
38
  const interopImport = async (thePackage) => {
27
39
  const resolved = await thePackage;
28
- return typeof resolved === "object" && resolved !== null && "default" in resolved ? resolved.default : resolved;
40
+ return typeof resolved === "object" && resolved && "default" in resolved ? resolved.default : resolved;
29
41
  };
30
42
  //#endregion
31
43
  //#region ../src/js/shared/utils/package-resolvers.ts
44
+ /**
45
+ * @internal @brnshkr/config
46
+ */
32
47
  const ESLINT_PACKAGES = {
33
48
  ESLINT_CSS: "@eslint/css",
34
- ESLINT_FLAT_CONFIG_GITIGNORE: "eslint-config-flat-gitignore",
35
- ESLINT_IMPORT_RESOVLER_TYPESCRIPT: "eslint-import-resolver-typescript",
49
+ ESLINT_IMPORT_RESOLVER_TYPESCRIPT: "eslint-import-resolver-typescript",
36
50
  ESLINT_JSON: "@eslint/json",
37
51
  ESLINT_MARKDOWN: "@eslint/markdown",
38
52
  ESLINT_MERGE_PROCESSORS: "eslint-merge-processors",
@@ -52,47 +66,74 @@ const ESLINT_PACKAGES = {
52
66
  ESLINT_PLUGIN_UNUSED_IMPORTS: "eslint-plugin-unused-imports",
53
67
  ESLINT_PLUGIN_YML: "eslint-plugin-yml",
54
68
  SVELTE: "svelte",
69
+ TAILWINDCSS: "tailwindcss",
55
70
  TYPESCRIPT: "typescript",
56
71
  TYPESCRIPT_ESLINT: "typescript-eslint",
57
72
  VITEST_ESLINT_PLUGIN: "@vitest/eslint-plugin"
58
73
  };
59
74
  const ESLINT_PACKAGE_RESOLVERS = {
60
- [ESLINT_PACKAGES.ESLINT_CSS]: async () => await interopImport(import("@eslint/css")),
61
- [ESLINT_PACKAGES.ESLINT_FLAT_CONFIG_GITIGNORE]: async () => await interopImport(import("eslint-config-flat-gitignore")),
62
- [ESLINT_PACKAGES.ESLINT_JSON]: async () => await interopImport(import("@eslint/json")),
63
- [ESLINT_PACKAGES.ESLINT_IMPORT_RESOVLER_TYPESCRIPT]: async () => await interopImport(import("eslint-import-resolver-typescript")),
64
- [ESLINT_PACKAGES.ESLINT_PLUGIN_ANTFU]: async () => await interopImport(import("eslint-plugin-antfu")),
65
- [ESLINT_PACKAGES.ESLINT_PLUGIN_ESLINT_COMMENTS]: async () => await interopImport(import("@eslint-community/eslint-plugin-eslint-comments")),
66
- [ESLINT_PACKAGES.ESLINT_PLUGIN_IMPORT_X]: async () => await interopImport(import("eslint-plugin-import-x")),
67
- [ESLINT_PACKAGES.ESLINT_PLUGIN_JSDOC]: async () => await interopImport(import("eslint-plugin-jsdoc")),
68
- [ESLINT_PACKAGES.ESLINT_PLUGIN_JSONC]: async () => await interopImport(import("eslint-plugin-jsonc")),
69
- [ESLINT_PACKAGES.ESLINT_PLUGIN_JSDOC_PROCESSOR]: async () => await interopImport(import("eslint-plugin-jsdoc/getJsdocProcessorPlugin.js")),
70
- [ESLINT_PACKAGES.ESLINT_MARKDOWN]: async () => await interopImport(import("@eslint/markdown")),
71
- [ESLINT_PACKAGES.ESLINT_MERGE_PROCESSORS]: async () => await interopImport(import("eslint-merge-processors")),
72
- [ESLINT_PACKAGES.ESLINT_PLUGIN_N]: async () => await interopImport(import("eslint-plugin-n")),
73
- [ESLINT_PACKAGES.ESLINT_PLUGIN_PERFECTIONIST]: async () => await interopImport(import("eslint-plugin-perfectionist")),
74
- [ESLINT_PACKAGES.ESLINT_PLUGIN_REGEXP]: async () => await interopImport(import("eslint-plugin-regexp")),
75
- [ESLINT_PACKAGES.ESLINT_PLUGIN_STYLISTIC]: async () => await interopImport(import("@stylistic/eslint-plugin")),
76
- [ESLINT_PACKAGES.ESLINT_PLUGIN_SVELTE]: async () => await interopImport(import("eslint-plugin-svelte")),
77
- [ESLINT_PACKAGES.ESLINT_PLUGIN_TOML]: async () => await interopImport(import("eslint-plugin-toml")),
78
- [ESLINT_PACKAGES.ESLINT_PLUGIN_UNICORN]: async () => await interopImport(import("eslint-plugin-unicorn")),
79
- [ESLINT_PACKAGES.ESLINT_PLUGIN_UNUSED_IMPORTS]: async () => await interopImport(import("eslint-plugin-unused-imports")),
80
- [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")),
81
95
  [ESLINT_PACKAGES.SVELTE]: () => isPackageExists(ESLINT_PACKAGES.SVELTE),
96
+ [ESLINT_PACKAGES.TAILWINDCSS]: () => isPackageExists(ESLINT_PACKAGES.TAILWINDCSS),
82
97
  [ESLINT_PACKAGES.TYPESCRIPT]: () => isPackageExists(ESLINT_PACKAGES.TYPESCRIPT),
83
- [ESLINT_PACKAGES.TYPESCRIPT_ESLINT]: async () => await interopImport(import("typescript-eslint")),
84
- [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)
85
126
  };
86
127
  const STYLELINT_PACKAGES = {
87
128
  POSTCSS_HTML: "postcss-html",
88
129
  STYLELINT_CONFIG_CSS_MODULES: "stylelint-config-css-modules",
89
130
  STYLELINT_CONFIG_HTML: "stylelint-config-html",
90
131
  STYLELINT_CONFIG_RECESS_ORDER: "stylelint-config-recess-order",
132
+ STYLELINT_CONFIG_STANDARD_LESS: "stylelint-config-standard-less",
91
133
  STYLELINT_CONFIG_STANDARD_SCSS: "stylelint-config-standard-scss",
92
134
  STYLELINT_DECLARATION_STRICT_VALUE: "stylelint-declaration-strict-value",
93
135
  STYLELINT_ORDER: "stylelint-order",
94
136
  STYLELINT_PLUGIN_DEFENSIVE_CSS: "stylelint-plugin-defensive-css",
95
- STYLELINT_PLUGIN_LOGICAL_CSS: "stylelint-plugin-logical-css",
96
137
  STYLELINT_PLUGIN_USE_BASELINE: "stylelint-plugin-use-baseline",
97
138
  STYLELINT_USE_NESTING: "stylelint-use-nesting",
98
139
  STYLISTIC_STYLELINT_CONFIG: "@stylistic/stylelint-config"
@@ -102,51 +143,98 @@ const STYLELINT_PACKAGE_RESOLVERS = {
102
143
  [STYLELINT_PACKAGES.STYLELINT_CONFIG_CSS_MODULES]: () => isPackageExists(STYLELINT_PACKAGES.STYLELINT_CONFIG_CSS_MODULES),
103
144
  [STYLELINT_PACKAGES.STYLELINT_CONFIG_HTML]: () => isPackageExists(STYLELINT_PACKAGES.STYLELINT_CONFIG_HTML),
104
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),
105
147
  [STYLELINT_PACKAGES.STYLELINT_CONFIG_STANDARD_SCSS]: () => isPackageExists(STYLELINT_PACKAGES.STYLELINT_CONFIG_STANDARD_SCSS),
106
148
  [STYLELINT_PACKAGES.STYLELINT_DECLARATION_STRICT_VALUE]: () => isPackageExists(STYLELINT_PACKAGES.STYLELINT_DECLARATION_STRICT_VALUE),
107
149
  [STYLELINT_PACKAGES.STYLELINT_ORDER]: () => isPackageExists(STYLELINT_PACKAGES.STYLELINT_ORDER),
108
150
  [STYLELINT_PACKAGES.STYLELINT_PLUGIN_DEFENSIVE_CSS]: () => isPackageExists(STYLELINT_PACKAGES.STYLELINT_PLUGIN_DEFENSIVE_CSS),
109
- [STYLELINT_PACKAGES.STYLELINT_PLUGIN_LOGICAL_CSS]: () => isPackageExists(STYLELINT_PACKAGES.STYLELINT_PLUGIN_LOGICAL_CSS),
110
151
  [STYLELINT_PACKAGES.STYLELINT_PLUGIN_USE_BASELINE]: () => isPackageExists(STYLELINT_PACKAGES.STYLELINT_PLUGIN_USE_BASELINE),
111
152
  [STYLELINT_PACKAGES.STYLELINT_USE_NESTING]: () => isPackageExists(STYLELINT_PACKAGES.STYLELINT_USE_NESTING),
112
153
  [STYLELINT_PACKAGES.STYLISTIC_STYLELINT_CONFIG]: () => isPackageExists(STYLELINT_PACKAGES.STYLISTIC_STYLELINT_CONFIG)
113
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
+ };
114
165
  //#endregion
115
166
  //#region ../src/js/shared/utils/string.ts
167
+ /**
168
+ * @internal @brnshkr/config
169
+ */
116
170
  const joinAsQuotedList = (strings, type = "conjunction") => strings.length > 1 ? `"${strings.slice(0, -1).join("\", \"")}" ${type === "conjunction" ? "and" : "or"} "${strings.slice(-1).join("")}"` : {
117
171
  0: "",
118
172
  1: `"${strings[0] ?? ""}"`
119
173
  }[strings.length] ?? "";
120
174
  //#endregion
121
175
  //#region ../src/js/shared/utils/module.ts
176
+ /**
177
+ * @internal @brnshkr/config
178
+ */
122
179
  const PACKAGE_RESOLVERS = {
180
+ ...COMMITLINT_PACKAGE_RESOLVERS,
123
181
  ...ESLINT_PACKAGE_RESOLVERS,
124
- ...STYLELINT_PACKAGE_RESOLVERS
182
+ ...MARKDOWNLINT_PACKAGE_RESOLVERS,
183
+ ...STYLELINT_PACKAGE_RESOLVERS,
184
+ ...VITEST_PACKAGE_RESOLVERS
125
185
  };
126
186
  const warnMissingPackages = (moduleInfo, packages, type) => {
127
- 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.`);
128
- 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.`);
129
189
  };
130
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
+ };
131
231
  const resolvePackagesSharedAsynchronously = async (moduleInfo, type) => {
132
232
  if (moduleInfo.packages === void 0) return {};
133
233
  const resolvedPackages = {};
134
234
  const packages = type === void 0 ? moduleInfo.packages : { [type]: moduleInfo.packages[type] };
135
235
  for (const [currentType, currentPackages] of objectEntries(packages)) {
136
236
  if (!currentPackages || currentPackages.length === 0) continue;
137
- resolvedPackages[currentType] ??= [];
138
- for (const currentPackage of currentPackages) try {
139
- const resolvedPackage = packageCache[currentPackage] ?? await PACKAGE_RESOLVERS[currentPackage]();
140
- packageCache[currentPackage] ??= resolvedPackage;
141
- if (resolvedPackage === false) throw new Error("Skip to catch block.");
142
- resolvedPackages[currentType].push(resolvedPackage);
143
- } catch {
144
- if (currentType === "requiredAll") {
145
- warnMissingPackages(moduleInfo, currentPackages, currentType);
146
- break;
147
- }
148
- resolvedPackages[currentType].push(void 0);
149
- }
237
+ resolvedPackages[currentType] = await collectPackagesAsynchronously(moduleInfo, currentType, currentPackages);
150
238
  if (currentType === "requiredAny" && resolvedPackages[currentType].filter((thePackage) => thePackage !== void 0).length === 0) warnMissingPackages(moduleInfo, currentPackages, currentType);
151
239
  }
152
240
  return type === void 0 ? resolvedPackages : resolvedPackages[type] ?? [];
@@ -157,19 +245,7 @@ const resolvePackagesSharedSynchronously = (moduleInfo, type) => {
157
245
  const packages = type === void 0 ? moduleInfo.packages : { [type]: moduleInfo.packages[type] };
158
246
  for (const [currentType, currentPackages] of objectEntries(packages)) {
159
247
  if (!currentPackages || currentPackages.length === 0) continue;
160
- resolvedPackages[currentType] ??= [];
161
- for (const currentPackage of currentPackages) try {
162
- const resolvedPackage = packageCache[currentPackage] ?? PACKAGE_RESOLVERS[currentPackage]();
163
- packageCache[currentPackage] ??= resolvedPackage;
164
- if (resolvedPackage === false) throw new Error("Skip to catch block.");
165
- resolvedPackages[currentType].push(resolvedPackage);
166
- } catch {
167
- if (currentType === "requiredAll") {
168
- warnMissingPackages(moduleInfo, currentPackages, currentType);
169
- break;
170
- }
171
- resolvedPackages[currentType].push(void 0);
172
- }
248
+ resolvedPackages[currentType] = collectPackagesSynchronously(moduleInfo, currentType, currentPackages);
173
249
  if (currentType === "requiredAny" && resolvedPackages[currentType].filter((thePackage) => thePackage !== void 0).length === 0) warnMissingPackages(moduleInfo, currentPackages, currentType);
174
250
  }
175
251
  return type === void 0 ? resolvedPackages : resolvedPackages[type] ?? [];
@@ -182,13 +258,31 @@ const isModuleEnabledByDefault = (moduleInfo) => {
182
258
  let isEnabled = false;
183
259
  for (const [currentType, currentPackages] of objectEntries(packages)) {
184
260
  if (currentType === "optional") continue;
185
- if (currentPackages && currentPackages.length > 0) isEnabled = currentType === "requiredAll" ? doAllPackagesExist(currentPackages) : doesAnyPackageExist(currentPackages);
261
+ if (currentPackages && currentPackages.length > 0) isEnabled = (currentType === "requiredAll" ? doAllPackagesExist : doesAnyPackageExist)(currentPackages);
186
262
  if (isEnabled) break;
187
263
  }
188
264
  return isEnabled;
189
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
+ };
190
275
  //#endregion
191
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)"];
192
286
  const GLOB_IGNORES = [
193
287
  "**/.cache/**",
194
288
  "**/.changeset/**",
@@ -197,6 +291,7 @@ const GLOB_IGNORES = [
197
291
  "**/.hg/store/**",
198
292
  "**/.history/**",
199
293
  "**/.idea/**",
294
+ "**/.local/**",
200
295
  "**/.next/**",
201
296
  "**/.nuxt/**",
202
297
  "**/.output/**",
@@ -206,7 +301,9 @@ const GLOB_IGNORES = [
206
301
  "**/.vercel/**",
207
302
  "**/.vite-inspect/**",
208
303
  "**/.vitepress/cache/**",
304
+ "**/.vitest/**",
209
305
  "**/.yarn/**",
306
+ "**/__generated__/**",
210
307
  "**/__snapshots__/**",
211
308
  "**/*.code-search",
212
309
  "**/*.example",
@@ -221,13 +318,256 @@ const GLOB_IGNORES = [
221
318
  "**/package-lock.json",
222
319
  "**/pnpm-lock.yaml",
223
320
  "**/temp/**",
321
+ "**/tests/**/[Ff]ixture?(s)/**",
224
322
  "**/tmp/**",
225
323
  "**/vendor/**",
226
324
  "**/vite.config.*.timestamp-*",
227
325
  "**/yarn.lock"
228
326
  ];
229
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
230
379
  //#region ../src/js/shared/utils/constants.ts
231
380
  const QUOTES = "single";
232
381
  //#endregion
233
- export { resolvePackagesSharedSynchronously as a, objectAssign as c, objectFromEntries as d, objectKeys as f, version as h, resolvePackagesSharedAsynchronously as i, objectEntries as l, packageOrganizationUpper as m, GLOB_IGNORES as n, ESLINT_PACKAGES as o, packageOrganization as p, isModuleEnabledByDefault as r, STYLELINT_PACKAGES as s, QUOTES as t, objectFreeze 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 {};