@sdeverywhere/create 0.1.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/dist/index.js ADDED
@@ -0,0 +1,725 @@
1
+ // src/index.ts
2
+ import { relative as relative3 } from "path";
3
+ import { bgCyan, black, bold as bold6, cyan as cyan6, green as green8 } from "kleur/colors";
4
+ import ora8 from "ora";
5
+ import prompts8 from "prompts";
6
+ import detectPackageManager from "which-pm-runs";
7
+ import yargs from "yargs-parser";
8
+
9
+ // src/step-config.ts
10
+ import { existsSync } from "fs";
11
+ import { mkdir, readFile, writeFile } from "fs/promises";
12
+ import { dirname, join as joinPath, parse as parsePath, relative, resolve as resolvePath } from "path";
13
+ import { bold, cyan, dim, green, reset, yellow } from "kleur/colors";
14
+ import ora from "ora";
15
+ import prompts from "prompts";
16
+ import yaml from "yaml";
17
+ import { parseAndGenerate, preprocessModel } from "@sdeverywhere/compile";
18
+ var sampleCheckContent = `# yaml-language-server: $schema=SCHEMA_PATH
19
+
20
+ # NOTE: This is just a simple check to get you started. Replace "Some output" with
21
+ # the name of some variable you'd like to test. Additional tests can be developed
22
+ # in the "playground" (beta) inside the model-check report.
23
+ - describe: Some output
24
+ tests:
25
+ - it: should be > 0 for all input scenarios
26
+ scenarios:
27
+ - preset: matrix
28
+ datasets:
29
+ - name: Some output
30
+ predicates:
31
+ - gt: 0
32
+ `;
33
+ async function updateSdeConfig(projDir, mdlPath) {
34
+ const configPath = joinPath(projDir, "sde.config.js");
35
+ let configText = await readFile(configPath, "utf8");
36
+ configText = configText.replaceAll("MODEL_NAME.mdl", mdlPath);
37
+ await writeFile(configPath, configText);
38
+ }
39
+ async function generateCheckYaml(projDir, mdlPath) {
40
+ const checkYamlFile = mdlPath.replace(".mdl", ".check.yaml");
41
+ const checkYamlPath = joinPath(projDir, checkYamlFile);
42
+ if (!existsSync(checkYamlPath)) {
43
+ let relProjPath = relative(dirname(checkYamlPath), projDir);
44
+ if (relProjPath.length === 0) {
45
+ relProjPath = "./";
46
+ }
47
+ const nodeModulesPart = joinPath(relProjPath, "node_modules");
48
+ const checkCorePart = "@sdeverywhere/check-core/schema/check.schema.json";
49
+ const schemaPath = `${nodeModulesPart}/${checkCorePart}`;
50
+ const checkContent = sampleCheckContent.replace("SCHEMA_PATH", schemaPath);
51
+ await writeFile(checkYamlPath, checkContent);
52
+ }
53
+ }
54
+ async function chooseGenConfig(projDir, mdlPath) {
55
+ let mdlVars;
56
+ try {
57
+ mdlVars = await readModelVars(projDir, mdlPath);
58
+ } catch (e) {
59
+ console.log(e);
60
+ ora(yellow("The mdl file failed to load. We will continue setting things up, and you can diagnose the issue later.")).warn();
61
+ return;
62
+ }
63
+ let initialTime;
64
+ let finalTime;
65
+ const validVars = [];
66
+ for (const v of mdlVars) {
67
+ const varName = v.name.toLowerCase();
68
+ let skip = false;
69
+ switch (varName) {
70
+ case "final time":
71
+ skip = true;
72
+ if (v.kind === "const") {
73
+ finalTime = v.value;
74
+ }
75
+ break;
76
+ case "initial time":
77
+ skip = true;
78
+ if (v.kind === "const") {
79
+ initialTime = v.value;
80
+ }
81
+ break;
82
+ case "saveper":
83
+ case "time":
84
+ case "time step":
85
+ skip = true;
86
+ break;
87
+ default:
88
+ break;
89
+ }
90
+ if (!skip) {
91
+ validVars.push(v);
92
+ }
93
+ }
94
+ if (initialTime === void 0) {
95
+ initialTime = 0;
96
+ }
97
+ if (finalTime === void 0) {
98
+ finalTime = 100;
99
+ }
100
+ const datFiles = [];
101
+ const datPart = datFiles.join(";");
102
+ const modelCsvFile = joinPath(projDir, "config", "model.csv");
103
+ const origModelCsvContent = await readFile(modelCsvFile, "utf8");
104
+ const modelCsvHeader = origModelCsvContent.split("\n")[0];
105
+ const modelCsvLine = `${initialTime},${finalTime},${initialTime},${finalTime},${datPart}`;
106
+ const newModelCsvContent = `${modelCsvHeader}
107
+ ${modelCsvLine}
108
+ `;
109
+ await writeFile(modelCsvFile, newModelCsvContent);
110
+ await chooseGenGraphConfig(projDir, validVars);
111
+ console.log();
112
+ await chooseGenSliderConfig(projDir, validVars);
113
+ }
114
+ async function chooseGenGraphConfig(projDir, mdlVars) {
115
+ const genResponse = await prompts({
116
+ type: "confirm",
117
+ name: "genGraph",
118
+ message: `Would you like to configure a graph to get you started? ${reset(dim("(recommended)"))}`,
119
+ initial: true
120
+ }, {
121
+ onCancel: () => {
122
+ ora().info(dim("Operation cancelled."));
123
+ process.exit(0);
124
+ }
125
+ });
126
+ if (!genResponse.genGraph) {
127
+ ora().info(dim(`No problem! You can edit the "${cyan("config/graphs.csv")}" file later.`));
128
+ return;
129
+ }
130
+ const outputVarNames = [];
131
+ for (const mdlVar of mdlVars) {
132
+ if (mdlVar.kind === "aux" || mdlVar.kind === "level") {
133
+ outputVarNames.push(mdlVar.name);
134
+ }
135
+ }
136
+ outputVarNames.sort((a, b) => {
137
+ return a.toLowerCase().localeCompare(b.toLowerCase());
138
+ });
139
+ const choices = outputVarNames.map((f) => {
140
+ return {
141
+ title: f,
142
+ value: f
143
+ };
144
+ });
145
+ const varsResponse = await prompts({
146
+ type: "autocompleteMultiselect",
147
+ name: "vars",
148
+ message: "Choose up to three output variables to display in the graph",
149
+ choices,
150
+ max: 3
151
+ }, {
152
+ onCancel: () => {
153
+ ora().info(dim("Operation cancelled."));
154
+ process.exit(0);
155
+ }
156
+ });
157
+ if (varsResponse.vars.length === 0) {
158
+ ora().info(dim(`No variables selected. You can edit the "${cyan("config/graphs.csv")}" file later.`));
159
+ return;
160
+ }
161
+ const graphsCsvFile = joinPath(projDir, "config", "graphs.csv");
162
+ const csvLine = graphsCsvLine(varsResponse.vars);
163
+ let graphsCsvContent = await readFile(graphsCsvFile, "utf8");
164
+ graphsCsvContent += `${csvLine}
165
+ `;
166
+ await writeFile(graphsCsvFile, graphsCsvContent);
167
+ ora(green(`Added graph to "${bold("config/graphs.csv")}". ${dim("You can configure graphs in that file later.")}`)).succeed();
168
+ }
169
+ function graphsCsvLine(outputVarNames) {
170
+ const colors = ["blue", "red", "green"];
171
+ const a = Array(131).fill("");
172
+ a[0] = "1";
173
+ a[2] = "Graphs";
174
+ a[3] = "Graph Title";
175
+ a[7] = "line";
176
+ let index = 26;
177
+ let colorIndex = 0;
178
+ for (const outputVarName of outputVarNames) {
179
+ const escapedName = escapeCsvField(outputVarName);
180
+ a[index + 0] = escapedName;
181
+ a[index + 2] = "line";
182
+ a[index + 3] = escapedName;
183
+ a[index + 4] = colors[colorIndex];
184
+ index += 7;
185
+ colorIndex++;
186
+ }
187
+ return a.join(",");
188
+ }
189
+ async function chooseGenSliderConfig(projDir, mdlVars) {
190
+ const genResponse = await prompts({
191
+ type: "confirm",
192
+ name: "genSliders",
193
+ message: `Would you like to configure a few sliders to get you started? ${reset(dim("(recommended)"))}`,
194
+ initial: true
195
+ }, {
196
+ onCancel: () => {
197
+ ora().info(dim("Operation cancelled."));
198
+ process.exit(0);
199
+ }
200
+ });
201
+ if (!genResponse.genSliders) {
202
+ ora().info(dim(`No problem! You can edit the "${cyan("config/inputs.csv")}" file later.`));
203
+ return;
204
+ }
205
+ const inputVarNames = [];
206
+ for (const mdlVar of mdlVars) {
207
+ if (mdlVar.kind === "const") {
208
+ inputVarNames.push(mdlVar.name);
209
+ }
210
+ }
211
+ inputVarNames.sort((a, b) => {
212
+ return a.toLowerCase().localeCompare(b.toLowerCase());
213
+ });
214
+ const choices = inputVarNames.map((f) => {
215
+ return {
216
+ title: f,
217
+ value: f
218
+ };
219
+ });
220
+ const varsResponse = await prompts({
221
+ type: "autocompleteMultiselect",
222
+ name: "vars",
223
+ message: "Choose up to three input variables to control with sliders",
224
+ choices,
225
+ max: 3
226
+ }, {
227
+ onCancel: () => {
228
+ ora().info(dim("Operation cancelled."));
229
+ process.exit(0);
230
+ }
231
+ });
232
+ if (varsResponse.vars.length === 0) {
233
+ ora().info(dim(`No variables selected. You can edit the "${cyan("config/inputs.csv")}" file later.`));
234
+ return;
235
+ }
236
+ const inputsCsvFile = joinPath(projDir, "config", "inputs.csv");
237
+ let inputsCsvContent = await readFile(inputsCsvFile, "utf8");
238
+ let idNumber = 1;
239
+ for (const inputVarName of varsResponse.vars) {
240
+ const inputVar = mdlVars.find((v) => v.name === inputVarName);
241
+ if (inputVar && inputVar.kind === "const") {
242
+ const defaultValue = inputVar.value;
243
+ const csvLine = inputsCsvLine(inputVarName, defaultValue, idNumber.toString());
244
+ inputsCsvContent += `${csvLine}
245
+ `;
246
+ idNumber++;
247
+ }
248
+ }
249
+ await writeFile(inputsCsvFile, inputsCsvContent);
250
+ const slidersText = varsResponse.vars.length > 1 ? "sliders" : "sliders";
251
+ ora(green(`Added ${slidersText} to "${bold("config/inputs.csv")}". ${dim("You can configure sliders in that file later.")}`)).succeed();
252
+ }
253
+ function inputsCsvLine(inputVarName, defaultValue, id) {
254
+ const escapedName = escapeCsvField(inputVarName);
255
+ const minValue = defaultValue - 1;
256
+ const maxValue = defaultValue + 1;
257
+ const step = 0.1;
258
+ const a = Array(28).fill("");
259
+ a[0] = id;
260
+ a[1] = "slider";
261
+ a[2] = "view1";
262
+ a[3] = escapedName;
263
+ a[4] = escapedName;
264
+ a[6] = "Sliders";
265
+ a[7] = minValue;
266
+ a[8] = maxValue;
267
+ a[9] = defaultValue;
268
+ a[10] = step;
269
+ a[11] = "(units)";
270
+ return a.join(",");
271
+ }
272
+ function escapeCsvField(s) {
273
+ return s.includes(",") ? `"${s}"` : s;
274
+ }
275
+ async function readModelVars(projDir, mdlPath) {
276
+ const buildDir = resolvePath(projDir, "sde-prep", "build");
277
+ await mkdir(buildDir, { recursive: true });
278
+ const spec = {};
279
+ const mdlFile = resolvePath(projDir, mdlPath);
280
+ const preprocessed = preprocessModel(mdlFile, spec, "genc", false);
281
+ const mdlDir = dirname(mdlFile);
282
+ const mdlName = parsePath(mdlFile).name;
283
+ await parseAndGenerate(preprocessed, spec, "printVarList", mdlDir, mdlName, buildDir);
284
+ const varsYamlFile = joinPath(buildDir, `${mdlName}_vars.yaml`);
285
+ const varsYamlContent = await readFile(varsYamlFile, "utf8");
286
+ const varObjs = yaml.parse(varsYamlContent);
287
+ const mdlVars = [];
288
+ for (const varObj of varObjs) {
289
+ switch (varObj.varType) {
290
+ case "const":
291
+ mdlVars.push({
292
+ kind: "const",
293
+ name: varObj.modelLHS,
294
+ value: Number.parseFloat(varObj.modelFormula)
295
+ });
296
+ break;
297
+ case "aux":
298
+ mdlVars.push({
299
+ kind: "aux",
300
+ name: varObj.modelLHS
301
+ });
302
+ break;
303
+ case "level":
304
+ mdlVars.push({
305
+ kind: "level",
306
+ name: varObj.modelLHS
307
+ });
308
+ break;
309
+ default:
310
+ break;
311
+ }
312
+ }
313
+ return mdlVars;
314
+ }
315
+
316
+ // src/step-deps.ts
317
+ import { execa } from "execa";
318
+ import { bold as bold2, cyan as cyan2, dim as dim2, green as green2, reset as reset2, yellow as yellow2 } from "kleur/colors";
319
+ import ora2 from "ora";
320
+ import prompts2 from "prompts";
321
+ async function chooseInstallDeps(projDir, args, pkgManager) {
322
+ const installResponse = await prompts2({
323
+ type: "confirm",
324
+ name: "install",
325
+ message: `Would you like to install ${pkgManager} dependencies? ${reset2(dim2("(recommended)"))}`,
326
+ initial: true
327
+ }, {
328
+ onCancel: () => {
329
+ ora2().info(dim2("Operation cancelled. Your project folder has been created, but no dependencies have been installed."));
330
+ process.exit(0);
331
+ }
332
+ });
333
+ if (args.dryRun) {
334
+ ora2().info(dim2(`--dry-run enabled, skipping.`));
335
+ return;
336
+ } else if (!installResponse.install) {
337
+ ora2().info(dim2(`No problem! Remember to install dependencies after setup.`));
338
+ return;
339
+ }
340
+ const installExec = execa(pkgManager, ["install"], { cwd: projDir });
341
+ const installingPackagesMsg = "Installing packages...";
342
+ const installSpinner = ora2(installingPackagesMsg).start();
343
+ try {
344
+ await new Promise((resolve, reject) => {
345
+ var _a, _b;
346
+ (_a = installExec.stdout) == null ? void 0 : _a.on("data", function(data) {
347
+ installSpinner.text = `${installingPackagesMsg}
348
+ ${bold2(`[${pkgManager}]`)} ${data}`;
349
+ });
350
+ (_b = installExec.stderr) == null ? void 0 : _b.on("data", function(data) {
351
+ installSpinner.text = `${installingPackagesMsg}
352
+ ${bold2(`[${pkgManager}]`)} ${data}`;
353
+ });
354
+ installExec.on("error", (error) => reject(error));
355
+ installExec.on("exit", (code) => reject(`Install failed (code=${code})`));
356
+ installExec.on("close", () => resolve());
357
+ });
358
+ installSpinner.text = green2("Packages installed!");
359
+ installSpinner.succeed();
360
+ } catch (e) {
361
+ installSpinner.text = yellow2(`There was an error installing packages. Try running ${cyan2(`${pkgManager} install`)} in your project directory later.`);
362
+ installSpinner.warn();
363
+ }
364
+ }
365
+
366
+ // src/step-directory.ts
367
+ import { existsSync as existsSync2 } from "fs";
368
+ import { resolve as resolvePath2 } from "path";
369
+ import { bold as bold3, dim as dim3, green as green3, red } from "kleur/colors";
370
+ import ora3 from "ora";
371
+ import prompts3 from "prompts";
372
+ async function chooseProjectDir(args) {
373
+ function isValidDir(dir) {
374
+ const packageJson = resolvePath2(dir, "package.json");
375
+ const packagesDir = resolvePath2(dir, "packages");
376
+ return !existsSync2(dir) || !existsSync2(packageJson) && !existsSync2(packagesDir);
377
+ }
378
+ const showValidDirMsg = (dir) => {
379
+ ora3(green3(`Using "${bold3(dir)}" as the project directory.`)).succeed();
380
+ };
381
+ const showInvalidDirMsg = (dir) => {
382
+ ora3(red(`"${bold3(dir)}" contains existing 'package.json' and/or 'packages' directory, stopping.`)).fail();
383
+ };
384
+ let projDir = args["_"][2];
385
+ if (projDir) {
386
+ if (isValidDir(projDir)) {
387
+ showValidDirMsg(projDir);
388
+ } else {
389
+ showInvalidDirMsg(projDir);
390
+ process.exit(0);
391
+ }
392
+ } else {
393
+ const dirResponse = await prompts3({
394
+ type: "text",
395
+ name: "directory",
396
+ message: "Where would you like to create your new project?",
397
+ initial: "<current directory>"
398
+ }, {
399
+ onCancel: () => {
400
+ ora3().info(dim3("Operation cancelled."));
401
+ process.exit(0);
402
+ }
403
+ });
404
+ projDir = dirResponse.directory;
405
+ if (projDir === "<current directory>") {
406
+ projDir = process.cwd();
407
+ }
408
+ if (isValidDir(projDir)) {
409
+ showValidDirMsg(projDir);
410
+ } else {
411
+ showInvalidDirMsg(projDir);
412
+ process.exit(0);
413
+ }
414
+ }
415
+ return projDir;
416
+ }
417
+
418
+ // src/step-emsdk.ts
419
+ import { existsSync as existsSync3, rmSync } from "fs";
420
+ import { join as joinPath2, resolve as resolvePath3 } from "path";
421
+ import { execa as execa2 } from "execa";
422
+ import { bold as bold4, cyan as cyan3, dim as dim4, green as green4, red as red2 } from "kleur/colors";
423
+ import ora4 from "ora";
424
+ import prompts4 from "prompts";
425
+ var version = "2.0.34";
426
+ async function chooseInstallEmsdk(projDir, args) {
427
+ const underParentDir = resolvePath3(projDir, "..", "emsdk");
428
+ const underProjDir = joinPath2(projDir, "emsdk");
429
+ const installResponse = await prompts4({
430
+ type: "select",
431
+ name: "install",
432
+ message: `Would you like to install the Emscripten SDK?`,
433
+ choices: [
434
+ {
435
+ title: `Install under parent directory (${bold4(underParentDir)})`,
436
+ description: "This is recommended so that it can be shared by multiple projects",
437
+ value: "parent"
438
+ },
439
+ {
440
+ title: `Install under project directory (${bold4(underProjDir)})"`,
441
+ description: "This is useful for keeping everything under a single project directory",
442
+ value: "project"
443
+ },
444
+ {
445
+ title: `Don't install`,
446
+ description: `It's OK, you can install it later`,
447
+ value: "skip"
448
+ }
449
+ ]
450
+ }, {
451
+ onCancel: () => {
452
+ ora4().info(dim4("Operation cancelled. Your project folder has been created, but the Emscripten SDK and other dependencies have not been installed."));
453
+ process.exit(0);
454
+ }
455
+ });
456
+ if (args.dryRun) {
457
+ ora4().info(dim4(`--dry-run enabled, skipping.`));
458
+ return;
459
+ } else if (installResponse.install === "skip") {
460
+ ora4().info(dim4(`No problem! Be sure to install the Emscripten SDK and configure it in "${cyan3("sde.config.js")}" after setup.`));
461
+ return;
462
+ }
463
+ const installDir = installResponse.install === "parent" ? underParentDir : underProjDir;
464
+ try {
465
+ await installEmscripten(installDir);
466
+ } catch (e) {
467
+ ora4(red2(`Failed to install Emscripten SDK: ${e.message}`)).fail();
468
+ process.exit(0);
469
+ }
470
+ ora4(green4(`Installed the Emscripten SDK in "${bold4(installDir)}"`)).succeed();
471
+ }
472
+ async function installEmscripten(emsdkDir) {
473
+ if (!existsSync3(emsdkDir)) {
474
+ console.log(`Downloading Emscripten SDK to ${emsdkDir}`);
475
+ await execa2("git", ["clone", "https://github.com/emscripten-core/emsdk.git", emsdkDir]);
476
+ } else {
477
+ console.log(`Found existing Emscripten SDK directory: ${emsdkDir}`);
478
+ }
479
+ if (process.env.CI) {
480
+ console.log("CI detected, removing .git directory...");
481
+ const gitDir = joinPath2(emsdkDir, ".git");
482
+ rmSync(gitDir, { recursive: true, force: true });
483
+ } else {
484
+ console.log("Local development detected, performing git pull...");
485
+ await execa2("git", ["pull"], { cwd: emsdkDir });
486
+ }
487
+ const emsdkCmd = async (...args) => {
488
+ return execa2("python3", ["emsdk.py", ...args], { cwd: emsdkDir });
489
+ };
490
+ console.log(`Activating Emscripten SDK ${version}...`);
491
+ await emsdkCmd("install", version);
492
+ await emsdkCmd("activate", version);
493
+ }
494
+
495
+ // src/step-git.ts
496
+ import { execaCommand } from "execa";
497
+ import { cyan as cyan4, dim as dim5, green as green5, reset as reset3 } from "kleur/colors";
498
+ import ora5 from "ora";
499
+ import prompts5 from "prompts";
500
+ async function chooseGitInit(projDir, args) {
501
+ const gitResponse = await prompts5({
502
+ type: "confirm",
503
+ name: "git",
504
+ message: `Would you like to initialize a new git repository? ${reset3(dim5("(optional)"))}`,
505
+ initial: true
506
+ }, {
507
+ onCancel: () => {
508
+ ora5().info(dim5("Operation cancelled. Your project folder has already been created."));
509
+ process.exit(0);
510
+ }
511
+ });
512
+ if (args.dryRun) {
513
+ ora5().info(dim5(`--dry-run enabled, skipping.`));
514
+ return;
515
+ } else if (!gitResponse.git) {
516
+ ora5().info(dim5(`No problem! You can come back and run ${cyan4(`git init`)} later.`));
517
+ return;
518
+ }
519
+ await execaCommand("git init", { cwd: projDir });
520
+ ora5().succeed(green5("Git repository initialized!"));
521
+ }
522
+
523
+ // src/step-mdl.ts
524
+ import { readdir, writeFile as writeFile2 } from "fs/promises";
525
+ import { join as joinPath3, relative as relative2, resolve as resolvePath4 } from "path";
526
+ import { bold as bold5, cyan as cyan5, dim as dim6, green as green6, yellow as yellow3 } from "kleur/colors";
527
+ import ora6 from "ora";
528
+ import prompts6 from "prompts";
529
+ var sampleMdlContent = `{UTF-8}
530
+
531
+ X = TIME
532
+ ~~|
533
+
534
+ Y = 0
535
+ ~ [-10,10,0.1]
536
+ ~
537
+ |
538
+
539
+ Z = X + Y
540
+ ~~|
541
+
542
+ INITIAL TIME = 2000 ~~|
543
+ FINAL TIME = 2100 ~~|
544
+ TIME STEP = 1 ~~|
545
+ SAVEPER = TIME STEP ~~|
546
+ `;
547
+ async function chooseMdlFile(projDir) {
548
+ async function getFiles(dir) {
549
+ const dirents = await readdir(dir, { withFileTypes: true });
550
+ const files = await Promise.all(dirents.map((dirent) => {
551
+ const res = resolvePath4(dir, dirent.name);
552
+ return dirent.isDirectory() ? getFiles(res) : res;
553
+ }));
554
+ return files.flat();
555
+ }
556
+ const allFiles = await getFiles(projDir);
557
+ const mdlFiles = allFiles.filter((f) => f.endsWith(".mdl")).map((f) => relative2(projDir, f));
558
+ const mdlChoices = mdlFiles.map((f) => {
559
+ return {
560
+ title: f,
561
+ value: f
562
+ };
563
+ });
564
+ let mdlFile;
565
+ if (mdlFiles.length === 0) {
566
+ const sampleMdlFile = joinPath3(projDir, "sample.mdl");
567
+ await writeFile2(sampleMdlFile, sampleMdlContent);
568
+ ora6(yellow3(`No mdl files were found in "${projDir}". A "${cyan5("sample.mdl")}" file has been added to the project to get you started.`)).warn();
569
+ mdlFile = "sample.mdl";
570
+ } else if (mdlFiles.length === 1) {
571
+ ora6().succeed(`Found "${mdlFiles[0]}", will configure the project to use that mdl file.`);
572
+ mdlFile = mdlFiles[0];
573
+ } else {
574
+ const options = await prompts6([
575
+ {
576
+ type: "select",
577
+ name: "mdlFile",
578
+ message: "It looks like there are multiple mdl files. Which one would you like to use?",
579
+ choices: mdlChoices
580
+ }
581
+ ], {
582
+ onCancel: () => {
583
+ ora6().info(dim6("Operation cancelled."));
584
+ process.exit(0);
585
+ }
586
+ });
587
+ mdlFile = options.mdlFile;
588
+ }
589
+ ora6(green6(`Using "${bold5(mdlFile)}" as the model for the project.`)).succeed();
590
+ return mdlFile;
591
+ }
592
+
593
+ // src/step-template.ts
594
+ import { existsSync as existsSync4, mkdtempSync, readdirSync, rmSync as rmSync2 } from "fs";
595
+ import { writeFile as writeFile3 } from "fs/promises";
596
+ import { copy } from "fs-extra";
597
+ import { tmpdir } from "os";
598
+ import { join as joinPath4 } from "path";
599
+ import degit from "degit";
600
+ import { dim as dim7, green as green7, red as red3, yellow as yellow4 } from "kleur/colors";
601
+ import ora7 from "ora";
602
+ import prompts7 from "prompts";
603
+ var TEMPLATES = [
604
+ {
605
+ title: "Default project",
606
+ description: "Includes recommended structure with config files, app, core library, model-check, etc",
607
+ value: "template-default"
608
+ },
609
+ {
610
+ title: "Minimal project",
611
+ description: "Includes simple config for model-check",
612
+ value: "template-minimal"
613
+ }
614
+ ];
615
+ async function chooseTemplate(projDir, args, pkgManager) {
616
+ const options = await prompts7([
617
+ {
618
+ type: "select",
619
+ name: "template",
620
+ message: "Which template would you like to use?",
621
+ choices: TEMPLATES
622
+ }
623
+ ], {
624
+ onCancel: () => {
625
+ ora7().info(dim7("Operation cancelled."));
626
+ process.exit(0);
627
+ }
628
+ });
629
+ if (args.dryRun) {
630
+ ora7().info(dim7(`--dry-run enabled, skipping.`));
631
+ return;
632
+ }
633
+ const defaultRev = "main";
634
+ const commit = args.commit || defaultRev;
635
+ const templateTarget = `climateinteractive/SDEverywhere/examples/${options.template}`;
636
+ const hash = `#${commit}`;
637
+ const templateSpinner = ora7("Copying project files...").start();
638
+ await runDegit(templateTarget, hash, projDir, args, templateSpinner);
639
+ templateSpinner.text = green7("Template copied!");
640
+ templateSpinner.succeed();
641
+ if (options.template === "template-default" && pkgManager === "pnpm") {
642
+ const workspaceFile = joinPath4(projDir, "pnpm-workspace.yaml");
643
+ const workspaceContent = `packages:
644
+ - packages/*
645
+ `;
646
+ await writeFile3(workspaceFile, workspaceContent);
647
+ }
648
+ return options.template;
649
+ }
650
+ async function runDegit(templateTarget, hash, dstDir, args, spinner) {
651
+ const verbose = args.verbose;
652
+ const emitter = degit(`${templateTarget}${hash}`, {
653
+ cache: false,
654
+ force: true,
655
+ verbose
656
+ });
657
+ try {
658
+ if (verbose) {
659
+ emitter.on("info", (info) => {
660
+ console.log(info.message);
661
+ });
662
+ }
663
+ const tmpDir = mkdtempSync(joinPath4(tmpdir(), "sde-create-"));
664
+ await emitter.clone(tmpDir);
665
+ if (!existsSync4(tmpDir) || readdirSync(tmpDir).length === 0) {
666
+ throw new Error("The requested template failed to download");
667
+ }
668
+ await copy(tmpDir, dstDir, {
669
+ overwrite: false,
670
+ errorOnExist: false
671
+ });
672
+ rmSync2(tmpDir, { recursive: true, force: true });
673
+ } catch (e) {
674
+ spinner.fail();
675
+ console.error(red3(e.message));
676
+ console.error(yellow4("There was a problem copying the template."));
677
+ console.error(yellow4("Please file a new issue with the command output here: https://github.com/climateinteractive/sdeverywhere/issues"));
678
+ process.exit(0);
679
+ }
680
+ }
681
+
682
+ // src/index.ts
683
+ async function main() {
684
+ var _a;
685
+ const pkgManager = ((_a = detectPackageManager()) == null ? void 0 : _a.name) || "npm";
686
+ const args = yargs(process.argv);
687
+ prompts8.override(args);
688
+ console.log(`
689
+ ${bold6("Welcome to SDEverywhere!")}`);
690
+ console.log(`Let's create a new SDEverywhere project for your model.
691
+ `);
692
+ const projDir = await chooseProjectDir(args);
693
+ console.log();
694
+ const templateName = await chooseTemplate(projDir, args, pkgManager);
695
+ console.log();
696
+ const mdlPath = await chooseMdlFile(projDir);
697
+ await updateSdeConfig(projDir, mdlPath);
698
+ await generateCheckYaml(projDir, mdlPath);
699
+ console.log();
700
+ if (templateName === "template-default" && !args.dryRun) {
701
+ await chooseGenConfig(projDir, mdlPath);
702
+ console.log();
703
+ }
704
+ await chooseInstallEmsdk(projDir, args);
705
+ console.log();
706
+ await chooseInstallDeps(projDir, args, pkgManager);
707
+ console.log();
708
+ await chooseGitInit(projDir, args);
709
+ console.log();
710
+ ora8(green8("Setup complete!")).succeed();
711
+ console.log(`
712
+ ${bgCyan(black(" Next steps "))}
713
+ `);
714
+ const relProjDir = relative3(process.cwd(), projDir);
715
+ const devCmd = pkgManager === "npm" ? "npm run dev" : `${pkgManager} dev`;
716
+ if (relProjDir !== "") {
717
+ console.log(`You can now ${bold6(cyan6("cd"))} into the ${bold6(cyan6(relProjDir))} project directory.`);
718
+ }
719
+ console.log(`Run ${bold6(cyan6(devCmd))} to start the local dev server. ${bold6(cyan6("CTRL-C"))} to close.`);
720
+ console.log("");
721
+ }
722
+ export {
723
+ main
724
+ };
725
+ //# sourceMappingURL=index.js.map