cognite-create 0.2.31 → 0.2.33
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/bin/index.js +119 -30
- package/bin/index.test.js +412 -0
- package/package.json +8 -1
- package/templates/components.json +1 -1
- package/templates/src/index.css +616 -0
- package/templates/vite.config.ts +14 -0
- package/templates/.env.example +0 -7
- package/templates/app/globals.css +0 -165
package/bin/index.js
CHANGED
|
@@ -6,6 +6,8 @@ const { spawn } = require("node:child_process");
|
|
|
6
6
|
const fs = require("node:fs/promises");
|
|
7
7
|
// eslint-disable-next-line @typescript-eslint/no-require-imports
|
|
8
8
|
const path = require("node:path");
|
|
9
|
+
// eslint-disable-next-line @typescript-eslint/no-require-imports
|
|
10
|
+
const readline = require("node:readline");
|
|
9
11
|
|
|
10
12
|
const REQUIRED_DEPENDENCIES = [
|
|
11
13
|
{ name: "@tanstack/react-query", version: "^5.90.2" },
|
|
@@ -17,23 +19,63 @@ const REQUIRED_DEPENDENCIES = [
|
|
|
17
19
|
{ name: "clsx", version: "^2.1.1" },
|
|
18
20
|
];
|
|
19
21
|
|
|
22
|
+
const REQUIRED_DEV_DEPENDENCIES = [
|
|
23
|
+
{ name: "tailwindcss", version: "^4.0.0" },
|
|
24
|
+
{ name: "@tailwindcss/vite", version: "^4.0.0" },
|
|
25
|
+
{ name: "@types/node", version: "^22.10.2" },
|
|
26
|
+
];
|
|
27
|
+
|
|
28
|
+
function promptForInput(question) {
|
|
29
|
+
const rl = readline.createInterface({
|
|
30
|
+
input: process.stdin,
|
|
31
|
+
output: process.stdout,
|
|
32
|
+
});
|
|
33
|
+
|
|
34
|
+
return new Promise((resolve) => {
|
|
35
|
+
rl.question(question, (answer) => {
|
|
36
|
+
rl.close();
|
|
37
|
+
resolve(answer.trim());
|
|
38
|
+
});
|
|
39
|
+
});
|
|
40
|
+
}
|
|
41
|
+
|
|
20
42
|
async function main() {
|
|
21
43
|
const args = process.argv.slice(2);
|
|
44
|
+
let projectDir;
|
|
45
|
+
let additionalArgs = [];
|
|
22
46
|
|
|
23
47
|
if (args.length === 0 || args[0].startsWith("-")) {
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
48
|
+
// No project name provided, prompt for one
|
|
49
|
+
projectDir = await promptForInput("Project name: ");
|
|
50
|
+
|
|
51
|
+
if (!projectDir) {
|
|
52
|
+
console.error("Error: Project name is required");
|
|
53
|
+
process.exit(1);
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
// Keep any flags that were provided
|
|
57
|
+
additionalArgs = args;
|
|
58
|
+
} else {
|
|
59
|
+
// First arg is the project name
|
|
60
|
+
projectDir = args[0];
|
|
61
|
+
// Rest are additional args
|
|
62
|
+
additionalArgs = args.slice(1);
|
|
28
63
|
}
|
|
29
64
|
|
|
30
|
-
|
|
31
|
-
const
|
|
32
|
-
|
|
65
|
+
// Add default template if not specified
|
|
66
|
+
const hasTemplate = additionalArgs.some(
|
|
67
|
+
(arg) => arg === "--template" || arg === "-t"
|
|
68
|
+
);
|
|
69
|
+
if (!hasTemplate) {
|
|
70
|
+
additionalArgs.push("--template", "react-ts");
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
const createArgs = ["create-vite@latest", projectDir, ...additionalArgs];
|
|
74
|
+
await runCreateVite(createArgs);
|
|
33
75
|
await addCogniteTemplates(projectDir);
|
|
34
76
|
}
|
|
35
77
|
|
|
36
|
-
function
|
|
78
|
+
function runCreateVite(cliArgs) {
|
|
37
79
|
const command = process.platform === "win32" ? "npx.cmd" : "npx";
|
|
38
80
|
|
|
39
81
|
return new Promise((resolve, reject) => {
|
|
@@ -42,7 +84,7 @@ function runCreateNextApp(cliArgs) {
|
|
|
42
84
|
child.on("error", reject);
|
|
43
85
|
child.on("close", (code) => {
|
|
44
86
|
if (code !== 0) {
|
|
45
|
-
const error = new Error(`create-
|
|
87
|
+
const error = new Error(`create-vite exited with code ${code}`);
|
|
46
88
|
error.exitCode = code;
|
|
47
89
|
return reject(error);
|
|
48
90
|
}
|
|
@@ -62,7 +104,7 @@ async function addCogniteTemplates(projectDir) {
|
|
|
62
104
|
if (error.code === "ENOENT") {
|
|
63
105
|
throw new Error(
|
|
64
106
|
`Expected to find the newly created project at "${projectDir}", but it was not found. ` +
|
|
65
|
-
"Check the create-
|
|
107
|
+
"Check the create-vite output for more details."
|
|
66
108
|
);
|
|
67
109
|
}
|
|
68
110
|
|
|
@@ -178,7 +220,7 @@ async function ensureDependenciesInstalled(targetRoot) {
|
|
|
178
220
|
`\nUnable to read or parse ${path.relative(
|
|
179
221
|
process.cwd(),
|
|
180
222
|
packageJsonPath
|
|
181
|
-
)}. Install
|
|
223
|
+
)}. Install dependencies manually.`
|
|
182
224
|
);
|
|
183
225
|
return false;
|
|
184
226
|
}
|
|
@@ -193,25 +235,49 @@ async function ensureDependenciesInstalled(targetRoot) {
|
|
|
193
235
|
)
|
|
194
236
|
);
|
|
195
237
|
|
|
196
|
-
|
|
238
|
+
const missingDevDependencies = REQUIRED_DEV_DEPENDENCIES.filter(
|
|
239
|
+
(dependency) =>
|
|
240
|
+
!(
|
|
241
|
+
(packageJson.dependencies &&
|
|
242
|
+
packageJson.dependencies[dependency.name]) ||
|
|
243
|
+
(packageJson.devDependencies &&
|
|
244
|
+
packageJson.devDependencies[dependency.name])
|
|
245
|
+
)
|
|
246
|
+
);
|
|
247
|
+
|
|
248
|
+
if (missingDependencies.length === 0 && missingDevDependencies.length === 0) {
|
|
197
249
|
return false;
|
|
198
250
|
}
|
|
199
251
|
|
|
200
252
|
const packageManager = await detectPackageManager(packageJson, targetRoot);
|
|
201
253
|
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
254
|
+
// Install regular dependencies
|
|
255
|
+
if (missingDependencies.length > 0) {
|
|
256
|
+
const dependencySummary = formatDependencySummary(missingDependencies);
|
|
257
|
+
console.log(`\nInstalling ${dependencySummary} using ${packageManager}...`);
|
|
258
|
+
await installDependencies(
|
|
259
|
+
packageManager,
|
|
260
|
+
targetRoot,
|
|
261
|
+
missingDependencies,
|
|
262
|
+
false
|
|
208
263
|
);
|
|
209
|
-
return false;
|
|
210
264
|
}
|
|
211
265
|
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
266
|
+
// Install dev dependencies
|
|
267
|
+
if (missingDevDependencies.length > 0) {
|
|
268
|
+
const devDependencySummary = formatDependencySummary(
|
|
269
|
+
missingDevDependencies
|
|
270
|
+
);
|
|
271
|
+
console.log(
|
|
272
|
+
`\nInstalling dev dependencies ${devDependencySummary} using ${packageManager}...`
|
|
273
|
+
);
|
|
274
|
+
await installDependencies(
|
|
275
|
+
packageManager,
|
|
276
|
+
targetRoot,
|
|
277
|
+
missingDevDependencies,
|
|
278
|
+
true
|
|
279
|
+
);
|
|
280
|
+
}
|
|
215
281
|
|
|
216
282
|
return true;
|
|
217
283
|
}
|
|
@@ -243,16 +309,26 @@ async function detectPackageManager(packageJson, targetRoot) {
|
|
|
243
309
|
}
|
|
244
310
|
}
|
|
245
311
|
|
|
246
|
-
|
|
312
|
+
// Default to npm if no lock file is found
|
|
313
|
+
return "npm";
|
|
247
314
|
}
|
|
248
315
|
|
|
249
|
-
async function installDependencies(
|
|
316
|
+
async function installDependencies(
|
|
317
|
+
packageManager,
|
|
318
|
+
cwd,
|
|
319
|
+
dependencies,
|
|
320
|
+
isDev = false
|
|
321
|
+
) {
|
|
250
322
|
const dependencySpecs = dependencies.map((dependency) =>
|
|
251
323
|
dependency.version
|
|
252
324
|
? `${dependency.name}@${dependency.version}`
|
|
253
325
|
: dependency.name
|
|
254
326
|
);
|
|
255
|
-
const { command, args } = getInstallCommand(
|
|
327
|
+
const { command, args } = getInstallCommand(
|
|
328
|
+
packageManager,
|
|
329
|
+
dependencySpecs,
|
|
330
|
+
isDev
|
|
331
|
+
);
|
|
256
332
|
|
|
257
333
|
if (!command) {
|
|
258
334
|
throw new Error(`Unsupported package manager: ${packageManager}`);
|
|
@@ -278,18 +354,31 @@ async function installDependencies(packageManager, cwd, dependencies) {
|
|
|
278
354
|
});
|
|
279
355
|
}
|
|
280
356
|
|
|
281
|
-
function getInstallCommand(packageManager, dependencySpecs) {
|
|
357
|
+
function getInstallCommand(packageManager, dependencySpecs, isDev = false) {
|
|
282
358
|
const normalized = normalizeCommand(packageManager);
|
|
359
|
+
const devFlag = isDev ? ["-D"] : [];
|
|
283
360
|
|
|
284
361
|
switch (packageManager) {
|
|
285
362
|
case "npm":
|
|
286
|
-
return {
|
|
363
|
+
return {
|
|
364
|
+
command: normalized,
|
|
365
|
+
args: ["install", ...devFlag, ...dependencySpecs],
|
|
366
|
+
};
|
|
287
367
|
case "pnpm":
|
|
288
|
-
return {
|
|
368
|
+
return {
|
|
369
|
+
command: normalized,
|
|
370
|
+
args: ["add", ...devFlag, ...dependencySpecs],
|
|
371
|
+
};
|
|
289
372
|
case "yarn":
|
|
290
|
-
return {
|
|
373
|
+
return {
|
|
374
|
+
command: normalized,
|
|
375
|
+
args: ["add", ...devFlag, ...dependencySpecs],
|
|
376
|
+
};
|
|
291
377
|
case "bun":
|
|
292
|
-
return {
|
|
378
|
+
return {
|
|
379
|
+
command: normalized,
|
|
380
|
+
args: ["add", ...devFlag, ...dependencySpecs],
|
|
381
|
+
};
|
|
293
382
|
default:
|
|
294
383
|
return { command: null, args: [] };
|
|
295
384
|
}
|
|
@@ -0,0 +1,412 @@
|
|
|
1
|
+
import { describe, it, expect, vi } from "vitest";
|
|
2
|
+
|
|
3
|
+
// Mock child_process spawn
|
|
4
|
+
vi.mock("node:child_process", () => ({
|
|
5
|
+
spawn: vi.fn(),
|
|
6
|
+
}));
|
|
7
|
+
|
|
8
|
+
// Mock readline
|
|
9
|
+
vi.mock("node:readline", () => ({
|
|
10
|
+
createInterface: vi.fn(() => ({
|
|
11
|
+
question: vi.fn(),
|
|
12
|
+
close: vi.fn(),
|
|
13
|
+
})),
|
|
14
|
+
}));
|
|
15
|
+
|
|
16
|
+
describe("CLI Command Tests", () => {
|
|
17
|
+
describe("Argument Parsing Logic", () => {
|
|
18
|
+
it("should use first argument as project name when provided", () => {
|
|
19
|
+
const args = ["my-project", "--template", "react-ts"];
|
|
20
|
+
let projectDir;
|
|
21
|
+
let additionalArgs = [];
|
|
22
|
+
|
|
23
|
+
if (args.length === 0 || args[0].startsWith("-")) {
|
|
24
|
+
additionalArgs = args;
|
|
25
|
+
} else {
|
|
26
|
+
projectDir = args[0];
|
|
27
|
+
additionalArgs = args.slice(1);
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
const createArgs = ["create-vite@latest", projectDir, ...additionalArgs];
|
|
31
|
+
|
|
32
|
+
expect(projectDir).toBe("my-project");
|
|
33
|
+
expect(additionalArgs).toEqual(["--template", "react-ts"]);
|
|
34
|
+
expect(createArgs).toEqual([
|
|
35
|
+
"create-vite@latest",
|
|
36
|
+
"my-project",
|
|
37
|
+
"--template",
|
|
38
|
+
"react-ts",
|
|
39
|
+
]);
|
|
40
|
+
});
|
|
41
|
+
|
|
42
|
+
it("should add default react-ts template when no template is specified", () => {
|
|
43
|
+
const args = ["my-project"];
|
|
44
|
+
let projectDir;
|
|
45
|
+
let additionalArgs = [];
|
|
46
|
+
|
|
47
|
+
if (args.length === 0 || args[0].startsWith("-")) {
|
|
48
|
+
additionalArgs = args;
|
|
49
|
+
} else {
|
|
50
|
+
projectDir = args[0];
|
|
51
|
+
additionalArgs = args.slice(1);
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
// Add default template if not specified
|
|
55
|
+
const hasTemplate = additionalArgs.some(
|
|
56
|
+
(arg) => arg === "--template" || arg === "-t"
|
|
57
|
+
);
|
|
58
|
+
if (!hasTemplate) {
|
|
59
|
+
additionalArgs.push("--template", "react-ts");
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
const createArgs = ["create-vite@latest", projectDir, ...additionalArgs];
|
|
63
|
+
|
|
64
|
+
expect(projectDir).toBe("my-project");
|
|
65
|
+
expect(additionalArgs).toEqual(["--template", "react-ts"]);
|
|
66
|
+
expect(createArgs).toEqual([
|
|
67
|
+
"create-vite@latest",
|
|
68
|
+
"my-project",
|
|
69
|
+
"--template",
|
|
70
|
+
"react-ts",
|
|
71
|
+
]);
|
|
72
|
+
});
|
|
73
|
+
|
|
74
|
+
it("should not add default template if --template is already specified", () => {
|
|
75
|
+
const args = ["my-project", "--template", "vue"];
|
|
76
|
+
let projectDir;
|
|
77
|
+
let additionalArgs = [];
|
|
78
|
+
|
|
79
|
+
if (args.length === 0 || args[0].startsWith("-")) {
|
|
80
|
+
additionalArgs = args;
|
|
81
|
+
} else {
|
|
82
|
+
projectDir = args[0];
|
|
83
|
+
additionalArgs = args.slice(1);
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
// Add default template if not specified
|
|
87
|
+
const hasTemplate = additionalArgs.some(
|
|
88
|
+
(arg) => arg === "--template" || arg === "-t"
|
|
89
|
+
);
|
|
90
|
+
if (!hasTemplate) {
|
|
91
|
+
additionalArgs.push("--template", "react-ts");
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
const createArgs = ["create-vite@latest", projectDir, ...additionalArgs];
|
|
95
|
+
|
|
96
|
+
expect(projectDir).toBe("my-project");
|
|
97
|
+
expect(additionalArgs).toEqual(["--template", "vue"]);
|
|
98
|
+
expect(createArgs).toEqual([
|
|
99
|
+
"create-vite@latest",
|
|
100
|
+
"my-project",
|
|
101
|
+
"--template",
|
|
102
|
+
"vue",
|
|
103
|
+
]);
|
|
104
|
+
});
|
|
105
|
+
|
|
106
|
+
it("should not add default template if -t is already specified", () => {
|
|
107
|
+
const args = ["my-project", "-t", "vue-ts"];
|
|
108
|
+
let projectDir;
|
|
109
|
+
let additionalArgs = [];
|
|
110
|
+
|
|
111
|
+
if (args.length === 0 || args[0].startsWith("-")) {
|
|
112
|
+
additionalArgs = args;
|
|
113
|
+
} else {
|
|
114
|
+
projectDir = args[0];
|
|
115
|
+
additionalArgs = args.slice(1);
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
// Add default template if not specified
|
|
119
|
+
const hasTemplate = additionalArgs.some(
|
|
120
|
+
(arg) => arg === "--template" || arg === "-t"
|
|
121
|
+
);
|
|
122
|
+
if (!hasTemplate) {
|
|
123
|
+
additionalArgs.push("--template", "react-ts");
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
const createArgs = ["create-vite@latest", projectDir, ...additionalArgs];
|
|
127
|
+
|
|
128
|
+
expect(projectDir).toBe("my-project");
|
|
129
|
+
expect(additionalArgs).toEqual(["-t", "vue-ts"]);
|
|
130
|
+
expect(createArgs).toEqual([
|
|
131
|
+
"create-vite@latest",
|
|
132
|
+
"my-project",
|
|
133
|
+
"-t",
|
|
134
|
+
"vue-ts",
|
|
135
|
+
]);
|
|
136
|
+
});
|
|
137
|
+
|
|
138
|
+
it("should handle flags-only arguments (requires prompt)", () => {
|
|
139
|
+
const args = ["--template", "react-ts"];
|
|
140
|
+
let projectDir;
|
|
141
|
+
let additionalArgs = [];
|
|
142
|
+
|
|
143
|
+
if (args.length === 0 || args[0].startsWith("-")) {
|
|
144
|
+
// Would prompt for project name
|
|
145
|
+
projectDir = undefined;
|
|
146
|
+
additionalArgs = args;
|
|
147
|
+
} else {
|
|
148
|
+
projectDir = args[0];
|
|
149
|
+
additionalArgs = args.slice(1);
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
expect(projectDir).toBeUndefined();
|
|
153
|
+
expect(additionalArgs).toEqual(["--template", "react-ts"]);
|
|
154
|
+
});
|
|
155
|
+
|
|
156
|
+
it("should handle empty arguments (requires prompt)", () => {
|
|
157
|
+
const args = [];
|
|
158
|
+
let projectDir;
|
|
159
|
+
let additionalArgs = [];
|
|
160
|
+
|
|
161
|
+
if (args.length === 0 || args[0]?.startsWith("-")) {
|
|
162
|
+
projectDir = undefined;
|
|
163
|
+
additionalArgs = args;
|
|
164
|
+
} else {
|
|
165
|
+
projectDir = args[0];
|
|
166
|
+
additionalArgs = args.slice(1);
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
expect(projectDir).toBeUndefined();
|
|
170
|
+
expect(additionalArgs).toEqual([]);
|
|
171
|
+
});
|
|
172
|
+
|
|
173
|
+
it("should properly construct create-vite command with all arguments", () => {
|
|
174
|
+
const projectDir = "test-app";
|
|
175
|
+
const additionalArgs = ["--template", "react-ts", "--force"];
|
|
176
|
+
const createArgs = ["create-vite@latest", projectDir, ...additionalArgs];
|
|
177
|
+
|
|
178
|
+
expect(createArgs).toEqual([
|
|
179
|
+
"create-vite@latest",
|
|
180
|
+
"test-app",
|
|
181
|
+
"--template",
|
|
182
|
+
"react-ts",
|
|
183
|
+
"--force",
|
|
184
|
+
]);
|
|
185
|
+
});
|
|
186
|
+
|
|
187
|
+
it("should properly construct create-vite command with project name only", () => {
|
|
188
|
+
const projectDir = "simple-app";
|
|
189
|
+
const additionalArgs = [];
|
|
190
|
+
const createArgs = ["create-vite@latest", projectDir, ...additionalArgs];
|
|
191
|
+
|
|
192
|
+
expect(createArgs).toEqual(["create-vite@latest", "simple-app"]);
|
|
193
|
+
});
|
|
194
|
+
});
|
|
195
|
+
|
|
196
|
+
describe("Command Platform Detection", () => {
|
|
197
|
+
it("should use npx.cmd on Windows", () => {
|
|
198
|
+
const platform = "win32";
|
|
199
|
+
const command = platform === "win32" ? "npx.cmd" : "npx";
|
|
200
|
+
expect(command).toBe("npx.cmd");
|
|
201
|
+
});
|
|
202
|
+
|
|
203
|
+
it("should use npx on non-Windows platforms", () => {
|
|
204
|
+
const platform = "darwin";
|
|
205
|
+
const command = platform === "win32" ? "npx.cmd" : "npx";
|
|
206
|
+
expect(command).toBe("npx");
|
|
207
|
+
});
|
|
208
|
+
});
|
|
209
|
+
|
|
210
|
+
describe("Package Manager Detection", () => {
|
|
211
|
+
it("should detect package manager from packageManager field", () => {
|
|
212
|
+
const packageJson = { packageManager: "pnpm@8.0.0" };
|
|
213
|
+
const [name] = packageJson.packageManager.split("@");
|
|
214
|
+
expect(name).toBe("pnpm");
|
|
215
|
+
});
|
|
216
|
+
|
|
217
|
+
it("should extract package manager name correctly", () => {
|
|
218
|
+
const examples = [
|
|
219
|
+
{ input: "npm@9.0.0", expected: "npm" },
|
|
220
|
+
{ input: "yarn@3.0.0", expected: "yarn" },
|
|
221
|
+
{ input: "pnpm@8.0.0", expected: "pnpm" },
|
|
222
|
+
{ input: "bun@1.0.0", expected: "bun" },
|
|
223
|
+
];
|
|
224
|
+
|
|
225
|
+
examples.forEach(({ input, expected }) => {
|
|
226
|
+
const [name] = input.split("@");
|
|
227
|
+
expect(name).toBe(expected);
|
|
228
|
+
});
|
|
229
|
+
});
|
|
230
|
+
|
|
231
|
+
it("should default to npm when no package manager is detected", () => {
|
|
232
|
+
// When no packageManager field exists and no lock files are found
|
|
233
|
+
// the function should default to 'npm' instead of returning null
|
|
234
|
+
const defaultPackageManager = "npm";
|
|
235
|
+
expect(defaultPackageManager).toBe("npm");
|
|
236
|
+
});
|
|
237
|
+
});
|
|
238
|
+
|
|
239
|
+
describe("Install Command Generation", () => {
|
|
240
|
+
function getInstallCommand(packageManager, dependencySpecs, isDev = false) {
|
|
241
|
+
const normalizeCommand = (cmd) => {
|
|
242
|
+
if (process.platform === "win32" && cmd !== "bun") {
|
|
243
|
+
return `${cmd}.cmd`;
|
|
244
|
+
}
|
|
245
|
+
return cmd;
|
|
246
|
+
};
|
|
247
|
+
|
|
248
|
+
const normalized = normalizeCommand(packageManager);
|
|
249
|
+
const devFlag = isDev ? ["-D"] : [];
|
|
250
|
+
|
|
251
|
+
switch (packageManager) {
|
|
252
|
+
case "npm":
|
|
253
|
+
return { command: normalized, args: ["install", ...devFlag, ...dependencySpecs] };
|
|
254
|
+
case "pnpm":
|
|
255
|
+
return { command: normalized, args: ["add", ...devFlag, ...dependencySpecs] };
|
|
256
|
+
case "yarn":
|
|
257
|
+
return { command: normalized, args: ["add", ...devFlag, ...dependencySpecs] };
|
|
258
|
+
case "bun":
|
|
259
|
+
return { command: normalized, args: ["add", ...devFlag, ...dependencySpecs] };
|
|
260
|
+
default:
|
|
261
|
+
return { command: null, args: [] };
|
|
262
|
+
}
|
|
263
|
+
}
|
|
264
|
+
|
|
265
|
+
it("should generate correct npm install command", () => {
|
|
266
|
+
const result = getInstallCommand("npm", ["package-a@1.0.0", "package-b"]);
|
|
267
|
+
expect(result.args).toEqual(["install", "package-a@1.0.0", "package-b"]);
|
|
268
|
+
});
|
|
269
|
+
|
|
270
|
+
it("should generate correct npm install command for dev dependencies", () => {
|
|
271
|
+
const result = getInstallCommand("npm", ["package-a@1.0.0", "package-b"], true);
|
|
272
|
+
expect(result.args).toEqual(["install", "-D", "package-a@1.0.0", "package-b"]);
|
|
273
|
+
});
|
|
274
|
+
|
|
275
|
+
it("should generate correct pnpm add command", () => {
|
|
276
|
+
const result = getInstallCommand("pnpm", ["package-a@1.0.0"]);
|
|
277
|
+
expect(result.args).toEqual(["add", "package-a@1.0.0"]);
|
|
278
|
+
});
|
|
279
|
+
|
|
280
|
+
it("should generate correct pnpm add command for dev dependencies", () => {
|
|
281
|
+
const result = getInstallCommand("pnpm", ["package-a@1.0.0"], true);
|
|
282
|
+
expect(result.args).toEqual(["add", "-D", "package-a@1.0.0"]);
|
|
283
|
+
});
|
|
284
|
+
|
|
285
|
+
it("should generate correct yarn add command", () => {
|
|
286
|
+
const result = getInstallCommand("yarn", ["package-a"]);
|
|
287
|
+
expect(result.args).toEqual(["add", "package-a"]);
|
|
288
|
+
});
|
|
289
|
+
|
|
290
|
+
it("should generate correct yarn add command for dev dependencies", () => {
|
|
291
|
+
const result = getInstallCommand("yarn", ["package-a"], true);
|
|
292
|
+
expect(result.args).toEqual(["add", "-D", "package-a"]);
|
|
293
|
+
});
|
|
294
|
+
|
|
295
|
+
it("should generate correct bun add command", () => {
|
|
296
|
+
const result = getInstallCommand("bun", ["package-a"]);
|
|
297
|
+
expect(result.args).toEqual(["add", "package-a"]);
|
|
298
|
+
});
|
|
299
|
+
|
|
300
|
+
it("should generate correct bun add command for dev dependencies", () => {
|
|
301
|
+
const result = getInstallCommand("bun", ["package-a"], true);
|
|
302
|
+
expect(result.args).toEqual(["add", "-D", "package-a"]);
|
|
303
|
+
});
|
|
304
|
+
|
|
305
|
+
it("should return null command for unsupported package manager", () => {
|
|
306
|
+
const result = getInstallCommand("unknown", ["package-a"]);
|
|
307
|
+
expect(result.command).toBeNull();
|
|
308
|
+
expect(result.args).toEqual([]);
|
|
309
|
+
});
|
|
310
|
+
});
|
|
311
|
+
|
|
312
|
+
describe("Dependency Formatting", () => {
|
|
313
|
+
function formatDependencySummary(dependencies) {
|
|
314
|
+
return dependencies
|
|
315
|
+
.map((dependency) =>
|
|
316
|
+
dependency.version
|
|
317
|
+
? `${dependency.name}@${dependency.version}`
|
|
318
|
+
: dependency.name
|
|
319
|
+
)
|
|
320
|
+
.join(", ");
|
|
321
|
+
}
|
|
322
|
+
|
|
323
|
+
it("should format dependencies with versions", () => {
|
|
324
|
+
const deps = [
|
|
325
|
+
{ name: "react", version: "^18.0.0" },
|
|
326
|
+
{ name: "vite", version: "^5.0.0" },
|
|
327
|
+
];
|
|
328
|
+
const result = formatDependencySummary(deps);
|
|
329
|
+
expect(result).toBe("react@^18.0.0, vite@^5.0.0");
|
|
330
|
+
});
|
|
331
|
+
|
|
332
|
+
it("should format dependencies without versions", () => {
|
|
333
|
+
const deps = [{ name: "react" }, { name: "vite" }];
|
|
334
|
+
const result = formatDependencySummary(deps);
|
|
335
|
+
expect(result).toBe("react, vite");
|
|
336
|
+
});
|
|
337
|
+
|
|
338
|
+
it("should handle mixed dependencies", () => {
|
|
339
|
+
const deps = [{ name: "react", version: "^18.0.0" }, { name: "vite" }];
|
|
340
|
+
const result = formatDependencySummary(deps);
|
|
341
|
+
expect(result).toBe("react@^18.0.0, vite");
|
|
342
|
+
});
|
|
343
|
+
});
|
|
344
|
+
|
|
345
|
+
describe("Path Normalization", () => {
|
|
346
|
+
it("should normalize Windows paths to forward slashes", () => {
|
|
347
|
+
const windowsPath = "app\\components\\Button.tsx";
|
|
348
|
+
const normalized = windowsPath.split("\\").join("/");
|
|
349
|
+
expect(normalized).toBe("app/components/Button.tsx");
|
|
350
|
+
});
|
|
351
|
+
|
|
352
|
+
it("should keep Unix paths unchanged", () => {
|
|
353
|
+
const unixPath = "app/components/Button.tsx";
|
|
354
|
+
const normalized = unixPath.split("/").join("/");
|
|
355
|
+
expect(normalized).toBe("app/components/Button.tsx");
|
|
356
|
+
});
|
|
357
|
+
});
|
|
358
|
+
|
|
359
|
+
describe("Missing Dependencies Detection", () => {
|
|
360
|
+
it("should detect missing dependencies", () => {
|
|
361
|
+
const requiredDeps = [
|
|
362
|
+
{ name: "@tanstack/react-query", version: "^5.90.2" },
|
|
363
|
+
{ name: "recharts", version: "^3.2.1" },
|
|
364
|
+
];
|
|
365
|
+
|
|
366
|
+
const packageJson = {
|
|
367
|
+
dependencies: {
|
|
368
|
+
"@tanstack/react-query": "^5.90.2",
|
|
369
|
+
},
|
|
370
|
+
};
|
|
371
|
+
|
|
372
|
+
const missingDeps = requiredDeps.filter(
|
|
373
|
+
(dep) =>
|
|
374
|
+
!(
|
|
375
|
+
(packageJson.dependencies && packageJson.dependencies[dep.name]) ||
|
|
376
|
+
(packageJson.devDependencies &&
|
|
377
|
+
packageJson.devDependencies[dep.name])
|
|
378
|
+
)
|
|
379
|
+
);
|
|
380
|
+
|
|
381
|
+
expect(missingDeps).toHaveLength(1);
|
|
382
|
+
expect(missingDeps[0].name).toBe("recharts");
|
|
383
|
+
});
|
|
384
|
+
|
|
385
|
+
it("should return empty array when all dependencies are present", () => {
|
|
386
|
+
const requiredDeps = [
|
|
387
|
+
{ name: "react", version: "^18.0.0" },
|
|
388
|
+
{ name: "vite", version: "^5.0.0" },
|
|
389
|
+
];
|
|
390
|
+
|
|
391
|
+
const packageJson = {
|
|
392
|
+
dependencies: {
|
|
393
|
+
react: "^18.0.0",
|
|
394
|
+
},
|
|
395
|
+
devDependencies: {
|
|
396
|
+
vite: "^5.0.0",
|
|
397
|
+
},
|
|
398
|
+
};
|
|
399
|
+
|
|
400
|
+
const missingDeps = requiredDeps.filter(
|
|
401
|
+
(dep) =>
|
|
402
|
+
!(
|
|
403
|
+
(packageJson.dependencies && packageJson.dependencies[dep.name]) ||
|
|
404
|
+
(packageJson.devDependencies &&
|
|
405
|
+
packageJson.devDependencies[dep.name])
|
|
406
|
+
)
|
|
407
|
+
);
|
|
408
|
+
|
|
409
|
+
expect(missingDeps).toHaveLength(0);
|
|
410
|
+
});
|
|
411
|
+
});
|
|
412
|
+
});
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "cognite-create",
|
|
3
|
-
"version": "0.2.
|
|
3
|
+
"version": "0.2.33",
|
|
4
4
|
"description": "Create a Next.js app preconfigured with Cognite defaults.",
|
|
5
5
|
"bin": {
|
|
6
6
|
"cognite-create": "./bin/index.js"
|
|
@@ -9,8 +9,15 @@
|
|
|
9
9
|
"bin",
|
|
10
10
|
"templates"
|
|
11
11
|
],
|
|
12
|
+
"scripts": {
|
|
13
|
+
"test": "vitest run",
|
|
14
|
+
"test:watch": "vitest"
|
|
15
|
+
},
|
|
12
16
|
"engines": {
|
|
13
17
|
"node": ">=18.0.0"
|
|
14
18
|
},
|
|
19
|
+
"devDependencies": {
|
|
20
|
+
"vitest": "^2.1.0"
|
|
21
|
+
},
|
|
15
22
|
"license": "UNLICENSED"
|
|
16
23
|
}
|