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

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -190,7 +190,7 @@ bun eslint --config ./conf/eslint.config.mjs --cache --cache-location ./.cache/e
190
190
  Example call, adjust as needed
191
191
 
192
192
  ```sh
193
- bun stylelint --config ./conf/stylelint.config.mjs --cache --cache-location ./.cache/stylelint.cache.json **/*.{css,ejs,html,less,postcss,scss,svelte,svg,vue}
193
+ bun stylelint --config ./conf/stylelint.config.mjs --config-basedir ./ --cache --cache-location ./.cache/stylelint.cache.json **/*.{css,ejs,html,less,postcss,scss,svelte,svg,vue}
194
194
  ```
195
195
 
196
196
  <!-- omit in toc -->
@@ -275,7 +275,7 @@ Here are some frequently used examples:
275
275
  #### Composer
276
276
 
277
277
  ```sh
278
- composer req --dev brnshkr/config
278
+ composer r --dev brnshkr/config
279
279
  ```
280
280
 
281
281
  This repository provides two ways to integrate configuration files and setup tools into your project:
@@ -336,6 +336,15 @@ cp -v ./vendor/brnshkr/config/conf/phpstan.php.example ./conf/phpstan.php \
336
336
  && cp -v ./vendor/brnshkr/config/conf/phpstan.dist.php.example ./conf/phpstan.dist.php
337
337
  ```
338
338
 
339
+ <!-- omit in toc -->
340
+ ##### Twig CS Fixer
341
+
342
+ ```sh
343
+ cp -v ./vendor/brnshkr/config/conf/twig-cs-fixer.php.example ./conf/twig-cs-fixer.php \
344
+ && cp -v ./vendor/brnshkr/config/conf/twig-cs-fixer.php.example ./conf/twig-cs-fixer.php.example \
345
+ && cp -v ./vendor/brnshkr/config/conf/twig-cs-fixer.dist.php.example ./conf/twig-cs-fixer.dist.php
346
+ ```
347
+
339
348
  <!-- omit in toc -->
340
349
  ##### Makefile
341
350
 
@@ -363,6 +372,9 @@ cp -v ./vendor/brnshkr/config/conf/php-cs-fixer.php.example ./conf/php-cs-fixer.
363
372
  && cp -v ./vendor/brnshkr/config/conf/phpstan.php.example ./conf/phpstan.php \
364
373
  && cp -v ./vendor/brnshkr/config/conf/phpstan.php.example ./conf/phpstan.php.example \
365
374
  && cp -v ./vendor/brnshkr/config/conf/phpstan.dist.php.example ./conf/phpstan.dist.php \
375
+ && cp -v ./vendor/brnshkr/config/conf/twig-cs-fixer.php.example ./conf/twig-cs-fixer.php \
376
+ && cp -v ./vendor/brnshkr/config/conf/twig-cs-fixer.php.example ./conf/twig-cs-fixer.php.example \
377
+ && cp -v ./vendor/brnshkr/config/conf/twig-cs-fixer.dist.php.example ./conf/twig-cs-fixer.dist.php \
366
378
  && cp -v ./vendor/brnshkr/config/conf/Makefile.example ./Makefile \
367
379
  && cp -v ./vendor/brnshkr/config/conf/.gitignore.example ./.gitignore
368
380
  ```
@@ -418,6 +430,21 @@ use Brnshkr\Config\PhpStan;
418
430
  return PhpStan::getConfig(/* customize */);
419
431
  ```
420
432
 
433
+ <!-- omit in toc -->
434
+ ##### Twig CS Fixer
435
+
436
+ ```php
437
+ // ./twig-cs-fixer.dist.php
438
+
439
+ <?php
440
+
441
+ declare(strict_types=1);
442
+
443
+ use Brnshkr\Config\TwigCsFixer;
444
+
445
+ return TwigCsFixer::getConfig(/* customize */);
446
+ ```
447
+
421
448
  <p align="right"><a href="#top" title="Back to top">&nbsp;&nbsp;&nbsp;⬆&nbsp;&nbsp;&nbsp;</a></p>
422
449
 
423
450
  ### 👀 Usage
@@ -458,6 +485,15 @@ Example call, adjust as needed
458
485
  php ./vendor/bin/phpstan analyze --configuration ./conf/phpstan.php -vv --memory-limit=-1
459
486
  ```
460
487
 
