@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 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
+ }
@@ -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,11 @@
1
+ {
2
+ "$schema": "https://oxc.rs/schemas/oxfmt/0.19.0/schema.json",
3
+ "printWidth": 100,
4
+ "tabWidth": 4,
5
+ "useTabs": false,
6
+ "semi": true,
7
+ "singleQuote": true,
8
+ "trailingComma": "all",
9
+ "bracketSpacing": true,
10
+ "endOfLine": "lf"
11
+ }
@@ -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
+ };