js-style-kit 0.6.1 → 0.8.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (67) hide show
  1. package/README.md +5 -0
  2. package/dist/bin/index.cjs +2 -1
  3. package/dist/bin/index.cjs.map +1 -1
  4. package/dist/index.d.ts +23 -7
  5. package/dist/index.js +135 -36
  6. package/dist/index.js.map +1 -1
  7. package/package.json +10 -7
  8. package/src/eslint/base/README.md +186 -0
  9. package/src/eslint/base/config.ts +37 -0
  10. package/src/eslint/base/rules.ts +444 -0
  11. package/src/eslint/base/types.ts +20 -0
  12. package/src/eslint/constants.ts +52 -0
  13. package/src/eslint/convex/README.md +30 -0
  14. package/src/eslint/convex/config.ts +34 -0
  15. package/src/eslint/convex/rules.ts +8 -0
  16. package/src/eslint/convex/types.ts +8 -0
  17. package/src/eslint/ignores.ts +34 -0
  18. package/src/eslint/import/README.md +397 -0
  19. package/src/eslint/import/config.ts +48 -0
  20. package/src/eslint/import/rules.ts +81 -0
  21. package/src/eslint/index.ts +273 -0
  22. package/src/eslint/jsdoc/README.md +399 -0
  23. package/src/eslint/jsdoc/config.ts +29 -0
  24. package/src/eslint/jsdoc/rules.ts +81 -0
  25. package/src/eslint/jsdoc/types.ts +56 -0
  26. package/src/eslint/nextjs/config.ts +25 -0
  27. package/src/eslint/nextjs/rules.ts +25 -0
  28. package/src/eslint/nextjs/types.ts +27 -0
  29. package/src/eslint/perfectionist/README.md +454 -0
  30. package/src/eslint/perfectionist/config.ts +25 -0
  31. package/src/eslint/perfectionist/rules.ts +39 -0
  32. package/src/eslint/prefer-arrow-function/config.ts +33 -0
  33. package/src/eslint/prefer-arrow-function/types.ts +13 -0
  34. package/src/eslint/process-custom-rules.ts +72 -0
  35. package/src/eslint/query/README.md +254 -0
  36. package/src/eslint/query/config.ts +27 -0
  37. package/src/eslint/query/rules.ts +11 -0
  38. package/src/eslint/query/types.ts +11 -0
  39. package/src/eslint/react/README.md +416 -0
  40. package/src/eslint/react/config.ts +65 -0
  41. package/src/eslint/react/rules.ts +188 -0
  42. package/src/eslint/react/types.ts +26 -0
  43. package/src/eslint/react-refresh/config.ts +28 -0
  44. package/src/eslint/react-refresh/rules.ts +48 -0
  45. package/src/eslint/storybook/README.md +424 -0
  46. package/src/eslint/storybook/config.ts +57 -0
  47. package/src/eslint/testing/README.md +436 -0
  48. package/src/eslint/testing/config.ts +99 -0
  49. package/src/eslint/testing/get-import-restrictions.ts +70 -0
  50. package/src/eslint/testing/jest-rules.ts +47 -0
  51. package/src/eslint/testing/vitest-rules.ts +42 -0
  52. package/src/eslint/turbo/README.md +380 -0
  53. package/src/eslint/turbo/config.ts +26 -0
  54. package/src/eslint/turbo/types.ts +7 -0
  55. package/src/eslint/types.ts +36 -0
  56. package/src/eslint/typescript/README.md +229 -0
  57. package/src/eslint/typescript/config.ts +48 -0
  58. package/src/eslint/typescript/rules.ts +137 -0
  59. package/src/eslint/typescript/types.ts +35 -0
  60. package/src/eslint/unicorn/README.md +497 -0
  61. package/src/eslint/unicorn/config.ts +36 -0
  62. package/src/eslint/unicorn/rules.ts +86 -0
  63. package/src/index.ts +3 -0
  64. package/src/modules.d.ts +5 -0
  65. package/src/prettier/README.md +413 -0
  66. package/src/prettier/index.ts +110 -0
  67. package/src/utils/is-type.ts +60 -0
