sprawlify 0.0.115 → 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.
Files changed (2) hide show
  1. package/dist/index.mjs +265 -12
  2. package/package.json +5 -3
package/dist/index.mjs CHANGED
@@ -7,8 +7,10 @@ import { z } from "zod";
7
7
  import { existsSync } from "fs";
8
8
  import prompts from "prompts";
9
9
  import { faker } from "@faker-js/faker";
10
+ import { cosmiconfig } from "cosmiconfig";
11
+ import { createMatchPath, loadConfig } from "tsconfig-paths";
10
12
  //#region package.json
11
- var version = "0.0.115";
13
+ var version = "0.1.0";
12
14
  //#endregion
13
15
  //#region src/utils/file-helper.ts
14
16
  const FILE_BACKUP_SUFFIX = ".bak";
@@ -2873,7 +2875,7 @@ function toKebabCase(value) {
2873
2875
  const generateProjectName = () => toKebabCase(`${faker.hacker.adjective()} ${faker.hacker.noun()}`);
2874
2876
  //#endregion
2875
2877
  //#region src/commands/init/prompts.ts
2876
- async function promptForMissingSelections(options) {
2878
+ async function promptForMissingSelections$1(options) {
2877
2879
  if (!options.name) {
2878
2880
  const { name } = await prompts({
2879
2881
  type: "text",
@@ -3886,7 +3888,7 @@ function tailwindCssPathPrefix(cwd) {
3886
3888
  function resolveTailwindCssFilePath(cwd, prefix, cssPath) {
3887
3889
  return path.resolve(cwd, prefix, cssPath);
3888
3890
  }
3889
- function resolveItemsToApply(presetItems, requestedComponents) {
3891
+ function resolveItemsToApply$1(presetItems, requestedComponents) {
3890
3892
  const presetItemsByName = new Map(presetItems.map((item) => [item.name, item]));
3891
3893
  const itemNamesToApply = /* @__PURE__ */ new Set();
3892
3894
  const packageNames = /* @__PURE__ */ new Set();
@@ -3920,7 +3922,7 @@ function resolveItemsToApply(presetItems, requestedComponents) {
3920
3922
  unresolvedNames
3921
3923
  };
3922
3924
  }
3923
- async function applyPresetPackageDependencies(cwd, frameworkPreset, packageNames) {
3925
+ async function applyPresetPackageDependencies$1(cwd, frameworkPreset, packageNames) {
3924
3926
  if (!frameworkPreset || packageNames.size === 0) return;
3925
3927
  const packageJsonPath = path.resolve(cwd, "package.json");
3926
3928
  if (!fsExtra.existsSync(packageJsonPath)) {
@@ -3979,11 +3981,18 @@ async function runInit(options) {
3979
3981
  css: "globals.css"
3980
3982
  };
3981
3983
  const config = {
3982
- $schema: "https://ui.primitives.com/schema.json",
3984
+ $schema: "https://ui.sprawlify.com/schema.json",
3983
3985
  framework: options.framework,
3984
3986
  preset: options.preset,
3985
3987
  metaframework: options.metaframework,
3986
- tailwindcss: { css: `${tailwindcss.cwd}/${tailwindcss.css}` }
3988
+ tsx: true,
3989
+ tailwindcss: { css: `${tailwindcss.cwd}/${tailwindcss.css}` },
3990
+ aliases: {
3991
+ components: "@/components",
3992
+ utils: "@/lib/utils",
3993
+ ui: "@/components/ui",
3994
+ lib: "@/lib"
3995
+ }
3987
3996
  };
3988
3997
  let backupPath = null;
3989
3998
  if (fsExtra.existsSync(componentsJsonPath)) {
@@ -4007,13 +4016,13 @@ async function runInit(options) {
4007
4016
  }
4008
4017
  throw error;
4009
4018
  }
4010
- const { itemsToApply, packageNames, unresolvedNames } = resolveItemsToApply(getPresetItems(options.preset, options.framework), new Set(options.components ?? []));
4019
+ const { itemsToApply, packageNames, unresolvedNames } = resolveItemsToApply$1(getPresetItems(options.preset, options.framework), new Set(options.components ?? []));
4011
4020
  if (unresolvedNames.size > 0) logger.warn(`Skipping unknown preset component options: ${[...unresolvedNames].map((componentName) => highlighter.info(componentName)).join(", ")}.`);
4012
4021
  if (itemsToApply.length === 0) {
4013
- await applyPresetPackageDependencies(cwd, frameworkPreset, packageNames);
4022
+ await applyPresetPackageDependencies$1(cwd, frameworkPreset, packageNames);
4014
4023
  return;
4015
4024
  }
4016
- await applyPresetPackageDependencies(cwd, frameworkPreset, packageNames);
4025
+ await applyPresetPackageDependencies$1(cwd, frameworkPreset, packageNames);
4017
4026
  for (const item of itemsToApply) for (const file of item.files ?? []) {
4018
4027
  const targetPath = resolvePresetFilePath(cwd, file);
4019
4028
  if (fsExtra.existsSync(targetPath) && !options.override) {
@@ -4023,6 +4032,7 @@ async function runInit(options) {
4023
4032
  await fsExtra.ensureDir(path.dirname(targetPath));
4024
4033
  await fsExtra.writeFile(targetPath, file.content, "utf8");
4025
4034
  }
4035
+ return config;
4026
4036
  }
4027
4037
  //#endregion
4028
4038
  //#region src/commands/init/done.ts
@@ -4036,12 +4046,12 @@ async function doneInit(options) {
4036
4046
  }
4037
4047
  //#endregion
4038
4048
  //#region src/commands/init/index.ts
4039
- const init = new Command().name("init").alias("create").description("initialize your project and install dependencies").argument("[components...]", "names, url or local path to component").option("-p, --preset <preset>", "the preset to use. (monochrome, clay)").option("-f, --framework <framework>", "the framework to use. (react, solid, svelte, vue)").option("--metaframework <metaframework>", "the metaframework to use. (next, react-router, nuxt, sveltekit)").option("-y, --yes", "skip confirmation prompt.", true).option("-d, --defaults", "use default configuration: --preset=monochrome --framework=react --metaframework=none", false).option("-o, --override", "override existing configuration.", false).option("-c, --cwd <cwd>", "the working directory. defaults to the current directory.", process.cwd()).option("-n, --name <name>", "the name for the new project.").option("-s, --silent", "mute output.", false).option("--reinstall", "re-install existing UI components.").option("--no-reinstall", "do not re-install existing UI components.").action(async (components, opts) => {
4049
+ const init = new Command().name("init").alias("create").description("initialize your project and install dependencies").argument("[components...]", "names of components").option("-p, --preset <preset>", "the preset to use. (monochrome, clay)").option("-f, --framework <framework>", "the framework to use. (react, solid, svelte, vue)").option("--metaframework <metaframework>", "the metaframework to use. (next, react-router, nuxt, sveltekit)").option("-y, --yes", "skip confirmation prompt.", true).option("-d, --defaults", "use default configuration: --preset=monochrome --framework=react --metaframework=none", false).option("-o, --override", "override existing configuration.", false).option("-c, --cwd <cwd>", "the working directory. defaults to the current directory.", process.cwd()).option("-n, --name <name>", "the name for the new project.").option("--reinstall", "re-install existing UI components.").option("--no-reinstall", "do not re-install existing UI components.").action(async (components, opts) => {
4040
4050
  try {
4041
4051
  const options = parseInitOptions(opts);
4042
4052
  applyDefaultInitOptions(options);
4043
4053
  validateExplicitOptions(options);
4044
- await promptForMissingSelections(options);
4054
+ await promptForMissingSelections$1(options);
4045
4055
  await ensureOverwriteAllowed(options);
4046
4056
  options.components = components;
4047
4057
  await loadEnvFiles(options.cwd);
@@ -4057,12 +4067,255 @@ const init = new Command().name("init").alias("create").description("initialize
4057
4067
  }
4058
4068
  });
4059
4069
  //#endregion
4070
+ //#region src/commands/add/options.ts
4071
+ const addOptionsSchema = z.object({
4072
+ components: z.array(z.string()).optional(),
4073
+ yes: z.boolean(),
4074
+ overwrite: z.boolean(),
4075
+ cwd: z.string(),
4076
+ all: z.boolean(),
4077
+ path: z.string().optional()
4078
+ });
4079
+ function parseAddOptions(opts, components) {
4080
+ const parsed = opts;
4081
+ return addOptionsSchema.parse({
4082
+ components,
4083
+ ...parsed,
4084
+ cwd: path.resolve(String(parsed.cwd ?? process.cwd()))
4085
+ });
4086
+ }
4087
+ //#endregion
4088
+ //#region src/commands/add/run.ts
4089
+ function resolvePresetFilePathFromConfig(config, file) {
4090
+ if (file.type === "registry:utils") return path.resolve(config.resolvedPaths.utils, file.path);
4091
+ if (file.type === "registry:ui") return path.resolve(config.resolvedPaths.ui, file.path);
4092
+ return path.resolve(config.resolvedPaths.cwd, file.path);
4093
+ }
4094
+ function resolveItemsToApply(presetItems, requestedComponents) {
4095
+ const presetItemsByName = new Map(presetItems.map((item) => [item.name, item]));
4096
+ const itemNamesToApply = /* @__PURE__ */ new Set();
4097
+ const packageNames = /* @__PURE__ */ new Set();
4098
+ const unresolvedNames = /* @__PURE__ */ new Set();
4099
+ const queue = [...requestedComponents];
4100
+ while (queue.length > 0) {
4101
+ const dependencyName = queue.shift();
4102
+ if (!dependencyName) continue;
4103
+ const matchingItem = presetItemsByName.get(dependencyName);
4104
+ if (matchingItem) {
4105
+ if (itemNamesToApply.has(matchingItem.name)) continue;
4106
+ itemNamesToApply.add(matchingItem.name);
4107
+ for (const nestedDependency of matchingItem.dependencies ?? []) queue.push(nestedDependency);
4108
+ continue;
4109
+ }
4110
+ if (dependencyName.startsWith("@/")) {
4111
+ const extractedName = dependencyName.split("/").pop();
4112
+ if (extractedName && presetItemsByName.has(extractedName)) {
4113
+ queue.push(extractedName);
4114
+ continue;
4115
+ }
4116
+ }
4117
+ packageNames.add(dependencyName);
4118
+ }
4119
+ for (const componentName of requestedComponents) if (!presetItemsByName.has(componentName)) unresolvedNames.add(componentName);
4120
+ return {
4121
+ itemsToApply: [...itemNamesToApply].map((itemName) => presetItemsByName.get(itemName)).filter((item) => Boolean(item)),
4122
+ packageNames,
4123
+ unresolvedNames
4124
+ };
4125
+ }
4126
+ async function applyPresetPackageDependencies(cwd, frameworkPreset, packageNames) {
4127
+ if (!frameworkPreset || packageNames.size === 0) return;
4128
+ const packageJsonPath = path.resolve(cwd, "package.json");
4129
+ if (!fsExtra.existsSync(packageJsonPath)) {
4130
+ logger.warn("Skipping preset package dependencies because package.json was not found.");
4131
+ return;
4132
+ }
4133
+ const packageJson = await fsExtra.readJson(packageJsonPath);
4134
+ let didChange = false;
4135
+ packageJson.dependencies ??= {};
4136
+ packageJson.devDependencies ??= {};
4137
+ for (const packageName of packageNames) {
4138
+ const dependencyVersion = frameworkPreset.dependencies?.[packageName];
4139
+ if (dependencyVersion) {
4140
+ if (!packageJson.dependencies[packageName]) {
4141
+ packageJson.dependencies[packageName] = dependencyVersion;
4142
+ didChange = true;
4143
+ }
4144
+ continue;
4145
+ }
4146
+ }
4147
+ if (frameworkPreset.devDependencies) {
4148
+ for (const [devDependencyName, devDependencyVersion] of Object.entries(frameworkPreset.devDependencies)) if (!packageJson.devDependencies[devDependencyName]) {
4149
+ packageJson.devDependencies[devDependencyName] = devDependencyVersion;
4150
+ didChange = true;
4151
+ }
4152
+ }
4153
+ if (didChange) {
4154
+ if (packageJson.dependencies && Object.keys(packageJson.dependencies).length > 0) packageJson.dependencies = Object.keys(packageJson.dependencies).sort().reduce((sorted, key) => {
4155
+ sorted[key] = packageJson.dependencies[key];
4156
+ return sorted;
4157
+ }, {});
4158
+ if (packageJson.devDependencies && Object.keys(packageJson.devDependencies).length > 0) packageJson.devDependencies = Object.keys(packageJson.devDependencies).sort().reduce((sorted, key) => {
4159
+ sorted[key] = packageJson.devDependencies[key];
4160
+ return sorted;
4161
+ }, {});
4162
+ await fsExtra.writeJson(packageJsonPath, packageJson, { spaces: 2 });
4163
+ }
4164
+ }
4165
+ async function runAdd(options, config) {
4166
+ const { components, overwrite, cwd } = options;
4167
+ if (!components || components.length === 0) {
4168
+ logger.info("No components specified to add.");
4169
+ return;
4170
+ }
4171
+ const { itemsToApply, packageNames, unresolvedNames } = resolveItemsToApply(getPresetItems(config.preset, config.framework), new Set(components));
4172
+ if (unresolvedNames.size > 0) logger.warn(`Skipping unknown components: ${[...unresolvedNames].map((componentName) => highlighter.info(componentName)).join(", ")}.`);
4173
+ if (itemsToApply.length === 0) {
4174
+ logger.info("No valid components to add.");
4175
+ return;
4176
+ }
4177
+ await applyPresetPackageDependencies(cwd, getFrameworkPreset(config.preset, config.framework), packageNames);
4178
+ for (const item of itemsToApply) for (const file of item.files ?? []) {
4179
+ const targetPath = resolvePresetFilePathFromConfig(config, file);
4180
+ if (fsExtra.existsSync(targetPath) && !overwrite) {
4181
+ logger.warn(`Skipping ${highlighter.info(path.relative(cwd, targetPath))} because it already exists. Use ${highlighter.info("--overwrite")} to replace it.`);
4182
+ continue;
4183
+ }
4184
+ await fsExtra.ensureDir(path.dirname(targetPath));
4185
+ await fsExtra.writeFile(targetPath, file.content, "utf8");
4186
+ logger.info(`Added ${highlighter.info(path.relative(cwd, targetPath))}`);
4187
+ }
4188
+ logger.info(`Successfully added ${itemsToApply.length} component(s).`);
4189
+ }
4190
+ //#endregion
4191
+ //#region src/commands/add/prompts.ts
4192
+ async function promptForMissingSelections(options, config) {
4193
+ if (!options.components || options.components.length === 0) {
4194
+ const frameworkPreset = getFrameworkPreset(config.preset, config.framework);
4195
+ if (!frameworkPreset) throw new Error(`No preset found for framework ${config.framework} and preset ${config.preset}`);
4196
+ const { components } = await prompts({
4197
+ type: "multiselect",
4198
+ name: "components",
4199
+ message: "Which components would you like to add?",
4200
+ hint: "Space to select. A to toggle all. Enter to submit.",
4201
+ instructions: false,
4202
+ choices: frameworkPreset.items?.map((item) => ({
4203
+ title: item,
4204
+ value: item,
4205
+ selected: options.all ? true : options.components?.includes(item.name) || false
4206
+ })) || []
4207
+ });
4208
+ if (!components?.length) {
4209
+ logger.warn("No components selected. Exiting.");
4210
+ logger.info("");
4211
+ process.exit(1);
4212
+ }
4213
+ options.components = components;
4214
+ }
4215
+ }
4216
+ //#endregion
4217
+ //#region src/registry/schema.ts
4218
+ const rawConfigSchema = z.object({
4219
+ preset: z.string(),
4220
+ framework: z.string(),
4221
+ tsx: z.coerce.boolean().default(true),
4222
+ tailwind: z.object({
4223
+ config: z.string().optional(),
4224
+ css: z.string()
4225
+ }),
4226
+ aliases: z.object({
4227
+ components: z.string(),
4228
+ utils: z.string(),
4229
+ ui: z.string().optional(),
4230
+ lib: z.string().optional()
4231
+ })
4232
+ });
4233
+ const configSchema = rawConfigSchema.extend({ resolvedPaths: z.object({
4234
+ cwd: z.string(),
4235
+ tailwindconfig: z.string(),
4236
+ tailwindcss: z.string(),
4237
+ utils: z.string(),
4238
+ components: z.string(),
4239
+ lib: z.string(),
4240
+ hooks: z.string(),
4241
+ ui: z.string()
4242
+ }) });
4243
+ //#endregion
4244
+ //#region src/utils/resolve-import.ts
4245
+ function resolveImport(importPath, config) {
4246
+ return createMatchPath(config.absoluteBaseUrl, config.paths)(importPath, void 0, () => true, [
4247
+ ".ts",
4248
+ ".tsx",
4249
+ ".jsx",
4250
+ ".js",
4251
+ ".css",
4252
+ ".svelte",
4253
+ ".vue"
4254
+ ]);
4255
+ }
4256
+ //#endregion
4257
+ //#region src/utils/config.ts
4258
+ const explorer = cosmiconfig("components", { searchPlaces: ["components.json"] });
4259
+ function resolveConfigPaths(cwd, config) {
4260
+ const tsConfig = loadConfig(cwd);
4261
+ if (tsConfig.resultType === "failed") throw new Error(`Failed to load ${config.tsx ? "tsconfig" : "jsconfig"}.json. ${tsConfig.message ?? ""}`.trim());
4262
+ return configSchema.parse({
4263
+ ...config,
4264
+ resolvedPaths: {
4265
+ cwd,
4266
+ tailwindconfig: config.tailwind.config ? path.resolve(cwd, config.tailwind.config) : "",
4267
+ tailwindcss: path.resolve(cwd, config.tailwind.css),
4268
+ utils: resolveImport(config.aliases["utils"], tsConfig),
4269
+ components: resolveImport(config.aliases["components"], tsConfig),
4270
+ ui: config.aliases["ui"] ? resolveImport(config.aliases["ui"], tsConfig) : path.resolve(resolveImport(config.aliases["components"], tsConfig) ?? cwd, "ui"),
4271
+ lib: config.aliases["lib"] ? resolveImport(config.aliases["lib"], tsConfig) : path.resolve(resolveImport(config.aliases["utils"], tsConfig) ?? cwd, "..")
4272
+ }
4273
+ });
4274
+ }
4275
+ async function getRawConfig(cwd) {
4276
+ try {
4277
+ const configResult = await explorer.search(cwd);
4278
+ if (!configResult) return null;
4279
+ return rawConfigSchema.parse(configResult.config);
4280
+ } catch (error) {
4281
+ const componentPath = `${cwd}/components.json`;
4282
+ if (error instanceof Error && error.message.includes("reserved registry")) throw error;
4283
+ throw new Error(`Invalid configuration found in ${highlighter.info(componentPath)}.`);
4284
+ }
4285
+ }
4286
+ async function getConfig(cwd) {
4287
+ const config = await getRawConfig(cwd);
4288
+ if (!config) return null;
4289
+ return resolveConfigPaths(cwd, config);
4290
+ }
4291
+ //#endregion
4292
+ //#region src/commands/add/index.ts
4293
+ const add = new Command().name("add").description("add a component to your project").argument("[components...]", "names or urls or to component").option("-o, --overwrite", "overwrite existing files.", false).option("-c, --cwd <cwd>", "the working directory. defaults to the current directory.", process.cwd()).option("-a, --all", "add all available components", false).option("-p, --path <path>", "the path to add the component to.").action(async (components, opts) => {
4294
+ try {
4295
+ const options = parseAddOptions(opts, components);
4296
+ await loadEnvFiles(options.cwd);
4297
+ const config = await getConfig(options.cwd);
4298
+ if (!config) {
4299
+ logger.error(`No components.json found in ${options.cwd}. Please run ${highlighter.info("npx sprawlify@latest init")} first to create a components.json before adding components.`);
4300
+ return;
4301
+ }
4302
+ await promptForMissingSelections(options, config);
4303
+ await runAdd(options, config);
4304
+ logger.break();
4305
+ } catch (error) {
4306
+ logger.break();
4307
+ handleError(error);
4308
+ } finally {
4309
+ clearRegistryContext();
4310
+ }
4311
+ });
4312
+ //#endregion
4060
4313
  //#region src/index.ts
4061
4314
  process.on("SIGINT", () => process.exit(0));
4062
4315
  process.on("SIGTERM", () => process.exit(0));
4063
4316
  async function main() {
4064
4317
  const program = new Command().name("sprawlify").description("add items from registries to your project").version(version || "1.0.0", "-v, --version", "display the version number");
4065
- program.addCommand(init);
4318
+ program.addCommand(init).addCommand(add);
4066
4319
  program.parse();
4067
4320
  }
4068
4321
  main();
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "sprawlify",
3
- "version": "0.0.115",
3
+ "version": "0.1.0",
4
4
  "description": "A command-line interface for Sprawlify.",
5
5
  "license": "MIT",
6
6
  "author": "sprawlify <npm@sprawlify.com>",
@@ -18,13 +18,15 @@
18
18
  "access": "public"
19
19
  },
20
20
  "dependencies": {
21
- "@dotenvx/dotenvx": "^1.55.1",
22
- "@faker-js/faker": "^10.3.0",
21
+ "@dotenvx/dotenvx": "^1.59.1",
22
+ "@faker-js/faker": "^10.4.0",
23
23
  "commander": "^14.0.3",
24
+ "cosmiconfig": "^9.0.1",
24
25
  "dedent": "^1.7.2",
25
26
  "fs-extra": "^11.3.4",
26
27
  "kleur": "^4.1.5",
27
28
  "prompts": "^2.4.2",
29
+ "tsconfig-paths": "^4.2.0",
28
30
  "zod": "^4.3.6"
29
31
  },
30
32
  "engines": {