488
+ <!-- omit in toc -->
489
+ ###### Twig CS Fixer
490
+
491
+ Example call, adjust as needed
492
+
493
+ ```sh
494
+ php ./vendor/bin/twig-cs-fixer fix --config ./conf/twig-cs-fixer.php -v
495
+ ```
496
+
461
497
  <!-- omit in toc -->
462
498
  ##### Option 2 — Run Helper Scripts (Make Only, @brnshkr Convention)
463
499
 
@@ -496,6 +532,15 @@ Expected configuration file: `./conf/phpstan.php`
496
532
  make phpstan
497
533
  ```
498
534
 
535
+ <!-- omit in toc -->
536
+ ###### Twig CS Fixer
537
+
538
+ Expected configuration file: `./conf/twig-cs-fixer.php`
539
+
540
+ ```sh
541
+ make twig-cs-fixer
542
+ ```
543
+
499
544
  <!-- omit in toc -->
500
545
  #### IDE Setup
501
546
 
@@ -528,7 +573,8 @@ Install dependencies and setup project tooling with the following commands and a
528
573
  composer install \
529
574
  && cp -v ./conf/php-cs-fixer.php.example ./conf/php-cs-fixer.php \
530
575
  && cp -v ./conf/rector.php.example ./conf/rector.php \
531
- && cp -v ./conf/phpstan.php.example ./conf/phpstan.php
576
+ && cp -v ./conf/phpstan.php.example ./conf/phpstan.php \
577
+ && cp -v ./conf/twig-cs-fixer.php.example ./conf/twig-cs-fixer.php
532
578
  ```
533
579
 
534
580
  <!-- omit in toc -->
@@ -551,14 +597,13 @@ Here are some frequently used examples (see `make help` for the complete list):
551
597
  - `make phpstan` — Run PHPStan static analysis
552
598
  - `make test` — Run PHPUnit test suite
553
599
  - `make test-update` — Run PHPUnit test suite and update snapshots
554
- - `make check` — Run Rector, PHP-CS-Fixer, PHPStan and PHPUnit
600
+ - `make check` — Run Rector, PHP-CS-Fixer, Twig-CS-Fixer, PHPStan and PHPUnit
555
601
 
556
602
  ## 🔨 TODOs / Roadmap
557
603
 
558
604
  - Add setup command for JS package (like `composer brnshkr:config:setup`)
