@valbuild/init 0.60.18 → 0.60.20

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.
@@ -406,6 +406,59 @@ function _slicedToArray(arr, i) {
406
406
  return _arrayWithHoles(arr) || _iterableToArrayLimit(arr, i) || _unsupportedIterableToArray(arr, i) || _nonIterableRest();
407
407
  }
408
408
 
409
+ function toPrimitive(t, r) {
410
+ if ("object" != typeof t || !t) return t;
411
+ var e = t[Symbol.toPrimitive];
412
+ if (void 0 !== e) {
413
+ var i = e.call(t, r || "default");
414
+ if ("object" != typeof i) return i;
415
+ throw new TypeError("@@toPrimitive must return a primitive value.");
416
+ }
417
+ return ("string" === r ? String : Number)(t);
418
+ }
419
+
420
+ function toPropertyKey(t) {
421
+ var i = toPrimitive(t, "string");
422
+ return "symbol" == typeof i ? i : String(i);
423
+ }
424
+
425
+ function _defineProperty(obj, key, value) {
426
+ key = toPropertyKey(key);
427
+ if (key in obj) {
428
+ Object.defineProperty(obj, key, {
429
+ value: value,
430
+ enumerable: true,
431
+ configurable: true,
432
+ writable: true
433
+ });
434
+ } else {
435
+ obj[key] = value;
436
+ }
437
+ return obj;
438
+ }
439
+
440
+ function ownKeys(e, r) {
441
+ var t = Object.keys(e);
442
+ if (Object.getOwnPropertySymbols) {
443
+ var o = Object.getOwnPropertySymbols(e);
444
+ r && (o = o.filter(function (r) {
445
+ return Object.getOwnPropertyDescriptor(e, r).enumerable;
446
+ })), t.push.apply(t, o);
447
+ }
448
+ return t;
449
+ }
450
+ function _objectSpread2(e) {
451
+ for (var r = 1; r < arguments.length; r++) {
452
+ var t = null != arguments[r] ? arguments[r] : {};
453
+ r % 2 ? ownKeys(Object(t), !0).forEach(function (r) {
454
+ _defineProperty(e, r, t[r]);
455
+ }) : Object.getOwnPropertyDescriptors ? Object.defineProperties(e, Object.getOwnPropertyDescriptors(t)) : ownKeys(Object(t)).forEach(function (r) {
456
+ Object.defineProperty(e, r, Object.getOwnPropertyDescriptor(t, r));
457
+ });
458
+ }
459
+ return e;
460
+ }
461
+
409
462
  function transformNextAppRouterValProvider(fileInfo, api, options) {
410
463
  if (!options.configImportPath) {
411
464
  throw new Error("configImportPath is required");
@@ -451,7 +504,7 @@ function transformNextAppRouterValProvider(fileInfo, api, options) {
451
504
 
452
505
  var packageJson = {
453
506
  name: "@valbuild/init",
454
- version: "0.60.18",
507
+ version: "0.60.20",
455
508
  description: "Initialize a new val.build project",
456
509
  exports: {
457
510
  "./main": {
@@ -522,12 +575,15 @@ var VAL_API_ROUTER = function VAL_API_ROUTER(valServerPath) {
522
575
  var VAL_APP_PAGE = function VAL_APP_PAGE(configImportPath) {
523
576
  return "import { ValApp } from \"@valbuild/next\";\nimport { config } from \"".concat(configImportPath, "\";\n\nexport default function Val() {\n return <ValApp config={config} />;\n}\n");
524
577
  };
578
+ var BASIC_EXAMPLE = function BASIC_EXAMPLE(moduleId, configImportPath, isJavaScript) {
579
+ return "".concat(isJavaScript ? "// @ts-check\n" : "", "/**\n * Val example file - generated by @valbuild/init\n **/\n\nimport {\n s /* s = schema */,\n c /* c = content */,").concat(isJavaScript ? "" : "\n type t /* t = type */,", "\n} from \"").concat(configImportPath, "\";\n\n/**\n * This is the schema for the content. It defines the structure of the content and the types of each field.\n *\n * @docs https://val.build/docs/api-reference\n */\nexport const testSchema = s.object({\n /**\n * Basic text field\n */\n text: s.string(),\n\n /**\n * Optional fields are marked with `.optional()`\n */\n optionals: s.string().optional(),\n\n arrays: s.array(s.string()),\n /**\n * Records are objects where entries can be added. Useful for array-like structures where you would use a key to uniquely identify each entry.\n */\n records: s.record(s.string()),\n\n /**\n * Rich text can be used for multiline text, but also for more complex text editing capabilities like links, images, lists, etc.\n *\n * @docs https://val.build/docs/api-reference/schema-types/richtext\n *\n * @see ValRichText will render rich text\n */\n richText: s.richtext({\n // All features enabled:\n bold: true,\n italic: true,\n lineThrough: true,\n headings: [\"h1\", \"h2\", \"h3\", \"h4\", \"h5\", \"h6\"],\n a: true,\n img: true,\n ul: true,\n ol: true,\n }),\n\n /**\n * Images in Val are stored as files in the public folder.\n *\n * @docs https://val.build/docs/api-reference/schema-types/image\n *\n * When defining content use the following syntax:\n * @example c.file('/public/myimage.png') // path to the image file, use the VS Code plugin or the `@valbuild/cli validate --fix` command to add metadata\n *\n * @see ValImage component to see how to render this in your app\n */\n image: s.image().optional(),\n\n /**\n * String enums: presents as a dropdown in the UI\n */\n stringEnum: s.union(s.literal(\"lit-0\"), s.literal(\"lit-1\")),\n\n /**\n * Raw strings disables the stega (steganography) feature that automatically tags content when using the overlay.\n * It is useful for slugs and other data that might be processed in code (parsed or matching for equality...)\n */\n slug: s.string().raw(),\n\n /**\n * Object unions: presents as a dropdown in the UI and the different fields\n *\n * @docs https://val.build/docs/api-reference/schema-types/union\n */\n objectUnions: s.union(\n \"type\",\n s.object({\n type: s.literal(\"page-type-1\"),\n value: s.number(),\n }),\n s.object({\n type: s.literal(\"page-type-2\"),\n text: s.string(),\n })\n ),\n});\n").concat(isJavaScript ? "" : "\n/**\n * t.inferSchema returns the type of the content.\n * This pattern is useful to type props of components that use this content (partially or whole)\n */\nexport type TestContent = t.inferSchema<typeof testSchema>;\n", "\n\n/**\n * This is the content definition. Add your content below.\n *\n * NOTE: the first argument, module id, must match the path of the file.\n */\nexport default c.define(\"").concat(moduleId, "\", testSchema, {\n text: \"Basic text content\",\n optionals: null,\n arrays: [\"A string\"],\n records: {\n \"unique-key-1\": \"A string\",\n },\n richText: c.richtext`# Title 1\n\n${c.rt.link(\"Val docs\", { href: \"https://val.build/docs\" })}\n\n- List item 1\n- List item 2\n`,\n image: null,\n slug: \"test\",\n objectUnions: {\n type: \"page-type-2\",\n text: \"String value\",\n },\n stringEnum: \"lit-1\",\n});\n");
580
+ };
525
581
 
526
582
  function error(message) {
527
- console.error(chalk.red("❌ ERROR: ") + message);
583
+ console.error(chalk.red(" ❌ ERROR: ") + message);
528
584
  }
529
585
  function warn(message) {
530
- console.error(chalk.yellow("⚠️ WARN:") + message);
586
+ console.error(chalk.yellow(" ⚠️ WARN:" + message));
531
587
  }
532
588
  function info(message) {
533
589
  var opts = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {};
@@ -536,7 +592,7 @@ function info(message) {
536
592
  return;
537
593
  }
538
594
  if (opts.isGood) {
539
- console.log(chalk.green("") + message);
595
+ console.log(chalk.hex("#37cd99")(" V") + message);
540
596
  return;
541
597
  }
542
598
  console.log(message);
@@ -561,7 +617,7 @@ function _init() {
561
617
  case 0:
562
618
  root = _args2.length > 0 && _args2[0] !== undefined ? _args2[0] : process.cwd();
563
619
  _ref = _args2.length > 1 && _args2[1] !== undefined ? _args2[1] : {}, defaultAnswers = _ref.yes;
564
- info('Initializing Val in "' + root + '"...\n');
620
+ info("Initializing " + chalk.bgBlack.hex("#37cd99")("Val") + ' in "' + root + '"...\n');
565
621
  process.stdout.write("Analyzing project...");
566
622
  _context2.next = 6;
567
623
  return analyze(path.resolve(root), walk(path.resolve(root)));
@@ -608,7 +664,7 @@ function walk(dir) {
608
664
  }
609
665
  var analyze = /*#__PURE__*/function () {
610
666
  var _ref2 = _asyncToGenerator( /*#__PURE__*/_regeneratorRuntime().mark(function _callee(root, files) {
611
- var analysis, packageJsonPath, packageJsonText, _packageJson, _semver$minVersion, minNextVersion, _semver$minVersion2, minValVersion, pagesRouterAppPath, appRouterLayoutPath, git, gitStatus, gitRemoteOrigin;
667
+ var analysis, packageJsonPath, packageJsonText, _packageJson, _semver$minVersion, minNextVersion, _semver$minVersion2, minValVersion, pagesRouterAppPath, appRouterLayoutPath, git, gitStatus, gitRemoteOrigin, parts, owner, repo, gitIgnorePath;
612
668
  return _regeneratorRuntime().wrap(function _callee$(_context) {
613
669
  while (1) switch (_context.prev = _context.next) {
614
670
  case 0:
@@ -720,18 +776,32 @@ var analyze = /*#__PURE__*/function () {
720
776
  analysis.hasGit = true;
721
777
  analysis.isGitHub = gitRemoteOrigin ? !!gitRemoteOrigin.includes("github.com") : false;
722
778
  analysis.isGitClean = getGitStatusIsClean(gitStatus);
723
- _context.next = 46;
779
+ // get owner and repo from git remote:
780
+ if (gitRemoteOrigin) {
781
+ // Split the URL by colon
782
+ parts = gitRemoteOrigin.split(":"); // Extract owner and repo
783
+ owner = parts[0].split("@")[1];
784
+ repo = parts[1].replace(".git", ""); // Remove .git extension if present
785
+ analysis.gitRemote = {
786
+ owner: owner,
787
+ repo: repo
788
+ };
789
+ }
790
+ _context.next = 47;
724
791
  break;
725
- case 44:
726
- _context.prev = 44;
792
+ case 45:
793
+ _context.prev = 45;
727
794
  _context.t1 = _context["catch"](31);
728
- case 46:
729
- return _context.abrupt("return", analysis);
730
795
  case 47:
796
+ gitIgnorePath = path.join(root, ".gitignore");
797
+ analysis.gitIgnorePath = gitIgnorePath;
798
+ analysis.gitIgnoreFile = fs.readFileSync(gitIgnorePath, "utf-8");
799
+ return _context.abrupt("return", analysis);
800
+ case 51:
731
801
  case "end":
732
802
  return _context.stop();
733
803
  }
734
- }, _callee, null, [[8, 17], [31, 44]]);
804
+ }, _callee, null, [[8, 17], [31, 45]]);
735
805
  }));
736
806
  return function analyze(_x, _x2) {
737
807
  return _ref2.apply(this, arguments);
@@ -768,6 +838,19 @@ function _plan() {
768
838
  _answer7,
769
839
  currentEslintRc,
770
840
  parsedEslint,
841
+ _answer8,
842
+ _answer9,
843
+ vscodeDir,
844
+ settingsPath,
845
+ currentSettings,
846
+ currentSettingsFile,
847
+ currentRecommendations,
848
+ valBuildIntelliSense,
849
+ _answer10,
850
+ exampleDir,
851
+ examplePath,
852
+ exampleImport,
853
+ exampleModuleId,
771
854
  _args3 = arguments;
772
855
  return _regeneratorRuntime().wrap(function _callee3$(_context3) {
773
856
  while (1) switch (_context3.prev = _context3.next) {
@@ -804,7 +887,7 @@ function _plan() {
804
887
  _context3.next = 15;
805
888
  break;
806
889
  }
807
- info(" Source dir: " + analysis.root, {
890
+ info(" Source dir: " + analysis.srcDir, {
808
891
  isGood: true
809
892
  });
810
893
  _context3.next = 17;
@@ -1304,12 +1387,117 @@ function _plan() {
1304
1387
  case 202:
1305
1388
  warn("Cannot patch eslint: failed to find eslint config file");
1306
1389
  case 203:
1390
+ if (!analysis.gitIgnorePath) {
1391
+ _context3.next = 210;
1392
+ break;
1393
+ }
1394
+ _context3.next = 206;
1395
+ return confirm({
1396
+ message: "Append .gitignore entry for Val cache? (recommended)",
1397
+ "default": true
1398
+ });
1399
+ case 206:
1400
+ _answer8 = _context3.sent;
1401
+ if (_answer8) {
1402
+ plan.updateGitIgnore = {
1403
+ path: analysis.gitIgnorePath,
1404
+ source: (analysis.gitIgnoreFile ? "".concat(analysis.gitIgnoreFile, "\n\n") : "") + "# Val local cache\n.val\n"
1405
+ };
1406
+ } else {
1407
+ plan.updateGitIgnore = false;
1408
+ }
1409
+ _context3.next = 211;
1410
+ break;
1411
+ case 210:
1412
+ plan.updateGitIgnore = false;
1413
+ case 211:
1414
+ _context3.next = 213;
1415
+ return confirm({
1416
+ message: "Add the Val Build IntelliSense to .vscode/extensions.json?",
1417
+ "default": true
1418
+ });
1419
+ case 213:
1420
+ _answer9 = _context3.sent;
1421
+ if (!_answer9) {
1422
+ _context3.next = 239;
1423
+ break;
1424
+ }
1425
+ vscodeDir = path.join(analysis.root, ".vscode");
1426
+ settingsPath = path.join(vscodeDir, "extensions.json");
1427
+ currentSettings = {};
1428
+ _context3.prev = 218;
1429
+ currentSettingsFile = fs.readFileSync(settingsPath, "utf-8");
1430
+ if (!currentSettingsFile) {
1431
+ _context3.next = 229;
1432
+ break;
1433
+ }
1434
+ _context3.prev = 221;
1435
+ currentSettings = JSON.parse(currentSettingsFile);
1436
+ _context3.next = 229;
1437
+ break;
1438
+ case 225:
1439
+ _context3.prev = 225;
1440
+ _context3.t8 = _context3["catch"](221);
1441
+ warn("Failed to parse VS Code extensions.json found here: ".concat(settingsPath, ".").concat(_context3.t8 instanceof Error ? "Parse error: ".concat(_context3.t8.message) : ""));
1442
+ return _context3.abrupt("return", {
1443
+ abort: true
1444
+ });
1445
+ case 229:
1446
+ _context3.next = 233;
1447
+ break;
1448
+ case 231:
1449
+ _context3.prev = 231;
1450
+ _context3.t9 = _context3["catch"](218);
1451
+ case 233:
1452
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
1453
+ currentRecommendations = currentSettings.recommendations;
1454
+ valBuildIntelliSense = "valbuild.vscode-val-build";
1455
+ if (!currentRecommendations.includes(valBuildIntelliSense)) {
1456
+ currentSettings = _objectSpread2(_objectSpread2({}, currentSettings), {}, {
1457
+ recommendations: (currentRecommendations || []).concat(valBuildIntelliSense)
1458
+ });
1459
+ }
1460
+ plan.updateVSCodeSettings = {
1461
+ path: settingsPath,
1462
+ source: JSON.stringify(currentSettings, null, 2)
1463
+ };
1464
+ _context3.next = 240;
1465
+ break;
1466
+ case 239:
1467
+ plan.updateVSCodeSettings = false;
1468
+ case 240:
1469
+ _context3.next = 242;
1470
+ return confirm({
1471
+ message: "Include example Val files?",
1472
+ "default": true
1473
+ });
1474
+ case 242:
1475
+ _answer10 = _context3.sent;
1476
+ if (!_answer10) {
1477
+ _context3.next = 251;
1478
+ break;
1479
+ }
1480
+ exampleDir = path.join(analysis.srcDir, "examples", "val");
1481
+ examplePath = path.join(exampleDir, "example.val." + (analysis.isJavaScript ? "js" : "ts"));
1482
+ exampleImport = path.relative(exampleDir, valConfigPath).replace(".js", "").replace(".ts", "");
1483
+ if (analysis.packageJsonDir) {
1484
+ _context3.next = 249;
1485
+ break;
1486
+ }
1487
+ throw Error("Could not detect package.json directory! This is a Val bug.");
1488
+ case 249:
1489
+ exampleModuleId = "/".concat(path.relative(analysis.packageJsonDir, examplePath).replace(".val", "").replace(".js", "").replace(".ts", ""));
1490
+ plan.includeExample = {
1491
+ path: examplePath,
1492
+ source: BASIC_EXAMPLE(exampleModuleId, exampleImport, !!analysis.isJavaScript)
1493
+ };
1494
+ case 251:
1307
1495
  return _context3.abrupt("return", plan);
1308
- case 204:
1496
+ case 252:
1309
1497
  case "end":
1310
1498
  return _context3.stop();
1311
1499
  }
1312
- }, _callee3);
1500
+ }, _callee3, null, [[218, 231], [221, 225]]);
1313
1501
  }));
1314
1502
  return _plan.apply(this, arguments);
1315
1503
  }
@@ -1318,7 +1506,7 @@ function execute(_x4) {
1318
1506
  }
1319
1507
  function _execute() {
1320
1508
  _execute = _asyncToGenerator( /*#__PURE__*/_regeneratorRuntime().mark(function _callee4(plan) {
1321
- var _i, _Object$entries, _Object$entries$_i, key, fileOp;
1509
+ var _i, _Object$entries, _Object$entries$_i, key, maybeFileOp;
1322
1510
  return _regeneratorRuntime().wrap(function _callee4$(_context4) {
1323
1511
  while (1) switch (_context4.prev = _context4.next) {
1324
1512
  case 0:
@@ -1336,10 +1524,13 @@ function _execute() {
1336
1524
  case 4:
1337
1525
  info("Executing...");
1338
1526
  for (_i = 0, _Object$entries = Object.entries(plan); _i < _Object$entries.length; _i++) {
1339
- _Object$entries$_i = _slicedToArray(_Object$entries[_i], 2), key = _Object$entries$_i[0], fileOp = _Object$entries$_i[1];
1340
- writeFile(fileOp, plan.root, key.startsWith("update"));
1527
+ _Object$entries$_i = _slicedToArray(_Object$entries[_i], 2), key = _Object$entries$_i[0], maybeFileOp = _Object$entries$_i[1];
1528
+ if (isFileOp(maybeFileOp)) {
1529
+ writeFile(maybeFileOp, plan.root, key.startsWith("update"));
1530
+ }
1341
1531
  }
1342
- case 6:
1532
+ info("\n \nVal was successfully initialized!\n\n Start the application:\n\n $ ".concat(chalk.gray("npm run dev"), "\n\n Open (assumes http://localhost:3000):\n\n ").concat(chalk.bgBlack.hex("#37cd99").underline("http://localhost:3000/val"), "\n\n When you want to enable editor support, import the project by opening the following link:\n \n ").concat(chalk.bgBlack.hex("#37cd99").underline("https://app.val.build/orgs/new".concat(plan.gitRemote ? "?org=".concat(encodeURIComponent(plan.gitRemote.owner), "&owner=").concat(encodeURIComponent(plan.gitRemote.owner), "&repo=").concat(encodeURIComponent(plan.gitRemote.repo)) : "")), "\n\n"));
1533
+ case 7:
1343
1534
  case "end":
1344
1535
  return _context4.stop();
1345
1536
  }
@@ -1348,7 +1539,7 @@ function _execute() {
1348
1539
  return _execute.apply(this, arguments);
1349
1540
  }
1350
1541
  function writeFile(fileOp, rootDir, isUpdate) {
1351
- if (fileOp && typeof fileOp !== "boolean" && typeof fileOp !== "string") {
1542
+ if (fileOp) {
1352
1543
  fs.mkdirSync(path.dirname(fileOp.path), {
1353
1544
  recursive: true
1354
1545
  });
@@ -1378,6 +1569,9 @@ function getGitStatusIsClean(gitStatus) {
1378
1569
  }
1379
1570
  return false;
1380
1571
  }
1572
+ function isFileOp(maybeFileOp) {
1573
+ return typeof maybeFileOp !== "boolean" && typeof maybeFileOp !== "string" && _typeof(maybeFileOp) === "object" && !!maybeFileOp && "path" in maybeFileOp && "source" in maybeFileOp && typeof maybeFileOp.path === "string" && typeof maybeFileOp.source === "string";
1574
+ }
1381
1575
 
1382
1576
  function main() {
1383
1577
  return _main.apply(this, arguments);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@valbuild/init",
3
- "version": "0.60.18",
3
+ "version": "0.60.20",
4
4
  "description": "Initialize a new val.build project",
5
5
  "exports": {
6
6
  "./main": {
package/src/init.ts CHANGED
@@ -9,6 +9,7 @@ import jcs from "jscodeshift";
9
9
  import semver from "semver";
10
10
  import packageJson from "../package.json";
11
11
  import {
12
+ BASIC_EXAMPLE,
12
13
  VAL_API_ROUTER,
13
14
  VAL_APP_PAGE,
14
15
  VAL_CLIENT,
@@ -26,7 +27,13 @@ export async function init(
26
27
  root: string = process.cwd(),
27
28
  { yes: defaultAnswers }: { yes?: boolean } = {}
28
29
  ) {
29
- logger.info('Initializing Val in "' + root + '"...\n');
30
+ logger.info(
31
+ "Initializing " +
32
+ chalk.bgBlack.hex("#37cd99")("Val") +
33
+ ' in "' +
34
+ root +
35
+ '"...\n'
36
+ );
30
37
  process.stdout.write("Analyzing project...");
31
38
  const analysis = await analyze(path.resolve(root), walk(path.resolve(root)));
32
39
  // reset cursor:
@@ -96,6 +103,12 @@ type Analysis = Partial<{
96
103
  hasGit: boolean;
97
104
  isGitHub: boolean;
98
105
  isGitClean: boolean | "packages";
106
+ gitIgnorePath: string;
107
+ gitIgnoreFile?: string;
108
+ gitRemote: {
109
+ owner: string;
110
+ repo: string;
111
+ };
99
112
 
100
113
  // TODO:
101
114
  // check if modules are used
@@ -208,9 +221,26 @@ const analyze = async (root: string, files: string[]): Promise<Analysis> => {
208
221
  ? !!gitRemoteOrigin.includes("github.com")
209
222
  : false;
210
223
  analysis.isGitClean = getGitStatusIsClean(gitStatus);
224
+ // get owner and repo from git remote:
225
+ if (gitRemoteOrigin) {
226
+ // Split the URL by colon
227
+ const parts = gitRemoteOrigin.split(":");
228
+
229
+ // Extract owner and repo
230
+ const owner = parts[0].split("@")[1];
231
+ const repo = parts[1].replace(".git", ""); // Remove .git extension if present
232
+
233
+ analysis.gitRemote = {
234
+ owner,
235
+ repo,
236
+ };
237
+ }
211
238
  } catch (err) {
212
239
  // console.error(err);
213
240
  }
241
+ const gitIgnorePath = path.join(root, ".gitignore");
242
+ analysis.gitIgnorePath = gitIgnorePath;
243
+ analysis.gitIgnoreFile = fs.readFileSync(gitIgnorePath, "utf-8");
214
244
  return analysis;
215
245
  };
216
246
 
@@ -232,6 +262,15 @@ type Plan = Partial<{
232
262
  useJavascript: boolean;
233
263
  abort: boolean;
234
264
  ignoreGitDirty: boolean;
265
+ updateGitIgnore: false | FileOp;
266
+ gitRemote:
267
+ | false
268
+ | {
269
+ owner: string;
270
+ repo: string;
271
+ };
272
+ includeExample: false | FileOp;
273
+ updateVSCodeSettings: false | FileOp;
235
274
  }>;
236
275
 
237
276
  async function plan(
@@ -255,7 +294,7 @@ async function plan(
255
294
  return { abort: true };
256
295
  }
257
296
  if (analysis.srcDir) {
258
- logger.info(" Source dir: " + analysis.root, { isGood: true });
297
+ logger.info(" Source dir: " + analysis.srcDir, { isGood: true });
259
298
  } else {
260
299
  logger.error("Failed to determine source directory");
261
300
  return { abort: true };
@@ -610,6 +649,113 @@ async function plan(
610
649
  }
611
650
  }
612
651
 
652
+ {
653
+ if (analysis.gitIgnorePath) {
654
+ const answer = await confirm({
655
+ message: "Append .gitignore entry for Val cache? (recommended)",
656
+ default: true,
657
+ });
658
+ if (answer) {
659
+ plan.updateGitIgnore = {
660
+ path: analysis.gitIgnorePath,
661
+ source:
662
+ (analysis.gitIgnoreFile ? `${analysis.gitIgnoreFile}\n\n` : "") +
663
+ "# Val local cache\n.val\n",
664
+ };
665
+ } else {
666
+ plan.updateGitIgnore = false;
667
+ }
668
+ } else {
669
+ plan.updateGitIgnore = false;
670
+ }
671
+ }
672
+ {
673
+ const answer = await confirm({
674
+ message: "Add the Val Build IntelliSense to .vscode/extensions.json?",
675
+ default: true,
676
+ });
677
+ if (answer) {
678
+ const vscodeDir = path.join(analysis.root, ".vscode");
679
+ const settingsPath = path.join(vscodeDir, "extensions.json");
680
+ let currentSettings = {};
681
+
682
+ try {
683
+ const currentSettingsFile = fs.readFileSync(settingsPath, "utf-8");
684
+ if (currentSettingsFile) {
685
+ try {
686
+ currentSettings = JSON.parse(currentSettingsFile);
687
+ } catch (err) {
688
+ logger.warn(
689
+ `Failed to parse VS Code extensions.json found here: ${settingsPath}.${
690
+ err instanceof Error ? `Parse error: ${err.message}` : ""
691
+ }`
692
+ );
693
+ return {
694
+ abort: true,
695
+ };
696
+ }
697
+ }
698
+ } catch {
699
+ // ignore - dir does not exist (most likely)
700
+ }
701
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
702
+ const currentRecommendations: string[] = (currentSettings as any)
703
+ .recommendations;
704
+ const valBuildIntelliSense = "valbuild.vscode-val-build";
705
+ if (!currentRecommendations.includes(valBuildIntelliSense)) {
706
+ currentSettings = {
707
+ ...currentSettings,
708
+ recommendations: (currentRecommendations || []).concat(
709
+ valBuildIntelliSense
710
+ ),
711
+ };
712
+ }
713
+ plan.updateVSCodeSettings = {
714
+ path: settingsPath,
715
+ source: JSON.stringify(currentSettings, null, 2),
716
+ };
717
+ } else {
718
+ plan.updateVSCodeSettings = false;
719
+ }
720
+ }
721
+
722
+ {
723
+ const answer = await confirm({
724
+ message: "Include example Val files?",
725
+ default: true,
726
+ });
727
+ if (answer) {
728
+ const exampleDir = path.join(analysis.srcDir, "examples", "val");
729
+ const examplePath = path.join(
730
+ exampleDir,
731
+ "example.val." + (analysis.isJavaScript ? "js" : "ts")
732
+ );
733
+ const exampleImport = path
734
+ .relative(exampleDir, valConfigPath)
735
+ .replace(".js", "")
736
+ .replace(".ts", "");
737
+ if (!analysis.packageJsonDir) {
738
+ throw Error(
739
+ "Could not detect package.json directory! This is a Val bug."
740
+ );
741
+ }
742
+ const exampleModuleId = `/${path
743
+ .relative(analysis.packageJsonDir, examplePath)
744
+ .replace(".val", "")
745
+ .replace(".js", "")
746
+ .replace(".ts", "")}`;
747
+
748
+ plan.includeExample = {
749
+ path: examplePath,
750
+ source: BASIC_EXAMPLE(
751
+ exampleModuleId,
752
+ exampleImport,
753
+ !!analysis.isJavaScript
754
+ ),
755
+ };
756
+ }
757
+ }
758
+
613
759
  return plan;
614
760
  }
615
761
 
@@ -621,17 +767,48 @@ async function execute(plan: Plan) {
621
767
  return logger.error("Failed to find root directory");
622
768
  }
623
769
  logger.info("Executing...");
624
- for (const [key, fileOp] of Object.entries(plan)) {
625
- writeFile(fileOp, plan.root, key.startsWith("update"));
770
+ for (const [key, maybeFileOp] of Object.entries(plan)) {
771
+ if (isFileOp(maybeFileOp)) {
772
+ writeFile(maybeFileOp, plan.root, key.startsWith("update"));
773
+ }
626
774
  }
775
+ logger.info(`
776
+
777
+ Val was successfully initialized!
778
+
779
+ Start the application:
780
+
781
+ $ ${chalk.gray("npm run dev")}
782
+
783
+ Open (assumes http://localhost:3000):
784
+
785
+ ${chalk.bgBlack.hex("#37cd99").underline(`http://localhost:3000/val`)}
786
+
787
+ When you want to enable editor support, import the project by opening the following link:
788
+
789
+ ${chalk.bgBlack
790
+ .hex("#37cd99")
791
+ .underline(
792
+ `https://app.val.build/orgs/new${
793
+ plan.gitRemote
794
+ ? `?org=${encodeURIComponent(
795
+ plan.gitRemote.owner
796
+ )}&owner=${encodeURIComponent(
797
+ plan.gitRemote.owner
798
+ )}&repo=${encodeURIComponent(plan.gitRemote.repo)}`
799
+ : ""
800
+ }`
801
+ )}
802
+
803
+ `);
627
804
  }
628
805
 
629
806
  function writeFile(
630
- fileOp: string | FileOp | undefined | boolean,
807
+ fileOp: FileOp | undefined,
631
808
  rootDir: string,
632
809
  isUpdate: boolean
633
810
  ) {
634
- if (fileOp && typeof fileOp !== "boolean" && typeof fileOp !== "string") {
811
+ if (fileOp) {
635
812
  fs.mkdirSync(path.dirname(fileOp.path), { recursive: true });
636
813
  fs.writeFileSync(fileOp.path, fileOp.source);
637
814
  logger.info(
@@ -667,3 +844,16 @@ function getGitStatusIsClean(gitStatus: StatusResult): Analysis["isGitClean"] {
667
844
  }
668
845
  return false;
669
846
  }
847
+
848
+ function isFileOp(maybeFileOp: unknown): maybeFileOp is FileOp {
849
+ return (
850
+ typeof maybeFileOp !== "boolean" &&
851
+ typeof maybeFileOp !== "string" &&
852
+ typeof maybeFileOp === "object" &&
853
+ !!maybeFileOp &&
854
+ "path" in maybeFileOp &&
855
+ "source" in maybeFileOp &&
856
+ typeof maybeFileOp.path === "string" &&
857
+ typeof maybeFileOp.source === "string"
858
+ );
859
+ }
package/src/logger.ts CHANGED
@@ -1,11 +1,11 @@
1
1
  import chalk from "chalk";
2
2
 
3
3
  export function error(message: string) {
4
- console.error(chalk.red("❌ ERROR: ") + message);
4
+ console.error(chalk.red(" ❌ ERROR: ") + message);
5
5
  }
6
6
 
7
7
  export function warn(message: string) {
8
- console.error(chalk.yellow("⚠️ WARN:") + message);
8
+ console.error(chalk.yellow(" ⚠️ WARN:" + message));
9
9
  }
10
10
 
11
11
  export function info(
@@ -17,7 +17,7 @@ export function info(
17
17
  return;
18
18
  }
19
19
  if (opts.isGood) {
20
- console.log(chalk.green("") + message);
20
+ console.log(chalk.hex("#37cd99")(" V") + message);
21
21
  return;
22
22
  }
23
23
  console.log(message);