@reliverse/dler 2.0.0 → 2.0.1
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/dist/cli.d.ts +2 -0
- package/dist/cli.js +3 -0
- package/dist/cmds/build/cmd.d.ts +2 -0
- package/dist/cmds/build/cmd.js +564 -0
- package/dist/cmds/clean/cmd.d.ts +2 -0
- package/dist/cmds/clean/cmd.js +146 -0
- package/dist/cmds/clean/impl.d.ts +2 -0
- package/dist/cmds/clean/impl.js +627 -0
- package/dist/cmds/clean/presets.d.ts +10 -0
- package/dist/cmds/clean/presets.js +112 -0
- package/dist/cmds/clean/types.d.ts +62 -0
- package/dist/cmds/clean/types.js +0 -0
- package/dist/cmds/init/cmd.d.ts +3 -0
- package/dist/cmds/init/cmd.js +56 -0
- package/dist/cmds/init/impl/config.d.ts +45 -0
- package/dist/cmds/init/impl/config.js +99 -0
- package/dist/cmds/init/impl/generators.d.ts +6 -0
- package/dist/cmds/init/impl/generators.js +178 -0
- package/dist/cmds/init/impl/prompts.d.ts +2 -0
- package/dist/cmds/init/impl/prompts.js +98 -0
- package/dist/cmds/init/impl/types.d.ts +22 -0
- package/dist/cmds/init/impl/types.js +0 -0
- package/dist/cmds/init/impl/utils.d.ts +4 -0
- package/dist/cmds/init/impl/utils.js +11 -0
- package/dist/cmds/init/impl/validators.d.ts +4 -0
- package/dist/cmds/init/impl/validators.js +42 -0
- package/dist/cmds/integrate/cmd.d.ts +3 -0
- package/dist/cmds/integrate/cmd.js +70 -0
- package/dist/cmds/integrate/impl.d.ts +7 -0
- package/dist/cmds/integrate/impl.js +127 -0
- package/dist/cmds/integrate/integrations/base.d.ts +13 -0
- package/dist/cmds/integrate/integrations/base.js +41 -0
- package/dist/cmds/integrate/integrations/nextjs.d.ts +16 -0
- package/dist/cmds/integrate/integrations/nextjs.js +167 -0
- package/dist/cmds/integrate/integrations/registry.d.ts +7 -0
- package/dist/cmds/integrate/integrations/registry.js +31 -0
- package/dist/cmds/integrate/integrations/ultracite.d.ts +11 -0
- package/dist/cmds/integrate/integrations/ultracite.js +40 -0
- package/dist/cmds/integrate/types.d.ts +39 -0
- package/dist/cmds/integrate/types.js +0 -0
- package/dist/cmds/integrate/utils/biome.d.ts +4 -0
- package/dist/cmds/integrate/utils/biome.js +140 -0
- package/dist/cmds/integrate/utils/context.d.ts +3 -0
- package/dist/cmds/integrate/utils/context.js +111 -0
- package/dist/cmds/integrate/utils/temp.d.ts +3 -0
- package/dist/cmds/integrate/utils/temp.js +36 -0
- package/dist/cmds/perf/analysis/bundle.d.ts +20 -0
- package/dist/cmds/perf/analysis/bundle.js +225 -0
- package/dist/cmds/perf/analysis/filesystem.d.ts +27 -0
- package/dist/cmds/perf/analysis/filesystem.js +246 -0
- package/dist/cmds/perf/analysis/monorepo.d.ts +29 -0
- package/dist/cmds/perf/analysis/monorepo.js +307 -0
- package/dist/cmds/perf/benchmarks/command.d.ts +21 -0
- package/dist/cmds/perf/benchmarks/command.js +162 -0
- package/dist/cmds/perf/benchmarks/memory.d.ts +41 -0
- package/dist/cmds/perf/benchmarks/memory.js +169 -0
- package/dist/cmds/perf/benchmarks/runner.d.ts +22 -0
- package/dist/cmds/perf/benchmarks/runner.js +157 -0
- package/dist/cmds/perf/cmd.d.ts +2 -0
- package/dist/cmds/perf/cmd.js +238 -0
- package/dist/cmds/perf/impl.d.ts +24 -0
- package/dist/cmds/perf/impl.js +304 -0
- package/dist/cmds/perf/reporters/console.d.ts +12 -0
- package/dist/cmds/perf/reporters/console.js +257 -0
- package/dist/cmds/perf/reporters/html.d.ts +27 -0
- package/dist/cmds/perf/reporters/html.js +881 -0
- package/dist/cmds/perf/reporters/json.d.ts +9 -0
- package/dist/cmds/perf/reporters/json.js +32 -0
- package/dist/cmds/perf/types.d.ts +184 -0
- package/dist/cmds/perf/types.js +0 -0
- package/dist/cmds/perf/utils/cache.d.ts +23 -0
- package/dist/cmds/perf/utils/cache.js +171 -0
- package/dist/cmds/perf/utils/formatter.d.ts +17 -0
- package/dist/cmds/perf/utils/formatter.js +134 -0
- package/dist/cmds/perf/utils/stats.d.ts +15 -0
- package/dist/cmds/perf/utils/stats.js +101 -0
- package/dist/cmds/publish/cmd.d.ts +3 -0
- package/dist/cmds/publish/cmd.js +189 -0
- package/dist/cmds/shell/cmd.d.ts +3 -0
- package/dist/cmds/shell/cmd.js +50 -0
- package/dist/cmds/tsc/cache.d.ts +27 -0
- package/dist/cmds/tsc/cache.js +160 -0
- package/dist/cmds/tsc/cmd.d.ts +2 -0
- package/dist/cmds/tsc/cmd.js +111 -0
- package/dist/cmds/tsc/impl.d.ts +41 -0
- package/dist/cmds/tsc/impl.js +572 -0
- package/dist/cmds/tsc/types.d.ts +57 -0
- package/dist/cmds/tsc/types.js +0 -0
- package/package.json +4 -11
- package/src/cli.ts +8 -0
- package/src/cmds/build/cmd.ts +582 -0
- package/src/cmds/clean/cmd.ts +166 -0
- package/src/cmds/clean/impl.ts +900 -0
- package/src/cmds/clean/presets.ts +158 -0
- package/src/cmds/clean/types.ts +71 -0
- package/src/cmds/init/cmd.ts +68 -0
- package/src/cmds/init/impl/config.ts +105 -0
- package/src/cmds/init/impl/generators.ts +220 -0
- package/src/cmds/init/impl/prompts.ts +137 -0
- package/src/cmds/init/impl/types.ts +25 -0
- package/src/cmds/init/impl/utils.ts +17 -0
- package/src/cmds/init/impl/validators.ts +55 -0
- package/src/cmds/integrate/cmd.ts +82 -0
- package/src/cmds/integrate/impl.ts +204 -0
- package/src/cmds/integrate/integrations/base.ts +69 -0
- package/src/cmds/integrate/integrations/nextjs.ts +227 -0
- package/src/cmds/integrate/integrations/registry.ts +45 -0
- package/src/cmds/integrate/integrations/ultracite.ts +53 -0
- package/src/cmds/integrate/types.ts +48 -0
- package/src/cmds/integrate/utils/biome.ts +173 -0
- package/src/cmds/integrate/utils/context.ts +148 -0
- package/src/cmds/integrate/utils/temp.ts +47 -0
- package/src/cmds/perf/analysis/bundle.ts +311 -0
- package/src/cmds/perf/analysis/filesystem.ts +324 -0
- package/src/cmds/perf/analysis/monorepo.ts +439 -0
- package/src/cmds/perf/benchmarks/command.ts +230 -0
- package/src/cmds/perf/benchmarks/memory.ts +249 -0
- package/src/cmds/perf/benchmarks/runner.ts +220 -0
- package/src/cmds/perf/cmd.ts +285 -0
- package/src/cmds/perf/impl.ts +411 -0
- package/src/cmds/perf/reporters/console.ts +331 -0
- package/src/cmds/perf/reporters/html.ts +984 -0
- package/src/cmds/perf/reporters/json.ts +42 -0
- package/src/cmds/perf/types.ts +220 -0
- package/src/cmds/perf/utils/cache.ts +234 -0
- package/src/cmds/perf/utils/formatter.ts +190 -0
- package/src/cmds/perf/utils/stats.ts +153 -0
- package/src/cmds/publish/cmd.ts +215 -0
- package/src/cmds/shell/cmd.ts +61 -0
- package/src/cmds/tsc/cache.ts +237 -0
- package/src/cmds/tsc/cmd.ts +139 -0
- package/src/cmds/tsc/impl.ts +855 -0
- package/src/cmds/tsc/types.ts +66 -0
- package/tsconfig.json +9 -0
- package/cli.js +0 -1316
|
@@ -0,0 +1,137 @@
|
|
|
1
|
+
import { logger } from "@reliverse/dler-logger";
|
|
2
|
+
import { askQuestion } from "@reliverse/dler-prompt";
|
|
3
|
+
import { DEFAULT_LICENSE, DEFAULT_VERSION, WORKSPACES } from "./config";
|
|
4
|
+
import type { MonorepoConfig, PackageInfo } from "./types";
|
|
5
|
+
import { createFullPath, fileExists, getWorkspaceScope } from "./utils";
|
|
6
|
+
import {
|
|
7
|
+
validateMonorepoName,
|
|
8
|
+
validatePackageName,
|
|
9
|
+
validateVersion,
|
|
10
|
+
} from "./validators";
|
|
11
|
+
|
|
12
|
+
export const promptMonorepoConfig = async (): Promise<MonorepoConfig> => {
|
|
13
|
+
logger.info("🚀 Bun Monorepo Bootstrapper\n");
|
|
14
|
+
|
|
15
|
+
// Try to read existing root package.json to avoid re-asking known values
|
|
16
|
+
const rootPath = process.cwd();
|
|
17
|
+
const rootPackageJsonPath = createFullPath(rootPath, "package.json");
|
|
18
|
+
const hasRootPackageJson = await fileExists(rootPackageJsonPath);
|
|
19
|
+
const existingRoot: Record<string, unknown> | null = hasRootPackageJson
|
|
20
|
+
? await Bun.file(rootPackageJsonPath)
|
|
21
|
+
.json()
|
|
22
|
+
.catch(() => null)
|
|
23
|
+
: null;
|
|
24
|
+
|
|
25
|
+
let name = "";
|
|
26
|
+
let isValidName = false;
|
|
27
|
+
|
|
28
|
+
if (existingRoot && typeof existingRoot.name === "string") {
|
|
29
|
+
name = existingRoot.name;
|
|
30
|
+
isValidName = true;
|
|
31
|
+
} else {
|
|
32
|
+
while (!isValidName) {
|
|
33
|
+
name = await askQuestion("Monorepo name", "my-monorepo");
|
|
34
|
+
const validation = validateMonorepoName(name);
|
|
35
|
+
|
|
36
|
+
if (!validation.valid) {
|
|
37
|
+
logger.error(`❌ ${validation.error}`);
|
|
38
|
+
continue;
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
isValidName = true;
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
const description =
|
|
46
|
+
existingRoot && typeof existingRoot.description === "string"
|
|
47
|
+
? (existingRoot.description as string)
|
|
48
|
+
: await askQuestion("Description", "A Bun monorepo project");
|
|
49
|
+
|
|
50
|
+
let version = "";
|
|
51
|
+
let isValidVersion = false;
|
|
52
|
+
|
|
53
|
+
if (existingRoot && typeof existingRoot.version === "string") {
|
|
54
|
+
version = existingRoot.version;
|
|
55
|
+
isValidVersion = true;
|
|
56
|
+
} else {
|
|
57
|
+
while (!isValidVersion) {
|
|
58
|
+
version = await askQuestion("Version", DEFAULT_VERSION);
|
|
59
|
+
const validation = validateVersion(version);
|
|
60
|
+
|
|
61
|
+
if (!validation.valid) {
|
|
62
|
+
logger.error(`❌ ${validation.error}`);
|
|
63
|
+
continue;
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
isValidVersion = true;
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
const author =
|
|
71
|
+
existingRoot && typeof (existingRoot as any).author === "string"
|
|
72
|
+
? ((existingRoot as any).author as string)
|
|
73
|
+
: await askQuestion("Author", "");
|
|
74
|
+
const license =
|
|
75
|
+
existingRoot && typeof (existingRoot as any).license === "string"
|
|
76
|
+
? ((existingRoot as any).license as string)
|
|
77
|
+
: await askQuestion("License", DEFAULT_LICENSE);
|
|
78
|
+
|
|
79
|
+
const packages = await promptPackages();
|
|
80
|
+
|
|
81
|
+
return {
|
|
82
|
+
name,
|
|
83
|
+
description,
|
|
84
|
+
version,
|
|
85
|
+
author,
|
|
86
|
+
license,
|
|
87
|
+
packages,
|
|
88
|
+
rootPath,
|
|
89
|
+
};
|
|
90
|
+
};
|
|
91
|
+
|
|
92
|
+
const promptPackages = async (): Promise<PackageInfo[]> => {
|
|
93
|
+
const packages: PackageInfo[] = [];
|
|
94
|
+
|
|
95
|
+
logger.info("\n📦 Package Configuration");
|
|
96
|
+
logger.info(
|
|
97
|
+
"Enter package names (one per prompt). Press Enter with empty input to finish.\n",
|
|
98
|
+
);
|
|
99
|
+
|
|
100
|
+
let continueAdding = true;
|
|
101
|
+
let packageIndex = 1;
|
|
102
|
+
|
|
103
|
+
while (continueAdding) {
|
|
104
|
+
const packageName = await askQuestion(
|
|
105
|
+
`Package ${packageIndex} name (or press Enter to finish)`,
|
|
106
|
+
);
|
|
107
|
+
|
|
108
|
+
if (!packageName) {
|
|
109
|
+
continueAdding = false;
|
|
110
|
+
continue;
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
const validation = validatePackageName(packageName);
|
|
114
|
+
|
|
115
|
+
if (!validation.valid) {
|
|
116
|
+
logger.error(`❌ ${validation.error}`);
|
|
117
|
+
continue;
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
const workspace = await askQuestion(
|
|
121
|
+
"Workspace directory",
|
|
122
|
+
WORKSPACES.PACKAGES,
|
|
123
|
+
);
|
|
124
|
+
const scope = getWorkspaceScope(workspace);
|
|
125
|
+
|
|
126
|
+
packages.push({
|
|
127
|
+
name: packageName,
|
|
128
|
+
workspace,
|
|
129
|
+
scope,
|
|
130
|
+
});
|
|
131
|
+
|
|
132
|
+
logger.success(`✅ Added ${scope}${packageName}\n`);
|
|
133
|
+
packageIndex++;
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
return packages;
|
|
137
|
+
};
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
export type PackageInfo = {
|
|
2
|
+
name: string;
|
|
3
|
+
workspace: string;
|
|
4
|
+
scope: string;
|
|
5
|
+
};
|
|
6
|
+
|
|
7
|
+
export type MonorepoConfig = {
|
|
8
|
+
name: string;
|
|
9
|
+
description: string;
|
|
10
|
+
version: string;
|
|
11
|
+
author: string;
|
|
12
|
+
license: string;
|
|
13
|
+
packages: PackageInfo[];
|
|
14
|
+
rootPath: string;
|
|
15
|
+
};
|
|
16
|
+
|
|
17
|
+
export type CatalogDependency = {
|
|
18
|
+
name: string;
|
|
19
|
+
version: string;
|
|
20
|
+
};
|
|
21
|
+
|
|
22
|
+
export type ValidationResult = {
|
|
23
|
+
valid: boolean;
|
|
24
|
+
error?: string;
|
|
25
|
+
};
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import { exists, mkdir } from "node:fs/promises";
|
|
2
|
+
import { join } from "node:path";
|
|
3
|
+
|
|
4
|
+
export const getWorkspaceScope = (workspace: string): string =>
|
|
5
|
+
`@${workspace.charAt(0)}/`;
|
|
6
|
+
|
|
7
|
+
export const ensureDir = async (path: string): Promise<void> => {
|
|
8
|
+
const dirExists = await exists(path);
|
|
9
|
+
if (!dirExists) {
|
|
10
|
+
await mkdir(path, { recursive: true });
|
|
11
|
+
}
|
|
12
|
+
};
|
|
13
|
+
|
|
14
|
+
export const fileExists = async (path: string): Promise<boolean> =>
|
|
15
|
+
exists(path);
|
|
16
|
+
|
|
17
|
+
export const createFullPath = (...paths: string[]): string => join(...paths);
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
import type { ValidationResult } from "./types";
|
|
2
|
+
|
|
3
|
+
export const validatePackageName = (name: string): ValidationResult => {
|
|
4
|
+
if (!name || name.trim().length === 0) {
|
|
5
|
+
return { valid: false, error: "Package name cannot be empty" };
|
|
6
|
+
}
|
|
7
|
+
|
|
8
|
+
if (!/^[a-z0-9-]+$/.test(name)) {
|
|
9
|
+
return {
|
|
10
|
+
valid: false,
|
|
11
|
+
error:
|
|
12
|
+
"Package name must contain only lowercase letters, numbers, and hyphens",
|
|
13
|
+
};
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
if (name.startsWith("-") || name.endsWith("-")) {
|
|
17
|
+
return {
|
|
18
|
+
valid: false,
|
|
19
|
+
error: "Package name cannot start or end with a hyphen",
|
|
20
|
+
};
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
return { valid: true };
|
|
24
|
+
};
|
|
25
|
+
|
|
26
|
+
export const validateMonorepoName = (name: string): ValidationResult => {
|
|
27
|
+
if (!name || name.trim().length === 0) {
|
|
28
|
+
return { valid: false, error: "Monorepo name cannot be empty" };
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
if (!/^[a-z0-9-]+$/.test(name)) {
|
|
32
|
+
return {
|
|
33
|
+
valid: false,
|
|
34
|
+
error:
|
|
35
|
+
"Monorepo name must contain only lowercase letters, numbers, and hyphens",
|
|
36
|
+
};
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
return { valid: true };
|
|
40
|
+
};
|
|
41
|
+
|
|
42
|
+
export const validateVersion = (version: string): ValidationResult => {
|
|
43
|
+
if (!version || version.trim().length === 0) {
|
|
44
|
+
return { valid: false, error: "Version cannot be empty" };
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
if (!/^\d+\.\d+\.\d+(-[a-z0-9.-]+)?$/.test(version)) {
|
|
48
|
+
return {
|
|
49
|
+
valid: false,
|
|
50
|
+
error: "Version must follow semantic versioning (e.g., 1.0.0)",
|
|
51
|
+
};
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
return { valid: true };
|
|
55
|
+
};
|
|
@@ -0,0 +1,82 @@
|
|
|
1
|
+
#!/usr/bin/env bun
|
|
2
|
+
|
|
3
|
+
import {
|
|
4
|
+
defineCmd,
|
|
5
|
+
defineCmdArgs,
|
|
6
|
+
defineCmdCfg,
|
|
7
|
+
} from "@reliverse/dler-launcher";
|
|
8
|
+
import { logger } from "@reliverse/dler-logger";
|
|
9
|
+
import type { IntegrateOptions } from "./impl";
|
|
10
|
+
import { runIntegrate } from "./impl";
|
|
11
|
+
|
|
12
|
+
const integrateCmd = async (args: any): Promise<void> => {
|
|
13
|
+
try {
|
|
14
|
+
const options: IntegrateOptions = {
|
|
15
|
+
x: args.x,
|
|
16
|
+
target: args.target,
|
|
17
|
+
verbose: args.verbose || false,
|
|
18
|
+
cwd: args.cwd,
|
|
19
|
+
};
|
|
20
|
+
|
|
21
|
+
await runIntegrate(options);
|
|
22
|
+
} catch (error) {
|
|
23
|
+
logger.error("\n❌ Integration failed:");
|
|
24
|
+
|
|
25
|
+
if (error instanceof Error) {
|
|
26
|
+
logger.error(error.message);
|
|
27
|
+
} else {
|
|
28
|
+
logger.error(String(error));
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
process.exit(1);
|
|
32
|
+
}
|
|
33
|
+
};
|
|
34
|
+
|
|
35
|
+
const integrateCmdArgs = defineCmdArgs({
|
|
36
|
+
x: {
|
|
37
|
+
type: "string",
|
|
38
|
+
description:
|
|
39
|
+
"Integration(s) to install (comma-separated, e.g., 'nextjs,ultracite')",
|
|
40
|
+
required: true,
|
|
41
|
+
},
|
|
42
|
+
target: {
|
|
43
|
+
type: "string",
|
|
44
|
+
description:
|
|
45
|
+
"Target package in monorepo (optional, will prompt if not specified)",
|
|
46
|
+
},
|
|
47
|
+
verbose: {
|
|
48
|
+
type: "boolean",
|
|
49
|
+
description: "Verbose mode for detailed logging (default: false)",
|
|
50
|
+
},
|
|
51
|
+
cwd: {
|
|
52
|
+
type: "string",
|
|
53
|
+
description: "Working directory (default: current directory)",
|
|
54
|
+
},
|
|
55
|
+
});
|
|
56
|
+
|
|
57
|
+
const integrateCmdCfg = defineCmdCfg({
|
|
58
|
+
name: "integrate",
|
|
59
|
+
description:
|
|
60
|
+
"Automatically install and configure integrations like Next.js and Ultracite/Biome. Supports both monorepo and single-repo contexts.",
|
|
61
|
+
examples: [
|
|
62
|
+
"dler integrate --x nextjs",
|
|
63
|
+
"dler integrate --x ultracite",
|
|
64
|
+
"dler integrate --x nextjs,ultracite",
|
|
65
|
+
"dler integrate --x nextjs --target my-app",
|
|
66
|
+
"dler integrate --x ultracite --verbose",
|
|
67
|
+
"dler integrate --x nextjs --cwd /path/to/project",
|
|
68
|
+
"",
|
|
69
|
+
"# Available integrations:",
|
|
70
|
+
"# - nextjs: Next.js React framework with App Router, TypeScript, and Tailwind CSS",
|
|
71
|
+
"# - ultracite: Ultracite preset for Biome (highly opinionated linter and formatter)",
|
|
72
|
+
"",
|
|
73
|
+
"# Monorepo usage:",
|
|
74
|
+
"# The command will automatically detect if you're in a monorepo and prompt you to",
|
|
75
|
+
"# select a target package, or you can specify one with --target",
|
|
76
|
+
"",
|
|
77
|
+
"# Single-repo usage:",
|
|
78
|
+
"# The command will install integrations directly into the current directory",
|
|
79
|
+
],
|
|
80
|
+
});
|
|
81
|
+
|
|
82
|
+
export default defineCmd(integrateCmd, integrateCmdArgs, integrateCmdCfg);
|
|
@@ -0,0 +1,204 @@
|
|
|
1
|
+
// apps/dler/src/cmds/integrate/impl.ts
|
|
2
|
+
|
|
3
|
+
import { logger } from "@reliverse/dler-logger";
|
|
4
|
+
import {
|
|
5
|
+
getIntegration,
|
|
6
|
+
validateIntegrationNames,
|
|
7
|
+
} from "./integrations/registry";
|
|
8
|
+
import type { IntegrationContext } from "./types";
|
|
9
|
+
import { detectProjectContext, selectTargetPackage } from "./utils/context";
|
|
10
|
+
import { createTempDirectory } from "./utils/temp";
|
|
11
|
+
|
|
12
|
+
export interface IntegrateOptions {
|
|
13
|
+
x: string;
|
|
14
|
+
target?: string;
|
|
15
|
+
verbose: boolean;
|
|
16
|
+
cwd?: string;
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
export const runIntegrate = async (
|
|
20
|
+
options: IntegrateOptions,
|
|
21
|
+
): Promise<void> => {
|
|
22
|
+
try {
|
|
23
|
+
// Check if running in Bun
|
|
24
|
+
if (typeof process.versions.bun === "undefined") {
|
|
25
|
+
logger.error("❌ This command requires Bun runtime. Sorry.");
|
|
26
|
+
process.exit(1);
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
// Parse integration names
|
|
30
|
+
const integrationNames = options.x
|
|
31
|
+
.split(",")
|
|
32
|
+
.map((name) => name.trim())
|
|
33
|
+
.filter(Boolean);
|
|
34
|
+
|
|
35
|
+
if (integrationNames.length === 0) {
|
|
36
|
+
logger.error(
|
|
37
|
+
"❌ No integrations specified. Use --x to specify integrations (e.g., --x nextjs,ultracite)",
|
|
38
|
+
);
|
|
39
|
+
process.exit(1);
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
// Validate integration names
|
|
43
|
+
const { valid, invalid } = validateIntegrationNames(integrationNames);
|
|
44
|
+
|
|
45
|
+
if (invalid.length > 0) {
|
|
46
|
+
logger.error(`❌ Invalid integrations: ${invalid.join(", ")}`);
|
|
47
|
+
logger.info(`Available integrations: ${valid.join(", ")}`);
|
|
48
|
+
process.exit(1);
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
// Detect project context
|
|
52
|
+
logger.info("🔍 Detecting project context...");
|
|
53
|
+
const projectContext = await detectProjectContext(options.cwd);
|
|
54
|
+
|
|
55
|
+
// Resolve target directory
|
|
56
|
+
let targetPath = projectContext.targetPath;
|
|
57
|
+
let selectedPackage = projectContext.selectedPackage;
|
|
58
|
+
|
|
59
|
+
if (projectContext.type === "monorepo") {
|
|
60
|
+
if (options.target) {
|
|
61
|
+
// Find specified target package
|
|
62
|
+
const targetPkg = projectContext.packages?.find(
|
|
63
|
+
(pkg) =>
|
|
64
|
+
pkg.name === options.target || pkg.name.endsWith(options.target!),
|
|
65
|
+
);
|
|
66
|
+
|
|
67
|
+
if (!targetPkg) {
|
|
68
|
+
logger.error(`❌ Target package '${options.target}' not found`);
|
|
69
|
+
logger.info(
|
|
70
|
+
`Available packages: ${projectContext.packages?.map((p) => p.name).join(", ")}`,
|
|
71
|
+
);
|
|
72
|
+
process.exit(1);
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
selectedPackage = targetPkg;
|
|
76
|
+
targetPath = targetPkg.path;
|
|
77
|
+
} else {
|
|
78
|
+
// Prompt user to select target package
|
|
79
|
+
if (projectContext.packages && projectContext.packages.length > 1) {
|
|
80
|
+
selectedPackage = await selectTargetPackage(projectContext.packages);
|
|
81
|
+
targetPath = selectedPackage.path;
|
|
82
|
+
} else if (
|
|
83
|
+
projectContext.packages &&
|
|
84
|
+
projectContext.packages.length === 1
|
|
85
|
+
) {
|
|
86
|
+
selectedPackage = projectContext.packages[0];
|
|
87
|
+
targetPath = selectedPackage!.path;
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
logger.info(`📦 Target package: ${selectedPackage?.name ?? "root"}`);
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
// Create temp directory for integrations that need it
|
|
95
|
+
const tempDir = await createTempDirectory();
|
|
96
|
+
|
|
97
|
+
// Create integration context
|
|
98
|
+
const integrationContext: IntegrationContext = {
|
|
99
|
+
targetPath,
|
|
100
|
+
isMonorepo: projectContext.type === "monorepo",
|
|
101
|
+
monorepoRoot:
|
|
102
|
+
projectContext.type === "monorepo"
|
|
103
|
+
? projectContext.rootPath
|
|
104
|
+
: undefined,
|
|
105
|
+
packageName: selectedPackage?.name,
|
|
106
|
+
verbose: options.verbose,
|
|
107
|
+
tempDir,
|
|
108
|
+
};
|
|
109
|
+
|
|
110
|
+
// Run integrations
|
|
111
|
+
const results = await runIntegrations(integrationNames, integrationContext);
|
|
112
|
+
|
|
113
|
+
// Install dependencies
|
|
114
|
+
logger.info("📦 Installing dependencies...");
|
|
115
|
+
await Bun.$`bun install`.cwd(targetPath).quiet();
|
|
116
|
+
|
|
117
|
+
// Clean up temp directory
|
|
118
|
+
await tempDir.cleanup();
|
|
119
|
+
|
|
120
|
+
// Report results
|
|
121
|
+
const successful = results.filter((r) => r.success);
|
|
122
|
+
const failed = results.filter((r) => !r.success);
|
|
123
|
+
|
|
124
|
+
if (successful.length > 0) {
|
|
125
|
+
logger.success(
|
|
126
|
+
`\n✅ Successfully integrated: ${successful.map((r) => r.name).join(", ")}`,
|
|
127
|
+
);
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
if (failed.length > 0) {
|
|
131
|
+
logger.error(
|
|
132
|
+
`\n❌ Failed integrations: ${failed.map((r) => r.name).join(", ")}`,
|
|
133
|
+
);
|
|
134
|
+
process.exit(1);
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
logger.success("\n🎉 All integrations completed successfully!");
|
|
138
|
+
} catch (error) {
|
|
139
|
+
logger.error("\n❌ Integration failed:");
|
|
140
|
+
|
|
141
|
+
if (error instanceof Error) {
|
|
142
|
+
logger.error(error.message);
|
|
143
|
+
} else {
|
|
144
|
+
logger.error(String(error));
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
process.exit(1);
|
|
148
|
+
}
|
|
149
|
+
};
|
|
150
|
+
|
|
151
|
+
interface IntegrationResult {
|
|
152
|
+
name: string;
|
|
153
|
+
success: boolean;
|
|
154
|
+
error?: string;
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
const runIntegrations = async (
|
|
158
|
+
integrationNames: string[],
|
|
159
|
+
context: IntegrationContext,
|
|
160
|
+
): Promise<IntegrationResult[]> => {
|
|
161
|
+
const results: IntegrationResult[] = [];
|
|
162
|
+
|
|
163
|
+
for (const name of integrationNames) {
|
|
164
|
+
logger.info(`\n🔧 Processing ${name} integration...`);
|
|
165
|
+
|
|
166
|
+
try {
|
|
167
|
+
const integration = getIntegration(name);
|
|
168
|
+
if (!integration) {
|
|
169
|
+
results.push({ name, success: false, error: "Integration not found" });
|
|
170
|
+
continue;
|
|
171
|
+
}
|
|
172
|
+
|
|
173
|
+
// Validate integration
|
|
174
|
+
const isValid = await integration.validate(context);
|
|
175
|
+
if (!isValid) {
|
|
176
|
+
results.push({
|
|
177
|
+
name,
|
|
178
|
+
success: true,
|
|
179
|
+
error: "Already installed, skipped",
|
|
180
|
+
});
|
|
181
|
+
continue;
|
|
182
|
+
}
|
|
183
|
+
|
|
184
|
+
// Install integration
|
|
185
|
+
await integration.install(context);
|
|
186
|
+
|
|
187
|
+
// Configure integration
|
|
188
|
+
await integration.configure(context);
|
|
189
|
+
|
|
190
|
+
// Run post-install hooks
|
|
191
|
+
await integration.postInstall(context);
|
|
192
|
+
|
|
193
|
+
results.push({ name, success: true });
|
|
194
|
+
logger.success(`✅ ${name} integration completed`);
|
|
195
|
+
} catch (error) {
|
|
196
|
+
const errorMessage =
|
|
197
|
+
error instanceof Error ? error.message : String(error);
|
|
198
|
+
logger.error(`❌ ${name} integration failed: ${errorMessage}`);
|
|
199
|
+
results.push({ name, success: false, error: errorMessage });
|
|
200
|
+
}
|
|
201
|
+
}
|
|
202
|
+
|
|
203
|
+
return results;
|
|
204
|
+
};
|
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
// apps/dler/src/cmds/integrate/integrations/base.ts
|
|
2
|
+
|
|
3
|
+
import { logger } from "@reliverse/dler-logger";
|
|
4
|
+
import type { Integration, IntegrationContext } from "../types";
|
|
5
|
+
|
|
6
|
+
export abstract class BaseIntegration implements Integration {
|
|
7
|
+
abstract name: string;
|
|
8
|
+
abstract description: string;
|
|
9
|
+
abstract dependencies: string[];
|
|
10
|
+
abstract devDependencies: string[];
|
|
11
|
+
|
|
12
|
+
async validate(_context: IntegrationContext): Promise<boolean> {
|
|
13
|
+
logger.debug(`🔍 Validating ${this.name} integration...`);
|
|
14
|
+
|
|
15
|
+
// Check if already installed
|
|
16
|
+
if (await this.isAlreadyInstalled(_context)) {
|
|
17
|
+
logger.warn(
|
|
18
|
+
`⚠️ ${this.name} appears to be already installed. Skipping...`,
|
|
19
|
+
);
|
|
20
|
+
return false;
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
return true;
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
abstract install(context: IntegrationContext): Promise<void>;
|
|
27
|
+
abstract configure(context: IntegrationContext): Promise<void>;
|
|
28
|
+
|
|
29
|
+
async postInstall(_context: IntegrationContext): Promise<void> {
|
|
30
|
+
logger.debug(`🔧 Running post-install for ${this.name}...`);
|
|
31
|
+
// Default implementation - can be overridden
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
protected async isAlreadyInstalled(
|
|
35
|
+
context: IntegrationContext,
|
|
36
|
+
): Promise<boolean> {
|
|
37
|
+
// Default implementation - check if dependencies exist in package.json
|
|
38
|
+
try {
|
|
39
|
+
const packageJsonPath = `${context.targetPath}/package.json`;
|
|
40
|
+
const packageJson = await Bun.file(packageJsonPath).json();
|
|
41
|
+
|
|
42
|
+
const allDeps = [...this.dependencies, ...this.devDependencies];
|
|
43
|
+
return allDeps.some(
|
|
44
|
+
(dep) =>
|
|
45
|
+
packageJson.dependencies?.[dep] || packageJson.devDependencies?.[dep],
|
|
46
|
+
);
|
|
47
|
+
} catch {
|
|
48
|
+
return false;
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
protected async installDependencies(
|
|
53
|
+
context: IntegrationContext,
|
|
54
|
+
): Promise<void> {
|
|
55
|
+
const { targetPath } = context;
|
|
56
|
+
|
|
57
|
+
if (this.dependencies.length > 0) {
|
|
58
|
+
logger.info(`📦 Installing ${this.name} dependencies...`);
|
|
59
|
+
const deps = this.dependencies.join(" ");
|
|
60
|
+
await Bun.$`bun add ${deps}`.cwd(targetPath).quiet();
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
if (this.devDependencies.length > 0) {
|
|
64
|
+
logger.info(`📦 Installing ${this.name} dev dependencies...`);
|
|
65
|
+
const devDeps = this.devDependencies.join(" ");
|
|
66
|
+
await Bun.$`bun add -D ${devDeps}`.cwd(targetPath).quiet();
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
}
|