foldcn 0.0.18 → 0.0.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.
Files changed (2) hide show
  1. package/dist/bin.js +110 -25
  2. package/package.json +1 -1
package/dist/bin.js CHANGED
@@ -17,7 +17,7 @@ var __export = (target, all) => {
17
17
  // src/bin.ts
18
18
  import * as NodeRuntime from "@effect/platform-node/NodeRuntime";
19
19
  import * as NodeServices from "@effect/platform-node/NodeServices";
20
- import { Effect as Effect12 } from "effect";
20
+ import { Console as Console4, Effect as Effect12 } from "effect";
21
21
 
22
22
  // src/cli.ts
23
23
  import { Effect as Effect11 } from "effect";
@@ -62,7 +62,7 @@ var CssVars = Schema.Struct({
62
62
  var Css = Schema.Record(Schema.String, Schema.Union([Schema.String, Schema.suspend(() => Css)])).annotate({ identifier: "Foldcn.Registry.Css" });
63
63
  var Item = Schema.Struct({
64
64
  $schema: Schema.optionalKey(Schema.String),
65
- name: Schema.String,
65
+ name: Schema.NonEmptyString,
66
66
  type: ItemType,
67
67
  title: Schema.optionalKey(Schema.String),
68
68
  description: Schema.optionalKey(Schema.String),
@@ -600,6 +600,18 @@ class RegistryNotConfiguredError extends Schema4.TaggedErrorClass()("RegistryNot
600
600
  }) {
601
601
  }
602
602
 
603
+ class RegistryTemplateError extends Schema4.TaggedErrorClass()("RegistryTemplateError", {
604
+ namespace: Schema4.String,
605
+ template: Schema4.String
606
+ }) {
607
+ }
608
+
609
+ class PackageManagerNotFoundError extends Schema4.TaggedErrorClass()("PackageManagerNotFoundError", {
610
+ packageManager: Schema4.String,
611
+ cause: Schema4.String
612
+ }) {
613
+ }
614
+
603
615
  class UnsafeTargetError extends Schema4.TaggedErrorClass()("UnsafeTargetError", {
604
616
  target: Schema4.String
605
617
  }) {
@@ -621,6 +633,22 @@ class StylesheetNotFoundError extends Schema4.TaggedErrorClass()("StylesheetNotF
621
633
  path: Schema4.String
622
634
  }) {
623
635
  }
636
+
637
+ class FileReadError extends Schema4.TaggedErrorClass()("FileReadError", {
638
+ path: Schema4.String,
639
+ message: Schema4.String
640
+ }) {
641
+ }
642
+
643
+ class DuplicateItemError extends Schema4.TaggedErrorClass()("DuplicateItemError", {
644
+ name: Schema4.String
645
+ }) {
646
+ }
647
+
648
+ class NotAProjectError extends Schema4.TaggedErrorClass()("NotAProjectError", {
649
+ cwd: Schema4.String
650
+ }) {
651
+ }
624
652
  // src/installer/installer.ts
625
653
  import { Effect as Effect4, FileSystem as FileSystem2, Path } from "effect";
626
654
  import { ChildProcess } from "effect/unstable/process";
@@ -686,7 +714,7 @@ var install = (plan) => Effect4.gen(function* () {
686
714
  stderr: "inherit"
687
715
  });
688
716
  return yield* handle.exitCode;
689
- }));
717
+ })).pipe(Effect4.catchTag("PlatformError", (error2) => Effect4.fail(new PackageManagerNotFoundError({ packageManager: plan.packageManager, cause: String(error2) }))));
690
718
  if (exitCode !== 0) {
691
719
  return yield* new InstallFailedError({
692
720
  packageManager: plan.packageManager,
@@ -757,6 +785,9 @@ var makeGetItem = (options) => Effect6.gen(function* () {
757
785
  if (template2 === undefined) {
758
786
  return yield* new RegistryNotConfiguredError({ namespace });
759
787
  }
788
+ if (!template2.includes(namePlaceholder)) {
789
+ return yield* new RegistryTemplateError({ namespace, template: template2 });
790
+ }
760
791
  return { location: template2.replace(namePlaceholder, name), template: template2 };
761
792
  }
762
793
  const originTemplate = Option2.flatMap(maybeOrigin, (origin) => Option2.fromNullishOr(keyTemplates.get(origin)));
@@ -850,7 +881,11 @@ var planFiles = (context, files) => Effect8.gen(function* () {
850
881
  const relativeToKind = stripRegistrySourcePrefix(file.path);
851
882
  const baseDir = file.type === "registry:lib" ? context.libDir : context.uiDir;
852
883
  const absolutePath = path.resolve(baseDir, relativeToKind);
853
- planned.push({ absolutePath, relativePath: path.relative(context.cwd, absolutePath), content });
884
+ const relativeFromCwd = path.relative(context.cwd, absolutePath);
885
+ if (relativeFromCwd.startsWith("..") || path.isAbsolute(relativeFromCwd)) {
886
+ return yield* new UnsafeTargetError({ target: relativeFromCwd });
887
+ }
888
+ planned.push({ absolutePath, relativePath: relativeFromCwd, content });
854
889
  }
855
890
  return planned;
856
891
  });
@@ -874,6 +909,22 @@ var runAdd = ({ components, cwd, dryRun, overwrite, registry: registry2 }) => Ef
874
909
  }
875
910
  const writableFiles = tree.files.filter((file) => file.type !== "registry:example");
876
911
  const allPlanned = yield* planFiles(context, writableFiles);
912
+ if (dryRun) {
913
+ yield* Console.log(`Would write ${allPlanned.length} ${allPlanned.length === 1 ? "file" : "files"}:`);
914
+ for (const file of allPlanned) {
915
+ const exists = yield* fileSystem.exists(file.absolutePath);
916
+ const existing = exists ? yield* fileSystem.readFileString(file.absolutePath) : undefined;
917
+ const note = exists && existing !== file.content ? " (exists — needs --overwrite)" : exists ? " (unchanged)" : "";
918
+ yield* Console.log(` ${file.relativePath}${note}`);
919
+ }
920
+ if (tree.dependencies.length > 0) {
921
+ yield* Console.log(`Would install: ${tree.dependencies.join(" ")}`);
922
+ }
923
+ if (tree.devDependencies.length > 0) {
924
+ yield* Console.log(`Would install (dev): ${tree.devDependencies.join(" ")}`);
925
+ }
926
+ return;
927
+ }
877
928
  const planned = [];
878
929
  const conflicts = [];
879
930
  for (const file of allPlanned) {
@@ -895,19 +946,6 @@ var runAdd = ({ components, cwd, dryRun, overwrite, registry: registry2 }) => Ef
895
946
  if (conflicts.length > 0) {
896
947
  return yield* new FileConflictError({ paths: conflicts });
897
948
  }
898
- if (dryRun) {
899
- yield* Console.log(`Would write ${planned.length} files:`);
900
- for (const file of planned) {
901
- yield* Console.log(` ${file.relativePath}`);
902
- }
903
- if (tree.dependencies.length > 0) {
904
- yield* Console.log(`Would install: ${tree.dependencies.join(" ")}`);
905
- }
906
- if (tree.devDependencies.length > 0) {
907
- yield* Console.log(`Would install (dev): ${tree.devDependencies.join(" ")}`);
908
- }
909
- return;
910
- }
911
949
  for (const file of planned) {
912
950
  yield* fileSystem.makeDirectory(path.dirname(file.absolutePath), { recursive: true });
913
951
  yield* fileSystem.writeFileString(file.absolutePath, file.content);
@@ -950,11 +988,17 @@ var registryArgument = Argument2.string("registry").pipe(Argument2.withDescripti
950
988
  var outputFlag = Flag2.string("output").pipe(Flag2.withAlias("o"), Flag2.withDescription("Destination directory for built registry JSON"), Flag2.withDefault("./public/r"));
951
989
  var cwdFlag2 = Flag2.string("cwd").pipe(Flag2.withAlias("c"), Flag2.withDescription("Working directory"), Flag2.withDefault("."));
952
990
  var isLocalName = (spec) => !spec.includes("://") && !spec.startsWith("@") && !spec.startsWith(".") && !spec.startsWith("/");
953
- var validateLocalDependencies = (manifest) => Effect9.gen(function* () {
954
- const names = new Set(manifest.items.map((item) => item.name));
991
+ var validateManifest = (manifest) => Effect9.gen(function* () {
992
+ const seen = new Set;
993
+ for (const item of manifest.items) {
994
+ if (seen.has(item.name)) {
995
+ return yield* new DuplicateItemError({ name: item.name });
996
+ }
997
+ seen.add(item.name);
998
+ }
955
999
  for (const item of manifest.items) {
956
1000
  for (const dependency of item.registryDependencies ?? []) {
957
- if (isLocalName(dependency) && !names.has(dependency)) {
1001
+ if (isLocalName(dependency) && !seen.has(dependency)) {
958
1002
  return yield* new UnknownRegistryDependencyError({ itemName: item.name, dependency });
959
1003
  }
960
1004
  }
@@ -965,14 +1009,21 @@ var buildCommand = Command2.make("build", { registry: registryArgument, output:
965
1009
  const path = yield* Path5.Path;
966
1010
  const cwdAbsolute = path.resolve(cwd);
967
1011
  const manifestPath = path.resolve(cwdAbsolute, registry2);
968
- const manifestJson = yield* readJsonFile(manifestPath);
1012
+ const manifestJson = yield* readJsonFile(manifestPath).pipe(Effect9.catchTag("PlatformError", (error2) => Effect9.fail(new FileReadError({ path: manifestPath, message: `registry manifest not readable: ${error2}` }))));
969
1013
  const manifest = yield* Schema7.decodeUnknownEffect(exports_registry.Manifest)(manifestJson);
970
- yield* validateLocalDependencies(manifest);
1014
+ yield* validateManifest(manifest);
971
1015
  const outputDir = path.resolve(cwdAbsolute, output);
972
1016
  yield* fileSystem.makeDirectory(outputDir, { recursive: true });
1017
+ const existingEntries = yield* fileSystem.readDirectory(outputDir).pipe(Effect9.orElseSucceed(() => []));
1018
+ yield* Effect9.forEach(existingEntries.filter((entry) => entry.endsWith(".json")), (entry) => fileSystem.remove(path.join(outputDir, entry)).pipe(Effect9.orElseSucceed(() => {
1019
+ return;
1020
+ })));
973
1021
  for (const item of manifest.items) {
974
1022
  const files = yield* Effect9.forEach(item.files ?? [], (file) => Effect9.gen(function* () {
975
- const content = yield* fileSystem.readFileString(path.resolve(cwdAbsolute, file.path));
1023
+ const content = yield* fileSystem.readFileString(path.resolve(cwdAbsolute, file.path)).pipe(Effect9.catchTag("PlatformError", (error2) => Effect9.fail(new FileReadError({
1024
+ path: file.path,
1025
+ message: `source file referenced by item "${item.name}" not readable: ${error2}`
1026
+ }))));
976
1027
  return { ...file, content };
977
1028
  }));
978
1029
  const builtItem = {
@@ -1076,7 +1127,7 @@ var initCommand = Command3.make("init", {
1076
1127
  const cwdAbsolute = path.resolve(cwd);
1077
1128
  const hasPackageJson = yield* fileSystem.exists(path.join(cwdAbsolute, "package.json"));
1078
1129
  if (!hasPackageJson) {
1079
- return yield* new ConfigNotFoundError({ cwd: cwdAbsolute });
1130
+ return yield* new NotAProjectError({ cwd: cwdAbsolute });
1080
1131
  }
1081
1132
  const packageJson = yield* readJsonFile(path.join(cwdAbsolute, "package.json")).pipe(Effect10.orElseSucceed(() => ({})));
1082
1133
  const dependencies = packageJson.dependencies ?? {};
@@ -1123,4 +1174,38 @@ var run = Effect11.fn("FoldcnCli.run")(function* (argv) {
1123
1174
  var main = Command4.run(command, { version });
1124
1175
 
1125
1176
  // src/bin.ts
1126
- NodeRuntime.runMain(main.pipe(Effect12.provide(NodeServices.layer)));
1177
+ var fail = (message) => Console4.error(message).pipe(Effect12.andThen(Effect12.sync(() => globalThis.process.exitCode = 1)));
1178
+ var handled = main.pipe(Effect12.catchTags({
1179
+ ConfigNotFoundError: (error2) => fail(`No components.json found in ${error2.cwd}.
1180
+ Run \`foldcn init\` to create one, then try again.`),
1181
+ NotAProjectError: (error2) => fail(`No package.json found in ${error2.cwd}.
1182
+ Run \`foldcn init\` from your project root (a FoldKit app).`),
1183
+ JsonParseError: (error2) => fail(`Could not parse ${error2.path}:
1184
+ ${error2.message}`),
1185
+ FileReadError: (error2) => fail(`Could not read ${error2.path}:
1186
+ ${error2.message}`),
1187
+ DuplicateItemError: (error2) => fail(`The registry manifest declares two items named "${error2.name}". Item names must be unique.`),
1188
+ RegistryFetchError: (error2) => fail(`Could not fetch registry item "${error2.spec}".
1189
+ ${error2.message}
1190
+ Check the component name and your registry configuration.`),
1191
+ RegistryNotConfiguredError: (error2) => fail(`No registry configured for "${error2.namespace}".
1192
+ Add it to the "registries" map in components.json.`),
1193
+ RegistryTemplateError: (error2) => fail(`The registry template for "${error2.namespace}" is missing the {name} placeholder:
1194
+ ${error2.template}
1195
+ Add {name} so each component resolves to its own file.`),
1196
+ UnsafeTargetError: (error2) => fail(`Refusing to write outside the project: ${error2.target}
1197
+ Check the "aliases" in components.json and the item's target.`),
1198
+ FileConflictError: (error2) => fail(`These files already exist and differ from the registry:
1199
+ ${error2.paths.map((path) => ` ${path}`).join(`
1200
+ `)}
1201
+ Pass --overwrite to replace them.`),
1202
+ InstallFailedError: (error2) => fail(`\`${error2.packageManager} ${error2.invocation}\` failed (exit ${error2.exitCode}).
1203
+ The component files were written; install the dependencies manually.`),
1204
+ PackageManagerNotFoundError: (error2) => fail(`Could not run the package manager "${error2.packageManager}".
1205
+ ${error2.cause}
1206
+ Install it (or use a project with a lockfile for another manager), then re-run.`),
1207
+ StylesheetNotFoundError: (error2) => fail(`Stylesheet not found: ${error2.path}
1208
+ Check the "css" path in components.json.`),
1209
+ UnknownRegistryDependencyError: (error2) => fail(`"${error2.itemName}" depends on "${error2.dependency}", which is not in the registry.`)
1210
+ }));
1211
+ NodeRuntime.runMain(handled.pipe(Effect12.provide(NodeServices.layer)));
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "$schema": "https://json.schemastore.org/package.json",
3
3
  "name": "foldcn",
4
- "version": "0.0.18",
4
+ "version": "0.0.20",
5
5
  "description": "shadcn-style CLI for FoldKit: add copy-in components from a registry",
6
6
  "type": "module",
7
7
  "license": "MIT",