startx 0.7.2 → 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/.prettierrc.js +2 -2
- package/.vscode/settings.json +2 -1
- package/apps/core-server/package.json +1 -1
- package/apps/core-server/src/index.ts +0 -1
- package/apps/startx-cli/dist/index.mjs +2 -2
- package/apps/startx-cli/src/commands/init.ts +183 -255
- package/apps/startx-cli/src/configs/files.ts +3 -4
- package/apps/startx-cli/src/configs/scripts.ts +24 -6
- package/apps/startx-cli/src/types.ts +18 -6
- package/apps/startx-cli/src/utils/cli-utils.ts +60 -49
- package/apps/startx-cli/src/utils/file-handler.ts +8 -3
- package/biome.json +1 -1
- package/configs/eslint-config/eslint.config.ts +0 -0
- package/configs/eslint-config/src/configs/base.ts +32 -79
- package/configs/eslint-config/src/configs/extend.ts +2 -2
- package/configs/eslint-config/src/configs/frontend.ts +29 -19
- package/configs/eslint-config/src/configs/node.ts +46 -6
- package/configs/vitest-config/package.json +3 -2
- package/package.json +3 -2
- package/packages/@repo/db/drizzle.config.ts +14 -0
- package/packages/@repo/db/package.json +5 -1
- package/packages/@repo/db/src/index.ts +1 -1
- package/packages/@repo/lib/src/otp-module/index.ts +6 -13
- package/packages/@repo/lib/tsconfig.json +2 -1
- package/packages/@repo/mail/eslint.config.ts +2 -2
- package/packages/@repo/mail/src/emails/admin/OtpEmail.tsx +3 -9
- package/packages/@repo/mail/src/emails/emails.ts +1 -0
- package/packages/@repo/mail/src/index.ts +10 -9
- package/packages/@repo/mail/tsconfig.json +4 -2
- package/packages/ui/package.json +1 -0
- package/packages/ui/src/components/ui/command.tsx +5 -15
- package/pnpm-workspace.yaml +1 -0
- package/turbo.json +9 -0
- package/apps/startx-cli/src/utils/config.ts +0 -104
|
@@ -2,25 +2,37 @@
|
|
|
2
2
|
import type { PackageJson } from "type-fest";
|
|
3
3
|
|
|
4
4
|
export type TAGS =
|
|
5
|
-
|
|
5
|
+
// Always installs
|
|
6
6
|
| "common"
|
|
7
|
+
// Used for apps
|
|
7
8
|
| "runnable"
|
|
9
|
+
// Formatter tags
|
|
8
10
|
| "biome"
|
|
9
11
|
| "prettier"
|
|
10
|
-
| "vitest"
|
|
11
12
|
| "eslint"
|
|
13
|
+
// Testing tags
|
|
14
|
+
| "vitest"
|
|
15
|
+
// Build tags
|
|
12
16
|
| "tsdown"
|
|
17
|
+
// Backend tags
|
|
13
18
|
| "node"
|
|
14
19
|
| "backend"
|
|
15
|
-
| "
|
|
20
|
+
| "express"
|
|
21
|
+
// Frontend tags
|
|
16
22
|
| "frontend"
|
|
23
|
+
| "react"
|
|
24
|
+
// Cli tags
|
|
17
25
|
| "cli"
|
|
18
26
|
| "commander"
|
|
19
|
-
|
|
20
|
-
| "extra"
|
|
27
|
+
// Mail
|
|
21
28
|
| "mail"
|
|
29
|
+
// Never installs or ignore
|
|
22
30
|
| "never"
|
|
23
|
-
|
|
31
|
+
// For handling workspace only files and scripts
|
|
32
|
+
| "root"
|
|
33
|
+
// Database tags
|
|
34
|
+
| "db"
|
|
35
|
+
| "drizzle";
|
|
24
36
|
|
|
25
37
|
export type SCRIPT = Record<string, Array<{ script: string; tags: TAGS[] }>>;
|
|
26
38
|
|
|
@@ -6,84 +6,94 @@ import z from "zod";
|
|
|
6
6
|
|
|
7
7
|
import type { PnpmWorkspace, StartXPackageJson } from "../types";
|
|
8
8
|
|
|
9
|
-
export type
|
|
9
|
+
export type PackageItem = {
|
|
10
10
|
type: "apps" | "configs" | "packages";
|
|
11
11
|
path: string;
|
|
12
|
+
relativePath: string;
|
|
12
13
|
name: string;
|
|
14
|
+
packageJson: StartXPackageJson;
|
|
13
15
|
};
|
|
14
16
|
|
|
15
17
|
const ENV = defineEnv({
|
|
16
18
|
STARTX_ENV: z.enum(["development", "production", "test", "staging"]).default("production"),
|
|
17
19
|
});
|
|
20
|
+
|
|
18
21
|
export class CliUtils {
|
|
19
22
|
static getDirectory() {
|
|
20
23
|
const __filename = fileURLToPath(import.meta.url);
|
|
21
24
|
let template = path.dirname(__filename);
|
|
22
25
|
const workspace = process.cwd();
|
|
26
|
+
|
|
23
27
|
if (ENV.STARTX_ENV === "development") {
|
|
24
28
|
template = path.resolve(template, "../../../../");
|
|
25
|
-
} else
|
|
29
|
+
} else {
|
|
30
|
+
template = path.resolve(template, "../../../");
|
|
31
|
+
}
|
|
26
32
|
|
|
27
33
|
return {
|
|
28
34
|
template,
|
|
29
35
|
workspace,
|
|
30
36
|
};
|
|
31
37
|
}
|
|
32
|
-
|
|
33
|
-
|
|
38
|
+
|
|
39
|
+
static async getPackageList(): Promise<PackageItem[]> {
|
|
34
40
|
const cliDirectory = this.getDirectory().template;
|
|
35
|
-
|
|
41
|
+
|
|
42
|
+
const fetchPackages = async (
|
|
43
|
+
subPath: string,
|
|
44
|
+
type: "apps" | "configs" | "packages",
|
|
45
|
+
namePrefix = "",
|
|
46
|
+
filterFn?: (name: string) => boolean
|
|
47
|
+
): Promise<PackageItem[]> => {
|
|
48
|
+
const dirPath = path.join(cliDirectory, ...subPath.split("/"));
|
|
49
|
+
|
|
36
50
|
try {
|
|
37
|
-
|
|
51
|
+
let names = await fsTool.listDirectories({ dir: dirPath });
|
|
52
|
+
if (filterFn) names = names.filter(filterFn);
|
|
53
|
+
|
|
54
|
+
const packages = await Promise.all(
|
|
55
|
+
names.map(async name => {
|
|
56
|
+
const pkgPath = path.join(dirPath, name);
|
|
57
|
+
const relativePath = path.relative(cliDirectory, pkgPath);
|
|
58
|
+
const pkgName = namePrefix ? `${namePrefix}${name}` : name;
|
|
59
|
+
let packageJson;
|
|
60
|
+
|
|
61
|
+
try {
|
|
62
|
+
packageJson = await this.parsePackageJson({ dir: pkgPath });
|
|
63
|
+
} catch {
|
|
64
|
+
packageJson = null;
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
if (!packageJson) {
|
|
68
|
+
console.error(`Ignoring this package failed to read package.json: ${pkgName}`);
|
|
69
|
+
return null;
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
return {
|
|
73
|
+
type,
|
|
74
|
+
path: pkgPath,
|
|
75
|
+
relativePath,
|
|
76
|
+
name: pkgName,
|
|
77
|
+
packageJson,
|
|
78
|
+
};
|
|
79
|
+
})
|
|
80
|
+
);
|
|
81
|
+
|
|
82
|
+
return packages.filter((pkg): pkg is PackageItem => pkg !== null);
|
|
38
83
|
} catch (error) {
|
|
39
|
-
console.error(
|
|
84
|
+
console.error(`Error reading directory ${dirPath}:`, error);
|
|
40
85
|
return [];
|
|
41
86
|
}
|
|
42
87
|
};
|
|
43
88
|
|
|
44
|
-
const
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
]);
|
|
51
|
-
|
|
52
|
-
// 2. Map Apps
|
|
53
|
-
availableApps.forEach(name => {
|
|
54
|
-
packages.push({
|
|
55
|
-
type: "apps",
|
|
56
|
-
path: path.join(cliDirectory, "apps", name),
|
|
57
|
-
name,
|
|
58
|
-
});
|
|
59
|
-
});
|
|
89
|
+
const results = await Promise.all([
|
|
90
|
+
fetchPackages("apps", "apps"),
|
|
91
|
+
fetchPackages("configs", "configs"),
|
|
92
|
+
fetchPackages("packages", "packages", "", name => name !== "@repo"),
|
|
93
|
+
fetchPackages("packages/@repo", "packages", "@repo/"),
|
|
94
|
+
]);
|
|
60
95
|
|
|
61
|
-
|
|
62
|
-
packages.push({
|
|
63
|
-
type: "configs",
|
|
64
|
-
path: path.join(cliDirectory, "configs", name),
|
|
65
|
-
name,
|
|
66
|
-
});
|
|
67
|
-
});
|
|
68
|
-
|
|
69
|
-
availablePackages.forEach(name => {
|
|
70
|
-
if (name === "@repo") return;
|
|
71
|
-
packages.push({
|
|
72
|
-
type: "packages",
|
|
73
|
-
path: path.join(cliDirectory, "packages", name),
|
|
74
|
-
name,
|
|
75
|
-
});
|
|
76
|
-
});
|
|
77
|
-
|
|
78
|
-
availableRepoPackages.forEach(name => {
|
|
79
|
-
packages.push({
|
|
80
|
-
type: "packages",
|
|
81
|
-
path: path.join(cliDirectory, "packages", "@repo", name),
|
|
82
|
-
name: `@repo/${name}`,
|
|
83
|
-
});
|
|
84
|
-
});
|
|
85
|
-
|
|
86
|
-
return packages;
|
|
96
|
+
return results.flat();
|
|
87
97
|
}
|
|
88
98
|
|
|
89
99
|
static async parsePackageJson({ dir, file = "package" }: { dir: string; file?: string }) {
|
|
@@ -93,6 +103,7 @@ export class CliUtils {
|
|
|
93
103
|
});
|
|
94
104
|
return packageJson;
|
|
95
105
|
}
|
|
106
|
+
|
|
96
107
|
static async parsePnpmWorkspace({ dir }: { dir: string }) {
|
|
97
108
|
const yaml = await fsTool.readYamlFile({
|
|
98
109
|
file: "pnpm-workspace",
|
|
@@ -5,9 +5,7 @@ import type { StartXPackageJson, TAGS } from "../types";
|
|
|
5
5
|
|
|
6
6
|
export class FileHandler {
|
|
7
7
|
private static objSorter(obj: Record<string, unknown>, sorter: string[] = []) {
|
|
8
|
-
const cleaned = Object.fromEntries(
|
|
9
|
-
Object.entries(obj).filter(([, v]) => v !== null && v !== undefined)
|
|
10
|
-
);
|
|
8
|
+
const cleaned = Object.fromEntries(Object.entries(obj).filter(([, v]) => v !== null && v !== undefined));
|
|
11
9
|
|
|
12
10
|
const sortedEntries: Array<[string, unknown]> = [];
|
|
13
11
|
|
|
@@ -63,6 +61,13 @@ export class FileHandler {
|
|
|
63
61
|
const devDependencies = filterDeps(props.app.devDependencies as Record<string, string>);
|
|
64
62
|
|
|
65
63
|
// Removing all workspace dependencies
|
|
64
|
+
for (const [key, value] of Object.entries(dependencies)) {
|
|
65
|
+
if (value.includes("workspace:")) {
|
|
66
|
+
delete dependencies[key];
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
// Removing all workspace dev dependencies
|
|
66
71
|
for (const [key, value] of Object.entries(devDependencies)) {
|
|
67
72
|
if (value.includes("workspace:")) {
|
|
68
73
|
delete devDependencies[key];
|
package/biome.json
CHANGED
|
File without changes
|
|
@@ -1,20 +1,15 @@
|
|
|
1
1
|
import { fixupPluginRules } from "@eslint/compat";
|
|
2
|
-
import js from "@eslint/js";
|
|
3
|
-
import stylisticPlugin from "@stylistic/eslint-plugin";
|
|
4
|
-
import { defineConfig } from "eslint/config";
|
|
5
2
|
import eslintConfigPrettier from "eslint-config-prettier";
|
|
6
|
-
import { createTypeScriptImportResolver } from "eslint-import-resolver-typescript";
|
|
7
3
|
import importPlugin from "eslint-plugin-import-x";
|
|
8
4
|
import lodashPlugin from "eslint-plugin-lodash";
|
|
9
|
-
import reactPlugin from "eslint-plugin-react";
|
|
10
|
-
import reactHooksPlugin from "eslint-plugin-react-hooks";
|
|
11
5
|
import unicornPlugin from "eslint-plugin-unicorn";
|
|
12
6
|
import unusedImportsPlugin from "eslint-plugin-unused-imports";
|
|
13
7
|
import globals from "globals";
|
|
14
8
|
import tseslint from "typescript-eslint";
|
|
15
9
|
import { localRulesPlugin } from "../plugin.js";
|
|
16
10
|
|
|
17
|
-
|
|
11
|
+
// Exported as a standard array for Flat Config
|
|
12
|
+
export const baseConfig = tseslint.config(
|
|
18
13
|
// 1. Global Ignores
|
|
19
14
|
{
|
|
20
15
|
ignores: [
|
|
@@ -29,11 +24,11 @@ export const baseConfig = defineConfig(
|
|
|
29
24
|
"vite.config.ts",
|
|
30
25
|
"vitest.config.ts",
|
|
31
26
|
"tsdown.config.ts",
|
|
27
|
+
"drizzle.config.ts",
|
|
32
28
|
],
|
|
33
29
|
},
|
|
34
30
|
|
|
35
31
|
// 2. Base Configurations
|
|
36
|
-
js.configs.recommended,
|
|
37
32
|
...tseslint.configs.recommended,
|
|
38
33
|
...tseslint.configs.recommendedTypeChecked,
|
|
39
34
|
importPlugin.flatConfigs.recommended as any,
|
|
@@ -43,17 +38,14 @@ export const baseConfig = defineConfig(
|
|
|
43
38
|
{
|
|
44
39
|
plugins: {
|
|
45
40
|
"unused-imports": fixupPluginRules(unusedImportsPlugin),
|
|
46
|
-
"react-hooks": fixupPluginRules(reactHooksPlugin as any),
|
|
47
41
|
lodash: fixupPluginRules(lodashPlugin),
|
|
48
|
-
"@stylistic": stylisticPlugin as any,
|
|
49
42
|
unicorn: unicornPlugin,
|
|
50
|
-
|
|
43
|
+
"local-rules": localRulesPlugin,
|
|
51
44
|
},
|
|
52
45
|
languageOptions: {
|
|
53
|
-
ecmaVersion:
|
|
46
|
+
ecmaVersion: "latest",
|
|
54
47
|
sourceType: "module",
|
|
55
48
|
globals: {
|
|
56
|
-
...globals.browser,
|
|
57
49
|
...globals.node,
|
|
58
50
|
...globals.es2021,
|
|
59
51
|
},
|
|
@@ -62,25 +54,16 @@ export const baseConfig = defineConfig(
|
|
|
62
54
|
tsconfigRootDir: import.meta.dirname,
|
|
63
55
|
},
|
|
64
56
|
},
|
|
65
|
-
settings: {
|
|
66
|
-
"import-x/resolver-next": [createTypeScriptImportResolver()],
|
|
67
|
-
react: {
|
|
68
|
-
version: "detect",
|
|
69
|
-
},
|
|
70
|
-
},
|
|
71
57
|
rules: {
|
|
72
58
|
// Core Rules
|
|
73
59
|
"no-void": ["error", { allowAsStatement: true }],
|
|
74
60
|
"no-constant-binary-expression": "error",
|
|
75
|
-
"no-console": ["warn", { allow: ["warn", "error"] }],
|
|
76
|
-
eqeqeq: "warn",
|
|
61
|
+
"no-console": ["warn", { allow: ["warn", "error", "info"] }], // Allowed info for debugging
|
|
62
|
+
eqeqeq: ["warn", "always", { null: "ignore" }],
|
|
77
63
|
"object-shorthand": "warn",
|
|
78
64
|
"prefer-const": "warn",
|
|
79
65
|
|
|
80
|
-
|
|
81
|
-
"import-x/no-named-as-default": "off",
|
|
82
|
-
|
|
83
|
-
// TypeScript
|
|
66
|
+
// TypeScript Quality of Life
|
|
84
67
|
"@typescript-eslint/await-thenable": "error",
|
|
85
68
|
"@typescript-eslint/no-floating-promises": ["error", { ignoreVoid: true }],
|
|
86
69
|
"@typescript-eslint/no-misused-promises": ["error", { checksVoidReturn: false }],
|
|
@@ -90,75 +73,60 @@ export const baseConfig = defineConfig(
|
|
|
90
73
|
"@typescript-eslint/no-unsafe-argument": "warn",
|
|
91
74
|
"@typescript-eslint/no-unsafe-call": "warn",
|
|
92
75
|
"@typescript-eslint/no-unsafe-return": "off",
|
|
93
|
-
"@typescript-eslint/no-unsafe-member-access": ["
|
|
76
|
+
"@typescript-eslint/no-unsafe-member-access": ["warn", { allowOptionalChaining: true }],
|
|
94
77
|
"@typescript-eslint/array-type": ["warn", { default: "array-simple" }],
|
|
95
|
-
"@typescript-eslint/consistent-type-assertions": "warn",
|
|
96
|
-
"@typescript-eslint/consistent-type-imports": "warn",
|
|
97
|
-
"@typescript-eslint/consistent-type-exports": "warn",
|
|
98
78
|
"@typescript-eslint/return-await": ["warn", "always"],
|
|
79
|
+
"@typescript-eslint/no-empty-object-type": ["warn"],
|
|
99
80
|
|
|
100
|
-
|
|
101
|
-
|
|
81
|
+
// Naming Conventions (Relaxed for APIs and strict for standard code)
|
|
102
82
|
"@typescript-eslint/naming-convention": [
|
|
103
83
|
"warn",
|
|
104
84
|
{ selector: "default", format: ["camelCase"] },
|
|
105
|
-
|
|
106
85
|
{ selector: "import", format: ["camelCase", "PascalCase"] },
|
|
107
|
-
|
|
108
86
|
{
|
|
109
87
|
selector: "variable",
|
|
110
88
|
format: ["camelCase", "snake_case", "UPPER_CASE", "PascalCase"],
|
|
111
|
-
leadingUnderscore: "
|
|
112
|
-
trailingUnderscore: "allowSingleOrDouble",
|
|
89
|
+
leadingUnderscore: "allow",
|
|
113
90
|
},
|
|
114
|
-
|
|
115
91
|
{
|
|
116
92
|
selector: "parameter",
|
|
117
93
|
format: ["camelCase"],
|
|
118
|
-
leadingUnderscore: "
|
|
94
|
+
leadingUnderscore: "allow",
|
|
119
95
|
},
|
|
120
|
-
|
|
121
96
|
{ selector: "typeLike", format: ["PascalCase"] },
|
|
122
|
-
{ selector: "enumMember", format: ["UPPER_CASE", "PascalCase"] },
|
|
123
|
-
],
|
|
124
|
-
"@typescript-eslint/no-restricted-types": [
|
|
125
|
-
"warn",
|
|
126
97
|
{
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
String: { message: "Use string instead", fixWith: "string" },
|
|
130
|
-
Boolean: { message: "Use boolean instead", fixWith: "boolean" },
|
|
131
|
-
Number: { message: "Use number instead", fixWith: "number" },
|
|
132
|
-
Symbol: { message: "Use symbol instead", fixWith: "symbol" },
|
|
133
|
-
},
|
|
98
|
+
selector: ["objectLiteralProperty", "typeProperty"],
|
|
99
|
+
format: null, // Critical: Allows API payloads with snake_case or headers
|
|
134
100
|
},
|
|
135
101
|
],
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
"
|
|
102
|
+
|
|
103
|
+
// Handled entirely by unused-imports
|
|
104
|
+
"@typescript-eslint/no-unused-vars": "off",
|
|
105
|
+
"unused-imports/no-unused-imports": "warn",
|
|
106
|
+
"unused-imports/no-unused-vars": [
|
|
140
107
|
"warn",
|
|
141
108
|
{
|
|
142
|
-
|
|
143
|
-
|
|
109
|
+
vars: "all",
|
|
110
|
+
varsIgnorePattern: "^_",
|
|
111
|
+
args: "after-used",
|
|
112
|
+
argsIgnorePattern: "^_",
|
|
144
113
|
},
|
|
145
114
|
],
|
|
146
115
|
|
|
116
|
+
// Imports
|
|
147
117
|
"import-x/no-cycle": "off",
|
|
148
|
-
"import-x/no-default
|
|
149
|
-
|
|
118
|
+
"import-x/no-named-as-default": "off",
|
|
150
119
|
"import-x/no-duplicates": "warn",
|
|
151
120
|
"import-x/order": [
|
|
152
121
|
"warn",
|
|
153
122
|
{
|
|
154
123
|
alphabetize: { order: "asc", caseInsensitive: true },
|
|
155
124
|
groups: [["builtin", "external"], "internal", ["parent", "index", "sibling"], "object"],
|
|
156
|
-
"newlines-between": "always",
|
|
125
|
+
// "newlines-between": "always",
|
|
157
126
|
},
|
|
158
127
|
],
|
|
159
128
|
|
|
160
129
|
// Plugins
|
|
161
|
-
"unused-imports/no-unused-imports": "warn",
|
|
162
130
|
"unicorn/no-unnecessary-await": "warn",
|
|
163
131
|
"unicorn/no-useless-promise-resolve-reject": "warn",
|
|
164
132
|
"lodash/path-style": ["warn", "as-needed"],
|
|
@@ -166,32 +134,17 @@ export const baseConfig = defineConfig(
|
|
|
166
134
|
},
|
|
167
135
|
},
|
|
168
136
|
|
|
169
|
-
// 4.
|
|
137
|
+
// 4. Test Overrides
|
|
170
138
|
{
|
|
171
|
-
files: ["**/*.
|
|
172
|
-
...reactPlugin.configs.flat.recommended,
|
|
173
|
-
...reactPlugin.configs.flat["jsx-runtime"],
|
|
174
|
-
rules: {
|
|
175
|
-
...reactHooksPlugin.configs.recommended.rules,
|
|
176
|
-
"react/prop-types": "off",
|
|
177
|
-
"react/react-in-jsx-scope": "off",
|
|
178
|
-
"react/jsx-no-leaked-render": "error", // Security/Perf rule (Keep as error)
|
|
179
|
-
},
|
|
180
|
-
},
|
|
181
|
-
|
|
182
|
-
// 5. Test Overrides
|
|
183
|
-
{
|
|
184
|
-
files: ["test/**/*.ts", "**/__tests__/*.ts", "**/*.test.ts", "**/*.cy.ts"],
|
|
139
|
+
files: ["test/**/*.ts", "**/__tests__/*.ts", "**/*.test.ts", "**/*.cy.ts", "**/*.spec.ts"],
|
|
185
140
|
rules: {
|
|
186
141
|
"local-rules/no-plain-errors": "off",
|
|
187
142
|
"@typescript-eslint/unbound-method": "off",
|
|
188
|
-
"
|
|
143
|
+
"@typescript-eslint/no-unsafe-call": "off",
|
|
144
|
+
"@typescript-eslint/no-unsafe-member-access": "off",
|
|
189
145
|
},
|
|
190
146
|
},
|
|
191
147
|
|
|
192
|
-
//
|
|
193
|
-
localRulesPlugin.configs.recommended,
|
|
194
|
-
|
|
195
|
-
// 7. Prettier (Must be last to cleanly override stylistic rules)
|
|
148
|
+
// 5. Prettier (Must be absolutely last)
|
|
196
149
|
eslintConfigPrettier
|
|
197
150
|
);
|
|
@@ -1,3 +1,3 @@
|
|
|
1
|
-
import
|
|
1
|
+
import tseslint from "typescript-eslint";
|
|
2
2
|
|
|
3
|
-
export const extend =
|
|
3
|
+
export const extend = tseslint.config;
|
|
@@ -1,23 +1,21 @@
|
|
|
1
1
|
import { fixupPluginRules } from "@eslint/compat";
|
|
2
|
-
import
|
|
2
|
+
import eslintConfigPrettier from "eslint-config-prettier";
|
|
3
3
|
import jsxA11yPlugin from "eslint-plugin-jsx-a11y";
|
|
4
4
|
import reactPlugin from "eslint-plugin-react";
|
|
5
5
|
import reactHooksPlugin from "eslint-plugin-react-hooks";
|
|
6
6
|
import globals from "globals";
|
|
7
|
+
import tseslint from "typescript-eslint";
|
|
8
|
+
|
|
7
9
|
import { baseConfig } from "./base.js";
|
|
8
|
-
|
|
9
|
-
|
|
10
|
+
|
|
11
|
+
const baseWithoutPrettier = baseConfig.filter(config => config !== eslintConfigPrettier);
|
|
12
|
+
|
|
13
|
+
export const frontendConfig = tseslint.config(
|
|
14
|
+
...baseWithoutPrettier,
|
|
10
15
|
|
|
11
16
|
// 1. Frontend Ignores
|
|
12
17
|
{
|
|
13
|
-
ignores: [
|
|
14
|
-
"**/dist/**",
|
|
15
|
-
"**/coverage/**",
|
|
16
|
-
"**/storybook-static/**",
|
|
17
|
-
"**/*.snap",
|
|
18
|
-
"**/*.d.ts",
|
|
19
|
-
"vite.config.ts",
|
|
20
|
-
],
|
|
18
|
+
ignores: ["**/coverage/**", "**/storybook-static/**", "**/*.snap", "**/*.d.ts"],
|
|
21
19
|
},
|
|
22
20
|
|
|
23
21
|
// 2. Browser/React Globals & Settings
|
|
@@ -37,8 +35,8 @@ export const frontendConfig = defineConfig(
|
|
|
37
35
|
},
|
|
38
36
|
|
|
39
37
|
// 3. React Recommended Configs
|
|
40
|
-
reactPlugin.configs.flat.recommended,
|
|
41
|
-
reactPlugin.configs.flat["jsx-runtime"],
|
|
38
|
+
reactPlugin.configs.flat.recommended as any,
|
|
39
|
+
reactPlugin.configs.flat["jsx-runtime"] as any,
|
|
42
40
|
|
|
43
41
|
// 4. React, Hooks, and Accessibility Rules
|
|
44
42
|
{
|
|
@@ -52,6 +50,7 @@ export const frontendConfig = defineConfig(
|
|
|
52
50
|
...jsxA11yPlugin.configs.recommended.rules,
|
|
53
51
|
|
|
54
52
|
// --- Naming Convention Override for React ---
|
|
53
|
+
// Allows PascalCase for functional components while keeping variables camelCase
|
|
55
54
|
"@typescript-eslint/naming-convention": [
|
|
56
55
|
"warn",
|
|
57
56
|
{ selector: "default", format: ["camelCase"] },
|
|
@@ -60,17 +59,18 @@ export const frontendConfig = defineConfig(
|
|
|
60
59
|
{
|
|
61
60
|
selector: "variable",
|
|
62
61
|
format: ["camelCase", "snake_case", "UPPER_CASE", "PascalCase"],
|
|
63
|
-
leadingUnderscore: "
|
|
64
|
-
trailingUnderscore: "allowSingleOrDouble",
|
|
62
|
+
leadingUnderscore: "allow",
|
|
65
63
|
},
|
|
66
64
|
{ selector: "typeLike", format: ["PascalCase"] },
|
|
67
|
-
{ selector: "enumMember", format: ["UPPER_CASE", "PascalCase"] },
|
|
68
65
|
],
|
|
69
66
|
|
|
70
67
|
// --- React Specifics ---
|
|
71
|
-
"react/prop-types": "off",
|
|
68
|
+
"react/prop-types": "off", // Using TS interfaces instead
|
|
69
|
+
"react/react-in-jsx-scope": "off", // Not needed for React 17+
|
|
70
|
+
"react/jsx-uses-react": "off", // Not needed for React 17+
|
|
72
71
|
"react/display-name": "warn",
|
|
73
72
|
"react/no-unescaped-entities": "warn",
|
|
73
|
+
"react/jsx-no-leaked-render": ["error", { validStrategies: ["ternary", "coerce"] }],
|
|
74
74
|
|
|
75
75
|
// --- React Hooks Specifics ---
|
|
76
76
|
"react-hooks/rules-of-hooks": "error",
|
|
@@ -81,7 +81,17 @@ export const frontendConfig = defineConfig(
|
|
|
81
81
|
"jsx-a11y/click-events-have-key-events": "warn",
|
|
82
82
|
"jsx-a11y/no-static-element-interactions": "warn",
|
|
83
83
|
"jsx-a11y/anchor-is-valid": "warn",
|
|
84
|
-
"jsx-a11y/no-onchange": "off",
|
|
84
|
+
"jsx-a11y/no-onchange": "off", // Deprecated rule, fine to turn off
|
|
85
|
+
},
|
|
86
|
+
},
|
|
87
|
+
|
|
88
|
+
{
|
|
89
|
+
files: ["**/emails/**/*.tsx", "**/emails/**/*.jsx"],
|
|
90
|
+
rules: {
|
|
91
|
+
"react/react-in-jsx-scope": "error",
|
|
92
|
+
"react/jsx-uses-react": "error",
|
|
85
93
|
},
|
|
86
|
-
}
|
|
94
|
+
},
|
|
95
|
+
|
|
96
|
+
eslintConfigPrettier
|
|
87
97
|
);
|
|
@@ -1,10 +1,50 @@
|
|
|
1
|
-
import { defineConfig } from "eslint/config";
|
|
2
1
|
import globals from "globals";
|
|
2
|
+
import tseslint from "typescript-eslint";
|
|
3
|
+
|
|
3
4
|
import { baseConfig } from "./base.js";
|
|
4
5
|
|
|
5
|
-
export const nodeConfig =
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
6
|
+
export const nodeConfig = tseslint.config(
|
|
7
|
+
...baseConfig,
|
|
8
|
+
|
|
9
|
+
// 1. Backend/Node Specific Globals & Environments
|
|
10
|
+
{
|
|
11
|
+
files: ["**/*.ts", "**/*.js", "**/*.cjs", "**/*.mjs"],
|
|
12
|
+
languageOptions: {
|
|
13
|
+
// "latest" perfectly aligns with Node 20+ (ES2023/ES2024 features)
|
|
14
|
+
ecmaVersion: "latest",
|
|
15
|
+
sourceType: "module",
|
|
16
|
+
globals: {
|
|
17
|
+
...globals.node,
|
|
18
|
+
...globals.nodeBuiltin, // Explicitly adds modern Node built-ins (like fetch, structuredClone)
|
|
19
|
+
},
|
|
20
|
+
},
|
|
9
21
|
},
|
|
10
|
-
|
|
22
|
+
|
|
23
|
+
// 2. Node-Specific Overrides & Best Practices
|
|
24
|
+
{
|
|
25
|
+
files: ["**/*.ts", "**/*.js"],
|
|
26
|
+
rules: {
|
|
27
|
+
// Enforce the 'node:' protocol for built-ins (e.g., `import fs from 'node:fs'`)
|
|
28
|
+
// This is a modern Node.js standard that improves performance and security
|
|
29
|
+
"unicorn/prefer-node-protocol": "error",
|
|
30
|
+
|
|
31
|
+
// Prevent accidental use of obscure browser globals that sometimes slip through
|
|
32
|
+
"no-restricted-globals": [
|
|
33
|
+
"error",
|
|
34
|
+
{
|
|
35
|
+
name: "name",
|
|
36
|
+
message: "Global 'name' is deprecated. Did you mean to declare a local variable?",
|
|
37
|
+
},
|
|
38
|
+
{
|
|
39
|
+
name: "event",
|
|
40
|
+
message: "Global 'event' is a browser feature. Do not use it in Node.js.",
|
|
41
|
+
},
|
|
42
|
+
],
|
|
43
|
+
|
|
44
|
+
// In Node apps, developers often accidentally leave floating promises
|
|
45
|
+
// which can cause unhandled promise rejections crashing the server.
|
|
46
|
+
// (This inherits from baseConfig, but I am noting it here as a critical backend safeguard).
|
|
47
|
+
// "@typescript-eslint/no-floating-promises": "error",
|
|
48
|
+
},
|
|
49
|
+
}
|
|
50
|
+
);
|
|
@@ -3,9 +3,10 @@
|
|
|
3
3
|
"version": "1.5.0",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"devDependencies": {
|
|
6
|
-
"
|
|
6
|
+
"jsdom": "^29.0.1",
|
|
7
7
|
"typescript-config": "workspace:*",
|
|
8
|
-
"vite": "catalog:"
|
|
8
|
+
"vite": "catalog:",
|
|
9
|
+
"vitest": "catalog:"
|
|
9
10
|
},
|
|
10
11
|
"exports": {
|
|
11
12
|
".": "./src/index.ts",
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "startx",
|
|
3
3
|
"description": "",
|
|
4
|
-
"version": "0.
|
|
4
|
+
"version": "0.8.0",
|
|
5
5
|
"repository": {
|
|
6
6
|
"type": "git",
|
|
7
7
|
"url": "git+https://github.com/avinashid/startx.git"
|
|
@@ -31,6 +31,7 @@
|
|
|
31
31
|
"typecheck": "turbo typecheck",
|
|
32
32
|
"clean": "turbo clean",
|
|
33
33
|
"test": "turbo test",
|
|
34
|
-
"format": "turbo format"
|
|
34
|
+
"format": "turbo format",
|
|
35
|
+
"db:push": "turbo db:push"
|
|
35
36
|
}
|
|
36
37
|
}
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import { defineConfig } from "drizzle-kit";
|
|
2
|
+
import { defineEnv } from "@repo/env";
|
|
3
|
+
import z from "zod";
|
|
4
|
+
const env = defineEnv({
|
|
5
|
+
DATABASE_URL: z.string(),
|
|
6
|
+
});
|
|
7
|
+
export default defineConfig({
|
|
8
|
+
out: "./drizzle",
|
|
9
|
+
schema: "./src/schema/index.ts",
|
|
10
|
+
dialect: "postgresql",
|
|
11
|
+
dbCredentials: {
|
|
12
|
+
url: env.DATABASE_URL!,
|
|
13
|
+
},
|
|
14
|
+
});
|