js-style-kit 0.7.0 → 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.
package/dist/index.d.ts CHANGED
@@ -39,13 +39,22 @@ interface EslintConfigObject<Rules extends Linter.RulesRecord = Linter.RulesReco
39
39
  }
40
40
  type FilenameCase = "camelCase" | "kebabCase" | "pascalCase" | "snakeCase";
41
41
  type FunctionStyle = "arrow" | "declaration" | "expression";
42
+ type ReactFramework = "next" | "none" | "react-router" | "remix" | "vite";
42
43
 
43
44
  interface TestingConfig {
44
45
  filenamePattern?: "spec" | "test";
45
46
  files?: string[];
46
47
  formattingRules?: boolean;
47
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;
48
56
  itOrTest?: "it" | "test";
57
+ typescript?: boolean;
49
58
  }
50
59
 
51
60
  interface EslintConfigOptions {
@@ -58,7 +67,7 @@ interface EslintConfigOptions {
58
67
  };
59
68
  query?: boolean;
60
69
  react?: boolean | {
61
- framework?: "next" | "none" | "react-router" | "remix" | "vite";
70
+ framework?: ReactFramework;
62
71
  reactCompiler?: boolean;
63
72
  reactRefresh?: boolean;
64
73
  };
@@ -93,8 +102,9 @@ interface EslintConfigOptions {
93
102
  * @param options.testing - An object with the following properties:
94
103
  * - `filenamePattern`: One of "spec" or "test" to determine which filename pattern to use.
95
104
  * - `files`: Array of file patterns to include in the configuration.
96
- * - `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.
97
106
  * - `formattingRules`: Whether to include formatting rules like padding around blocks.
107
+ * - `importRestrictions`: Whether to enforce imports from the correct testing framework.
98
108
  * - `itOrTest`: One of "it" or "test" to determine which test function to use.
99
109
  * @param options.typescript - Whether to include TypeScript rules. Can be a boolean or a string with path to tsconfig.
100
110
  * @param options.turbo - Whether to include Turborepo rules. Defaults to false.
@@ -137,4 +147,4 @@ interface PrettierConfigWithPlugins extends Config, SortJsonOptions, PluginOptio
137
147
  */
138
148
  declare const prettierConfig: (options?: PrettierConfigOptions) => PrettierConfigWithPlugins;
139
149
 
140
- export { type EslintConfigObject, type EslintConfigOptions, type EslintRuleConfig, type EslintSeverity, type FilenameCase, 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
@@ -514,13 +514,14 @@ var convexConfig = (customRules) => ({
514
514
 
515
515
  // src/eslint/ignores.ts
516
516
  var ignoresConfig = ({
517
- next = false,
518
- storybook = false,
519
- userIgnores = []
520
- } = {}) => ({
517
+ reactFramework,
518
+ storybook,
519
+ userIgnores
520
+ }) => ({
521
521
  ignores: [
522
522
  "**/dist/",
523
- ...next ? [".next"] : [],
523
+ ...reactFramework === "next" ? [".next"] : [],
524
+ ...reactFramework === "react-router" ? [".react-router"] : [],
524
525
  ...storybook ? ["!.storybook"] : [],
525
526
  ...userIgnores
526
527
  ],
@@ -1158,6 +1159,56 @@ var storybookConfig = (customRules) => [
1158
1159
  import jest from "eslint-plugin-jest";
1159
1160
  import vitest from "eslint-plugin-vitest";
1160
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
+
1161
1212
  // src/eslint/testing/jest-rules.ts
1162
1213
  var jestRules = (itOrTest = "test") => ({
1163
1214
  "jest/consistent-test-it": [
@@ -1248,17 +1299,14 @@ var vitestRules = (itOrTest = "test") => ({
1248
1299
 
1249
1300
  // src/eslint/testing/config.ts
1250
1301
  var testingConfig = ({
1251
- filenamePattern,
1302
+ filenamePattern = "test",
1252
1303
  files,
1253
- formattingRules,
1254
- framework,
1255
- itOrTest
1256
- } = {
1257
- filenamePattern: "test",
1258
- formattingRules: true,
1259
- framework: "vitest",
1260
- itOrTest: "test"
1261
- }, customRules) => ({
1304
+ formattingRules = true,
1305
+ framework = "vitest",
1306
+ importRestrictions = true,
1307
+ itOrTest = "test",
1308
+ typescript = true
1309
+ } = {}, customRules) => ({
1262
1310
  files: files ?? ["**/*.{test,spec}.{ts,tsx,js,jsx}"],
1263
1311
  languageOptions: {
1264
1312
  globals: framework === "vitest" ? { ...vitest.environments.env.globals } : jest.environments.globals.globals
@@ -1269,8 +1317,8 @@ var testingConfig = ({
1269
1317
  vitest
1270
1318
  },
1271
1319
  rules: {
1320
+ ...typescript ? { "@typescript-eslint/unbound-method": "off" } : {},
1272
1321
  // jest doesn't have a file name rule, so we'll use this one for both
1273
- "@typescript-eslint/unbound-method": "off",
1274
1322
  "vitest/consistent-test-filename": [
1275
1323
  "warn",
1276
1324
  {
@@ -1288,6 +1336,7 @@ var testingConfig = ({
1288
1336
  "jest/padding-around-expect-groups": "warn",
1289
1337
  "jest/padding-around-test-blocks": "warn"
1290
1338
  } : {},
1339
+ ...importRestrictions ? getImportRestrictions(framework) : {},
1291
1340
  ...customRules ?? {}
1292
1341
  },
1293
1342
  ...framework !== "jest" && framework !== "vitest" ? {
@@ -1576,13 +1625,6 @@ var unicornConfig = ({
1576
1625
  });
1577
1626
 
1578
1627
  // src/eslint/index.ts
1579
- var defaultTestingConfig = {
1580
- filenamePattern: "test",
1581
- files: ["**/*.{test,spec}.{ts,tsx,js,jsx}"],
1582
- formattingRules: true,
1583
- framework: "vitest",
1584
- itOrTest: "it"
1585
- };
1586
1628
  var eslintConfig = ({
1587
1629
  convex = false,
1588
1630
  functionStyle = "arrow",
@@ -1594,16 +1636,15 @@ var eslintConfig = ({
1594
1636
  rules: rules2,
1595
1637
  sorting = true,
1596
1638
  storybook = false,
1597
- testing = defaultTestingConfig,
1639
+ testing,
1598
1640
  turbo: turbo2 = false,
1599
1641
  typescript = true,
1600
1642
  unicorn: unicorn2 = { filenameCase: "kebabCase" }
1601
1643
  } = {}, ...additionalConfigs) => {
1602
1644
  const categorizedRules = rules2 === void 0 ? {} : processCustomRules(rules2);
1603
- const usingNextjs = isObject(react2) && react2.framework === "next";
1604
1645
  const configs = [
1605
1646
  ignoresConfig({
1606
- next: usingNextjs,
1647
+ reactFramework: isObject(react2) && react2.framework ? react2.framework : "none",
1607
1648
  storybook,
1608
1649
  userIgnores: ignores
1609
1650
  }),
@@ -1654,7 +1695,7 @@ var eslintConfig = ({
1654
1695
  typescript: Boolean(typescript)
1655
1696
  })
1656
1697
  );
1657
- if (usingNextjs) {
1698
+ if (isObject(react2) && react2.framework === "next") {
1658
1699
  configs.push(nextjsConfig(categorizedRules[configNames.nextjs]));
1659
1700
  }
1660
1701
  }
@@ -1665,8 +1706,23 @@ var eslintConfig = ({
1665
1706
  configs.push(convexConfig(categorizedRules[configNames.convex]));
1666
1707
  }
1667
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
+ };
1668
1717
  const mergedTestingConfig = isObject(testing) ? { ...defaultTestingConfig, ...testing } : defaultTestingConfig;
1669
- const { filenamePattern, files, formattingRules, framework, itOrTest } = mergedTestingConfig;
1718
+ const {
1719
+ filenamePattern,
1720
+ files,
1721
+ formattingRules,
1722
+ framework,
1723
+ importRestrictions,
1724
+ itOrTest
1725
+ } = mergedTestingConfig;
1670
1726
  configs.push(
1671
1727
  testingConfig(
1672
1728
  {
@@ -1674,7 +1730,9 @@ var eslintConfig = ({
1674
1730
  files,
1675
1731
  formattingRules,
1676
1732
  framework,
1677
- itOrTest
1733
+ importRestrictions,
1734
+ itOrTest,
1735
+ typescript: Boolean(typescript)
1678
1736
  },
1679
1737
  categorizedRules[configNames.testing]
1680
1738
  )