jsrepo 1.10.2 → 1.12.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 CHANGED
@@ -1,17 +1,17 @@
1
1
  // src/index.ts
2
- import fs12 from "node:fs";
2
+ import fs13 from "node:fs";
3
3
  import { fileURLToPath } from "node:url";
4
4
  import { program as program8 } from "commander";
5
- import path12 from "pathe";
5
+ import path13 from "pathe";
6
6
 
7
7
  // src/commands/add.ts
8
- import fs6 from "node:fs";
8
+ import fs7 from "node:fs";
9
9
  import { cancel, confirm, isCancel, multiselect, outro, spinner as spinner2, text } from "@clack/prompts";
10
10
  import color8 from "chalk";
11
11
  import { Command, program as program2 } from "commander";
12
12
  import { resolveCommand as resolveCommand2 } from "package-manager-detector/commands";
13
13
  import { detect } from "package-manager-detector/detect";
14
- import path6 from "pathe";
14
+ import path7 from "pathe";
15
15
  import * as v5 from "valibot";
16
16
 
17
17
  // src/utils/ascii.ts
@@ -694,10 +694,12 @@ import * as v2 from "valibot";
694
694
  // src/utils/language-support.ts
695
695
  import fs2 from "node:fs";
696
696
  import { builtinModules } from "node:module";
697
+ import { Biome, Distribution } from "@biomejs/js-api";
697
698
  import * as v from "@vue/compiler-sfc";
698
699
  import color2 from "chalk";
699
700
  import { walk } from "estree-walker";
700
701
  import path2 from "pathe";
702
+ import * as prettier from "prettier";
701
703
  import * as sv from "svelte/compiler";
702
704
  import { Project } from "ts-morph";
703
705
  import validatePackageName from "validate-npm-package-name";
@@ -749,9 +751,9 @@ var findNearestPackageJson = (startDir, until) => {
749
751
  const segments = startDir.split(/[\/\\]/);
750
752
  return findNearestPackageJson(segments.slice(0, segments.length - 1).join("/"), until);
751
753
  };
