@caspeco/eslint-config-ts 5.0.0-beta.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/README.md ADDED
@@ -0,0 +1,77 @@
1
+ # @caspeco/eslint-config-ts
2
+
3
+ Caspeco ESLint configuration for TypeScript projects.
4
+
5
+ ## Installation
6
+
7
+ ```bash
8
+ npm install --save-dev @caspeco/eslint-config-ts
9
+ ```
10
+
11
+ ## Usage
12
+
13
+ Create an `eslint.config.js` file in your project root:
14
+
15
+ ```js
16
+ import config from "@caspeco/eslint-config-ts";
17
+
18
+ export default [{ ignores: ["dist/**/*", "build/**/*"] }, ...config];
19
+ ```
20
+
21
+ ## What's Included
22
+
23
+ This configuration includes:
24
+
25
+ - **TypeScript ESLint** - Using [recommended-type-checked](https://typescript-eslint.io/users/configs/#recommended-type-checked) config
26
+ - **No Barrel Files** - Prevents barrel file usage (index files that just re-export)
27
+ - **File Naming Conventions** - Enforces kebab-case for files and folders
28
+ - **Prettier Integration** - Compatible with Prettier formatting
29
+
30
+ ## Peer Dependencies
31
+
32
+ This package requires the following peer dependencies (automatically installed by npm 7+):
33
+
34
+ - `@eslint/js` >=9
35
+ - `eslint` >=9
36
+ - `eslint-config-prettier` >=9
37
+ - `eslint-plugin-check-file` >=2.8.0
38
+ - `eslint-plugin-no-barrel-files` >=1.2.0
39
+ - `typescript-eslint` >=8.39.0
40
+
41
+ ## Plugins
42
+
43
+ ### typescript-eslint
44
+
45
+ Using [recommended-type-checked](https://typescript-eslint.io/users/configs/#recommended-type-checked) config with project service enabled.
46
+
47
+ ### eslint-plugin-no-barrel-files
48
+
49
+ [eslint-plugin-no-barrel-files](https://github.com/art0rz/eslint-plugin-no-barrel-files)
50
+
51
+ We opt out of using barrel files as they can:
52
+
53
+ - Slow down builds and tests
54
+ - Cause circular dependencies
55
+ - Make tree shaking more difficult
56
+
57
+ **Note:** We allow barrel files for libraries consumed as npm packages.
58
+
59
+ More information:
60
+
61
+ - [Please Stop Using Barrel Files](https://tkdodo.eu/blog/please-stop-using-barrel-files)
62
+
63
+ ### eslint-plugin-check-file
64
+
65
+ [eslint-plugin-check-file](https://github.com/dukeluo/eslint-plugin-check-file/)
66
+
67
+ Enforces `KEBAB_CASE` for filenames and folders.
68
+
69
+ #### Why kebab-case?
70
+
71
+ 1. **Cross-Platform Consistency**: Avoids issues on case-insensitive systems (Windows, macOS) where `MyFile.js` and `myfile.js` are treated the same
72
+ 2. **Readability**: Clear, natural separation of words with hyphens
73
+ 3. **Predictability**: Consistent file-naming pattern across the project
74
+
75
+ ## License
76
+
77
+ MIT
package/bin/README.md ADDED
@@ -0,0 +1,60 @@
1
+ # Migration Tool
2
+
3
+ This migration tool helps you upgrade from `@caspeco/eslint-config` v4.x to `@caspeco/eslint-config-ts` v5.x.
4
+
5
+ ## Usage
6
+
7
+ ```bash
8
+ # Preview changes without modifying files
9
+ npx caspeco-ts-migrate-v5 . --dry-run
10
+
11
+ # Apply changes to your project
12
+ npx caspeco-ts-migrate-v5 .
13
+
14
+ # Or specify a directory
15
+ npx caspeco-ts-migrate-v5 src/
16
+ ```
17
+
18
+ ## What it does
19
+
20
+ The migration script automatically updates:
21
+
22
+ 1. **Package imports**: Changes `@caspeco/eslint-config` to `@caspeco/eslint-config-ts`
23
+ 2. **Named imports**: Converts `{ vanillaConfig }` to default import `config`
24
+ 3. **Variable references**: Updates `vanillaConfig` to `config` throughout your config files
25
+
26
+ ## Example
27
+
28
+ **Before:**
29
+
30
+ ```js
31
+ import { vanillaConfig } from "@caspeco/eslint-config";
32
+
33
+ export default [{ ignores: ["dist/**/*"] }, ...vanillaConfig];
34
+ ```
35
+
36
+ **After:**
37
+
38
+ ```js
39
+ import config from "@caspeco/eslint-config-ts";
40
+
41
+ export default [{ ignores: ["dist/**/*"] }, ...config];
42
+ ```
43
+
44
+ ## Files processed
45
+
46
+ The script processes files with these extensions:
47
+
48
+ - `.js`, `.jsx`
49
+ - `.ts`, `.tsx`
50
+ - `.mjs`, `.cjs`
51
+ - `.json`
52
+ - `.md`
53
+
54
+ It automatically skips:
55
+
56
+ - `node_modules/`
57
+ - `.git/`
58
+ - `dist/`
59
+ - `build/`
60
+ - `coverage/`
@@ -0,0 +1,170 @@
1
+ #!/usr/bin/env node
2
+
3
+ /**
4
+ * Migration script for v4.x to v5.x (TypeScript/Vanilla)
5
+ *
6
+ * This script updates:
7
+ * - Package imports: @caspeco/eslint-config → @caspeco/eslint-config-ts
8
+ * - Named imports: { vanillaConfig } → default import
9
+ * - Variable references: vanillaConfig → config
10
+ *
11
+ * Usage:
12
+ * npx caspeco-ts-migrate-v5 [path] [--dry-run]
13
+ *
14
+ * Examples:
15
+ * npx caspeco-ts-migrate-v5 .
16
+ * npx caspeco-ts-migrate-v5 . --dry-run
17
+ */
18
+
19
+ import fs from "fs";
20
+ import path from "path";
21
+ import { fileURLToPath } from "url";
22
+
23
+ const __filename = fileURLToPath(import.meta.url);
24
+ const __dirname = path.dirname(__filename);
25
+
26
+ const args = process.argv.slice(2);
27
+ const isDryRun = args.includes("--dry-run");
28
+ const targetPath = args.find((arg) => !arg.startsWith("--")) || ".";
29
+
30
+ // Import transformation for eslint.config.js files
31
+ function transformImports(content, filename) {
32
+ if (!filename.includes("eslint.config.")) {
33
+ return { content, hasChanges: false };
34
+ }
35
+
36
+ let transformed = content;
37
+ let hasChanges = false;
38
+
39
+ // Replace import { vanillaConfig } from "@caspeco/eslint-config" → import config from "@caspeco/eslint-config-ts"
40
+ const importPattern = /import\s*\{\s*vanillaConfig\s*\}\s*from\s*["']@caspeco\/eslint-config["']/;
41
+ if (importPattern.test(transformed)) {
42
+ transformed = transformed.replace(importPattern, 'import config from "@caspeco/eslint-config-ts"');
43
+ hasChanges = true;
44
+
45
+ // If we changed the import, also update variable references
46
+ transformed = transformed.replace(/\bvanillaConfig\b/g, "config");
47
+ }
48
+
49
+ return { content: transformed, hasChanges };
50
+ }
51
+
52
+ // File extensions to process
53
+ const extensions = [".js", ".jsx", ".ts", ".tsx", ".json", ".md", ".mjs", ".cjs"];
54
+
55
+ let filesProcessed = 0;
56
+ let filesChanged = 0;
57
+ let totalReplacements = 0;
58
+
59
+ /**
60
+ * Recursively process directory
61
+ */
62
+ function processDirectory(dir) {
63
+ const entries = fs.readdirSync(dir, { withFileTypes: true });
64
+
65
+ for (const entry of entries) {
66
+ const fullPath = path.join(dir, entry.name);
67
+
68
+ // Skip node_modules, .git, dist, build directories
69
+ if (entry.isDirectory()) {
70
+ if (["node_modules", ".git", "dist", "build", "coverage"].includes(entry.name)) {
71
+ continue;
72
+ }
73
+ processDirectory(fullPath);
74
+ } else {
75
+ const ext = path.extname(entry.name);
76
+ if (extensions.includes(ext)) {
77
+ processFile(fullPath);
78
+ }
79
+ }
80
+ }
81
+ }
82
+
83
+ /**
84
+ * Process a single file
85
+ */
86
+ function processFile(filePath) {
87
+ filesProcessed++;
88
+
89
+ let content;
90
+ try {
91
+ content = fs.readFileSync(filePath, "utf8");
92
+ } catch (err) {
93
+ console.error(`Error reading ${filePath}:`, err.message);
94
+ return;
95
+ }
96
+
97
+ let newContent = content;
98
+ let fileReplacements = 0;
99
+
100
+ // Try import transformations for config files
101
+ const filename = path.basename(filePath);
102
+ const importResult = transformImports(newContent, filename);
103
+ if (importResult.hasChanges) {
104
+ newContent = importResult.content;
105
+ fileReplacements++; // Count as one logical replacement (import + references)
106
+ }
107
+
108
+ // If changes were made
109
+ if (fileReplacements > 0) {
110
+ filesChanged++;
111
+ totalReplacements += fileReplacements;
112
+
113
+ const relativePath = path.relative(process.cwd(), filePath);
114
+ console.log(
115
+ `${isDryRun ? "[DRY RUN] " : ""}✓ ${relativePath} (${fileReplacements} replacement${fileReplacements > 1 ? "s" : ""})`,
116
+ );
117
+
118
+ if (!isDryRun) {
119
+ try {
120
+ fs.writeFileSync(filePath, newContent, "utf8");
121
+ } catch (err) {
122
+ console.error(`Error writing ${filePath}:`, err.message);
123
+ }
124
+ }
125
+ }
126
+ }
127
+
128
+ /**
129
+ * Main execution
130
+ */
131
+ function main() {
132
+ console.log("Caspeco TypeScript Migration Tool");
133
+ console.log("==================================\n");
134
+
135
+ if (isDryRun) {
136
+ console.log("🔍 Running in DRY RUN mode - no files will be modified\n");
137
+ }
138
+
139
+ const resolvedPath = path.resolve(targetPath);
140
+
141
+ if (!fs.existsSync(resolvedPath)) {
142
+ console.error(`Error: Path "${targetPath}" does not exist`);
143
+ process.exit(1);
144
+ }
145
+
146
+ console.log(`Scanning: ${resolvedPath}\n`);
147
+
148
+ const stat = fs.statSync(resolvedPath);
149
+ if (stat.isDirectory()) {
150
+ processDirectory(resolvedPath);
151
+ } else {
152
+ processFile(resolvedPath);
153
+ }
154
+
155
+ console.log("\nSummary:");
156
+ console.log("========");
157
+ console.log(`Files scanned: ${filesProcessed}`);
158
+ console.log(`Files with changes: ${filesChanged}`);
159
+ console.log(`Total replacements: ${totalReplacements}`);
160
+
161
+ if (isDryRun && filesChanged > 0) {
162
+ console.log("\n💡 Run without --dry-run to apply changes");
163
+ } else if (filesChanged > 0) {
164
+ console.log("\n✅ Migration complete!");
165
+ } else {
166
+ console.log("\n✨ No files needed migration");
167
+ }
168
+ }
169
+
170
+ main();
package/package.json ADDED
@@ -0,0 +1,51 @@
1
+ {
2
+ "name": "@caspeco/eslint-config-ts",
3
+ "version": "5.0.0-beta.0",
4
+ "description": "Caspeco ESLint configuration for TypeScript projects",
5
+ "keywords": [
6
+ "eslint",
7
+ "eslint-config",
8
+ "typescript",
9
+ "caspeco"
10
+ ],
11
+ "homepage": "https://github.com/Caspeco/eslint-config#readme",
12
+ "bugs": {
13
+ "url": "https://github.com/Caspeco/eslint-config/issues"
14
+ },
15
+ "repository": {
16
+ "type": "git",
17
+ "url": "https://github.com/Caspeco/eslint-config",
18
+ "directory": "packages/eslint-config-ts"
19
+ },
20
+ "license": "MIT",
21
+ "type": "module",
22
+ "main": "src/index.js",
23
+ "bin": {
24
+ "caspeco-ts-migrate-v5": "./bin/caspeco-ts-migrate-v5.js"
25
+ },
26
+ "files": [
27
+ "src",
28
+ "bin"
29
+ ],
30
+ "scripts": {
31
+ "test": "vitest"
32
+ },
33
+ "devDependencies": {
34
+ "@caspeco/test-utils": "*"
35
+ },
36
+ "peerDependencies": {
37
+ "@eslint/js": ">=9",
38
+ "eslint": ">=9",
39
+ "eslint-config-prettier": ">=9",
40
+ "eslint-plugin-check-file": ">=2.8.0",
41
+ "eslint-plugin-no-barrel-files": ">=1.2.0",
42
+ "typescript-eslint": ">=8.39.0"
43
+ },
44
+ "engines": {
45
+ "node": ">=24",
46
+ "npm": ">=11.5.1"
47
+ },
48
+ "publishConfig": {
49
+ "access": "public"
50
+ }
51
+ }
package/src/index.js ADDED
@@ -0,0 +1,57 @@
1
+ import { configs as typescriptEslintConfig, parser, plugin } from "typescript-eslint";
2
+ import js from "@eslint/js";
3
+ import checkFile from "eslint-plugin-check-file";
4
+ import eslintConfigPrettier from "eslint-config-prettier";
5
+ import noBarrelFilesPlugin from "eslint-plugin-no-barrel-files";
6
+
7
+ /** @type {import('@typescript-eslint/utils').TSESLint.FlatConfig.ConfigArray} */
8
+ const flatConfig = [
9
+ js.configs.recommended,
10
+ ...typescriptEslintConfig.recommendedTypeChecked,
11
+ {
12
+ languageOptions: {
13
+ parserOptions: {
14
+ projectService: true,
15
+ // @ts-ignore
16
+ tsconfigRootDir: import.meta.name,
17
+ },
18
+ },
19
+ },
20
+ {
21
+ files: ["**/*.ts", "**/*.tsx"],
22
+ ignores: ["**/*.d.ts", "**/dist/**/*", "**/node_modules/**/*"],
23
+ linterOptions: {
24
+ reportUnusedDisableDirectives: "warn",
25
+ },
26
+ languageOptions: {
27
+ parser: parser,
28
+ sourceType: "module",
29
+ },
30
+ plugins: {
31
+ "@typescript-eslint": plugin,
32
+ "check-file": checkFile,
33
+ "no-barrel-files": noBarrelFilesPlugin,
34
+ },
35
+ rules: {
36
+ "no-barrel-files/no-barrel-files": "error",
37
+ "check-file/filename-naming-convention": [
38
+ "error",
39
+ {
40
+ "**/*.{js,ts,tsx}": "KEBAB_CASE",
41
+ },
42
+ {
43
+ ignoreMiddleExtensions: true,
44
+ },
45
+ ],
46
+ "check-file/folder-naming-convention": [
47
+ "error",
48
+ {
49
+ "src/**/": "KEBAB_CASE",
50
+ },
51
+ ],
52
+ },
53
+ },
54
+ eslintConfigPrettier,
55
+ ];
56
+
57
+ export default flatConfig;