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
|
@@ -5,8 +5,8 @@ import path from "path";
|
|
|
5
5
|
import z from "zod";
|
|
6
6
|
|
|
7
7
|
import { FileCheck } from "../configs/files";
|
|
8
|
-
import type {
|
|
9
|
-
import { CliUtils } from "../utils/cli-utils";
|
|
8
|
+
import type { TAGS } from "../types";
|
|
9
|
+
import { CliUtils, type PackageItem } from "../utils/cli-utils";
|
|
10
10
|
import { FileHandler } from "../utils/file-handler";
|
|
11
11
|
import { CommonInquirer } from "../utils/inquirer";
|
|
12
12
|
|
|
@@ -14,13 +14,6 @@ type InitOptions = {
|
|
|
14
14
|
dir?: string;
|
|
15
15
|
};
|
|
16
16
|
|
|
17
|
-
type PackageWithJson = {
|
|
18
|
-
packageJson: StartXPackageJson | null;
|
|
19
|
-
type: "apps" | "configs" | "packages";
|
|
20
|
-
path: string;
|
|
21
|
-
name: string;
|
|
22
|
-
};
|
|
23
|
-
|
|
24
17
|
export class InitCommand {
|
|
25
18
|
static command = new Command("init")
|
|
26
19
|
.argument("[projectName]")
|
|
@@ -28,81 +21,65 @@ export class InitCommand {
|
|
|
28
21
|
.action(InitCommand.run.bind(InitCommand));
|
|
29
22
|
|
|
30
23
|
private static async run(projectName: string | undefined, options: InitOptions) {
|
|
31
|
-
const packageList
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
}))
|
|
36
|
-
);
|
|
37
|
-
|
|
38
|
-
const prefs = await InitCommand.getPrefs({
|
|
39
|
-
projectName,
|
|
40
|
-
options,
|
|
41
|
-
projects: packageList.filter(e => {
|
|
42
|
-
if (e.type !== "apps") return false;
|
|
43
|
-
if (e.packageJson?.startx?.mode === "silent") return false;
|
|
44
|
-
return true;
|
|
45
|
-
}),
|
|
46
|
-
});
|
|
47
|
-
|
|
48
|
-
const packages = await Promise.all(
|
|
49
|
-
packageList
|
|
50
|
-
.filter(e => e.type !== "apps")
|
|
51
|
-
.map(async e => ({
|
|
52
|
-
...e,
|
|
53
|
-
packageJson: await CliUtils.parsePackageJson({ dir: e.path }),
|
|
54
|
-
}))
|
|
55
|
-
);
|
|
24
|
+
const packageList = await CliUtils.getPackageList();
|
|
25
|
+
const availableApps = packageList.filter(pkg => pkg.type === "apps" && pkg.packageJson?.startx?.mode !== "silent");
|
|
26
|
+
const prefs = await this.getPrefs({ projectName, options, projects: availableApps });
|
|
27
|
+
const nonAppPackages = packageList.filter(pkg => pkg.type !== "apps");
|
|
56
28
|
|
|
57
29
|
const config = await this.getConfigPrefs({
|
|
58
30
|
selectedApps: prefs.selectedApps,
|
|
59
|
-
packages,
|
|
31
|
+
packages: nonAppPackages,
|
|
60
32
|
});
|
|
61
33
|
|
|
62
34
|
const packagePrefs = await this.getPackagesPrefs({
|
|
63
35
|
selectedApps: prefs.selectedApps,
|
|
64
36
|
selectedPackages: config.selectedConfigs,
|
|
65
|
-
packages,
|
|
66
|
-
tags: config.
|
|
37
|
+
packages: nonAppPackages,
|
|
38
|
+
tags: config.gTags,
|
|
67
39
|
});
|
|
40
|
+
|
|
41
|
+
// Installing Workspace
|
|
42
|
+
const workspaceTags = [...packagePrefs.gTags, "runnable"] as TAGS[];
|
|
68
43
|
await this.installWorkspace({
|
|
69
44
|
name: prefs.projectName,
|
|
70
|
-
tags: [...
|
|
71
|
-
dir:
|
|
72
|
-
workspace: prefs.directory.workspace,
|
|
73
|
-
template: prefs.directory.template,
|
|
74
|
-
},
|
|
45
|
+
tags: [...workspaceTags, "runnable"],
|
|
46
|
+
dir: prefs.directory,
|
|
75
47
|
});
|
|
76
48
|
|
|
49
|
+
// Installing Apps
|
|
50
|
+
const allSelectedPackages = [...packagePrefs.selectedPackages, ...prefs.selectedApps];
|
|
77
51
|
await Promise.all(
|
|
78
|
-
|
|
79
|
-
const appDeps
|
|
80
|
-
const tags = new Set(packagePrefs.
|
|
52
|
+
allSelectedPackages.map(async pkg => {
|
|
53
|
+
const appDeps: Record<string, string> = {};
|
|
54
|
+
const tags = new Set<TAGS>(packagePrefs.gTags);
|
|
81
55
|
|
|
82
|
-
if (pkg.packageJson?.startx?.mode === "standalone")
|
|
56
|
+
if (pkg.packageJson?.startx?.mode === "standalone") {
|
|
57
|
+
tags.add("runnable");
|
|
58
|
+
}
|
|
83
59
|
|
|
84
60
|
if (pkg.type === "apps") {
|
|
85
61
|
tags.add("runnable");
|
|
86
62
|
|
|
87
63
|
packagePrefs.selectedPackages
|
|
88
|
-
.filter(
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
64
|
+
.filter(depPkg => {
|
|
65
|
+
if (depPkg.type !== "packages") return false;
|
|
66
|
+
if (depPkg.packageJson?.startx?.mode !== "standalone") return false;
|
|
67
|
+
const sharesTags = depPkg.packageJson?.startx?.iTags?.every(tag =>
|
|
68
|
+
pkg.packageJson?.startx?.gTags?.includes(tag)
|
|
69
|
+
);
|
|
70
|
+
return sharesTags;
|
|
71
|
+
})
|
|
72
|
+
.forEach(depPkg => {
|
|
73
|
+
const depName = depPkg.packageJson?.name || depPkg.name;
|
|
74
|
+
appDeps[depName] = "workspace:^";
|
|
75
|
+
});
|
|
96
76
|
}
|
|
97
77
|
|
|
98
78
|
await this.installPackage({
|
|
99
|
-
|
|
100
|
-
directory:
|
|
101
|
-
workspace: prefs.directory.workspace,
|
|
102
|
-
template: prefs.directory.template,
|
|
103
|
-
},
|
|
79
|
+
pkg,
|
|
80
|
+
directory: prefs.directory,
|
|
104
81
|
tags: Array.from(tags),
|
|
105
|
-
dependencies:
|
|
82
|
+
dependencies: appDeps,
|
|
106
83
|
});
|
|
107
84
|
})
|
|
108
85
|
);
|
|
@@ -112,104 +89,69 @@ export class InitCommand {
|
|
|
112
89
|
projectName?: string;
|
|
113
90
|
directory?: string;
|
|
114
91
|
options: InitOptions;
|
|
115
|
-
projects:
|
|
92
|
+
projects: PackageItem[];
|
|
116
93
|
}) {
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
let workspace;
|
|
128
|
-
if (props.options.dir) {
|
|
129
|
-
try {
|
|
130
|
-
workspace = path.resolve(directory.workspace, props.options.dir);
|
|
131
|
-
} catch {
|
|
132
|
-
throw new Error("Invalid template directory");
|
|
133
|
-
}
|
|
134
|
-
} else {
|
|
135
|
-
workspace = path.join(directory.workspace, props.projectName);
|
|
136
|
-
}
|
|
137
|
-
|
|
94
|
+
const projectName = await CommonInquirer.getText({
|
|
95
|
+
message: "Project name",
|
|
96
|
+
name: "projectName",
|
|
97
|
+
default: props.projectName,
|
|
98
|
+
schema: z
|
|
99
|
+
.string()
|
|
100
|
+
.min(1, "Package name is required")
|
|
101
|
+
.max(214, "Package name too long")
|
|
102
|
+
.regex(/^(?:@[a-z0-9-~][a-z0-9-._~]*\/)?[a-z0-9-~][a-z0-9-._~]*$/, "Invalid package name"),
|
|
103
|
+
});
|
|
138
104
|
if (props.projects.length === 0) {
|
|
139
|
-
throw new Error("No apps found");
|
|
105
|
+
throw new Error("No apps found to install.");
|
|
140
106
|
}
|
|
107
|
+
const directory = CliUtils.getDirectory();
|
|
108
|
+
const workspace = props.options.dir
|
|
109
|
+
? path.resolve(directory.workspace, props.options.dir)
|
|
110
|
+
: path.join(directory.workspace, projectName);
|
|
141
111
|
|
|
142
112
|
const selectedAppNames = await CommonInquirer.choose({
|
|
143
113
|
message: "Select apps to install",
|
|
144
|
-
options: props.projects.map(
|
|
114
|
+
options: props.projects.map(pkg => pkg.name),
|
|
145
115
|
includeAllOption: true,
|
|
146
116
|
mode: "multiple",
|
|
147
117
|
required: true,
|
|
148
118
|
});
|
|
149
119
|
|
|
150
|
-
const selectedApps = props.projects.filter(e => selectedAppNames.includes(e.name));
|
|
151
|
-
|
|
152
120
|
return {
|
|
153
|
-
projectName
|
|
154
|
-
directory: {
|
|
155
|
-
|
|
156
|
-
template: directory.template,
|
|
157
|
-
},
|
|
158
|
-
selectedApps,
|
|
121
|
+
projectName,
|
|
122
|
+
directory: { workspace, template: directory.template },
|
|
123
|
+
selectedApps: props.projects.filter(pkg => selectedAppNames.includes(pkg.name)),
|
|
159
124
|
};
|
|
160
125
|
}
|
|
161
126
|
|
|
162
|
-
private static async getConfigPrefs(props: {
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
.
|
|
176
|
-
|
|
177
|
-
...(app.packageJson?.startx?.requiredDevDeps || []),
|
|
178
|
-
])
|
|
179
|
-
.forEach(pkg => {
|
|
180
|
-
const config = props.packages.find(e => e.packageJson?.name === pkg);
|
|
181
|
-
if (config) configs.set(config.name, config);
|
|
182
|
-
});
|
|
183
|
-
|
|
184
|
-
const availableConfigs = props.packages.filter(e => {
|
|
185
|
-
if (e.type !== "configs") return false;
|
|
186
|
-
if (e.packageJson?.startx?.mode === "silent") {
|
|
187
|
-
return false;
|
|
188
|
-
}
|
|
189
|
-
if (configs.has(e.name)) return false;
|
|
190
|
-
if (!e.packageJson?.startx?.iTags?.every(t => tags.has(t))) return false;
|
|
191
|
-
return true;
|
|
192
|
-
});
|
|
193
|
-
|
|
194
|
-
if (availableConfigs.length === 0)
|
|
195
|
-
return {
|
|
196
|
-
tags: Array.from(tags),
|
|
197
|
-
selectedConfigs: Array.from(configs.values()),
|
|
198
|
-
};
|
|
199
|
-
|
|
200
|
-
const rawSelectedConfigs = await CommonInquirer.choose({
|
|
201
|
-
message: "Select configs to install",
|
|
202
|
-
options: availableConfigs.map(e => e.name),
|
|
203
|
-
includeAllOption: true,
|
|
204
|
-
mode: "multiple",
|
|
205
|
-
required: false,
|
|
127
|
+
private static async getConfigPrefs(props: { packages: PackageItem[]; selectedApps: PackageItem[] }) {
|
|
128
|
+
const gTags = new Set<TAGS>(["common"]);
|
|
129
|
+
const configs = new Map<string, PackageItem>();
|
|
130
|
+
// Selected apps globals tags and dependencies resolver
|
|
131
|
+
this.getGlobalTags({ pkgs: props.selectedApps }).forEach(tag => gTags.add(tag));
|
|
132
|
+
this.getPackageDeps({
|
|
133
|
+
allPkgs: props.packages,
|
|
134
|
+
pkgs: props.selectedApps,
|
|
135
|
+
}).forEach(pkg => configs.set(pkg.name, pkg));
|
|
136
|
+
|
|
137
|
+
const availableConfigs = props.packages.filter(pkg => {
|
|
138
|
+
if (pkg.type !== "configs") return false;
|
|
139
|
+
if (pkg.packageJson?.startx?.mode === "silent") return false;
|
|
140
|
+
if (configs.has(pkg.name)) return false;
|
|
141
|
+
return pkg.packageJson?.startx?.iTags?.every(t => gTags.has(t)) ?? true;
|
|
206
142
|
});
|
|
143
|
+
if (availableConfigs.length > 0) {
|
|
144
|
+
const rawSelectedConfigs = await CommonInquirer.choose({
|
|
145
|
+
message: "Select configs to install",
|
|
146
|
+
options: availableConfigs.map(pkg => pkg.name),
|
|
147
|
+
includeAllOption: true,
|
|
148
|
+
mode: "multiple",
|
|
149
|
+
required: false,
|
|
150
|
+
});
|
|
151
|
+
availableConfigs.filter(pkg => rawSelectedConfigs.includes(pkg.name)).forEach(pkg => configs.set(pkg.name, pkg));
|
|
152
|
+
}
|
|
207
153
|
|
|
208
|
-
|
|
209
|
-
.filter(e => rawSelectedConfigs.includes(e.name))
|
|
210
|
-
.forEach(e => configs.set(e.name, e));
|
|
211
|
-
|
|
212
|
-
if (tags.has("node")) {
|
|
154
|
+
if (gTags.has("node")) {
|
|
213
155
|
const formatter: string | string[] = await CommonInquirer.choose({
|
|
214
156
|
message: "Select formatter",
|
|
215
157
|
options: ["prettier + biome", "prettier"],
|
|
@@ -218,100 +160,73 @@ export class InitCommand {
|
|
|
218
160
|
required: true,
|
|
219
161
|
});
|
|
220
162
|
if (formatter === "prettier") {
|
|
221
|
-
|
|
163
|
+
gTags.add("prettier");
|
|
222
164
|
} else {
|
|
223
|
-
|
|
224
|
-
|
|
165
|
+
gTags.add("biome");
|
|
166
|
+
gTags.add("prettier");
|
|
225
167
|
}
|
|
226
168
|
}
|
|
227
169
|
|
|
228
|
-
//
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
const required = [...requiredDeps, ...requiredDevDeps];
|
|
234
|
-
required.forEach(pkg => {
|
|
235
|
-
const config = props.packages.find(e => e.packageJson?.name === pkg);
|
|
236
|
-
if (config) {
|
|
237
|
-
configs.set(config.name, config);
|
|
238
|
-
}
|
|
239
|
-
});
|
|
240
|
-
});
|
|
170
|
+
// Resolving deps for selected configs
|
|
171
|
+
this.getPackageDeps({
|
|
172
|
+
allPkgs: props.packages,
|
|
173
|
+
pkgs: Array.from(configs.values()),
|
|
174
|
+
}).forEach(pkg => configs.set(pkg.name, pkg));
|
|
241
175
|
|
|
242
|
-
//
|
|
243
|
-
Array.from(configs.values()).forEach(
|
|
244
|
-
const gTags = e.packageJson?.startx?.gTags || [];
|
|
245
|
-
gTags.forEach(tag => tags.add(tag));
|
|
246
|
-
});
|
|
176
|
+
// Adding global tags
|
|
177
|
+
this.getGlobalTags({ pkgs: Array.from(configs.values()) }).forEach(tag => gTags.add(tag));
|
|
247
178
|
|
|
248
179
|
return {
|
|
249
|
-
|
|
180
|
+
gTags: Array.from(gTags),
|
|
250
181
|
selectedConfigs: Array.from(configs.values()),
|
|
251
182
|
};
|
|
252
183
|
}
|
|
253
184
|
|
|
254
185
|
private static async getPackagesPrefs(props: {
|
|
255
186
|
tags: TAGS[];
|
|
256
|
-
packages:
|
|
257
|
-
selectedApps:
|
|
258
|
-
selectedPackages:
|
|
187
|
+
packages: PackageItem[];
|
|
188
|
+
selectedApps: PackageItem[];
|
|
189
|
+
selectedPackages: PackageItem[];
|
|
259
190
|
}) {
|
|
260
|
-
const
|
|
261
|
-
const packages = new Map<string,
|
|
191
|
+
const gTags = new Set<TAGS>(props.tags);
|
|
192
|
+
const packages = new Map<string, PackageItem>(props.selectedPackages.map(pkg => [pkg.name, pkg]));
|
|
262
193
|
const availablePackages = props.packages.filter(pkg => {
|
|
263
194
|
if (pkg.type !== "packages") return false;
|
|
264
|
-
if (pkg.packageJson?.startx?.mode === "silent")
|
|
265
|
-
return false;
|
|
266
|
-
}
|
|
195
|
+
if (pkg.packageJson?.startx?.mode === "silent") return false;
|
|
267
196
|
if (packages.has(pkg.name)) return false;
|
|
268
|
-
|
|
269
|
-
return true;
|
|
270
|
-
});
|
|
271
|
-
if (!availablePackages.length)
|
|
272
|
-
return {
|
|
273
|
-
selectedPackages: Array.from(packages.values()),
|
|
274
|
-
tags: Array.from(appTags),
|
|
275
|
-
};
|
|
276
|
-
const rawSelectedPackages = await CommonInquirer.choose({
|
|
277
|
-
message: "Select packages to install",
|
|
278
|
-
options: availablePackages.map(e => e.name),
|
|
279
|
-
includeAllOption: true,
|
|
280
|
-
mode: "multiple",
|
|
281
|
-
required: false,
|
|
197
|
+
return pkg.packageJson?.startx?.iTags?.every(t => gTags.has(t)) ?? false;
|
|
282
198
|
});
|
|
199
|
+
if (availablePackages.length > 0) {
|
|
200
|
+
const rawSelectedPackages = await CommonInquirer.choose({
|
|
201
|
+
message: "Select packages to install",
|
|
202
|
+
options: availablePackages.map(pkg => pkg.name),
|
|
203
|
+
includeAllOption: true,
|
|
204
|
+
mode: "multiple",
|
|
205
|
+
required: false,
|
|
206
|
+
});
|
|
283
207
|
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
Array.from(packages.values()).forEach(e => {
|
|
289
|
-
const requiredDeps = e.packageJson?.startx?.requiredDeps || [];
|
|
290
|
-
const requiredDevDeps = e.packageJson?.startx?.requiredDevDeps || [];
|
|
208
|
+
availablePackages
|
|
209
|
+
.filter(pkg => rawSelectedPackages.includes(pkg.name))
|
|
210
|
+
.forEach(pkg => packages.set(pkg.name, pkg));
|
|
211
|
+
}
|
|
291
212
|
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
packages.set(config.name, config);
|
|
297
|
-
}
|
|
298
|
-
});
|
|
299
|
-
});
|
|
213
|
+
this.getPackageDeps({
|
|
214
|
+
allPkgs: props.packages,
|
|
215
|
+
pkgs: Array.from(packages.values()),
|
|
216
|
+
}).forEach(pkg => packages.set(pkg.name, pkg));
|
|
300
217
|
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
gTags.forEach(tag => appTags.add(tag));
|
|
305
|
-
});
|
|
218
|
+
this.getGlobalTags({
|
|
219
|
+
pkgs: Array.from(packages.values()),
|
|
220
|
+
}).forEach(tag => gTags.add(tag));
|
|
306
221
|
|
|
307
222
|
return {
|
|
308
|
-
|
|
223
|
+
gTags: Array.from(gTags),
|
|
309
224
|
selectedPackages: Array.from(packages.values()),
|
|
310
225
|
};
|
|
311
226
|
}
|
|
312
227
|
|
|
313
228
|
private static async installPackage(props: {
|
|
314
|
-
|
|
229
|
+
pkg: PackageItem;
|
|
315
230
|
directory: {
|
|
316
231
|
workspace: string;
|
|
317
232
|
template: string;
|
|
@@ -319,57 +234,41 @@ export class InitCommand {
|
|
|
319
234
|
tags: TAGS[];
|
|
320
235
|
dependencies: Record<string, string>;
|
|
321
236
|
}) {
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
237
|
+
if (!props.pkg.packageJson) {
|
|
238
|
+
throw new Error(`Missing package.json for ${props.pkg.name}`);
|
|
239
|
+
}
|
|
240
|
+
const tags = new Set<TAGS>([...props.tags, ...(props.pkg.packageJson.startx?.tags || [])]);
|
|
241
|
+
const ignoreList = props.pkg.packageJson.startx?.ignore || [];
|
|
242
|
+
if (ignoreList.includes("eslint-config")) tags.delete("eslint");
|
|
243
|
+
if (ignoreList.includes("vitest-config")) tags.delete("vitest");
|
|
327
244
|
const { packageJson, isWorkspace } = FileHandler.handlePackageJson({
|
|
328
|
-
app: props.
|
|
245
|
+
app: props.pkg.packageJson,
|
|
329
246
|
tags: Array.from(tags),
|
|
330
|
-
name: props.
|
|
247
|
+
name: props.pkg.name,
|
|
331
248
|
});
|
|
332
249
|
|
|
333
|
-
if (isWorkspace)
|
|
334
|
-
|
|
335
|
-
let iDirectory = path.join(props.directory.workspace, props.packages.type);
|
|
336
|
-
let iTemplate = path.join(props.directory.template, props.packages.type);
|
|
337
|
-
if (props.packages.packageJson?.name?.startsWith("@repo")) {
|
|
338
|
-
const repoName = props.packages.packageJson.name.split("/")[1];
|
|
339
|
-
iDirectory = path.join(iDirectory, "@repo", repoName);
|
|
340
|
-
iTemplate = path.join(iTemplate, "@repo", repoName);
|
|
341
|
-
} else {
|
|
342
|
-
iDirectory = path.join(iDirectory, props.packages.name);
|
|
343
|
-
iTemplate = path.join(iTemplate, props.packages.name);
|
|
250
|
+
if (isWorkspace) {
|
|
251
|
+
throw new Error(`Cannot install workspace as a package: ${props.pkg.name}`);
|
|
344
252
|
}
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
});
|
|
351
|
-
|
|
352
|
-
const files = await fsTool.listFiles({ dir: iTemplate });
|
|
353
|
-
for (const file of files) {
|
|
354
|
-
const checked = FileCheck[file];
|
|
355
|
-
if (checked && !checked.tags.every(e => tags.has(e))) continue;
|
|
356
|
-
try {
|
|
357
|
-
await fsTool.copyFile({
|
|
358
|
-
from: path.join(iTemplate, file),
|
|
359
|
-
to: path.join(iDirectory, file),
|
|
360
|
-
});
|
|
361
|
-
} catch (error) {
|
|
362
|
-
logger.error(`Failed to copy file ${file}:`, error);
|
|
363
|
-
}
|
|
253
|
+
if (Object.keys(props.dependencies).length > 0) {
|
|
254
|
+
packageJson.dependencies = {
|
|
255
|
+
...(packageJson.dependencies as object),
|
|
256
|
+
...props.dependencies,
|
|
257
|
+
};
|
|
364
258
|
}
|
|
365
259
|
|
|
366
|
-
|
|
260
|
+
const iDirectory = path.join(props.directory.workspace, props.pkg.relativePath);
|
|
261
|
+
const iTemplate = path.join(props.pkg.path);
|
|
262
|
+
await fsTool.writeJSONFile({ dir: iDirectory, file: "package", content: packageJson });
|
|
263
|
+
|
|
264
|
+
await this.copyValidatedFilesFromFolder(iTemplate, iDirectory, tags);
|
|
367
265
|
await fsTool.copyDirectory({
|
|
368
266
|
from: path.join(iTemplate, "src"),
|
|
369
267
|
to: path.join(iDirectory, "src"),
|
|
370
268
|
exclude: !tags.has("vitest") ? /\.test\.tsx?$/ : undefined,
|
|
371
269
|
});
|
|
372
|
-
|
|
270
|
+
|
|
271
|
+
logger.info(`Successfully installed ${props.pkg.name}`);
|
|
373
272
|
}
|
|
374
273
|
|
|
375
274
|
private static async installWorkspace(props: {
|
|
@@ -380,19 +279,19 @@ export class InitCommand {
|
|
|
380
279
|
template: string;
|
|
381
280
|
};
|
|
382
281
|
}) {
|
|
383
|
-
const rootTags = ["root", ...props.tags] as TAGS[];
|
|
384
282
|
const rawPackage = await CliUtils.parsePackageJson({ dir: props.dir.template });
|
|
385
283
|
const startXRawPackage = await CliUtils.parsePackageJson({
|
|
386
284
|
dir: props.dir.template,
|
|
387
285
|
file: "startx",
|
|
388
286
|
});
|
|
389
287
|
|
|
390
|
-
if (!rawPackage) throw new Error("Failed to parse package.json");
|
|
391
|
-
rawPackage.dependencies = startXRawPackage?.dependencies || {};
|
|
392
|
-
rawPackage.devDependencies = startXRawPackage?.devDependencies || {};
|
|
288
|
+
if (!rawPackage) throw new Error("Failed to parse root package.json");
|
|
289
|
+
rawPackage.dependencies = { ...rawPackage.dependencies, ...(startXRawPackage?.dependencies || {}) };
|
|
290
|
+
rawPackage.devDependencies = { ...rawPackage.devDependencies, ...(startXRawPackage?.devDependencies || {}) };
|
|
291
|
+
|
|
393
292
|
const { packageJson } = FileHandler.handlePackageJson({
|
|
394
293
|
app: rawPackage,
|
|
395
|
-
tags:
|
|
294
|
+
tags: ["root", ...props.tags] as TAGS[],
|
|
396
295
|
name: props.name,
|
|
397
296
|
});
|
|
398
297
|
|
|
@@ -401,15 +300,44 @@ export class InitCommand {
|
|
|
401
300
|
file: "package",
|
|
402
301
|
content: packageJson,
|
|
403
302
|
});
|
|
303
|
+
await this.copyValidatedFilesFromFolder(
|
|
304
|
+
props.dir.template,
|
|
305
|
+
props.dir.workspace,
|
|
306
|
+
new Set(["root", ...props.tags] as TAGS[])
|
|
307
|
+
);
|
|
308
|
+
}
|
|
404
309
|
|
|
405
|
-
|
|
310
|
+
// Helpers
|
|
311
|
+
private static getPackageDeps(props: { pkgs: PackageItem[]; allPkgs: PackageItem[] }) {
|
|
312
|
+
const deps = new Map<string, PackageItem>(props.pkgs.map(pkg => [pkg.name, pkg]));
|
|
313
|
+
Array.from(deps.values()).forEach(pkg => {
|
|
314
|
+
const required = [
|
|
315
|
+
...(pkg.packageJson?.startx?.requiredDeps || []),
|
|
316
|
+
...(pkg.packageJson?.startx?.requiredDevDeps || []),
|
|
317
|
+
];
|
|
318
|
+
required.forEach(reqPkgName => {
|
|
319
|
+
const config = props.allPkgs.find(p => p.packageJson?.name === reqPkgName);
|
|
320
|
+
if (config) deps.set(config.name, config);
|
|
321
|
+
});
|
|
322
|
+
});
|
|
323
|
+
return Array.from(deps.values());
|
|
324
|
+
}
|
|
325
|
+
private static getGlobalTags(props: { pkgs: PackageItem[]; gTags?: TAGS[] }) {
|
|
326
|
+
const tags = new Set<TAGS>(props.gTags || []);
|
|
327
|
+
props.pkgs.forEach(pkg => {
|
|
328
|
+
pkg.packageJson?.startx?.gTags?.forEach(tag => tags.add(tag));
|
|
329
|
+
});
|
|
330
|
+
return Array.from(tags);
|
|
331
|
+
}
|
|
332
|
+
private static async copyValidatedFilesFromFolder(source: string, destination: string, tags: Set<TAGS>) {
|
|
333
|
+
const files = await fsTool.listFiles({ dir: source }).catch(() => []);
|
|
406
334
|
for (const file of files) {
|
|
407
335
|
const checked = FileCheck[file];
|
|
408
|
-
if (checked && !checked.tags.every(
|
|
336
|
+
if (checked && !checked.tags.every(tag => tags.has(tag))) continue;
|
|
409
337
|
try {
|
|
410
338
|
await fsTool.copyFile({
|
|
411
|
-
from: path.join(
|
|
412
|
-
to: path.join(
|
|
339
|
+
from: path.join(source, file),
|
|
340
|
+
to: path.join(destination, file),
|
|
413
341
|
});
|
|
414
342
|
} catch (error) {
|
|
415
343
|
logger.error(`Failed to copy file ${file}:`, error);
|
|
@@ -1,4 +1,3 @@
|
|
|
1
|
-
/* eslint-disable @typescript-eslint/naming-convention */
|
|
2
1
|
import type { WHITELIST_FILES } from "../types";
|
|
3
2
|
|
|
4
3
|
export const FileCheck: WHITELIST_FILES = {
|
|
@@ -11,12 +10,12 @@ export const FileCheck: WHITELIST_FILES = {
|
|
|
11
10
|
".npmrc": {
|
|
12
11
|
tags: ["never"],
|
|
13
12
|
},
|
|
14
|
-
".prettierignore": {
|
|
15
|
-
tags: ["prettier"],
|
|
16
|
-
},
|
|
17
13
|
".prettier.js": {
|
|
18
14
|
tags: ["prettier"],
|
|
19
15
|
},
|
|
16
|
+
".prettierignore": {
|
|
17
|
+
tags: ["biome"],
|
|
18
|
+
},
|
|
20
19
|
"biome.json": {
|
|
21
20
|
tags: ["biome"],
|
|
22
21
|
},
|
|
@@ -1,5 +1,3 @@
|
|
|
1
|
-
/* eslint-disable @typescript-eslint/naming-convention */
|
|
2
|
-
|
|
3
1
|
import type { SCRIPT } from "../types";
|
|
4
2
|
|
|
5
3
|
export const scripts: SCRIPT = {
|
|
@@ -10,7 +8,7 @@ export const scripts: SCRIPT = {
|
|
|
10
8
|
},
|
|
11
9
|
{
|
|
12
10
|
script: "tsx watch src/index.ts",
|
|
13
|
-
tags: ["runnable", "node", "backend", "
|
|
11
|
+
tags: ["runnable", "node", "backend", "express"],
|
|
14
12
|
},
|
|
15
13
|
{
|
|
16
14
|
script: "email dev --port 3014 --dir ./src",
|
|
@@ -24,7 +22,7 @@ export const scripts: SCRIPT = {
|
|
|
24
22
|
},
|
|
25
23
|
{
|
|
26
24
|
script: "tsx watch --inspect src/index.ts",
|
|
27
|
-
tags: ["backend", "node", "runnable", "
|
|
25
|
+
tags: ["backend", "node", "runnable", "express"],
|
|
28
26
|
},
|
|
29
27
|
],
|
|
30
28
|
"bun:dev": [
|
|
@@ -34,7 +32,7 @@ export const scripts: SCRIPT = {
|
|
|
34
32
|
},
|
|
35
33
|
{
|
|
36
34
|
script: "bun --watch src/index.ts",
|
|
37
|
-
tags: ["backend", "node", "runnable", "
|
|
35
|
+
tags: ["backend", "node", "runnable", "express"],
|
|
38
36
|
},
|
|
39
37
|
],
|
|
40
38
|
"build": [
|
|
@@ -44,7 +42,7 @@ export const scripts: SCRIPT = {
|
|
|
44
42
|
},
|
|
45
43
|
{
|
|
46
44
|
script: "tsdown --config-loader unrun",
|
|
47
|
-
tags: ["runnable", "node"],
|
|
45
|
+
tags: ["runnable", "node", "tsdown"],
|
|
48
46
|
},
|
|
49
47
|
],
|
|
50
48
|
"cli": [
|
|
@@ -107,6 +105,26 @@ export const scripts: SCRIPT = {
|
|
|
107
105
|
tags: ["node"],
|
|
108
106
|
},
|
|
109
107
|
],
|
|
108
|
+
"db:push": [
|
|
109
|
+
{
|
|
110
|
+
script: "drizzle-kit push",
|
|
111
|
+
tags: ["drizzle", "db"],
|
|
112
|
+
},
|
|
113
|
+
{
|
|
114
|
+
script: "turbo run db:push",
|
|
115
|
+
tags: ["db", "root"],
|
|
116
|
+
},
|
|
117
|
+
],
|
|
118
|
+
"db:studio": [
|
|
119
|
+
{
|
|
120
|
+
script: "drizzle-kit studio",
|
|
121
|
+
tags: ["drizzle", "db"],
|
|
122
|
+
},
|
|
123
|
+
{
|
|
124
|
+
script: "turbo run db:studio",
|
|
125
|
+
tags: ["db", "root"],
|
|
126
|
+
},
|
|
127
|
+
],
|
|
110
128
|
"typecheck": [
|
|
111
129
|
{
|
|
112
130
|
script: "turbo run typecheck",
|