752
- var getPackage = (path13) => {
753
- if (!fs.existsSync(path13)) return Err(`${path13} doesn't exist`);
754
- const contents = fs.readFileSync(path13).toString();
754
+ var getPackage = (path14) => {
755
+ if (!fs.existsSync(path14)) return Err(`${path14} doesn't exist`);
756
+ const contents = fs.readFileSync(path14).toString();
755
757
  return Ok(JSON.parse(contents));
756
758
  };
757
759
  var returnShouldInstall = (dependencies, devDependencies, { cwd }) => {
@@ -800,7 +802,7 @@ var parsePackageName = (input) => {
800
802
  // src/utils/language-support.ts
801
803
  var typescript = {
802
804
  matches: (fileName) => fileName.endsWith(".ts") || fileName.endsWith(".js") || fileName.endsWith(".tsx") || fileName.endsWith(".jsx"),
803
- resolveDependencies: ({ filePath, category, isSubDir, excludeDeps }) => {
805
+ resolveDependencies: ({ filePath, isSubDir, excludeDeps, cwd }) => {
804
806
  const project = new Project();
805
807
  const blockFile = project.addSourceFileAtPath(filePath);
806
808
  const imports = blockFile.getImportDeclarations();
@@ -810,8 +812,9 @@ var typescript = {
810
812
  const localDeps = /* @__PURE__ */ new Set();
811
813
  for (const relativeImport of relativeImports) {
812
814
  const mod = relativeImport.getModuleSpecifierValue();
813
- const localDep = resolveLocalImport(mod, category, isSubDir);
814
- if (localDep) localDeps.add(localDep);
815
+ const localDep = resolveLocalImport(mod, isSubDir, { filePath, cwd });
816
+ if (localDep.isErr()) return Err(localDep.unwrapErr());
817
+ if (localDep) localDeps.add(localDep.unwrap());
815
818
  }
816
819
  const deps = imports.filter((declaration) => !declaration.getModuleSpecifierValue().startsWith(".")).map((declaration) => declaration.getModuleSpecifierValue());
817
820
  const { devDependencies, dependencies } = resolveRemoteDeps(
@@ -826,14 +829,27 @@ var typescript = {
826
829
  });
827
830
  },
828
831
  comment: (content) => `/*
829
- ${content}
830
- */`
832
+ ${join(get(content), { prefix: () => " " })}
833
+ */`,
834
+ format: async (code, { formatter, filePath, prettierOptions, biomeOptions }) => {
835
+ if (!formatter) return code;
836
+ if (formatter === "prettier") {
837
+ return await prettier.format(code, { filepath: filePath, ...prettierOptions });
838
+ }
839
+ const biome = await Biome.create({
840
+ distribution: Distribution.NODE
841
+ });
842
+ if (biomeOptions) {
843
+ biome.applyConfiguration(biomeOptions);
844
+ }
845
+ return biome.formatContent(code, { filePath }).content;
846
+ }
831
847
  };
832
848
  var svelte = {
833
849
  matches: (fileName) => fileName.endsWith(".svelte"),
834
- resolveDependencies: ({ filePath, category, isSubDir, excludeDeps }) => {
850
+ resolveDependencies: ({ filePath, isSubDir, excludeDeps, cwd }) => {
835
851
  const sourceCode = fs2.readFileSync(filePath).toString();
836
- const root = sv.parse(sourceCode, { modern: true });
852
+ const root = sv.parse(sourceCode, { modern: true, filename: filePath });
837
853
  if (!root.instance) return Ok({ dependencies: [], devDependencies: [], local: [] });
838
854
  const localDeps = /* @__PURE__ */ new Set();
839
855
  const deps = /* @__PURE__ */ new Set();
@@ -842,12 +858,12 @@ var svelte = {
842
858
  if (node.type === "ImportDeclaration") {
843
859
  if (typeof node.source.value === "string") {
844
860
  if (node.source.value.startsWith(".")) {
845
- const localDep = resolveLocalImport(
846
- node.source.value,
847
- category,
848
- isSubDir
849
- );
850
- if (localDep) localDeps.add(localDep);
861
+ const localDep = resolveLocalImport(node.source.value, isSubDir, {
862
+ filePath,
863
+ cwd
864
+ });
865
+ if (localDep.isErr()) return Err(localDep.unwrapErr());
866
+ if (localDep) localDeps.add(localDep.unwrap());
851
867
  } else {
852
868
  deps.add(node.source.value);
853
869
  }
@@ -866,25 +882,41 @@ var svelte = {
866
882
  });
867
883
  },
868
884
  comment: (content) => `<!--
869
- ${content}
870
- -->`
885
+ ${join(get(content), { prefix: () => " " })}
886
+ -->`,
887
+ format: async (code, { formatter, filePath, prettierOptions }) => {
888
+ if (!formatter) return code;
889
+ if (formatter === "prettier" && prettierOptions && prettierOptions.plugins?.find((plugin) => plugin === "prettier-plugin-svelte")) {
890
+ return await prettier.format(code, { filepath: filePath, ...prettierOptions });
891
+ }
892
+ return code;
893
+ }
871
894
  };
872
895
  var vue = {
873
896
  matches: (fileName) => fileName.endsWith(".vue"),
874
- resolveDependencies: ({ filePath, category, isSubDir, excludeDeps }) => {
897
+ resolveDependencies: ({ filePath, isSubDir, excludeDeps, cwd }) => {
875
898
  const sourceCode = fs2.readFileSync(filePath).toString();
876
- const parsed = v.parse(sourceCode);
899
+ const parsed = v.parse(sourceCode, { filename: filePath });
877
900
  if (!parsed.descriptor.script?.content && !parsed.descriptor.scriptSetup?.content)
878
901
  return Ok({ dependencies: [], devDependencies: [], local: [] });
879
902
  const localDeps = /* @__PURE__ */ new Set();
880
903
  const deps = /* @__PURE__ */ new Set();
881
- const compiled = v.compileScript(parsed.descriptor, { id: "shut-it" });
904
+ let compiled;
905
+ try {
906
+ compiled = v.compileScript(parsed.descriptor, { id: "shut-it" });
907
+ } catch (err) {
908
+ return Err(`Compile error: ${err}`);
909
+ }
882
910
  if (!compiled.imports) return Ok({ dependencies: [], devDependencies: [], local: [] });
883
911
  const imports = Object.values(compiled.imports);
884
912
  for (const imp of imports) {
885
913
  if (imp.source.startsWith(".")) {
886
- const localDep = resolveLocalImport(imp.source, category, isSubDir);
887
- if (localDep) localDeps.add(localDep);
914
+ const localDep = resolveLocalImport(imp.source, isSubDir, {
915
+ filePath,
916
+ cwd
917
+ });
918
+ if (localDep.isErr()) return Err(localDep.unwrapErr());
919
+ if (localDep) localDeps.add(localDep.unwrap());
888
920
  } else {
889
921
  deps.add(imp.source);
890
922
  }
@@ -900,25 +932,43 @@ var vue = {
900
932
  });
901
933
  },
902
934
  comment: (content) => `<!--
903
- ${content}
904
- -->`
935
+ ${join(get(content), { prefix: () => " " })}
936
+ -->`,
937
+ format: async (code, { formatter, prettierOptions }) => {
938
+ if (!formatter) return code;
939
+ if (formatter === "prettier") {
940
+ return await prettier.format(code, { parser: "vue", ...prettierOptions });
941
+ }
942
+ return code;
943
+ }
905
944
  };
906
945
  var yaml = {
907
946
  matches: (fileName) => fileName.endsWith(".yml") || fileName.endsWith(".yaml"),
908
947
  resolveDependencies: () => Ok({ dependencies: [], local: [], devDependencies: [] }),
909
- comment: (content) => join(get(content), { prefix: () => "# " })
910
- };
911
- var resolveLocalImport = (mod, category, isSubDir) => {
912
- if (isSubDir && mod.startsWith("./")) return void 0;
913
- if (mod.startsWith("./")) {
914
- return `${category}/${path2.parse(path2.basename(mod)).name}`;
948
+ comment: (content) => join(get(content), { prefix: () => "# " }),
949
+ format: async (code, { formatter, prettierOptions }) => {
950
+ if (!formatter) return code;
951
+ if (formatter === "prettier") {
952
+ return await prettier.format(code, { parser: "yaml", ...prettierOptions });
953
+ }
954
+ return code;
915
955
  }
916
- if (isSubDir && mod.startsWith("../") && !mod.startsWith("../.")) {
917
- return `${category}/${path2.parse(path2.basename(mod)).name}`;
956
+ };
957
+ var resolveLocalImport = (mod, isSubDir, { filePath, cwd }) => {
958
+ const categoryDir = isSubDir ? path2.join(filePath, "../../") : path2.join(filePath, "../");
959
+ const modPath = path2.join(path2.join(filePath, "../"), mod);
960
+ const fullDir = path2.join(cwd, path2.join(categoryDir, "../"));
961
+ if (modPath.startsWith(fullDir)) {
962
+ let [category, block] = modPath.slice(fullDir.length).split("/");
963
+ if (block.includes(".")) {
964
+ block = block.slice(0, block.length - path2.parse(block).ext.length);
965
+ }
966
+ return Ok(`${category}/${block}`);
918
967
  }
919
- const segments = mod.replaceAll("../", "").split("/");
920
- if (segments.length < 2) return void 0;
921
- return `${segments[0]}/${segments[1]}`;
968
+ return Err(
969
+ `${filePath}:
970
+ ${mod} references code not contained in ${categoryDir} and cannot be resolved.`
971
+ );
922
972
  };
923
973
  var resolveRemoteDeps = (deps, filePath, doNotInstall = []) => {
924
974
  const exemptDeps = new Set(doNotInstall);
@@ -1043,7 +1093,8 @@ var buildBlocksDirectory = (blocksPath, { cwd, excludeDeps, includeBlocks, inclu
1043
1093
  filePath: blockDir,
1044
1094
  category: categoryName,
1045
1095
  isSubDir: false,
1046
- excludeDeps
1096
+ excludeDeps,
1097
+ cwd
1047
1098
  }).match(
1048
1099
  (val) => val,
1049
1100
  (err) => {
@@ -1115,7 +1166,8 @@ var buildBlocksDirectory = (blocksPath, { cwd, excludeDeps, includeBlocks, inclu
1115
1166
  filePath: path3.join(blockDir, f),
1116
1167
  category: categoryName,
1117
1168
  isSubDir: true,
1118
- excludeDeps
1169
+ excludeDeps,
1170
+ cwd
1119
1171
  }).match(
1120
1172
  (val) => val,
1121
1173
  (err) => {
@@ -1469,12 +1521,14 @@ import fs5 from "node:fs";
1469
1521
  import path5 from "pathe";
1470
1522
  import * as v4 from "valibot";
1471
1523
  var CONFIG_NAME = "jsrepo.json";
1524
+ var formatterSchema = v4.union([v4.literal("prettier"), v4.literal("biome")]);
1472
1525
  var schema = v4.object({
1473
1526
  $schema: v4.string(),
1474
1527
  repos: v4.optional(v4.array(v4.string()), []),
1475
1528
  includeTests: v4.boolean(),
1476
1529
  path: v4.pipe(v4.string(), v4.minLength(1)),
1477
- watermark: v4.optional(v4.boolean(), true)
1530
+ watermark: v4.optional(v4.boolean(), true),
1531
+ formatter: v4.optional(formatterSchema)
1478
1532
  });
1479
1533
  var getConfig = (cwd) => {
1480
1534
  if (!fs5.existsSync(path5.join(cwd, CONFIG_NAME))) {
@@ -1521,11 +1575,36 @@ var installDependencies = async ({
1521
1575
  }
1522
1576
  };
1523
1577
 
1578
+ // src/utils/format.ts
1579
+ import fs6 from "node:fs";
1580
+ import path6 from "pathe";
1581
+ import * as prettier2 from "prettier";
1582
+ var loadFormatterConfig = async ({
1583
+ formatter,
1584
+ cwd
1585
+ }) => {
1586
+ let prettierOptions = null;
1587
+ if (formatter === "prettier") {
1588
+ prettierOptions = await prettier2.resolveConfig(path6.join(cwd, ".prettierrc"));
1589
+ }
1590
+ let biomeOptions = null;
1591
+ if (formatter === "biome") {
1592
+ const configPath = path6.join(cwd, "biome.json");
1593
+ if (fs6.existsSync(configPath)) {
1594
+ biomeOptions = JSON.parse(fs6.readFileSync(configPath).toString());
1595
+ }
1596
+ }
1597
+ return {
1598
+ biomeOptions,
1599
+ prettierOptions
1600
+ };
1601
+ };
1602
+
1524
1603
  // src/utils/get-watermark.ts
1525
1604
  var getWatermark = (version2, repoUrl) => {
1526
- return ` jsrepo ${version2}
1527
- Installed from ${repoUrl}
1528
- ${(/* @__PURE__ */ new Date()).toLocaleDateString().replaceAll("/", "-")}`;
1605
+ return `jsrepo ${version2}
1606
+ Installed from ${repoUrl}
1607
+ ${(/* @__PURE__ */ new Date()).toLocaleDateString().replaceAll("/", "-")}`;
1529
1608
  };
1530
1609
 
1531
1610
  // src/utils/prompts.ts
@@ -1769,13 +1848,17 @@ var _add = async (blockNames, options) => {
1769
1848
  config.watermark = addWatermark;
1770
1849
  }
1771
1850
  }
1851
+ const { prettierOptions, biomeOptions } = await loadFormatterConfig({
1852
+ formatter: config.formatter,
1853
+ cwd: options.cwd
1854
+ });
1772
1855
  for (const { block } of installingBlocks) {
1773
1856
  const fullSpecifier = `${block.sourceRepo.url}/${block.category}/${block.name}`;
1774
1857
  const watermark = getWatermark(context.package.version, block.sourceRepo.url);
1775
1858
  const providerInfo = block.sourceRepo;
1776
1859
  verbose(`Setting up ${fullSpecifier}`);
1777
- const directory = path6.join(options.cwd, config.path, block.category);
1778
- const blockExists = !block.subdirectory && fs6.existsSync(path6.join(directory, block.files[0])) || block.subdirectory && fs6.existsSync(path6.join(directory, block.name));
1860
+ const directory = path7.join(options.cwd, config.path, block.category);
1861
+ const blockExists = !block.subdirectory && fs7.existsSync(path7.join(directory, block.files[0])) || block.subdirectory && fs7.existsSync(path7.join(directory, block.name));
1779
1862
  if (blockExists && !options.yes) {
1780
1863
  const result = await confirm({
1781
1864
  message: `${color8.bold(block.name)} already exists in your project would you like to overwrite it?`,
@@ -1791,7 +1874,7 @@ var _add = async (blockNames, options) => {
1791
1874
  completedMessage: `Added ${fullSpecifier}`,
1792
1875
  run: async () => {
1793
1876
  verbose(`Creating directory ${color8.bold(directory)}`);
1794
- fs6.mkdirSync(directory, { recursive: true });
1877
+ fs7.mkdirSync(directory, { recursive: true });
1795
1878
  const files = [];
1796
1879
  const getSourceFile = async (filePath) => {
1797
1880
  const content = await providerInfo.provider.fetchRaw(providerInfo, filePath);
@@ -1805,36 +1888,42 @@ var _add = async (blockNames, options) => {
1805
1888
  };
1806
1889
  for (const sourceFile of block.files) {
1807
1890
  if (!config.includeTests && isTestFile(sourceFile)) continue;
1808
- const sourcePath = path6.join(block.directory, sourceFile);
1891
+ const sourcePath = path7.join(block.directory, sourceFile);
1809
1892
  let destPath;
1810
1893
  if (block.subdirectory) {
1811
- destPath = path6.join(directory, block.name, sourceFile);
1894
+ destPath = path7.join(directory, block.name, sourceFile);
1812
1895
  } else {
1813
- destPath = path6.join(directory, sourceFile);
1896
+ destPath = path7.join(directory, sourceFile);
1814
1897
  }
1815
1898
  const content = await getSourceFile(sourcePath);
1816
- fs6.mkdirSync(destPath.slice(0, destPath.length - sourceFile.length), {
1899
+ fs7.mkdirSync(destPath.slice(0, destPath.length - sourceFile.length), {
1817
1900
  recursive: true
1818
1901
  });
1819
1902
  files.push({ content, destPath });
1820
1903
  }
1821
1904
  for (const file of files) {
1905
+ const lang = languages.find((lang2) => lang2.matches(file.destPath));
1822
1906
  let content = file.content;
1823
- if (config.watermark) {
1824
- const lang = languages.find((lang2) => lang2.matches(file.destPath));
1825
- if (lang) {
1907
+ if (lang) {
1908
+ if (config.watermark) {
1826
1909
  const comment = lang.comment(watermark);
1827
1910
  content = `${comment}
1828
1911
 
1829
1912
  ${content}`;
1830
1913
  }
1914
+ content = await lang.format(content, {
1915
+ filePath: file.destPath,
1916
+ formatter: config.formatter,
1917
+ prettierOptions,
1918
+ biomeOptions
1919
+ });
1831
1920
  }
1832
- fs6.writeFileSync(file.destPath, content);
1921
+ fs7.writeFileSync(file.destPath, content);
1833
1922
  }
1834
1923
  if (config.includeTests) {
1835
1924
  verbose("Trying to include tests");
1836
1925
  const { devDependencies } = JSON.parse(
1837
- fs6.readFileSync(path6.join(options.cwd, "package.json")).toString()
1926
+ fs7.readFileSync(path7.join(options.cwd, "package.json")).toString()
1838
1927
  );
1839
1928
  if (devDependencies.vitest === void 0) {
1840
1929
  devDeps.add("vitest");
@@ -2016,11 +2105,11 @@ var _auth = async (options) => {
2016
2105
  };
2017
2106
 
2018
2107
  // src/commands/build.ts
2019
- import fs7 from "node:fs";
2108
+ import fs8 from "node:fs";
2020
2109
  import { outro as outro3, spinner as spinner3 } from "@clack/prompts";
2021
2110
  import color10 from "chalk";
2022
2111
  import { Command as Command3, program as program3 } from "commander";
2023
- import path7 from "pathe";
2112
+ import path8 from "pathe";
2024
2113
  import * as v7 from "valibot";
2025
2114
  var schema4 = v7.object({
2026
2115
  dirs: v7.array(v7.string()),
@@ -2049,11 +2138,11 @@ var build = new Command3("build").description(`Builds the provided --dirs in the
2049
2138
  var _build = async (options) => {
2050
2139
  const loading = spinner3();
2051
2140
  const categories = [];
2052
- const outFile = path7.join(options.cwd, OUTPUT_FILE);
2141
+ const outFile = path8.join(options.cwd, OUTPUT_FILE);
2053
2142
  for (const dir of options.dirs) {
2054
- const dirPath = path7.join(options.cwd, dir);
2143
+ const dirPath = path8.join(options.cwd, dir);
2055
2144
  loading.start(`Building ${color10.cyan(dirPath)}`);
2056
- if (options.output && fs7.existsSync(outFile)) fs7.rmSync(outFile);
2145
+ if (options.output && fs8.existsSync(outFile)) fs8.rmSync(outFile);
2057
2146
  const builtCategories = buildBlocksDirectory(dirPath, { ...options });
2058
2147
  for (const category of builtCategories) {
2059
2148
  if (categories.find((cat) => cat.name === category.name) !== void 0) {
@@ -2127,18 +2216,18 @@ var _build = async (options) => {
2127
2216
  }
2128
2217
  if (options.output) {
2129
2218
  loading.start(`Writing output to \`${color10.cyan(outFile)}\``);
2130
- fs7.writeFileSync(outFile, JSON.stringify(categories, null, " "));
2219
+ fs8.writeFileSync(outFile, JSON.stringify(categories, null, " "));
2131
2220
  loading.stop(`Wrote output to \`${color10.cyan(outFile)}\``);
2132
2221
  }
2133
2222
  };
2134
2223
 
2135
2224
  // src/commands/diff.ts
2136
- import fs8 from "node:fs";
2225
+ import fs9 from "node:fs";
2137
2226
  import { cancel as cancel3, confirm as confirm3, isCancel as isCancel3, outro as outro4, spinner as spinner4 } from "@clack/prompts";
2138
2227
  import color12 from "chalk";
2139
2228
  import { Command as Command4, program as program4 } from "commander";
2140
2229
  import { diffLines } from "diff";
2141
- import path8 from "pathe";
2230
+ import path9 from "pathe";
2142
2231
  import * as v8 from "valibot";
2143
2232
 
2144
2233
  // src/utils/diff.ts
@@ -2381,22 +2470,22 @@ var _diff = async (options) => {
2381
2470
  if (!config.includeTests && isTestFile(file)) continue;
2382
2471
  process.stdout.write(`${VERTICAL_LINE}
2383
2472
  `);
2384
- const sourcePath = path8.join(block.directory, file);
2473
+ const sourcePath = path9.join(block.directory, file);
2385
2474
  const response = await providerInfo.provider.fetchRaw(providerInfo, sourcePath);
2386
2475
  if (response.isErr()) {
2387
2476
  program4.error(color12.red(`There was an error trying to get ${fullSpecifier}`));
2388
2477
  }
2389
2478
  let remoteContent = response.unwrap();
2390
- const directory = path8.join(options.cwd, config.path, block.category);
2391
- let localPath = path8.join(directory, file);
2392
- let prettyLocalPath = path8.join(config.path, block.category, file);
2479
+ const directory = path9.join(options.cwd, config.path, block.category);
2480
+ let localPath = path9.join(directory, file);
2481
+ let prettyLocalPath = path9.join(config.path, block.category, file);
2393
2482
  if (block.subdirectory) {
2394
- localPath = path8.join(directory, block.name, file);
2395
- prettyLocalPath = path8.join(config.path, block.category, block.name, file);
2483
+ localPath = path9.join(directory, block.name, file);
2484
+ prettyLocalPath = path9.join(config.path, block.category, block.name, file);
2396
2485
  }
2397
2486
  let fileContent = "";
2398
- if (fs8.existsSync(localPath)) {
2399
- fileContent = fs8.readFileSync(localPath).toString();
2487
+ if (fs9.existsSync(localPath)) {
2488
+ fileContent = fs9.readFileSync(localPath).toString();
2400
2489
  }
2401
2490
  if (config.watermark) {
2402
2491
  const lang = languages.find((lang2) => lang2.matches(sourcePath));
@@ -2408,7 +2497,7 @@ ${remoteContent}`;
2408
2497
  }
2409
2498
  }
2410
2499
  const changes = diffLines(fileContent, remoteContent);
2411
- const from = path8.join(
2500
+ const from = path9.join(
2412
2501
  `${providerInfo.name}/${providerInfo.owner}/${providerInfo.repoName}`,
2413
2502
  sourcePath
2414
2503
  );
@@ -2445,18 +2534,19 @@ ${prefix?.() ?? ""}
2445
2534
  };
2446
2535
 
2447
2536
  // src/commands/init.ts
2448
- import fs9 from "node:fs";
2537
+ import fs10 from "node:fs";
2449
2538
  import { cancel as cancel4, confirm as confirm4, isCancel as isCancel4, outro as outro5, password as password2, select as select2, spinner as spinner5, text as text2 } from "@clack/prompts";
2450
2539
  import color13 from "chalk";
2451
- import { Command as Command5, program as program5 } from "commander";
2540
+ import { Command as Command5, Option as Option2, program as program5 } from "commander";
2452
2541
  import { detect as detect2, resolveCommand as resolveCommand3 } from "package-manager-detector";
2453
- import path9 from "pathe";
2542
+ import path10 from "pathe";
2454
2543
  import * as v9 from "valibot";
2455
2544
  var schema6 = v9.object({
2456
2545
  path: v9.optional(v9.string()),
2457
2546
  repos: v9.optional(v9.array(v9.string())),
2458
2547
  watermark: v9.boolean(),
2459
2548
  tests: v9.optional(v9.boolean()),
2549
+ formatter: v9.optional(formatterSchema),
2460
2550
  project: v9.optional(v9.boolean()),
2461
2551
  registry: v9.optional(v9.boolean()),
2462
2552
  script: v9.string(),
@@ -2466,7 +2556,12 @@ var schema6 = v9.object({
2466
2556
  var init = new Command5("init").description("Initializes your project with a configuration file.").option("--path <path>", "Path to install the blocks / Path to build the blocks from.").option("--repos [repos...]", "Repository to install the blocks from.").option(
2467
2557
  "--no-watermark",
2468
2558
  "Will not add a watermark to each file upon adding it to your project."
2469
- ).option("--tests", "Will include tests with the blocks.").option("-P, --project", "Takes you through the steps to initialize a project.").option("-R, --registry", "Takes you through the steps to initialize a registry.").option("--script <name>", "The name of the build script. (For Registry setup)", "build").option("-y, --yes", "Skip confirmation prompt.", false).option("--cwd <path>", "The current working directory.", process.cwd()).action(async (opts) => {
2559
+ ).option("--tests", "Will include tests with the blocks.").addOption(
2560
+ new Option2(
2561
+ "--formatter <formatter>",
2562
+ "What formatter to use when adding or updating blocks."
2563
+ ).choices(["prettier", "biome"])
2564
+ ).option("-P, --project", "Takes you through the steps to initialize a project.").option("-R, --registry", "Takes you through the steps to initialize a registry.").option("--script <name>", "The name of the build script. (For Registry setup)", "build").option("-y, --yes", "Skip confirmation prompt.", false).option("--cwd <path>", "The current working directory.", process.cwd()).action(async (opts) => {
2470
2565
  const options = v9.parse(schema6, opts);
2471
2566
  _intro(context.package.version);
2472
2567
  if (options.registry !== void 0 && options.project !== void 0) {
@@ -2575,26 +2670,51 @@ var _initProject = async (options) => {
2575
2670
  options.repos.push(result);
2576
2671
  }
2577
2672
  }
2673
+ if (!options.formatter) {
2674
+ let defaultFormatter = initialConfig.isErr() ? "none" : initialConfig.unwrap().formatter ?? "none";
2675
+ if (fs10.existsSync(path10.join(options.cwd, ".prettierrc"))) {
2676
+ defaultFormatter = "prettier";
2677
+ }
2678
+ if (fs10.existsSync(path10.join(options.cwd, "biome.json"))) {
2679
+ defaultFormatter = "biome";
2680
+ }
2681
+ const response = await select2({
2682
+ message: "What formatter would you like to use?",
2683
+ options: ["Prettier", "Biome", "None"].map((val) => ({
2684
+ value: val.toLowerCase(),
2685
+ label: val
2686
+ })),
2687
+ initialValue: defaultFormatter
2688
+ });
2689
+ if (isCancel4(response)) {
2690
+ cancel4("Canceled!");
2691
+ process.exit(0);
2692
+ }
2693
+ if (response !== "none") {
2694
+ options.formatter = response;
2695
+ }
2696
+ }
2578
2697
  const config = {
2579
2698
  $schema: `https://unpkg.com/jsrepo@${context.package.version}/schema.json`,
2580
2699
  repos: options.repos,
2581
2700
  path: options.path,
2582
2701
  includeTests: initialConfig.isOk() && options.tests === void 0 ? initialConfig.unwrap().includeTests : options.tests ?? false,
2583
- watermark: options.watermark
2702
+ watermark: options.watermark,
2703
+ formatter: options.formatter
2584
2704
  };
2585
2705
  loading.start(`Writing config to \`${CONFIG_NAME}\``);
2586
- fs9.writeFileSync(
2587
- path9.join(options.cwd, CONFIG_NAME),
2706
+ fs10.writeFileSync(
2707
+ path10.join(options.cwd, CONFIG_NAME),
2588
2708
  `${JSON.stringify(config, null, " ")}
2589
2709
  `
2590
2710
  );
2591
- fs9.mkdirSync(path9.join(options.cwd, config.path), { recursive: true });
2711
+ fs10.mkdirSync(path10.join(options.cwd, config.path), { recursive: true });
2592
2712
  loading.stop(`Wrote config to \`${CONFIG_NAME}\`.`);
2593
2713
  };
2594
2714
  var _initRegistry = async (options) => {
2595
2715
  const loading = spinner5();
2596
- const packagePath = path9.join(options.cwd, "package.json");
2597
- if (!fs9.existsSync(packagePath)) {
2716
+ const packagePath = path10.join(options.cwd, "package.json");
2717
+ if (!fs10.existsSync(packagePath)) {
2598
2718
  program5.error(color13.red(`Couldn't find your ${color13.bold("package.json")}!`));
2599
2719
  }
2600
2720
  if (!options.path) {
@@ -2610,7 +2730,7 @@ var _initRegistry = async (options) => {
2610
2730
  }
2611
2731
  options.path = response;
2612
2732
  }
2613
- const pkg = JSON.parse(fs9.readFileSync(packagePath).toString());
2733
+ const pkg = JSON.parse(fs10.readFileSync(packagePath).toString());
2614
2734
  const scriptAlreadyExists = pkg.scripts !== void 0 && pkg.scripts[options.script] !== void 0;
2615
2735
  if (!options.yes && scriptAlreadyExists) {
2616
2736
  const response = await confirm4({
@@ -2669,7 +2789,7 @@ var _initRegistry = async (options) => {
2669
2789
  pkg.scripts[options.script] = buildScript;
2670
2790
  loading.start(`Adding \`${color13.cyan(options.script)}\` to scripts in package.json`);
2671
2791
  try {
2672
- fs9.writeFileSync(packagePath, JSON.stringify(pkg, null, " "));
2792
+ fs10.writeFileSync(packagePath, JSON.stringify(pkg, null, " "));
2673
2793
  } catch (err) {
2674
2794
  program5.error(color13.red(`Error writing to \`${color13.bold(packagePath)}\`. Error: ${err}`));
2675
2795
  }
@@ -2724,14 +2844,14 @@ var _initRegistry = async (options) => {
2724
2844
  };
2725
2845
 
2726
2846
  // src/commands/test.ts
2727
- import fs10 from "node:fs";
2847
+ import fs11 from "node:fs";
2728
2848
  import { cancel as cancel5, confirm as confirm5, isCancel as isCancel5, outro as outro6, spinner as spinner6 } from "@clack/prompts";
2729
2849
  import color14 from "chalk";
2730
2850
  import { Argument, Command as Command6, program as program6 } from "commander";
2731
2851
  import { execa as execa2 } from "execa";
2732
2852
  import { resolveCommand as resolveCommand4 } from "package-manager-detector/commands";
2733
2853
  import { detect as detect3 } from "package-manager-detector/detect";
2734
- import path10 from "pathe";
2854
+ import path11 from "pathe";
2735
2855
  import { Project as Project2 } from "ts-morph";
2736
2856
  import * as v10 from "valibot";
2737
2857
  var schema7 = v10.object({
@@ -2806,13 +2926,13 @@ var _test = async (blockNames, options) => {
2806
2926
  }
2807
2927
  verbose(`Retrieved blocks from ${color14.cyan(repoPaths.join(", "))}`);
2808
2928
  if (!options.verbose) loading.stop(`Retrieved blocks from ${color14.cyan(repoPaths.join(", "))}`);
2809
- const tempTestDirectory = path10.resolve(
2810
- path10.join(options.cwd, `blocks-tests-temp-${Date.now()}`)
2929
+ const tempTestDirectory = path11.resolve(
2930
+ path11.join(options.cwd, `blocks-tests-temp-${Date.now()}`)
2811
2931
  );
2812
2932
  verbose(`Trying to create the temp directory ${color14.bold(tempTestDirectory)}.`);
2813
- fs10.mkdirSync(tempTestDirectory, { recursive: true });
2933
+ fs11.mkdirSync(tempTestDirectory, { recursive: true });
2814
2934
  const cleanUp = () => {
2815
- fs10.rmSync(tempTestDirectory, { recursive: true, force: true });
2935
+ fs11.rmSync(tempTestDirectory, { recursive: true, force: true });
2816
2936
  };
2817
2937
  const installedBlocks = getInstalled(blocksMap, config, options.cwd).map(
2818
2938
  (val) => val.specifier
@@ -2897,9 +3017,9 @@ var _test = async (blockNames, options) => {
2897
3017
  verbose(`Downloading and copying test files for ${fullSpecifier}`);
2898
3018
  const testFiles = [];
2899
3019
  for (const testFile of block.files.filter((file) => isTestFile(file))) {
2900
- const content = await getSourceFile(path10.join(block.directory, testFile));
2901
- const destPath = path10.join(tempTestDirectory, testFile);
2902
- fs10.writeFileSync(destPath, content);
3020
+ const content = await getSourceFile(path11.join(block.directory, testFile));
3021
+ const destPath = path11.join(tempTestDirectory, testFile);
3022
+ fs11.writeFileSync(destPath, content);
2903
3023
  testFiles.push(destPath);
2904
3024
  }
2905
3025
  const project = new Project2();
@@ -2911,7 +3031,7 @@ var _test = async (blockNames, options) => {
2911
3031
  let newModuleSpecifier = void 0;
2912
3032
  if (moduleSpecifier.startsWith(".")) {
2913
3033
  if (block.subdirectory) {
2914
- newModuleSpecifier = path10.join(
3034
+ newModuleSpecifier = path11.join(
2915
3035
  "../",
2916
3036
  config.path,
2917
3037
  block.category,
@@ -2919,7 +3039,7 @@ var _test = async (blockNames, options) => {
2919
3039
  moduleSpecifier
2920
3040
  );
2921
3041
  } else {
2922
- newModuleSpecifier = path10.join(
3042
+ newModuleSpecifier = path11.join(
2923
3043
  "../",
2924
3044
  config.path,
2925
3045
  block.category,
@@ -2975,14 +3095,14 @@ var _test = async (blockNames, options) => {
2975
3095
  };
2976
3096
 
2977
3097
  // src/commands/update.ts
2978
- import fs11 from "node:fs";
3098
+ import fs12 from "node:fs";
2979
3099
  import { cancel as cancel6, confirm as confirm6, isCancel as isCancel6, multiselect as multiselect2, outro as outro7, spinner as spinner7 } from "@clack/prompts";
2980
3100
  import color15 from "chalk";
2981
3101
  import { Command as Command7, program as program7 } from "commander";
2982
3102
  import { diffLines as diffLines2 } from "diff";
2983
3103
  import { resolveCommand as resolveCommand5 } from "package-manager-detector/commands";
2984
3104
  import { detect as detect4 } from "package-manager-detector/detect";
2985
- import path11 from "pathe";
3105
+ import path12 from "pathe";
2986
3106
  import * as v11 from "valibot";
2987
3107
  var schema8 = v11.object({
2988
3108
  all: v11.boolean(),
@@ -3051,6 +3171,13 @@ var _update = async (blockNames, options) => {
3051
3171
  if (!options.verbose) loading.stop(`Retrieved blocks from ${color15.cyan(repoPaths.join(", "))}`);
3052
3172
  verbose(`Retrieved blocks from ${color15.cyan(repoPaths.join(", "))}`);
3053
3173
  const installedBlocks = getInstalled(blocksMap, config, options.cwd);
3174
+ if (installedBlocks.length === 0) {
3175
+ program7.error(
3176
+ color15.red(
3177
+ `You haven't installed any blocks yet. Did you mean to \`${color15.bold("add")}\`?`
3178
+ )
3179
+ );
3180
+ }
3054
3181
  let updatingBlockNames = blockNames;
3055
3182
  if (options.all) {
3056
3183
  updatingBlockNames = installedBlocks.map((block) => block.specifier);
@@ -3081,12 +3208,16 @@ var _update = async (blockNames, options) => {
3081
3208
  const tasks = [];
3082
3209
  let devDeps = /* @__PURE__ */ new Set();
3083
3210
  let deps = /* @__PURE__ */ new Set();
3211
+ const { prettierOptions, biomeOptions } = await loadFormatterConfig({
3212
+ formatter: config.formatter,
3213
+ cwd: options.cwd
3214
+ });
3084
3215
  for (const { block } of updatingBlocks) {
3085
3216
  const fullSpecifier = `${block.sourceRepo.url}/${block.category}/${block.name}`;
3086
3217
  const watermark = getWatermark(context.package.version, block.sourceRepo.url);
3087
3218
  const providerInfo = block.sourceRepo;
3088
3219
  verbose(`Attempting to add ${fullSpecifier}`);
3089
- const directory = path11.join(options.cwd, config.path, block.category);
3220
+ const directory = path12.join(options.cwd, config.path, block.category);
3090
3221
  const files = [];
3091
3222
  const getSourceFile = async (filePath) => {
3092
3223
  const content = await providerInfo.provider.fetchRaw(providerInfo, filePath);
@@ -3098,15 +3229,15 @@ var _update = async (blockNames, options) => {
3098
3229
  };
3099
3230
  for (const sourceFile of block.files) {
3100
3231
  if (!config.includeTests && isTestFile(sourceFile)) continue;
3101
- const sourcePath = path11.join(block.directory, sourceFile);
3232
+ const sourcePath = path12.join(block.directory, sourceFile);
3102
3233
  let destPath;
3103
3234
  if (block.subdirectory) {
3104
- destPath = path11.join(directory, block.name, sourceFile);
3235
+ destPath = path12.join(directory, block.name, sourceFile);
3105
3236
  } else {
3106
- destPath = path11.join(directory, sourceFile);
3237
+ destPath = path12.join(directory, sourceFile);
3107
3238
  }
3108
3239
  const content = await getSourceFile(sourcePath);
3109
- fs11.mkdirSync(destPath.slice(0, destPath.length - sourceFile.length), {
3240
+ fs12.mkdirSync(destPath.slice(0, destPath.length - sourceFile.length), {
3110
3241
  recursive: true
3111
3242
  });
3112
3243
  files.push({ content, destPath, fileName: sourceFile });
@@ -3116,30 +3247,36 @@ var _update = async (blockNames, options) => {
3116
3247
  process.stdout.write(`${VERTICAL_LINE} ${fullSpecifier}
3117
3248
  `);
3118
3249
  for (const file of files) {
3250
+ const lang = languages.find((lang2) => lang2.matches(file.destPath));
3119
3251
  let remoteContent = file.content;
3120
- if (config.watermark) {
3121
- const lang = languages.find((lang2) => lang2.matches(file.destPath));
3122
- if (lang) {
3252
+ if (lang) {
3253
+ if (config.watermark) {
3123
3254
  const comment = lang.comment(watermark);
3124
3255
  remoteContent = `${comment}
3125
3256
 
3126
3257
  ${remoteContent}`;
3127
3258
  }
3259
+ remoteContent = await lang.format(remoteContent, {
3260
+ filePath: file.destPath,
3261
+ formatter: config.formatter,
3262
+ prettierOptions,
3263
+ biomeOptions
3264
+ });
3128
3265
  }
3129
3266
  let acceptedChanges = options.yes;
3130
3267
  if (!options.yes) {
3131
3268
  process.stdout.write(`${VERTICAL_LINE}
3132
3269
  `);
3133
3270
  let localContent = "";
3134
- if (fs11.existsSync(file.destPath)) {
3135
- localContent = fs11.readFileSync(file.destPath).toString();
3271
+ if (fs12.existsSync(file.destPath)) {
3272
+ localContent = fs12.readFileSync(file.destPath).toString();
3136
3273
  }
3137
3274
  const changes = diffLines2(localContent, remoteContent);
3138
- const from = path11.join(
3275
+ const from = path12.join(
3139
3276
  `${providerInfo.name}/${providerInfo.owner}/${providerInfo.repoName}`,
3140
3277
  file.fileName
3141
3278
  );
3142
- const to = path11.relative(options.cwd, file.destPath);
3279
+ const to = path12.relative(options.cwd, file.destPath);
3143
3280
  const formattedDiff = formatDiff({
3144
3281
  from,
3145
3282
  to,
@@ -3179,7 +3316,7 @@ ${prefix?.() ?? ""}
3179
3316
  {
3180
3317
  loadingMessage: `Writing changes to ${color15.cyan(file.destPath)}`,
3181
3318
  completedMessage: `Wrote changes to ${color15.cyan(file.destPath)}.`,
3182
- run: async () => fs11.writeFileSync(file.destPath, remoteContent)
3319
+ run: async () => fs12.writeFileSync(file.destPath, remoteContent)
3183
3320
  }
3184
3321
  ],
3185
3322
  {
@@ -3191,7 +3328,7 @@ ${prefix?.() ?? ""}
3191
3328
  if (config.includeTests) {
3192
3329
  verbose("Trying to include tests");
3193
3330
  const { devDependencies } = JSON.parse(
3194
- fs11.readFileSync(path11.join(options.cwd, "package.json")).toString()
3331
+ fs12.readFileSync(path12.join(options.cwd, "package.json")).toString()
3195
3332
  );
3196
3333
  if (devDependencies.vitest === void 0) {
3197
3334
  devDeps.add("vitest");
@@ -3290,10 +3427,10 @@ ${prefix?.() ?? ""}
3290
3427
  // src/index.ts
3291
3428
  var resolveRelativeToRoot = (p) => {
3292
3429
  const dirname = fileURLToPath(import.meta.url);
3293
- return path12.join(dirname, "../..", p);
3430
+ return path13.join(dirname, "../..", p);
3294
3431
  };
3295
3432
  var { version, name, description, repository } = JSON.parse(
3296
- fs12.readFileSync(resolveRelativeToRoot("package.json"), "utf-8")
3433
+ fs13.readFileSync(resolveRelativeToRoot("package.json"), "utf-8")
3297
3434
  );
3298
3435
  var context = {
3299
3436
  package: {