cognite-create 0.1.6 → 0.1.8

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 CHANGED
@@ -1,34 +1,41 @@
1
1
  #!/usr/bin/env node
2
2
 
3
3
  // eslint-disable-next-line @typescript-eslint/no-require-imports
4
- const { spawn } = require('node:child_process');
4
+ const { spawn } = require("node:child_process");
5
5
  // eslint-disable-next-line @typescript-eslint/no-require-imports
6
- const fs = require('node:fs/promises');
6
+ const fs = require("node:fs/promises");
7
7
  // eslint-disable-next-line @typescript-eslint/no-require-imports
8
- const path = require('node:path');
8
+ const path = require("node:path");
9
+
10
+ const REQUIRED_DEPENDENCIES = [
11
+ { name: "tw-animate-css", version: "^1.3.8" },
12
+ { name: "clsx", version: "^2.1.1" },
13
+ ];
9
14
 
10
15
  async function main() {
11
16
  const args = process.argv.slice(2);
12
17
 
13
- if (args.length === 0 || args[0].startsWith('-')) {
14
- console.error('Usage: cognite-create <project-name> [create-next-app options]');
18
+ if (args.length === 0 || args[0].startsWith("-")) {
19
+ console.error(
20
+ "Usage: cognite-create <project-name> [create-next-app options]"
21
+ );
15
22
  process.exit(1);
16
23
  }
17
24
 
18
25
  const projectDir = args[0];
19
- const createArgs = ['create-next-app@latest', ...args];
26
+ const createArgs = ["create-next-app@latest", ...args];
20
27
  await runCreateNextApp(createArgs);
21
28
  await addCogniteTemplates(projectDir);
22
29
  }
23
30
 
24
31
  function runCreateNextApp(cliArgs) {
25
- const command = process.platform === 'win32' ? 'npx.cmd' : 'npx';
32
+ const command = process.platform === "win32" ? "npx.cmd" : "npx";
26
33
 
27
34
  return new Promise((resolve, reject) => {
28
- const child = spawn(command, cliArgs, { stdio: 'inherit' });
35
+ const child = spawn(command, cliArgs, { stdio: "inherit" });
29
36
 
30
- child.on('error', reject);
31
- child.on('close', (code) => {
37
+ child.on("error", reject);
38
+ child.on("close", (code) => {
32
39
  if (code !== 0) {
33
40
  const error = new Error(`create-next-app exited with code ${code}`);
34
41
  error.exitCode = code;
@@ -42,22 +49,24 @@ function runCreateNextApp(cliArgs) {
42
49
 
43
50
  async function addCogniteTemplates(projectDir) {
44
51
  const targetRoot = path.resolve(process.cwd(), projectDir);
45
- const templateRoot = path.resolve(__dirname, '..', 'templates');
52
+ const templateRoot = path.resolve(__dirname, "..", "templates");
46
53
 
47
54
  try {
48
55
  await fs.access(targetRoot);
49
56
  } catch (error) {
50
- if (error.code === 'ENOENT') {
57
+ if (error.code === "ENOENT") {
51
58
  throw new Error(
52
59
  `Expected to find the newly created project at "${projectDir}", but it was not found. ` +
53
- 'Check the create-next-app output for more details.'
60
+ "Check the create-next-app output for more details."
54
61
  );
55
62
  }
56
63
 
57
64
  throw error;
58
65
  }
59
66
 
60
- const srcDirectory = path.join(targetRoot, 'src');
67
+ await ensureDependenciesInstalled(targetRoot);
68
+
69
+ const srcDirectory = path.join(targetRoot, "src");
61
70
  const templateFiles = await collectTemplateFiles(templateRoot);
62
71
 
63
72
  await fs.mkdir(srcDirectory, { recursive: true });
@@ -66,12 +75,14 @@ async function addCogniteTemplates(projectDir) {
66
75
  const normalizedPath = normalizeRelativePath(relativePath);
67
76
  const source = path.join(templateRoot, relativePath);
68
77
 
69
- const pathSegments = normalizedPath.split('/');
78
+ const pathSegments = normalizedPath.split("/");
70
79
 
71
- if (pathSegments[0] === 'app' || pathSegments[0] === 'lib') {
80
+ if (pathSegments[0] === "app" || pathSegments[0] === "lib") {
72
81
  const target = path.join(srcDirectory, ...pathSegments);
73
82
  const isGlobalsCss =
74
- pathSegments[0] === 'app' && pathSegments.length === 2 && pathSegments[1] === 'globals.css';
83
+ pathSegments[0] === "app" &&
84
+ pathSegments.length === 2 &&
85
+ pathSegments[1] === "globals.css";
75
86
 
76
87
  if (isGlobalsCss) {
77
88
  await copyFileWithOverwrite(source, target);
@@ -86,7 +97,7 @@ async function addCogniteTemplates(projectDir) {
86
97
  await copyFileIfMissing(source, target);
87
98
  }
88
99
 
89
- console.log('\nCognite templates copied successfully.');
100
+ console.log("\nCognite templates copied successfully.");
90
101
  }
91
102
 
92
103
  async function copyFileIfMissing(source, target) {
@@ -96,7 +107,7 @@ async function copyFileIfMissing(source, target) {
96
107
  await fs.access(target);
97
108
  console.warn(`Skipped existing ${path.relative(process.cwd(), target)}`);
98
109
  } catch (error) {
99
- if (error.code !== 'ENOENT') {
110
+ if (error.code !== "ENOENT") {
100
111
  throw error;
101
112
  }
102
113
 
@@ -137,11 +148,163 @@ async function collectTemplateFiles(rootDir) {
137
148
  }
138
149
 
139
150
  function normalizeRelativePath(relativePath) {
140
- return relativePath.split(path.sep).join('/');
151
+ return relativePath.split(path.sep).join("/");
152
+ }
153
+
154
+ function formatDependencySummary(dependencies) {
155
+ return dependencies
156
+ .map((dependency) =>
157
+ dependency.version ? `${dependency.name}@${dependency.version}` : dependency.name
158
+ )
159
+ .join(", ");
160
+ }
161
+
162
+ async function ensureDependenciesInstalled(targetRoot) {
163
+ const packageJsonPath = path.join(targetRoot, "package.json");
164
+ let packageJson;
165
+
166
+ try {
167
+ const packageJsonContent = await fs.readFile(packageJsonPath, "utf8");
168
+ packageJson = JSON.parse(packageJsonContent);
169
+ } catch {
170
+ console.warn(
171
+ `\nUnable to read or parse ${path.relative(
172
+ process.cwd(),
173
+ packageJsonPath
174
+ )}. Install ${formatDependencySummary(REQUIRED_DEPENDENCIES)} manually.`
175
+ );
176
+ return false;
177
+ }
178
+
179
+ const missingDependencies = REQUIRED_DEPENDENCIES.filter(
180
+ (dependency) =>
181
+ !(
182
+ (packageJson.dependencies && packageJson.dependencies[dependency.name]) ||
183
+ (packageJson.devDependencies && packageJson.devDependencies[dependency.name])
184
+ )
185
+ );
186
+
187
+ if (missingDependencies.length === 0) {
188
+ return false;
189
+ }
190
+
191
+ const packageManager = await detectPackageManager(packageJson, targetRoot);
192
+
193
+ if (!packageManager) {
194
+ console.warn(
195
+ `\nCould not determine package manager for ${path.relative(
196
+ process.cwd(),
197
+ targetRoot
198
+ )}. Install ${formatDependencySummary(missingDependencies)} manually.`
199
+ );
200
+ return false;
201
+ }
202
+
203
+ const dependencySummary = formatDependencySummary(missingDependencies);
204
+ console.log(`\nInstalling ${dependencySummary} using ${packageManager}...`);
205
+ await installDependencies(packageManager, targetRoot, missingDependencies);
206
+
207
+ return true;
208
+ }
209
+
210
+ async function detectPackageManager(packageJson, targetRoot) {
211
+ if (
212
+ packageJson.packageManager &&
213
+ typeof packageJson.packageManager === "string"
214
+ ) {
215
+ const [name] = packageJson.packageManager.split("@");
216
+
217
+ if (name) {
218
+ return name;
219
+ }
220
+ }
221
+
222
+ const candidates = [
223
+ { name: "pnpm", lockFile: "pnpm-lock.yaml" },
224
+ { name: "yarn", lockFile: "yarn.lock" },
225
+ { name: "bun", lockFile: "bun.lockb" },
226
+ { name: "npm", lockFile: "package-lock.json" },
227
+ ];
228
+
229
+ for (const candidate of candidates) {
230
+ const lockFilePath = path.join(targetRoot, candidate.lockFile);
231
+
232
+ if (await pathExists(lockFilePath)) {
233
+ return candidate.name;
234
+ }
235
+ }
236
+
237
+ return null;
238
+ }
239
+
240
+ async function installDependencies(packageManager, cwd, dependencies) {
241
+ const dependencySpecs = dependencies.map((dependency) =>
242
+ dependency.version ? `${dependency.name}@${dependency.version}` : dependency.name
243
+ );
244
+ const { command, args } = getInstallCommand(packageManager, dependencySpecs);
245
+
246
+ if (!command) {
247
+ throw new Error(`Unsupported package manager: ${packageManager}`);
248
+ }
249
+
250
+ return new Promise((resolve, reject) => {
251
+ const child = spawn(command, args, { stdio: "inherit", cwd });
252
+
253
+ child.on("error", reject);
254
+ child.on("close", (code) => {
255
+ if (code !== 0) {
256
+ const error = new Error(
257
+ `${packageManager} exited with code ${code} while installing ${dependencySpecs.join(", ")}`
258
+ );
259
+ error.exitCode = code;
260
+ return reject(error);
261
+ }
262
+
263
+ resolve();
264
+ });
265
+ });
266
+ }
267
+
268
+ function getInstallCommand(packageManager, dependencySpecs) {
269
+ const normalized = normalizeCommand(packageManager);
270
+
271
+ switch (packageManager) {
272
+ case "npm":
273
+ return { command: normalized, args: ["install", ...dependencySpecs] };
274
+ case "pnpm":
275
+ return { command: normalized, args: ["add", ...dependencySpecs] };
276
+ case "yarn":
277
+ return { command: normalized, args: ["add", ...dependencySpecs] };
278
+ case "bun":
279
+ return { command: normalized, args: ["add", ...dependencySpecs] };
280
+ default:
281
+ return { command: null, args: [] };
282
+ }
283
+ }
284
+
285
+ function normalizeCommand(command) {
286
+ if (process.platform === "win32" && command !== "bun") {
287
+ return `${command}.cmd`;
288
+ }
289
+
290
+ return command;
291
+ }
292
+
293
+ async function pathExists(filePath) {
294
+ try {
295
+ await fs.access(filePath);
296
+ return true;
297
+ } catch (error) {
298
+ if (error && error.code === "ENOENT") {
299
+ return false;
300
+ }
301
+
302
+ throw error;
303
+ }
141
304
  }
142
305
 
143
306
  main().catch((error) => {
144
- if (error && typeof error.exitCode === 'number') {
307
+ if (error && typeof error.exitCode === "number") {
145
308
  process.exit(error.exitCode);
146
309
  }
147
310
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "cognite-create",
3
- "version": "0.1.6",
3
+ "version": "0.1.8",
4
4
  "description": "Create a Next.js app preconfigured with Cognite defaults.",
5
5
  "private": false,
6
6
  "bin": {
@@ -4,4 +4,4 @@ globs:
4
4
  alwaysApply: true
5
5
  ---
6
6
 
7
- Use the ShadCN MCP server to check the Cognite registry for components before making your own.
7
+ Use the ShadCN MCP server to check the Cognite registry for components you could use before making your own. If there are Cognite components available for what you are trying to do you must always use them over alternatives. We want to make sure our app aligns with the Cognite design system. Make sure to check the globals.css file for styling.