package/dist/index.d.ts CHANGED
@@ -10,6 +10,7 @@ import { PluginOptions } from 'prettier-plugin-tailwindcss';
10
10
  */
11
11
  declare const configNames: {
12
12
  readonly base: "base";
13
+ readonly convex: "convex";
13
14
  readonly disableTypeChecked: "UserConfig[1] > typescript-eslint/disable-type-checked";
14
15
  readonly ignores: "ignores";
15
16
  readonly import: "import";
@@ -33,20 +34,31 @@ type ConfigName = (typeof configNames)[keyof typeof configNames];
33
34
 
34
35
  type EslintSeverity = 0 | 1 | 2 | "error" | "off" | "warn";
35
36
  type EslintRuleConfig<TOptions = Record<string, unknown>, TOptions2 = Record<string, unknown>> = [EslintSeverity, string | TOptions | undefined] | [EslintSeverity, string | undefined, TOptions2 | undefined] | [EslintSeverity, TOptions | undefined, TOptions2 | undefined] | [EslintSeverity] | EslintSeverity;
36
- type FunctionStyle = "arrow" | "declaration" | "expression";
37
37
  interface EslintConfigObject<Rules extends Linter.RulesRecord = Linter.RulesRecord> extends Linter.Config<Rules> {
38
38
  name: ConfigName;
39
39
  }
40
+ type FilenameCase = "camelCase" | "kebabCase" | "pascalCase" | "snakeCase";
41
+ type FunctionStyle = "arrow" | "declaration" | "expression";
42
+ type ReactFramework = "next" | "none" | "react-router" | "remix" | "vite";
40
43
 
41
44
  interface TestingConfig {
42
45
  filenamePattern?: "spec" | "test";
43
46
  files?: string[];
44
47
  formattingRules?: boolean;
45
48
  framework?: "bun" | "jest" | "node" | "vitest";
49
+ /**
50
+ * Whether to enforce imports from the correct testing framework.
51
+ * Uses the built-in ESLint `no-restricted-imports` rule.
52
+ *
53
+ * @default true
54
+ */
55
+ importRestrictions?: boolean;
46
56
  itOrTest?: "it" | "test";
57
+ typescript?: boolean;
47
58
  }
48
59
 
49
60
  interface EslintConfigOptions {
61
+ convex?: boolean;
50
62
  functionStyle?: "off" | FunctionStyle;
51
63
  ignores?: string[];
52
64
  importPlugin?: boolean;
@@ -55,7 +67,7 @@ interface EslintConfigOptions {
55
67
  };
56
68
  query?: boolean;
57
69
  react?: boolean | {
58
- framework?: "next" | "none" | "react-router" | "remix" | "vite";
70
+ framework?: ReactFramework;
59
71
  reactCompiler?: boolean;
60
72
  reactRefresh?: boolean;
61
73
  };
@@ -65,12 +77,15 @@ interface EslintConfigOptions {
65
77
  testing?: false | TestingConfig;
66
78
  turbo?: boolean;
67
79
  typescript?: boolean | string;
68
- unicorn?: boolean;
80
+ unicorn?: boolean | {
81
+ filenameCase?: FilenameCase;
82
+ };
69
83
  }
70
84
  /**
71
85
  * Configures ESLint based on provided options.
72
86
  *
73
87
  * @param options - The optional configuration object.
88
+ * @param options.convex - Whether to include Convex rules.
74
89
  * @param options.functionStyle - The function style to enforce. Defaults to "arrow".
75
90
  * @param options.ignores - Additional paths to ignore. Already excludes `node_modules` and `dist`.
76
91
  * @param options.importPlugin - Whether to include the import plugin. Defaults to true.
@@ -87,17 +102,18 @@ interface EslintConfigOptions {
87
102
  * @param options.testing - An object with the following properties:
88
103
  * - `filenamePattern`: One of "spec" or "test" to determine which filename pattern to use.
89
104
  * - `files`: Array of file patterns to include in the configuration.
90
- * - `framework`: One of "vitest" or "jest" to determine which testing library to use.
105
+ * - `framework`: One of "vitest" or "jest" or "bun" or "node" to determine which testing library to use.
91
106
  * - `formattingRules`: Whether to include formatting rules like padding around blocks.
107
+ * - `importRestrictions`: Whether to enforce imports from the correct testing framework.
92
108
  * - `itOrTest`: One of "it" or "test" to determine which test function to use.
93
109
  * @param options.typescript - Whether to include TypeScript rules. Can be a boolean or a string with path to tsconfig.
94
110
  * @param options.turbo - Whether to include Turborepo rules. Defaults to false.
95
- * @param options.unicorn - Whether to include Unicorn rules. Defaults to true.
111
+ * @param options.unicorn - Whether to include Unicorn rules. Defaults to true. Can be an object with filenameCase property.
96
112
  * @param options.rules - This is for rules that you need to alter or turn off.
97
113
  * @param additionalConfigs - Additional ESLint config objects to be merged into the final configuration.
98
114
  * @returns An array of ESLint configuration objects.
99
115
  */
100
- declare const eslintConfig: ({ functionStyle, ignores, importPlugin, jsdoc, query, react, rules, sorting, storybook, testing, turbo, typescript, unicorn, }?: EslintConfigOptions, ...additionalConfigs: Linter.Config[]) => Linter.Config[];
116
+ declare const eslintConfig: ({ convex, functionStyle, ignores, importPlugin, jsdoc, query, react, rules, sorting, storybook, testing, turbo, typescript, unicorn, }?: EslintConfigOptions, ...additionalConfigs: Linter.Config[]) => Linter.Config[];
101
117
 
102
118
  interface PrettierConfigOptions extends Config {
103
119
  cssOrderPlugin?: boolean;
@@ -131,4 +147,4 @@ interface PrettierConfigWithPlugins extends Config, SortJsonOptions, PluginOptio
131
147
  */
132
148
  declare const prettierConfig: (options?: PrettierConfigOptions) => PrettierConfigWithPlugins;
133
149
 
134
- export { type EslintConfigObject, type EslintConfigOptions, type EslintRuleConfig, type EslintSeverity, type FunctionStyle, type PrettierConfigOptions, type PrettierConfigWithPlugins, eslintConfig, prettierConfig };
150
+ export { type EslintConfigObject, type EslintConfigOptions, type EslintRuleConfig, type EslintSeverity, type FilenameCase, type FunctionStyle, type PrettierConfigOptions, type PrettierConfigWithPlugins, type ReactFramework, eslintConfig, prettierConfig };
package/dist/index.js CHANGED
@@ -9,6 +9,7 @@ var isObject = (value) => {
9
9
  // src/eslint/constants.ts
10
10
  var configNames = {
11
11
  base: "base",
12
+ convex: "convex",
12
13
  disableTypeChecked: "UserConfig[1] > typescript-eslint/disable-type-checked",
13
14
  ignores: "ignores",
14
15
  import: "import",
@@ -29,6 +30,7 @@ var configNames = {
29
30
  unicorn: "unicorn"
30
31
  };
31
32
  var pluginPrefixMap = /* @__PURE__ */ new Map([
33
+ ["@convex-dev", configNames.convex],
32
34
  ["@tanstack/query", configNames.query],
33
35
  ["@typescript-eslint", configNames.typescript],
34
36
  ["import", configNames.import],
@@ -484,15 +486,42 @@ var baseEslintConfig = (functionStyle, typescript, customRules) => ({
484
486
  }
485
487
  });
486
488
 
489
+ // src/eslint/convex/config.ts
490
+ import { createRequire } from "module";
491
+
492
+ // src/eslint/convex/rules.ts
493
+ var convexRules = {
494
+ "@convex-dev/import-wrong-runtime": "warn",
495
+ "@convex-dev/no-args-without-validator": "warn",
496
+ "@convex-dev/no-missing-args-validator": "warn",
497
+ "@convex-dev/no-old-registered-function-syntax": "warn"
498
+ };
499
+
500
+ // src/eslint/convex/config.ts
501
+ var require2 = createRequire(import.meta.url);
502
+ var convexPlugin = require2("@convex-dev/eslint-plugin");
503
+ var convexConfig = (customRules) => ({
504
+ files: ["**/convex/**/*.{ts,js}"],
505
+ name: configNames.convex,
506
+ plugins: {
507
+ "@convex-dev": convexPlugin
508
+ },
509
+ rules: {
510
+ ...convexRules,
511
+ ...customRules ?? {}
512
+ }
513
+ });
514
+
487
515
  // src/eslint/ignores.ts
488
516
  var ignoresConfig = ({
489
- next = false,
490
- storybook = false,
491
- userIgnores = []
492
- } = {}) => ({
517
+ reactFramework,
518
+ storybook,
519
+ userIgnores
520
+ }) => ({
493
521
  ignores: [
494
522
  "**/dist/",
495
- ...next ? [".next"] : [],
523
+ ...reactFramework === "next" ? [".next"] : [],
524
+ ...reactFramework === "react-router" ? [".react-router"] : [],
496
525
  ...storybook ? ["!.storybook"] : [],
497
526
  ...userIgnores
498
527
  ],
@@ -1130,6 +1159,56 @@ var storybookConfig = (customRules) => [
1130
1159
  import jest from "eslint-plugin-jest";
1131
1160
  import vitest from "eslint-plugin-vitest";
1132
1161
 
1162
+ // src/eslint/testing/get-import-restrictions.ts
1163
+ var commonTestImports = [
1164
+ "describe",
1165
+ "it",
1166
+ "test",
1167
+ "expect",
1168
+ "beforeAll",
1169
+ "beforeEach",
1170
+ "afterAll",
1171
+ "afterEach",
1172
+ "vi",
1173
+ "mock",
1174
+ "spyOn"
1175
+ ];
1176
+ var frameworkConfig = {
1177
+ bun: {
1178
+ allowed: "'bun:test'",
1179
+ restricted: ["vitest", "jest", "@jest/globals", "node:test"]
1180
+ },
1181
+ jest: {
1182
+ allowed: "'jest' or '@jest/globals'",
1183
+ restricted: ["vitest", "bun:test", "node:test"]
1184
+ },
1185
+ node: {
1186
+ allowed: "'node:test'",
1187
+ restricted: ["vitest", "jest", "@jest/globals", "bun:test"]
1188
+ },
1189
+ vitest: {
1190
+ allowed: "'vitest'",
1191
+ restricted: ["jest", "@jest/globals", "bun:test", "node:test"]
1192
+ }
1193
+ };
1194
+ var getRestrictionMessage = (allowedFramework) => `This project is setup to use ${allowedFramework} for testing. Importing from other testing frameworks is not allowed. Change this setting in eslint.config.js under testing.framework`;
1195
+ var getImportRestrictions = (framework) => {
1196
+ const config = frameworkConfig[framework];
1197
+ const message = getRestrictionMessage(config.allowed);
1198
+ return {
1199
+ "no-restricted-imports": [
1200
+ "warn",
1201
+ {
1202
+ paths: config.restricted.map((name) => ({
1203
+ importNames: commonTestImports,
1204
+ message,
1205
+ name
1206
+ }))
1207
+ }
1208
+ ]
1209
+ };
1210
+ };
1211
+
1133
1212
  // src/eslint/testing/jest-rules.ts
1134
1213
  var jestRules = (itOrTest = "test") => ({
1135
1214
  "jest/consistent-test-it": [
@@ -1220,17 +1299,14 @@ var vitestRules = (itOrTest = "test") => ({
1220
1299
 
1221
1300
  // src/eslint/testing/config.ts
1222
1301
  var testingConfig = ({
1223
- filenamePattern,
1302
+ filenamePattern = "test",
1224
1303
  files,
1225
- formattingRules,
1226
- framework,
1227
- itOrTest
1228
- } = {
1229
- filenamePattern: "test",
1230
- formattingRules: true,
1231
- framework: "vitest",
1232
- itOrTest: "test"
1233
- }, customRules) => ({
1304
+ formattingRules = true,
1305
+ framework = "vitest",
1306
+ importRestrictions = true,
1307
+ itOrTest = "test",
1308
+ typescript = true
1309
+ } = {}, customRules) => ({
1234
1310
  files: files ?? ["**/*.{test,spec}.{ts,tsx,js,jsx}"],
1235
1311
  languageOptions: {
1236
1312
  globals: framework === "vitest" ? { ...vitest.environments.env.globals } : jest.environments.globals.globals
@@ -1241,8 +1317,8 @@ var testingConfig = ({
1241
1317
  vitest
1242
1318
  },
1243
1319
  rules: {
1320
+ ...typescript ? { "@typescript-eslint/unbound-method": "off" } : {},
1244
1321
  // jest doesn't have a file name rule, so we'll use this one for both
1245
- "@typescript-eslint/unbound-method": "off",
1246
1322
  "vitest/consistent-test-filename": [
1247
1323
  "warn",
1248
1324
  {
@@ -1260,6 +1336,7 @@ var testingConfig = ({
1260
1336
  "jest/padding-around-expect-groups": "warn",
1261
1337
  "jest/padding-around-test-blocks": "warn"
1262
1338
  } : {},
1339
+ ...importRestrictions ? getImportRestrictions(framework) : {},
1263
1340
  ...customRules ?? {}
1264
1341
  },
1265
1342
  ...framework !== "jest" && framework !== "vitest" ? {
@@ -1457,7 +1534,7 @@ var tseslintConfig = (tsconfigPath, customRules) => {
1457
1534
  import unicorn from "eslint-plugin-unicorn";
1458
1535
 
1459
1536
  // src/eslint/unicorn/rules.ts
1460
- var rules = {
1537
+ var rules = (filenameCase = "kebabCase") => ({
1461
1538
  /**
1462
1539
  * Enforce better string content.
1463
1540
  *
@@ -1478,7 +1555,7 @@ var rules = {
1478
1555
  "unicorn/filename-case": [
1479
1556
  "warn",
1480
1557
  {
1481
- case: "kebabCase"
1558
+ case: filenameCase
1482
1559
  }
1483
1560
  ],
1484
1561
  /**
@@ -1530,29 +1607,26 @@ var rules = {
1530
1607
  * 🔧 Fixable - https://github.com/sindresorhus/eslint-plugin-unicorn/blob/main/docs/rules/text-encoding-identifier-case.md
1531
1608
  */
1532
1609
  "unicorn/text-encoding-identifier-case": "warn"
1533
- };
1610
+ });
1534
1611
 
1535
1612
  // src/eslint/unicorn/config.ts
1536
- var unicornConfig = (customRules) => ({
1613
+ var unicornConfig = ({
1614
+ customRules,
1615
+ filenameCase = "kebabCase"
1616
+ }) => ({
1537
1617
  name: configNames.unicorn,
1538
1618
  plugins: {
1539
1619
  unicorn
1540
1620
  },
1541
1621
  rules: {
1542
- ...rules,
1622
+ ...rules(filenameCase),
1543
1623
  ...customRules ?? {}
1544
1624
  }
1545
1625
  });
1546
1626
 
1547
1627
  // src/eslint/index.ts
1548
- var defaultTestingConfig = {
1549
- filenamePattern: "test",
1550
- files: ["**/*.{test,spec}.{ts,tsx,js,jsx}"],
1551
- formattingRules: true,
1552
- framework: "vitest",
1553
- itOrTest: "it"
1554
- };
1555
1628
  var eslintConfig = ({
1629
+ convex = false,
1556
1630
  functionStyle = "arrow",
1557
1631
  ignores = [],
1558
1632
  importPlugin = true,
@@ -1562,16 +1636,15 @@ var eslintConfig = ({
1562
1636
  rules: rules2,
1563
1637
  sorting = true,
1564
1638
  storybook = false,
1565
- testing = defaultTestingConfig,
1639
+ testing,
1566
1640
  turbo: turbo2 = false,
1567
1641
  typescript = true,
1568
- unicorn: unicorn2 = true
1642
+ unicorn: unicorn2 = { filenameCase: "kebabCase" }
1569
1643
  } = {}, ...additionalConfigs) => {
1570
1644
  const categorizedRules = rules2 === void 0 ? {} : processCustomRules(rules2);
1571
- const usingNextjs = isObject(react2) && react2.framework === "next";
1572
1645
  const configs = [
1573
1646
  ignoresConfig({
1574
- next: usingNextjs,
1647
+ reactFramework: isObject(react2) && react2.framework ? react2.framework : "none",
1575
1648
  storybook,
1576
1649
  userIgnores: ignores
1577
1650
  }),
@@ -1622,16 +1695,34 @@ var eslintConfig = ({
1622
1695
  typescript: Boolean(typescript)
1623
1696
  })
1624
1697
  );
1625
- if (usingNextjs) {
1698
+ if (isObject(react2) && react2.framework === "next") {
1626
1699
  configs.push(nextjsConfig(categorizedRules[configNames.nextjs]));
1627
1700
  }
1628
1701
  }
1629
1702
  if (query) {
1630
1703
  configs.push(queryConfig(categorizedRules[configNames.query]));
1631
1704
  }
1705
+ if (convex) {
1706
+ configs.push(convexConfig(categorizedRules[configNames.convex]));
1707
+ }
1632
1708
  if (testing !== false) {
1709
+ const defaultTestingConfig = {
1710
+ filenamePattern: "test",
1711
+ files: ["**/*.{test,spec}.{ts,tsx,js,jsx}"],
1712
+ formattingRules: true,
1713
+ framework: "vitest",
1714
+ importRestrictions: true,
1715
+ itOrTest: "it"
1716
+ };
1633
1717
  const mergedTestingConfig = isObject(testing) ? { ...defaultTestingConfig, ...testing } : defaultTestingConfig;
1634
- const { filenamePattern, files, formattingRules, framework, itOrTest } = mergedTestingConfig;
1718
+ const {
1719
+ filenamePattern,
1720
+ files,
1721
+ formattingRules,
1722
+ framework,
1723
+ importRestrictions,
1724
+ itOrTest
1725
+ } = mergedTestingConfig;
1635
1726
  configs.push(
1636
1727
  testingConfig(
1637
1728
  {
@@ -1639,7 +1730,9 @@ var eslintConfig = ({
1639
1730
  files,
1640
1731
  formattingRules,
1641
1732
  framework,
1642
- itOrTest
1733
+ importRestrictions,
1734
+ itOrTest,
1735
+ typescript: Boolean(typescript)
1643
1736
  },
1644
1737
  categorizedRules[configNames.testing]
1645
1738
  )
@@ -1651,7 +1744,13 @@ var eslintConfig = ({
1651
1744
  );
1652
1745
  }
1653
1746
  if (unicorn2) {
1654
- configs.push(unicornConfig(categorizedRules[configNames.unicorn]));
1747
+ const filenameCase = isObject(unicorn2) ? unicorn2.filenameCase : void 0;
1748
+ configs.push(
1749
+ unicornConfig({
1750
+ customRules: categorizedRules[configNames.unicorn],
1751
+ filenameCase
1752
+ })
1753
+ );
1655
1754
  }
1656
1755
  if (functionStyle === "arrow") {
1657
1756
  configs.push(