@dawitworku/projectcli 0.2.2 → 3.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/src/settings.js CHANGED
@@ -1,5 +1,6 @@
1
1
  const chalk = require("chalk");
2
2
  const { loadConfig, saveConfig, CONFIG_PATH } = require("./config");
3
+ const { listPresets, getPreset } = require("./presets");
3
4
 
4
5
  async function runConfig({ prompt }) {
5
6
  const config = loadConfig();
@@ -8,6 +9,16 @@ async function runConfig({ prompt }) {
8
9
  console.log(chalk.dim(`File: ${CONFIG_PATH}\n`));
9
10
 
10
11
  const answers = await prompt([
12
+ {
13
+ type: "list",
14
+ name: "preset",
15
+ message: "Default Preset:",
16
+ choices: listPresets().map((id) => {
17
+ const p = getPreset(id);
18
+ return { name: `${p.id} (${p.label})`, value: p.id };
19
+ }),
20
+ default: config.preset || "startup",
21
+ },
11
22
  {
12
23
  type: "list",
13
24
  name: "packageManager",
package/src/upgrade.js ADDED
@@ -0,0 +1,228 @@
1
+ const fs = require("node:fs");
2
+ const path = require("node:path");
3
+ const chalk = require("chalk");
4
+
5
+ const { detectLanguage, detectPackageManager } = require("./detect");
6
+ const { generateCI, generateDocker } = require("./cicd");
7
+ const { generateDevContainer } = require("./devcontainer");
8
+ const { runSteps } = require("./run");
9
+
10
+ function readFileSafe(filePath) {
11
+ try {
12
+ return fs.readFileSync(filePath, "utf8");
13
+ } catch {
14
+ return null;
15
+ }
16
+ }
17
+
18
+ function normalizeNewlines(s) {
19
+ return String(s || "").replace(/\r\n/g, "\n");
20
+ }
21
+
22
+ function parseUpgradeArgs(argv) {
23
+ const out = {
24
+ preview: false,
25
+ yes: false,
26
+ dryRun: false,
27
+ only: null,
28
+ };
29
+
30
+ const args = Array.isArray(argv) ? argv : [];
31
+ for (let i = 0; i < args.length; i++) {
32
+ const a = args[i];
33
+ if (a === "--preview") out.preview = true;
34
+ else if (a === "--yes" || a === "-y") out.yes = true;
35
+ else if (a === "--dry-run") out.dryRun = true;
36
+ else if (a === "--only") {
37
+ out.only = args[i + 1] || null;
38
+ i++;
39
+ } else if (a.startsWith("--only=")) {
40
+ out.only = a.slice("--only=".length) || null;
41
+ }
42
+ }
43
+
44
+ if (typeof out.only === "string") {
45
+ out.only = out.only.trim().toLowerCase();
46
+ }
47
+
48
+ return out;
49
+ }
50
+
51
+ function printStepsPreview(steps) {
52
+ console.log(chalk.bold.cyan("\nPlanned actions:"));
53
+ for (const step of steps) {
54
+ const type = step.type || "command";
55
+ if (type === "writeFile") {
56
+ const mode = step.overwrite ? "update" : "write";
57
+ console.log(chalk.gray("- ") + chalk.yellow(`${mode} ${step.path}`));
58
+ } else if (type === "mkdir") {
59
+ console.log(chalk.gray("- ") + chalk.yellow(`mkdir -p ${step.path}`));
60
+ } else if (type === "command") {
61
+ const where = step.cwdFromProjectRoot ? "(in project)" : "(here)";
62
+ console.log(
63
+ chalk.gray("- ") +
64
+ chalk.green(`${step.program} ${(step.args || []).join(" ")}`) +
65
+ chalk.dim(` ${where}`)
66
+ );
67
+ } else {
68
+ console.log(chalk.gray(`- ${type}`));
69
+ }
70
+ }
71
+ console.log("");
72
+ }
73
+
74
+ function computeCIUpgradeSteps(projectRoot, language, pm) {
75
+ const langForTemplates =
76
+ language === "JavaScript/TypeScript" ? "JavaScript" : language;
77
+ const candidateSteps = generateCI(projectRoot, langForTemplates, pm);
78
+
79
+ const out = [];
80
+ for (const step of candidateSteps) {
81
+ if (!step || step.type !== "writeFile" || typeof step.path !== "string")
82
+ continue;
83
+
84
+ const target = path.join(projectRoot, step.path);
85
+ const existing = readFileSafe(target);
86
+ const desired = step.content;
87
+
88
+ if (existing !== null) {
89
+ if (normalizeNewlines(existing) === normalizeNewlines(desired)) {
90
+ continue; // already up-to-date
91
+ }
92
+ }
93
+
94
+ out.push({ ...step, overwrite: true });
95
+ }
96
+
97
+ return out;
98
+ }
99
+
100
+ function computeDockerUpgradeSteps(projectRoot, language) {
101
+ const langForTemplates =
102
+ language === "JavaScript/TypeScript" ? "JavaScript" : language;
103
+ const candidateSteps = generateDocker(projectRoot, langForTemplates);
104
+
105
+ const out = [];
106
+ for (const step of candidateSteps) {
107
+ if (!step || step.type !== "writeFile" || typeof step.path !== "string")
108
+ continue;
109
+
110
+ const target = path.join(projectRoot, step.path);
111
+ const existing = readFileSafe(target);
112
+ const desired = step.content;
113
+
114
+ if (existing !== null) {
115
+ if (normalizeNewlines(existing) === normalizeNewlines(desired)) {
116
+ continue;
117
+ }
118
+ }
119
+
120
+ out.push({ ...step, overwrite: true });
121
+ }
122
+
123
+ return out;
124
+ }
125
+
126
+ function computeDevContainerUpgradeSteps(projectRoot, language) {
127
+ const langForTemplates =
128
+ language === "JavaScript/TypeScript" ? "JavaScript" : language;
129
+ const candidateSteps = generateDevContainer(projectRoot, langForTemplates);
130
+
131
+ const out = [];
132
+ for (const step of candidateSteps) {
133
+ if (!step) continue;
134
+ if (step.type === "mkdir") {
135
+ // Safe to keep; mkdir -p is idempotent.
136
+ out.push(step);
137
+ continue;
138
+ }
139
+ if (step.type !== "writeFile" || typeof step.path !== "string") continue;
140
+
141
+ const target = path.join(projectRoot, step.path);
142
+ const existing = readFileSafe(target);
143
+ const desired = step.content;
144
+
145
+ if (existing !== null) {
146
+ if (normalizeNewlines(existing) === normalizeNewlines(desired)) {
147
+ continue;
148
+ }
149
+ }
150
+
151
+ out.push({ ...step, overwrite: true });
152
+ }
153
+
154
+ return out;
155
+ }
156
+
157
+ async function runUpgrade({ prompt, argv }) {
158
+ const flags = parseUpgradeArgs(argv);
159
+ const projectRoot = process.cwd();
160
+ const language = detectLanguage(projectRoot);
161
+ const pm = detectPackageManager(projectRoot);
162
+
163
+ const only = flags.only;
164
+ const wantCI = !only || only === "ci";
165
+ const wantDocker = !only || only === "docker";
166
+ const wantDevContainer = !only || only === "devcontainer";
167
+
168
+ if (only && !wantCI && !wantDocker && !wantDevContainer) {
169
+ throw new Error(
170
+ `Unsupported --only value: ${only}. Supported: ci, docker, devcontainer`
171
+ );
172
+ }
173
+
174
+ const steps = [];
175
+ if (wantCI) {
176
+ steps.push(...computeCIUpgradeSteps(projectRoot, language, pm));
177
+ }
178
+
179
+ if (wantDocker) {
180
+ steps.push(...computeDockerUpgradeSteps(projectRoot, language));
181
+ }
182
+
183
+ if (wantDevContainer) {
184
+ steps.push(...computeDevContainerUpgradeSteps(projectRoot, language));
185
+ }
186
+
187
+ console.log(chalk.bold("\nProjectCLI Upgrade"));
188
+ console.log(chalk.dim(`Project: ${projectRoot}`));
189
+ console.log(chalk.dim(`Detected: ${language} (${pm})`));
190
+
191
+ if (steps.length === 0) {
192
+ console.log(chalk.green("\nNothing to upgrade."));
193
+ return;
194
+ }
195
+
196
+ printStepsPreview(steps);
197
+
198
+ if (flags.preview || flags.dryRun) {
199
+ console.log(
200
+ chalk.dim(
201
+ flags.preview
202
+ ? "Preview: no changes applied."
203
+ : "Dry run: nothing executed."
204
+ )
205
+ );
206
+ return;
207
+ }
208
+
209
+ if (!flags.yes) {
210
+ const { ok } = await prompt([
211
+ {
212
+ type: "confirm",
213
+ name: "ok",
214
+ message: "Apply these upgrades?",
215
+ default: true,
216
+ },
217
+ ]);
218
+ if (!ok) return;
219
+ }
220
+
221
+ await runSteps(steps, { projectRoot });
222
+ console.log(chalk.green("\nDone."));
223
+ }
224
+
225
+ module.exports = {
226
+ runUpgrade,
227
+ parseUpgradeArgs,
228
+ };