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