559
605
  - Expand [`⚙️ Worflows`](#️-workflows) section in readme
560
606
  - Write sections about custom PHPStan and ESLint rules
561
- - Add all around support for enforcing TypeScript aliases with ESLint
562
607
  - Add Vue support
563
608
  - Add React support
564
609
  - Add Tailwind support via <https://github.com/schoero/eslint-plugin-better-tailwindcss>
@@ -599,7 +644,9 @@ See [./.github/workflows](https://github.com/brnshkr/config/blob/master/.github/
599
644
 
600
645
  This project follows [Semantic Versioning 2.0.0][semver-2.0.0-url].
601
646
  The NPM and Composer packages are versioned in sync, so a version change does not necessarily indicate a change in a specific package.
602
- Also please note the following additional information:
647
+
648
+ > ❗ **Note** ❗
649
+ > Since changes to rules and dependencies are not considered breaking, even a patch release may introduce new errors in code that hasn't changed and break your CI without notice. We therefore strongly recommend pinning to an exact version (`-E` for the JS package managers, `composer r --dev brnshkr/config:X.Y.Z` for Composer) so updates stay opt-in and can be applied on your own schedule.
603
650
 
604
651
  <!-- omit in toc -->
605
652
  ### Changes Considered as Breaking Changes
@@ -632,8 +679,9 @@ Distributed under the MIT License. See [LICENSE](./LICENSE) for more information
632
679
  - [PHP](https://www.php.net)
633
680
  - [PHPStan](https://github.com/phpstan/phpstan)
634
681
  - [Rector](https://github.com/rectorphp/rector)
682
+ - [PHP-CS-Fixer](https://github.com/PHP-CS-Fixer/PHP-CS-Fixer)
683
+ - [Twig-CS-Fixer](https://github.com/VincentLanglet/Twig-CS-Fixer)
635
684
  - [GNU Make](https://www.gnu.org/software/make)
636
- - [PHP Coding Standards Fixer](https://github.com/PHP-CS-Fixer/PHP-CS-Fixer)
637
685
  - [Best-README-Template](https://github.com/othneildrew/Best-README-Template) by [othneildrew](https://github.com/othneildrew)
638
686
  - [Choose an Open Source License](https://choosealicense.com)
639
687
  - [Shields.io](https://shields.io)
@@ -37,6 +37,7 @@
37
37
 
38
38
  // Modules
39
39
  "allowImportingTsExtensions": true,
40
+ "module": "preserve",
40
41
  "moduleResolution": "bundler",
41
42
  "resolveJsonModule": true,
42
43
  "types": ["bun"],
@@ -125,6 +125,10 @@ interface RuleOptions {
125
125
  * @deprecated
126
126
  */
127
127
  'brace-style'?: Linter.RuleEntry<BraceStyle>;
128
+ /**
129
+ * Require imports to use TypeScript path aliases when the target file is reachable through a configured alias.
130
+ */
131
+ 'brnshkr/require-import-alias'?: Linter.RuleEntry<BrnshkrRequireImportAlias>;
128
132
  /**
129
133
  * Require non-JavaScript imports (e.g. .json and .css) to include import attributes.
130
134
  */
@@ -6522,6 +6526,13 @@ type ArrowSpacing = [] | [{
6522
6526
  type BlockSpacing = [] | [("always" | "never")]; // ----- brace-style -----
6523
6527
  type BraceStyle = [] | [("1tbs" | "stroustrup" | "allman")] | [("1tbs" | "stroustrup" | "allman"), {
6524
6528
  allowSingleLine?: boolean;
6529
+ }]; // ----- brnshkr/require-import-alias -----
6530
+ type BrnshkrRequireImportAlias = [] | [{
6531
+ aliases?: {
6532
+ [k: string]: string[] | undefined;
6533
+ };
6534
+ tsConfigPath?: string;
6535
+ ignoredPaths?: string[];
6525
6536
  }]; // ----- callback-return -----
6526
6537
  type CallbackReturn = [] | [string[]]; // ----- camelcase -----
6527
6538
  type Camelcase = [] | [{
@@ -1,10 +1,10 @@
1
- import { c as objectAssign, d as objectFromEntries, f as objectKeys, h as version, i as resolvePackagesSharedAsynchronously, l as objectEntries, m as packageOrganizationUpper, n as GLOB_IGNORES, o as ESLINT_PACKAGES, p as packageOrganization, r as isModuleEnabledByDefault, t as QUOTES, u as objectFreeze } from "../shared.mjs";
1
+ import { c as objectAssign, d as packageOrganization, f as packageOrganizationUpper, i as resolvePackagesSharedAsynchronously, l as objectEntries, n as GLOB_IGNORES, o as ESLINT_PACKAGES, p as version, r as isModuleEnabledByDefault, t as QUOTES, u as objectFromEntries } from "../shared.mjs";
2
2
  import { FlatConfigComposer } from "eslint-flat-config-utils";
3
+ import fs from "node:fs";
4
+ import path from "node:path";
3
5
  import jsEslint from "@eslint/js";
4
6
  import confusingBrowserGlobals from "confusing-browser-globals";
5
7
  import globals from "globals";
6
- import fs from "node:fs/promises";
7
- import path from "node:path";
8
8
  //#region ../src/js/eslint/types/scopes.ts
9
9
  const MAIN_SCOPES = {
10
10
  [packageOrganizationUpper]: "builtin",
@@ -118,7 +118,182 @@ const GLOB_TEST_FILES = [
118
118
  "**/*.benchmark.?(c|m)[jt]s"
119
119
  ];
120
120
  //#endregion
121
- //#region ../src/js/eslint/configs/builtin.ts
121
+ //#region ../src/js/eslint/utils/tsconfig.ts
122
+ const DEFAULT_TSCONFIG_FILENAME = "tsconfig.json";
123
+ const JSONC_TOKEN_PATTERN = /"(?:[^"\\]|\\.)*"|\/\/[^\n]*|\/\*[\s\S]*?\*\//gv;
124
+ const TRAILING_COMMA_PATTERN = /,(?=\s*[\]\}])/gv;
125
+ const stripJsonc = (input) => input.replaceAll(JSONC_TOKEN_PATTERN, (match) => match.startsWith("\"") ? match : "").replaceAll(TRAILING_COMMA_PATTERN, "");
126
+ const parseTsConfigFile = (filePath) => {
127
+ try {
128
+ const content = fs.readFileSync(filePath, "utf-8");
129
+ return JSON.parse(stripJsonc(content));
130
+ } catch {
131
+ return;
132
+ }
133
+ };
134
+ const resolveExtends = (extendsValue, fromDirectory) => {
135
+ if (extendsValue.startsWith(".") || path.isAbsolute(extendsValue)) {
136
+ const resolved = path.resolve(fromDirectory, extendsValue);
137
+ return path.extname(resolved) === "" ? `${resolved}.json` : resolved;
138
+ }
139
+ return path.resolve(fromDirectory, "node_modules", extendsValue);
140
+ };
141
+ const normalizeExtends = (raw) => {
142
+ if (raw.extends === void 0) return [];
143
+ return Array.isArray(raw.extends) ? raw.extends : [raw.extends];
144
+ };
145
+ const loadInternal = (filePath, visitedPaths) => {
146
+ if (visitedPaths.has(filePath)) return;
147
+ visitedPaths.add(filePath);
148
+ const parsedConfig = parseTsConfigFile(filePath);
149
+ if (!parsedConfig) return;
150
+ const fromDirectory = path.dirname(filePath);
151
+ let compilerOptions = {};
152
+ for (const value of normalizeExtends(parsedConfig)) compilerOptions = {
153
+ ...compilerOptions,
154
+ ...loadInternal(resolveExtends(value, fromDirectory), visitedPaths)?.compilerOptions
155
+ };
156
+ return { compilerOptions: {
157
+ ...compilerOptions,
158
+ ...parsedConfig.compilerOptions
159
+ } };
160
+ };
161
+ const cache = /* @__PURE__ */ new Map();
162
+ const toPosix$1 = (filePath) => filePath.replaceAll("\\", "/");
163
+ const loadTsConfigPaths = (tsConfigPath) => {
164
+ const absolutePath = path.resolve(tsConfigPath);
165
+ if (cache.has(absolutePath)) return cache.get(absolutePath);
166
+ const mergedConfig = loadInternal(absolutePath, /* @__PURE__ */ new Set());
167
+ const paths = mergedConfig?.compilerOptions?.paths;
168
+ if (!paths) {
169
+ cache.set(absolutePath, void 0);
170
+ return;
171
+ }
172
+ const baseUrl = path.resolve(path.dirname(absolutePath), mergedConfig.compilerOptions?.baseUrl ?? ".");
173
+ const result = objectFromEntries(objectEntries(paths).map(([pattern, targets]) => [pattern, targets.map((target) => toPosix$1(path.resolve(baseUrl, target)))]));
174
+ cache.set(absolutePath, result);
175
+ return result;
176
+ };
177
+ const doesTsConfigExist = (tsConfigPath) => {
178
+ try {
179
+ fs.accessSync(tsConfigPath, fs.constants.R_OK);
180
+ return true;
181
+ } catch {
182
+ return false;
183
+ }
184
+ };
185
+ const resolveTsConfigPath = (typescriptOptions) => {
186
+ let tsconfig = DEFAULT_TSCONFIG_FILENAME;
187
+ if (typescriptOptions) {
188
+ if (typeof typescriptOptions.typeAware === "string") tsconfig = typescriptOptions.typeAware;
189
+ else if (typeof typescriptOptions.typeAware === "object") tsconfig = typescriptOptions.typeAware.tsconfig ?? DEFAULT_TSCONFIG_FILENAME;
190
+ }
191
+ return path.resolve(process.cwd(), tsconfig);
192
+ };
193
+ //#endregion
194
+ //#region ../src/js/eslint/configs/builtin/require-import-alias.ts
195
+ const MESSAGE_ID_PREFER_ALIAS = "preferAlias";
196
+ const MESSAGE_ID_MISSING_ALIAS = "missingAlias";
197
+ const WILDCARD_SUFFIX = "/*";
198
+ const WILDCARD_SUFFIX_LENGTH = 2;
199
+ const RELATIVE_SPECIFIER_PREFIXES = ["./", "../"];
200
+ const resolveAliases = (options) => options.aliases ?? loadTsConfigPaths(options.tsConfigPath ?? resolveTsConfigPath()) ?? {};
201
+ const toPosix = (value) => value.replaceAll("\\", "/");
202
+ const buildAliasMappings = (aliases) => objectEntries(aliases).filter(([pattern]) => pattern.endsWith(WILDCARD_SUFFIX)).flatMap(([pattern, targets]) => {
203
+ const prefix = pattern.slice(0, -WILDCARD_SUFFIX_LENGTH);
204
+ return targets.filter((target) => target.endsWith(WILDCARD_SUFFIX)).map((target) => ({
205
+ prefix,
206
+ baseDirectory: toPosix(target).slice(0, -WILDCARD_SUFFIX_LENGTH)
207
+ }));
208
+ }).toSorted((left, right) => right.baseDirectory.length - left.baseDirectory.length);
209
+ const findAliasReplacement = (mappings, absolutePath) => {
210
+ for (const { prefix, baseDirectory } of mappings) {
211
+ if (absolutePath === baseDirectory) return prefix;
212
+ if (absolutePath.startsWith(`${baseDirectory}/`)) return `${prefix}/${absolutePath.slice(baseDirectory.length + 1)}`;
213
+ }
214
+ };
215
+ const isRelativeSpecifier = (source) => RELATIVE_SPECIFIER_PREFIXES.some((prefix) => source.startsWith(prefix));
216
+ const isAlreadyAliased = (source, mappings) => mappings.some(({ prefix }) => source === prefix || source.startsWith(`${prefix}/`));
217
+ const isFileIgnored = (filename, patterns) => patterns.some((pattern) => path.matchesGlob(toPosix(filename), pattern) || path.matchesGlob(toPosix(path.relative(process.cwd(), filename)), pattern));
218
+ const getQuote = (sourceNode) => "raw" in sourceNode && typeof sourceNode.raw === "string" && sourceNode.raw.startsWith("\"") ? "\"" : "'";
219
+ const requireImportAliasRule = {
220
+ meta: {
221
+ type: "suggestion",
222
+ fixable: "code",
223
+ docs: { description: "Require imports to use TypeScript path aliases when the target file is reachable through a configured alias." },
224
+ schema: [{
225
+ type: "object",
226
+ additionalProperties: false,
227
+ properties: {
228
+ aliases: {
229
+ type: "object",
230
+ additionalProperties: {
231
+ type: "array",
232
+ items: { type: "string" }
233
+ }
234
+ },
235
+ tsConfigPath: { type: "string" },
236
+ ignoredPaths: {
237
+ type: "array",
238
+ items: { type: "string" }
239
+ }
240
+ }
241
+ }],
242
+ messages: {
243
+ [MESSAGE_ID_PREFER_ALIAS]: "Import path '{{ source }}' must use the configured alias '{{ alias }}'.",
244
+ [MESSAGE_ID_MISSING_ALIAS]: "Import path '{{ source }}' resolves outside any configured TypeScript path alias. Add an alias for this location, remove all other aliases, or disable this rule."
245
+ }
246
+ },
247
+ create: (context) => {
248
+ const options = context.options[0] ?? {};
249
+ const ignoredPaths = options.ignoredPaths ?? [];
250
+ const mappings = buildAliasMappings(resolveAliases(options));
251
+ if (mappings.length === 0 || isFileIgnored(context.filename, ignoredPaths)) return {};
252
+ const fileDirectory = toPosix(path.dirname(context.filename));
253
+ const checkSource = (sourceNode) => {
254
+ if (sourceNode?.type !== "Literal" || typeof sourceNode.value !== "string") return;
255
+ const source = sourceNode.value;
256
+ if (!isRelativeSpecifier(source) || isAlreadyAliased(source, mappings)) return;
257
+ const replacement = findAliasReplacement(mappings, path.posix.normalize(`${fileDirectory}/${source}`));
258
+ if (replacement === void 0) {
259
+ context.report({
260
+ node: sourceNode,
261
+ messageId: MESSAGE_ID_MISSING_ALIAS,
262
+ data: { source }
263
+ });
264
+ return;
265
+ }
266
+ context.report({
267
+ node: sourceNode,
268
+ messageId: MESSAGE_ID_PREFER_ALIAS,
269
+ data: {
270
+ source,
271
+ alias: replacement
272
+ },
273
+ fix: (fixer) => fixer.replaceText(sourceNode, `${getQuote(sourceNode)}${replacement}${getQuote(sourceNode)}`)
274
+ });
275
+ };
276
+ return {
277
+ ImportDeclaration: (node) => {
278
+ checkSource(node.source);
279
+ },
280
+ ExportNamedDeclaration: (node) => {
281
+ checkSource(node.source);
282
+ },
283
+ ExportAllDeclaration: (node) => {
284
+ checkSource(node.source);
285
+ },
286
+ ImportExpression: (node) => {
287
+ checkSource(node.source);
288
+ }
289
+ };
290
+ }
291
+ };
292
+ //#endregion
293
+ //#region ../src/js/eslint/configs/builtin/require-import-attributes.ts
294
+ const MESSAGE_ID_MISSING_WITH_KEYWORD = "missingWithKeyword";
295
+ const MESSAGE_ID_MISSING_TYPE_PROPERTY = "missingTypeProperty";
296
+ const MESSAGE_ID_WRONG_TYPE_VALUE = "wrongTypeValue";
122
297
  const FILE_TYPE_MAP = {
123
298
  ".json": "json",
124
299
  ".css": "css",
@@ -130,58 +305,59 @@ const FILE_TYPE_MAP = {
130
305
  ".oct": "bytes",
131
306
  ".wasm": "webassembly"
132
307
  };
133
- const MESSAGE_ID_MISSING_WITH_KEYWORD = "missingWithKeyword";
134
- const MESSAGE_ID_MISSING_TYPE_PROPERTY = "missingTypeProperty";
135
- const MESSAGE_ID_WRONG_TYPE_VALUE = "wrongTypeValue";
136
- const RULE_DEFINITIONS = { "require-import-attributes": {
137
- meta: {
138
- type: "problem",
139
- docs: { description: "Require non-JavaScript imports (e.g. .json and .css) to include import attributes." },
140
- messages: {
141
- [MESSAGE_ID_MISSING_WITH_KEYWORD]: "Non-JavaScript import ('{{ extension }}') requires an import attributes object with the 'type' property set to '{{ expectedValue }}'.",
142
- [MESSAGE_ID_MISSING_TYPE_PROPERTY]: "Import attributes for non-JavaScript imports must include the 'type' property.",
143
- [MESSAGE_ID_WRONG_TYPE_VALUE]: "Import attribute 'type' for '{{ file }}' must be '{{ expectedValue }}'."
144
- }
145
- },
146
- create: (context) => ({ ImportDeclaration: (node) => {
147
- const sourceValue = node.source.value;
148
- const extensionIndex = sourceValue.lastIndexOf(".");
149
- if (extensionIndex === -1) return;
150
- const extension = sourceValue.slice(extensionIndex).toLowerCase();
151
- const expectedValue = FILE_TYPE_MAP[extension];
152
- if (expectedValue === void 0) return;
153
- const { attributes } = node;
154
- if (attributes.length === 0) {
155
- context.report({
308
+ //#endregion
309
+ //#region ../src/js/eslint/configs/builtin/index.ts
310
+ const RULE_DEFINITIONS = {
311
+ "require-import-attributes": {
312
+ meta: {
313
+ type: "problem",
314
+ docs: { description: "Require non-JavaScript imports (e.g. .json and .css) to include import attributes." },
315
+ messages: {
316
+ [MESSAGE_ID_MISSING_WITH_KEYWORD]: "Non-JavaScript import ('{{ extension }}') requires an import attributes object with the 'type' property set to '{{ expectedValue }}'.",
317
+ [MESSAGE_ID_MISSING_TYPE_PROPERTY]: "Import attributes for non-JavaScript imports must include the 'type' property.",
318
+ [MESSAGE_ID_WRONG_TYPE_VALUE]: "Import attribute 'type' for '{{ file }}' must be '{{ expectedValue }}'."
319
+ }
320
+ },
321
+ create: (context) => ({ ImportDeclaration: (node) => {
322
+ const sourceValue = node.source.value;
323
+ const extensionIndex = sourceValue.lastIndexOf(".");
324
+ if (extensionIndex === -1) return;
325
+ const extension = sourceValue.slice(extensionIndex).toLowerCase();
326
+ const expectedValue = FILE_TYPE_MAP[extension];
327
+ if (expectedValue === void 0) return;
328
+ const { attributes } = node;
329
+ if (attributes.length === 0) {
330
+ context.report({
331
+ node,
332
+ messageId: MESSAGE_ID_MISSING_WITH_KEYWORD,
333
+ data: {
334
+ extension,
335
+ expectedValue
336
+ }
337
+ });
338
+ return;
339
+ }
340
+ const typeProperty = attributes.find((attribute) => attribute.key.type === "Identifier" && "name" in attribute.key && attribute.key.name === "type" || attribute.key.type === "Literal" && "value" in attribute.key && attribute.key.value === "type");
341
+ if (!typeProperty) {
342
+ context.report({
343
+ node,
344
+ messageId: MESSAGE_ID_MISSING_TYPE_PROPERTY
345
+ });
346
+ return;
347
+ }
348
+ if (typeProperty.value.value !== expectedValue) context.report({
156
349
  node,
157
- messageId: MESSAGE_ID_MISSING_WITH_KEYWORD,
350
+ messageId: MESSAGE_ID_WRONG_TYPE_VALUE,
158
351
  data: {
159
- extension,
160
- expectedValue
352
+ expectedValue,
353
+ file: sourceValue
161
354
  }
162
355
  });
163
- return;
164
- }
165
- const typeProperty = attributes.find((attribute) => attribute.key.type === "Identifier" && "name" in attribute.key && attribute.key.name === "type" || attribute.key.type === "Literal" && "value" in attribute.key && attribute.key.value === "type");
166
- if (!typeProperty) {
167
- context.report({
168
- node,
169
- messageId: MESSAGE_ID_MISSING_TYPE_PROPERTY
170
- });
171
- return;
172
- }
173
- if (typeProperty.value.value !== expectedValue) context.report({
174
- node,
175
- messageId: MESSAGE_ID_WRONG_TYPE_VALUE,
176
- data: {
177
- expectedValue,
178
- file: sourceValue
179
- }
180
- });
181
- } })
182
- } };
183
- const RULES = objectFreeze(objectFromEntries(objectKeys(RULE_DEFINITIONS).map((ruleName) => [`${packageOrganization}/${ruleName}`, "error"])));
184
- const builtin = () => [{
356
+ } })
357
+ },
358
+ "require-import-alias": requireImportAliasRule
359
+ };
360
+ const builtin = (typescriptOptions) => [{
185
361
  name: buildConfigName(MAIN_SCOPES[packageOrganizationUpper], SUB_SCOPES.SETUP),
186
362
  plugins: { [packageOrganization]: {
187
363
  meta: {
@@ -193,7 +369,10 @@ const builtin = () => [{
193
369
  }, {
194
370
  name: buildConfigName(MAIN_SCOPES[packageOrganizationUpper], SUB_SCOPES.RULES),
195
371
  files: GLOB_SCRIPT_FILES,
196
- rules: RULES
372
+ rules: {
373
+ [`${packageOrganization}/require-import-alias`]: ["error", { tsConfigPath: resolveTsConfigPath(typeof typescriptOptions === "object" ? typescriptOptions : void 0) }],
374
+ [`${packageOrganization}/require-import-attributes`]: "error"
375
+ }
197
376
  }];
198
377
  const builtinConfig = { [packageOrganization]: builtin };
199
378
  //#endregion
@@ -354,6 +533,14 @@ const imports = async () => {
354
533
  if (pluginImport) {
355
534
  plugins["import"] = pluginImport;
356
535
  settings["import-x/resolver-next"] = [pluginImport.createNodeResolver(), importResovlerTypescript === void 0 ? void 0 : importResovlerTypescript.createTypeScriptImportResolver({ bun: true })].filter(Boolean);
536
+ settings["import-x/core-modules"] = [
537
+ "bun",
538
+ "bun:bundle",
539
+ "bun:ffi",
540
+ "bun:jsc",
541
+ "bun:sqlite",
542
+ "bun:test"
543
+ ];
357
544
  const pluginImportTsRules = isModuleEnabled(MODULES.typescript) ? renameRules(pluginImport.flatConfigs.typescript.rules, { "import-x": "import" }) : {};
358
545
  pluginImportRules = {
359
546
  ...renameRules(pluginImport.flatConfigs.recommended.rules, { "import-x": "import" }),
@@ -809,15 +996,10 @@ const typescript = async (options) => {
809
996
  if (!isTypescriptInstalled || !tsEslint) return [];
810
997
  const { optional: [, pluginUnusedImports] } = await resolvePackages(MODULES.javascript);
811
998
  const cwd = process.cwd();
812
- let hasTsConfig = false;
813
- try {
814
- await fs.access(path.resolve(cwd, "tsconfig.json"), fs.constants.R_OK);
815
- hasTsConfig = true;
816
- } catch {}
817
999
  const resolvedOptions = {
818
1000
  extraFileExtensions: [],
819
1001
  parserOptions: {},
820
- typeAware: hasTsConfig,
1002
+ typeAware: doesTsConfigExist(resolveTsConfigPath(options)),
821
1003
  ...options
822
1004
  };
823
1005
  const ignores = resolvedOptions.ignores ?? [];
@@ -2626,7 +2808,7 @@ const getConfig = (optionsAndGlobalConfig, ...additionalConfigs) => {
2626
2808
  if (isModuleEnabled(MODULES.test)) appendToComposer(configs.test());
2627
2809
  if (isModuleEnabled(MODULES.jsdoc)) appendToComposer(configs.jsdoc());
2628
2810
  if (isModuleEnabled(MODULES.node)) appendToComposer(configs.node(typeof resolvedOptions.node === "object" ? resolvedOptions.node : void 0));
2629
- if (isModuleEnabled(MODULES[packageOrganization])) appendToComposer(configs[packageOrganization]());
2811
+ if (isModuleEnabled(MODULES[packageOrganization])) appendToComposer(configs[packageOrganization](resolvedOptions.typescript));
2630
2812
  if (isModuleEnabled(MODULES.comments)) appendToComposer(configs.comments());
2631
2813
  if (isModuleEnabled(MODULES.regexp)) appendToComposer(configs.regexp());
2632
2814
  if (isModuleEnabled(MODULES.unicorn)) appendToComposer(configs.unicorn());
@@ -16,6 +16,8 @@ spawn("bun", [
16
16
  "stylelint",
17
17
  "--config",
18
18
  "./conf/stylelint.config.mjs",
19
+ "--config-basedir",
20
+ "./",
19
21
  "--cache",
20
22
  "--cache-location",
21
23
  "./.cache/stylelint.cache.json",
package/dist/shared.mjs CHANGED
@@ -1,7 +1,7 @@
1
1
  import { isPackageExists } from "local-pkg";
2
2
  //#region ../package.json
3
3
  var name = "@brnshkr/config";
4
- var version = "0.0.1-beta.1";
4
+ var version = "0.0.1-beta.3";
5
5
  //#endregion
6
6
  //#region ../src/js/shared/utils/package-json.ts
7
7
  const packageOrganizationInternal = name.split("/").at(0)?.replace(/^@/v, "");
@@ -16,10 +16,8 @@ const log = (type, message, context) => {
16
16
  };
17
17
  //#endregion
18
18
  //#region ../src/js/shared/utils/object.ts
19
- const objectKeys = (object) => Object.keys(object);
20
19
  const objectEntries = (object) => Object.entries(object);
21
20
  const objectFromEntries = (entries) => Object.fromEntries(entries);
22
- const objectFreeze = (object) => Object.freeze(object);
23
21
  const objectAssign = (target, source) => Object.assign(target, source);
24
22
  //#endregion
25
23
  //#region ../src/js/shared/utils/interop-import.ts
@@ -230,4 +228,4 @@ const GLOB_IGNORES = [
230
228
  //#region ../src/js/shared/utils/constants.ts
231
229
  const QUOTES = "single";
232
230
  //#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 };
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 };
@@ -1,4 +1,4 @@
1
- import { a as resolvePackagesSharedSynchronously, l as objectEntries, n as GLOB_IGNORES, p as packageOrganization, r as isModuleEnabledByDefault, s as STYLELINT_PACKAGES, t as QUOTES } from "../shared.mjs";
1
+ import { a as resolvePackagesSharedSynchronously, d as packageOrganization, l as objectEntries, n as GLOB_IGNORES, r as isModuleEnabledByDefault, s as STYLELINT_PACKAGES, t as QUOTES } from "../shared.mjs";
2
2
  //#region ../src/js/stylelint/utils/module.ts
3
3
  const MODULES = {
4
4
  baseline: {
@@ -126,14 +126,18 @@ const defensive = () => {
126
126
  return [{
127
127
  plugins: STYLELINT_PACKAGES.STYLELINT_PLUGIN_DEFENSIVE_CSS,
128
128
  extends: `${STYLELINT_PACKAGES.STYLELINT_PLUGIN_DEFENSIVE_CSS}/configs/strict`,
129
- rules: { "defensive-css/require-pure-selectors": [true, {
130
- ignoreElements: [
131
- "*",
132
- "html",
133
- "body"
134
- ],
135
- ignoreAttributeSelectors: true
136
- }] }
129
+ rules: {
130
+ "defensive-css/require-custom-property-fallback": true,
131
+ "defensive-css/require-pure-selectors": [true, {
132
+ ignoreElements: [
133
+ "*",
134
+ "html",
135
+ "body"
136
+ ],
137
+ ignoreAttributeSelectors: true,
138
+ strict: true
139
+ }]
140
+ }
137
141
  }];
138
142
  };
139
143
  //#endregion
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "$schema": "https://json.schemastore.org/package",
3
3
  "name": "@brnshkr/config",
4
- "version": "0.0.1-beta.1",
4
+ "version": "0.0.1-beta.3",
5
5
  "description": "Centralized collection of configuration and tooling used across all @brnshkr projects",
6
6
  "license": "MIT",
7
7
  "author": "Patrick Rupp",