@jterrazz/codestyle 1.0.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/LICENSE +21 -0
- package/README.md +125 -0
- package/package.json +49 -0
- package/src/codestyle.sh +160 -0
- package/src/index.js +14 -0
- package/src/oxfmt/index.json +11 -0
- package/src/oxlint/base.json +74 -0
- package/src/oxlint/expo.json +15 -0
- package/src/oxlint/nextjs.json +10 -0
- package/src/oxlint/node.json +8 -0
- package/src/oxlint/plugins/remove-ts-extensions.js +58 -0
- package/src/oxlint/plugins/require-js-extensions.js +65 -0
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2025 Jean-Baptiste Terrazzoni <contact@jterrazz.com>
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.md
ADDED
|
@@ -0,0 +1,125 @@
|
|
|
1
|
+
_Hey there - I'm Jean-Baptiste, just another developer doing weird things with code. All my projects live on [jterrazz.com](https://jterrazz.com) - complete with backstories and lessons learned. Feel free to poke around - you might just find something useful!_
|
|
2
|
+
|
|
3
|
+
# @jterrazz/codestyle
|
|
4
|
+
|
|
5
|
+
Unified lint/format configs to standardize TypeScript codebases with Oxlint + Oxfmt.
|
|
6
|
+
|
|
7
|
+
## Installation
|
|
8
|
+
|
|
9
|
+
Install the package using npm:
|
|
10
|
+
|
|
11
|
+
```bash
|
|
12
|
+
npm install @jterrazz/codestyle --save-dev
|
|
13
|
+
```
|
|
14
|
+
|
|
15
|
+
## Usage
|
|
16
|
+
|
|
17
|
+
### Oxlint Configuration
|
|
18
|
+
|
|
19
|
+
Choose the configuration that matches your environment:
|
|
20
|
+
|
|
21
|
+
**For Node.js projects:**
|
|
22
|
+
|
|
23
|
+
```json
|
|
24
|
+
// oxlint.json
|
|
25
|
+
{
|
|
26
|
+
"extends": ["@jterrazz/codestyle/oxlint/node"]
|
|
27
|
+
}
|
|
28
|
+
```
|
|
29
|
+
|
|
30
|
+
**For Expo/React Native projects:**
|
|
31
|
+
|
|
32
|
+
```json
|
|
33
|
+
// oxlint.json
|
|
34
|
+
{
|
|
35
|
+
"extends": ["@jterrazz/codestyle/oxlint/expo"]
|
|
36
|
+
}
|
|
37
|
+
```
|
|
38
|
+
|
|
39
|
+
**For Next.js projects:**
|
|
40
|
+
|
|
41
|
+
```json
|
|
42
|
+
// oxlint.json
|
|
43
|
+
{
|
|
44
|
+
"extends": ["@jterrazz/codestyle/oxlint/nextjs"]
|
|
45
|
+
}
|
|
46
|
+
```
|
|
47
|
+
|
|
48
|
+
### Oxfmt Configuration
|
|
49
|
+
|
|
50
|
+
```json
|
|
51
|
+
// oxfmt.json
|
|
52
|
+
{
|
|
53
|
+
"extends": ["@jterrazz/codestyle/oxfmt"]
|
|
54
|
+
}
|
|
55
|
+
```
|
|
56
|
+
|
|
57
|
+
## Features
|
|
58
|
+
|
|
59
|
+
### Environment-Specific Configurations
|
|
60
|
+
|
|
61
|
+
**Node.js Configuration:**
|
|
62
|
+
|
|
63
|
+
- Requires explicit file extensions (`.js`) for imports
|
|
64
|
+
- Optimized for Node.js patterns
|
|
65
|
+
|
|
66
|
+
**Expo/React Native Configuration:**
|
|
67
|
+
|
|
68
|
+
- No file extensions in imports (auto-fixed)
|
|
69
|
+
- React plugin enabled
|
|
70
|
+
|
|
71
|
+
**Next.js Configuration:**
|
|
72
|
+
|
|
73
|
+
- No file extensions in imports (Turbopack compatibility, auto-fixed)
|
|
74
|
+
- React and Next.js plugins enabled
|
|
75
|
+
|
|
76
|
+
### Shared Features
|
|
77
|
+
|
|
78
|
+
- **TypeScript**: Strict type checking with consistent type imports
|
|
79
|
+
- **Import Sorting**: Automated import organization with architectural grouping (via perfectionist)
|
|
80
|
+
- **Code Quality**: Perfectionist plugin for consistent code style
|
|
81
|
+
- **Performance**: Oxlint is 50-100x faster than ESLint, Oxfmt is 10x faster than Prettier, tsgo is 10x faster than tsc
|
|
82
|
+
|
|
83
|
+
## CLI Tools
|
|
84
|
+
|
|
85
|
+
This package includes a CLI tool for running quality checks:
|
|
86
|
+
|
|
87
|
+
```bash
|
|
88
|
+
# Run all quality checks (tsgo, Oxlint, Oxfmt) in parallel
|
|
89
|
+
npx codestyle
|
|
90
|
+
|
|
91
|
+
# Automatically fix all fixable issues (Oxlint --fix, Oxfmt format)
|
|
92
|
+
npx codestyle --fix
|
|
93
|
+
|
|
94
|
+
# Run individual checks
|
|
95
|
+
npx codestyle --type # TypeScript type checking only
|
|
96
|
+
npx codestyle --lint # Linting only
|
|
97
|
+
npx codestyle --format # Format checking only
|
|
98
|
+
|
|
99
|
+
# Combine flags
|
|
100
|
+
npx codestyle --lint --fix # Fix lint issues only
|
|
101
|
+
npx codestyle --format --fix # Format files only
|
|
102
|
+
```
|
|
103
|
+
|
|
104
|
+
## Scripts
|
|
105
|
+
|
|
106
|
+
Add these scripts to your `package.json` for common development tasks:
|
|
107
|
+
|
|
108
|
+
```json
|
|
109
|
+
{
|
|
110
|
+
"scripts": {
|
|
111
|
+
"codestyle": "codestyle",
|
|
112
|
+
"codestyle:fix": "codestyle --fix"
|
|
113
|
+
}
|
|
114
|
+
}
|
|
115
|
+
```
|
|
116
|
+
|
|
117
|
+
## Configuration
|
|
118
|
+
|
|
119
|
+
The configurations are fully modular and include:
|
|
120
|
+
|
|
121
|
+
- **Base configuration**: Common rules for TypeScript, import sorting, code quality
|
|
122
|
+
- **Environment-specific**: Tailored rules for Node.js, Expo/React Native, and Next.js
|
|
123
|
+
- **Custom plugins**: JS plugins for import extension enforcement
|
|
124
|
+
|
|
125
|
+
Happy coding!
|
package/package.json
ADDED
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@jterrazz/codestyle",
|
|
3
|
+
"version": "1.0.0",
|
|
4
|
+
"author": "Jean-Baptiste Terrazzoni <contact@jterrazz.com>",
|
|
5
|
+
"bin": {
|
|
6
|
+
"codestyle": "./src/codestyle.sh"
|
|
7
|
+
},
|
|
8
|
+
"files": [
|
|
9
|
+
"src"
|
|
10
|
+
],
|
|
11
|
+
"type": "module",
|
|
12
|
+
"main": "src/index.js",
|
|
13
|
+
"exports": {
|
|
14
|
+
".": "./src/index.js",
|
|
15
|
+
"./oxlint/node": "./src/oxlint/node.json",
|
|
16
|
+
"./oxlint/expo": "./src/oxlint/expo.json",
|
|
17
|
+
"./oxlint/nextjs": "./src/oxlint/nextjs.json",
|
|
18
|
+
"./oxfmt": "./src/oxfmt/index.json"
|
|
19
|
+
},
|
|
20
|
+
"publishConfig": {
|
|
21
|
+
"registry": "https://registry.npmjs.org/"
|
|
22
|
+
},
|
|
23
|
+
"scripts": {
|
|
24
|
+
"lint": "oxlint --ignore-pattern '**/fixtures/**'",
|
|
25
|
+
"lint:fix": "oxlint --fix --ignore-pattern '**/fixtures/**'",
|
|
26
|
+
"format": "oxfmt",
|
|
27
|
+
"format:check": "oxfmt --check",
|
|
28
|
+
"test": "vitest --run",
|
|
29
|
+
"test:watch": "vitest",
|
|
30
|
+
"build": "# no build script"
|
|
31
|
+
},
|
|
32
|
+
"dependencies": {
|
|
33
|
+
"@typescript/native-preview": "^7.0.0-dev",
|
|
34
|
+
"eslint-plugin-perfectionist": "^4.15.1",
|
|
35
|
+
"oxfmt": "^0.19.0",
|
|
36
|
+
"oxlint": "^1.35.0"
|
|
37
|
+
},
|
|
38
|
+
"devDependencies": {
|
|
39
|
+
"vitest": "^3.2.4"
|
|
40
|
+
},
|
|
41
|
+
"peerDependencies": {
|
|
42
|
+
"@types/node": "*"
|
|
43
|
+
},
|
|
44
|
+
"peerDependenciesMeta": {
|
|
45
|
+
"@types/node": {
|
|
46
|
+
"optional": true
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
}
|
package/src/codestyle.sh
ADDED
|
@@ -0,0 +1,160 @@
|
|
|
1
|
+
#!/bin/bash
|
|
2
|
+
|
|
3
|
+
# Colors for output (using Vitest-like colors)
|
|
4
|
+
RED='\033[0;31m'
|
|
5
|
+
GREEN='\033[0;32m'
|
|
6
|
+
CYAN_BG='\033[46m' # Cyan background
|
|
7
|
+
BRIGHT_WHITE='\033[1;30m' # Bold black text
|
|
8
|
+
NC='\033[0m' # No Color
|
|
9
|
+
|
|
10
|
+
# Parse arguments
|
|
11
|
+
FIX_MODE=false
|
|
12
|
+
RUN_TYPE=false
|
|
13
|
+
RUN_LINT=false
|
|
14
|
+
RUN_FORMAT=false
|
|
15
|
+
RUN_ALL=true
|
|
16
|
+
EXTRA_ARGS=()
|
|
17
|
+
|
|
18
|
+
for arg in "$@"; do
|
|
19
|
+
case $arg in
|
|
20
|
+
--fix)
|
|
21
|
+
FIX_MODE=true
|
|
22
|
+
;;
|
|
23
|
+
--type)
|
|
24
|
+
RUN_TYPE=true
|
|
25
|
+
RUN_ALL=false
|
|
26
|
+
;;
|
|
27
|
+
--lint)
|
|
28
|
+
RUN_LINT=true
|
|
29
|
+
RUN_ALL=false
|
|
30
|
+
;;
|
|
31
|
+
--format)
|
|
32
|
+
RUN_FORMAT=true
|
|
33
|
+
RUN_ALL=false
|
|
34
|
+
;;
|
|
35
|
+
*)
|
|
36
|
+
EXTRA_ARGS+=("$arg")
|
|
37
|
+
;;
|
|
38
|
+
esac
|
|
39
|
+
done
|
|
40
|
+
|
|
41
|
+
# If running all, enable all checks
|
|
42
|
+
if [ "$RUN_ALL" = true ]; then
|
|
43
|
+
RUN_TYPE=true
|
|
44
|
+
RUN_LINT=true
|
|
45
|
+
RUN_FORMAT=true
|
|
46
|
+
fi
|
|
47
|
+
|
|
48
|
+
# Create a temporary directory for log files
|
|
49
|
+
tmp_dir=$(mktemp -d)
|
|
50
|
+
cleanup() {
|
|
51
|
+
rm -rf "$tmp_dir"
|
|
52
|
+
}
|
|
53
|
+
trap cleanup EXIT
|
|
54
|
+
|
|
55
|
+
if [ "$FIX_MODE" = true ]; then
|
|
56
|
+
printf "${CYAN_BG}${BRIGHT_WHITE} START ${NC} Running quality fixes\n"
|
|
57
|
+
else
|
|
58
|
+
printf "${CYAN_BG}${BRIGHT_WHITE} START ${NC} Running quality checks\n"
|
|
59
|
+
fi
|
|
60
|
+
|
|
61
|
+
# Run selected commands in parallel and save their outputs
|
|
62
|
+
type_pid=""
|
|
63
|
+
code_pid=""
|
|
64
|
+
style_pid=""
|
|
65
|
+
|
|
66
|
+
if [ "$RUN_TYPE" = true ]; then
|
|
67
|
+
tsgo --noEmit "${EXTRA_ARGS[@]}" > "$tmp_dir/type.log" 2>&1 &
|
|
68
|
+
type_pid=$!
|
|
69
|
+
fi
|
|
70
|
+
|
|
71
|
+
if [ "$RUN_LINT" = true ]; then
|
|
72
|
+
if [ "$FIX_MODE" = true ]; then
|
|
73
|
+
oxlint --fix "${EXTRA_ARGS[@]:-.}" > "$tmp_dir/code.log" 2>&1 &
|
|
74
|
+
else
|
|
75
|
+
oxlint "${EXTRA_ARGS[@]:-.}" > "$tmp_dir/code.log" 2>&1 &
|
|
76
|
+
fi
|
|
77
|
+
code_pid=$!
|
|
78
|
+
fi
|
|
79
|
+
|
|
80
|
+
if [ "$RUN_FORMAT" = true ]; then
|
|
81
|
+
if [ "$FIX_MODE" = true ]; then
|
|
82
|
+
oxfmt "${EXTRA_ARGS[@]:-.}" > "$tmp_dir/style.log" 2>&1 &
|
|
83
|
+
else
|
|
84
|
+
oxfmt --check "${EXTRA_ARGS[@]:-.}" > "$tmp_dir/style.log" 2>&1 &
|
|
85
|
+
fi
|
|
86
|
+
style_pid=$!
|
|
87
|
+
fi
|
|
88
|
+
|
|
89
|
+
# Function to print output with a header
|
|
90
|
+
print_output() {
|
|
91
|
+
local file=$1
|
|
92
|
+
local header=$2
|
|
93
|
+
local status=$3
|
|
94
|
+
|
|
95
|
+
printf "\n${CYAN_BG}${BRIGHT_WHITE} RUN ${NC} %s\n\n" "$header"
|
|
96
|
+
if [ -s "$file" ]; then
|
|
97
|
+
cat "$file"
|
|
98
|
+
fi
|
|
99
|
+
if [ $status -ne 0 ]; then
|
|
100
|
+
printf "${RED}✗ Failed with exit code %d${NC}\n" $status
|
|
101
|
+
else
|
|
102
|
+
printf "${GREEN}✓ Passed${NC}\n"
|
|
103
|
+
fi
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
# Wait for processes and collect statuses
|
|
107
|
+
type_status=0
|
|
108
|
+
code_status=0
|
|
109
|
+
style_status=0
|
|
110
|
+
|
|
111
|
+
if [ -n "$type_pid" ]; then
|
|
112
|
+
wait $type_pid
|
|
113
|
+
type_status=$?
|
|
114
|
+
fi
|
|
115
|
+
|
|
116
|
+
if [ -n "$code_pid" ]; then
|
|
117
|
+
wait $code_pid
|
|
118
|
+
code_status=$?
|
|
119
|
+
fi
|
|
120
|
+
|
|
121
|
+
if [ -n "$style_pid" ]; then
|
|
122
|
+
wait $style_pid
|
|
123
|
+
style_status=$?
|
|
124
|
+
fi
|
|
125
|
+
|
|
126
|
+
# Print outputs with headers
|
|
127
|
+
if [ "$RUN_TYPE" = true ]; then
|
|
128
|
+
print_output "$tmp_dir/type.log" "TypeScript Check" $type_status
|
|
129
|
+
fi
|
|
130
|
+
|
|
131
|
+
if [ "$RUN_LINT" = true ]; then
|
|
132
|
+
if [ "$FIX_MODE" = true ]; then
|
|
133
|
+
print_output "$tmp_dir/code.log" "Oxlint Fix" $code_status
|
|
134
|
+
else
|
|
135
|
+
print_output "$tmp_dir/code.log" "Oxlint Check" $code_status
|
|
136
|
+
fi
|
|
137
|
+
fi
|
|
138
|
+
|
|
139
|
+
if [ "$RUN_FORMAT" = true ]; then
|
|
140
|
+
if [ "$FIX_MODE" = true ]; then
|
|
141
|
+
print_output "$tmp_dir/style.log" "Oxfmt Format" $style_status
|
|
142
|
+
else
|
|
143
|
+
print_output "$tmp_dir/style.log" "Oxfmt Check" $style_status
|
|
144
|
+
fi
|
|
145
|
+
fi
|
|
146
|
+
|
|
147
|
+
# Print final summary
|
|
148
|
+
if [ "$FIX_MODE" = true ]; then
|
|
149
|
+
printf "\n${CYAN_BG}${BRIGHT_WHITE} END ${NC} Finalizing quality fixes\n\n"
|
|
150
|
+
else
|
|
151
|
+
printf "\n${CYAN_BG}${BRIGHT_WHITE} END ${NC} Finalizing quality checks\n\n"
|
|
152
|
+
fi
|
|
153
|
+
|
|
154
|
+
if [ $type_status -eq 0 ] && [ $code_status -eq 0 ] && [ $style_status -eq 0 ]; then
|
|
155
|
+
printf "${GREEN}✓ All checks passed${NC}\n"
|
|
156
|
+
exit 0
|
|
157
|
+
else
|
|
158
|
+
printf "${RED}✗ Some checks failed${NC}\n"
|
|
159
|
+
exit 1
|
|
160
|
+
fi
|
package/src/index.js
ADDED
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import oxfmtConfig from "./oxfmt/index.json" with { type: "json" };
|
|
2
|
+
import expoConfig from "./oxlint/expo.json" with { type: "json" };
|
|
3
|
+
import nextjsConfig from "./oxlint/nextjs.json" with { type: "json" };
|
|
4
|
+
import nodeConfig from "./oxlint/node.json" with { type: "json" };
|
|
5
|
+
|
|
6
|
+
export const oxfmt = oxfmtConfig;
|
|
7
|
+
|
|
8
|
+
export const oxlint = {
|
|
9
|
+
expo: expoConfig,
|
|
10
|
+
nextjs: nextjsConfig,
|
|
11
|
+
node: nodeConfig,
|
|
12
|
+
};
|
|
13
|
+
|
|
14
|
+
export default { oxfmt, oxlint };
|
|
@@ -0,0 +1,74 @@
|
|
|
1
|
+
{
|
|
2
|
+
"$schema": "https://raw.githubusercontent.com/oxc-project/oxc/main/npm/oxlint/configuration_schema.json",
|
|
3
|
+
"plugins": ["typescript", "import", "oxc"],
|
|
4
|
+
"jsPlugins": ["eslint-plugin-perfectionist"],
|
|
5
|
+
"categories": {
|
|
6
|
+
"correctness": "error",
|
|
7
|
+
"suspicious": "warn",
|
|
8
|
+
"perf": "warn",
|
|
9
|
+
"pedantic": "off",
|
|
10
|
+
"style": "warn",
|
|
11
|
+
"nursery": "off"
|
|
12
|
+
},
|
|
13
|
+
"rules": {
|
|
14
|
+
"no-unused-vars": "error",
|
|
15
|
+
"sort-keys": "off",
|
|
16
|
+
"sort-imports": "off",
|
|
17
|
+
"func-style": "off",
|
|
18
|
+
"no-magic-numbers": "off",
|
|
19
|
+
"import/no-named-export": "off",
|
|
20
|
+
"import/no-anonymous-default-export": "off",
|
|
21
|
+
"import/group-exports": "off",
|
|
22
|
+
"no-ternary": "off",
|
|
23
|
+
"init-declarations": "off",
|
|
24
|
+
"capitalized-comments": "off",
|
|
25
|
+
"id-length": "off",
|
|
26
|
+
"typescript/consistent-type-definitions": "off",
|
|
27
|
+
"typescript/consistent-type-imports": [
|
|
28
|
+
"error",
|
|
29
|
+
{
|
|
30
|
+
"disallowTypeAnnotations": true,
|
|
31
|
+
"fixStyle": "inline-type-imports",
|
|
32
|
+
"prefer": "type-imports"
|
|
33
|
+
}
|
|
34
|
+
],
|
|
35
|
+
"typescript/no-unused-expressions": [
|
|
36
|
+
"error",
|
|
37
|
+
{
|
|
38
|
+
"allowShortCircuit": true,
|
|
39
|
+
"allowTernary": true,
|
|
40
|
+
"allowTaggedTemplates": true
|
|
41
|
+
}
|
|
42
|
+
],
|
|
43
|
+
"perfectionist/sort-array-includes": ["error", { "type": "natural" }],
|
|
44
|
+
"perfectionist/sort-classes": ["error", { "type": "natural" }],
|
|
45
|
+
"perfectionist/sort-heritage-clauses": ["error", { "type": "natural" }],
|
|
46
|
+
"perfectionist/sort-intersection-types": ["error", { "type": "natural" }],
|
|
47
|
+
"perfectionist/sort-jsx-props": ["error", { "type": "natural" }],
|
|
48
|
+
"perfectionist/sort-named-exports": ["error", { "type": "natural" }],
|
|
49
|
+
"perfectionist/sort-named-imports": ["error", { "type": "natural" }],
|
|
50
|
+
"perfectionist/sort-switch-case": ["error", { "type": "natural" }],
|
|
51
|
+
"perfectionist/sort-union-types": ["error", { "type": "natural" }],
|
|
52
|
+
"perfectionist/sort-variable-declarations": [
|
|
53
|
+
"error",
|
|
54
|
+
{ "type": "natural" }
|
|
55
|
+
],
|
|
56
|
+
"perfectionist/sort-imports": [
|
|
57
|
+
"error",
|
|
58
|
+
{
|
|
59
|
+
"type": "alphabetical",
|
|
60
|
+
"order": "asc",
|
|
61
|
+
"ignoreCase": true,
|
|
62
|
+
"newlinesBetween": "always",
|
|
63
|
+
"groups": [
|
|
64
|
+
["builtin", "external"],
|
|
65
|
+
"internal",
|
|
66
|
+
["parent", "sibling", "index"],
|
|
67
|
+
"style",
|
|
68
|
+
"unknown"
|
|
69
|
+
]
|
|
70
|
+
}
|
|
71
|
+
]
|
|
72
|
+
},
|
|
73
|
+
"ignorePatterns": ["dist/**", "node_modules/**"]
|
|
74
|
+
}
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
{
|
|
2
|
+
"$schema": "https://raw.githubusercontent.com/oxc-project/oxc/main/npm/oxlint/configuration_schema.json",
|
|
3
|
+
"extends": ["./base.json"],
|
|
4
|
+
"plugins": ["typescript", "import", "react"],
|
|
5
|
+
"jsPlugins": ["./plugins/remove-ts-extensions.js"],
|
|
6
|
+
"rules": {
|
|
7
|
+
"typescript/no-require-imports": [
|
|
8
|
+
"error",
|
|
9
|
+
{
|
|
10
|
+
"allow": ["\\.png$", "\\.jpg$", "\\.jpeg$", "\\.gif$", "\\.webp$"]
|
|
11
|
+
}
|
|
12
|
+
],
|
|
13
|
+
"remove-ts-extensions/remove-ts-extensions": "error"
|
|
14
|
+
}
|
|
15
|
+
}
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
{
|
|
2
|
+
"$schema": "https://raw.githubusercontent.com/oxc-project/oxc/main/npm/oxlint/configuration_schema.json",
|
|
3
|
+
"extends": ["./base.json"],
|
|
4
|
+
"plugins": ["typescript", "import", "react", "nextjs"],
|
|
5
|
+
"jsPlugins": ["./plugins/remove-ts-extensions.js"],
|
|
6
|
+
"ignorePatterns": ["dist/**", "node_modules/**", ".next/**", "next-env.d.ts"],
|
|
7
|
+
"rules": {
|
|
8
|
+
"remove-ts-extensions/remove-ts-extensions": "error"
|
|
9
|
+
}
|
|
10
|
+
}
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
{
|
|
2
|
+
"$schema": "https://raw.githubusercontent.com/oxc-project/oxc/main/npm/oxlint/configuration_schema.json",
|
|
3
|
+
"extends": ["./base.json"],
|
|
4
|
+
"jsPlugins": ["./plugins/require-js-extensions.js"],
|
|
5
|
+
"rules": {
|
|
6
|
+
"require-js-extensions/require-js-extensions": "error"
|
|
7
|
+
}
|
|
8
|
+
}
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
// Custom rule to remove TS/JS extensions from imports
|
|
2
|
+
// Compatible with both ESLint and Oxlint JS plugin API
|
|
3
|
+
|
|
4
|
+
const removeTsExtensionsRule = {
|
|
5
|
+
meta: {
|
|
6
|
+
type: "problem",
|
|
7
|
+
docs: {
|
|
8
|
+
description: "Remove .js, .jsx, .ts, .tsx extensions from imports",
|
|
9
|
+
category: "Best Practices",
|
|
10
|
+
},
|
|
11
|
+
fixable: "code",
|
|
12
|
+
schema: [],
|
|
13
|
+
},
|
|
14
|
+
create(context) {
|
|
15
|
+
const extensionsToRemove = /\.(js|jsx|ts|tsx)$/;
|
|
16
|
+
|
|
17
|
+
function checkNode(node) {
|
|
18
|
+
if (!node.source || !node.source.value) {
|
|
19
|
+
return;
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
const importPath = node.source.value;
|
|
23
|
+
|
|
24
|
+
// Check both relative imports (starting with . or ..) AND path alias imports (starting with @/)
|
|
25
|
+
if (!importPath.startsWith(".") && !importPath.startsWith("@/")) {
|
|
26
|
+
return;
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
if (extensionsToRemove.test(importPath)) {
|
|
30
|
+
const match = importPath.match(extensionsToRemove);
|
|
31
|
+
context.report({
|
|
32
|
+
node: node.source,
|
|
33
|
+
message: `Remove "${match[0]}" extension from import`,
|
|
34
|
+
fix(fixer) {
|
|
35
|
+
const newPath = importPath.replace(extensionsToRemove, "");
|
|
36
|
+
return fixer.replaceText(node.source, `'${newPath}'`);
|
|
37
|
+
},
|
|
38
|
+
});
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
return {
|
|
43
|
+
ImportDeclaration: checkNode,
|
|
44
|
+
ExportNamedDeclaration: checkNode,
|
|
45
|
+
ExportAllDeclaration: checkNode,
|
|
46
|
+
};
|
|
47
|
+
},
|
|
48
|
+
};
|
|
49
|
+
|
|
50
|
+
export default {
|
|
51
|
+
meta: {
|
|
52
|
+
name: "remove-ts-extensions",
|
|
53
|
+
version: "1.0.0",
|
|
54
|
+
},
|
|
55
|
+
rules: {
|
|
56
|
+
"remove-ts-extensions": removeTsExtensionsRule,
|
|
57
|
+
},
|
|
58
|
+
};
|
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
// Custom rule to require .js extensions in imports (for Node.js ESM)
|
|
2
|
+
// Compatible with both ESLint and Oxlint JS plugin API
|
|
3
|
+
|
|
4
|
+
const requireJsExtensionsRule = {
|
|
5
|
+
meta: {
|
|
6
|
+
type: "problem",
|
|
7
|
+
docs: {
|
|
8
|
+
description: "Require .js extension in imports for Node.js ESM compatibility",
|
|
9
|
+
category: "Best Practices",
|
|
10
|
+
},
|
|
11
|
+
fixable: "code",
|
|
12
|
+
schema: [],
|
|
13
|
+
},
|
|
14
|
+
create(context) {
|
|
15
|
+
const hasExtension = /\.[a-zA-Z0-9]+$/;
|
|
16
|
+
|
|
17
|
+
function checkNode(node) {
|
|
18
|
+
if (!node.source || !node.source.value) {
|
|
19
|
+
return;
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
const importPath = node.source.value;
|
|
23
|
+
|
|
24
|
+
// Only check relative imports
|
|
25
|
+
if (!importPath.startsWith(".")) {
|
|
26
|
+
return;
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
// Skip if it already has an extension
|
|
30
|
+
if (hasExtension.test(importPath)) {
|
|
31
|
+
return;
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
// Skip type-only imports (they are erased at runtime)
|
|
35
|
+
if (node.importKind === "type") {
|
|
36
|
+
return;
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
context.report({
|
|
40
|
+
node: node.source,
|
|
41
|
+
message: "Missing .js extension in import (required for Node.js ESM)",
|
|
42
|
+
fix(fixer) {
|
|
43
|
+
const newPath = `${importPath}.js`;
|
|
44
|
+
return fixer.replaceText(node.source, `'${newPath}'`);
|
|
45
|
+
},
|
|
46
|
+
});
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
return {
|
|
50
|
+
ImportDeclaration: checkNode,
|
|
51
|
+
ExportNamedDeclaration: checkNode,
|
|
52
|
+
ExportAllDeclaration: checkNode,
|
|
53
|
+
};
|
|
54
|
+
},
|
|
55
|
+
};
|
|
56
|
+
|
|
57
|
+
export default {
|
|
58
|
+
meta: {
|
|
59
|
+
name: "require-js-extensions",
|
|
60
|
+
version: "1.0.0",
|
|
61
|
+
},
|
|
62
|
+
rules: {
|
|
63
|
+
"require-js-extensions": requireJsExtensionsRule,
|
|
64
|
+
},
|
|
65
|
+
};
|