sprawlify 0.0.90 → 0.0.92

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 +239 -162
  2. package/package.json +2 -1
package/dist/index.mjs CHANGED
@@ -1,13 +1,14 @@
1
1
  #!/usr/bin/env node
2
2
  import { Command } from "commander";
3
- import fsExtra from "fs-extra";
4
3
  import path, { join } from "path";
5
- import prompts from "prompts";
6
- import { z } from "zod";
4
+ import fsExtra from "fs-extra";
7
5
  import { cyan, green, red, yellow } from "kleur/colors";
6
+ import { z } from "zod";
8
7
  import { existsSync } from "fs";
8
+ import prompts from "prompts";
9
+ import dedent from "dedent";
9
10
  //#region package.json
10
- var version = "0.0.90";
11
+ var version = "0.0.92";
11
12
  //#endregion
12
13
  //#region src/utils/file-helper.ts
13
14
  const FILE_BACKUP_SUFFIX = ".bak";
@@ -148,27 +149,6 @@ function clearRegistryContext() {
148
149
  context.headers = {};
149
150
  }
150
151
  //#endregion
151
- //#region src/preset/presets.ts
152
- const PRESETS = {
153
- clay: {
154
- name: "Clay",
155
- frameworks: ["react"]
156
- },
157
- monochrome: {
158
- name: "Monochrome",
159
- frameworks: [
160
- "react",
161
- "solid",
162
- "svelte",
163
- "vue"
164
- ]
165
- }
166
- };
167
- //#endregion
168
- //#region src/frameworks/index.ts
169
- const FRAMEWORKS = {};
170
- const METAFRAMEWORKS = {};
171
- //#endregion
172
152
  //#region src/utils/env-loader.ts
173
153
  async function loadEnvFiles(cwd = process.cwd()) {
174
154
  try {
@@ -191,7 +171,46 @@ async function loadEnvFiles(cwd = process.cwd()) {
191
171
  }
192
172
  }
193
173
  //#endregion
194
- //#region src/commands/init.ts
174
+ //#region src/frameworks/index.ts
175
+ const FRAMEWORKS = {
176
+ react: { title: "React" },
177
+ solid: { title: "Solid" },
178
+ svelte: { title: "Svelte" },
179
+ vue: { title: "Vue" }
180
+ };
181
+ const METAFRAMEWORKS = {
182
+ next: {
183
+ title: "Next.js",
184
+ frameworks: ["react"]
185
+ },
186
+ nuxt: {
187
+ title: "Nuxt",
188
+ frameworks: ["vue"]
189
+ },
190
+ sveltekit: {
191
+ title: "SvelteKit",
192
+ frameworks: ["svelte"]
193
+ }
194
+ };
195
+ //#endregion
196
+ //#region src/preset/presets.ts
197
+ const PRESETS = {
198
+ clay: {
199
+ name: "Clay",
200
+ frameworks: ["react"]
201
+ },
202
+ monochrome: {
203
+ name: "Monochrome",
204
+ frameworks: [
205
+ "react",
206
+ "solid",
207
+ "svelte",
208
+ "vue"
209
+ ]
210
+ }
211
+ };
212
+ //#endregion
213
+ //#region src/commands/init/options.ts
195
214
  const initOptionsSchema = z.object({
196
215
  cwd: z.string(),
197
216
  name: z.string().optional(),
@@ -199,102 +218,200 @@ const initOptionsSchema = z.object({
199
218
  components: z.array(z.string()).optional(),
200
219
  yes: z.boolean(),
201
220
  defaults: z.boolean(),
202
- force: z.boolean(),
221
+ override: z.boolean(),
203
222
  reinstall: z.boolean().optional(),
204
223
  framework: z.string().optional(),
205
224
  metaframework: z.string().optional()
206
225
  });
207
- async function runInit(options) {
208
- const cwd = options.cwd;
209
- const componentsJsonPath = path.resolve(cwd, "components.json");
210
- logger.info("Starting project initialization...");
211
- if (!options.defaults && !options.skipPreflight) {
212
- if (!options.framework) {
213
- const { framework } = await prompts({
214
- type: "select",
215
- name: "framework",
216
- message: "Which framework would you like to use?",
217
- choices: [
218
- {
219
- title: "React",
220
- value: "react"
221
- },
222
- {
223
- title: "Solid",
224
- value: "solid"
225
- },
226
- {
227
- title: "Svelte",
228
- value: "svelte"
229
- },
230
- {
231
- title: "Vue",
232
- value: "vue"
233
- }
234
- ],
235
- initial: 0
236
- });
237
- if (!framework) {
238
- logger.error("Framework selection is required.");
239
- process.exit(1);
240
- }
241
- options.framework = framework;
226
+ function parseInitOptions(opts) {
227
+ const parsed = opts;
228
+ return initOptionsSchema.parse({
229
+ ...parsed,
230
+ reinstall: parsed.reinstall,
231
+ cwd: path.resolve(String(parsed.cwd ?? process.cwd()))
232
+ });
233
+ }
234
+ function applyDefaultInitOptions(options) {
235
+ if (!options.defaults) return;
236
+ options.preset = options.preset || "monochrome";
237
+ options.framework = options.framework || "react";
238
+ options.reinstall = options.reinstall ?? false;
239
+ }
240
+ function validateExplicitOptions(options) {
241
+ if (options.preset && !(options.preset in PRESETS)) exitForInvalidOption("preset", options.preset, Object.keys(PRESETS));
242
+ if (options.framework && !(options.framework in FRAMEWORKS)) exitForInvalidOption("framework", options.framework, Object.keys(FRAMEWORKS));
243
+ if (options.metaframework && !(options.metaframework in METAFRAMEWORKS)) exitForInvalidOption("metaframework", options.metaframework, Object.keys(METAFRAMEWORKS));
244
+ }
245
+ function exitForInvalidOption(optionName, receivedValue, allowedValues) {
246
+ logger.error(`Invalid ${optionName}: ${highlighter.info(receivedValue)}. Available ${optionName}s: ${allowedValues.map((value) => highlighter.info(value)).join(", ")}.`);
247
+ logger.break();
248
+ process.exit(1);
249
+ }
250
+ //#endregion
251
+ //#region src/commands/init/prompts.ts
252
+ async function promptForMissingSelections(options) {
253
+ if (!options.name) {
254
+ const { name } = await prompts({
255
+ type: "text",
256
+ name: "name",
257
+ message: "What is your project name?",
258
+ initial: "my-project",
259
+ validate: (value) => value.trim().length > 0 || "Project name is required."
260
+ });
261
+ if (!name) {
262
+ logger.error("Project name is required.");
263
+ process.exit(1);
242
264
  }
243
- if (!options.preset) {
244
- const { preset } = await prompts({
245
- type: "select",
246
- name: "preset",
247
- message: "Which preset would you like to use?",
248
- choices: Object.entries(PRESETS).map(([key, preset]) => ({
249
- title: preset.name,
250
- value: key,
251
- disabled: !preset.frameworks.includes(options.framework)
252
- })),
253
- initial: 0
254
- });
255
- if (!preset) {
256
- logger.error("Preset selection is required.");
257
- process.exit(1);
258
- }
259
- options.preset = preset;
265
+ options.name = name;
266
+ }
267
+ if (!options.framework) {
268
+ const { framework } = await prompts({
269
+ type: "select",
270
+ name: "framework",
271
+ message: "Which framework would you like to use?",
272
+ choices: Object.keys(FRAMEWORKS).map((frameworkKey) => ({
273
+ title: FRAMEWORKS[frameworkKey].title,
274
+ value: frameworkKey
275
+ })),
276
+ initial: 0
277
+ });
278
+ if (!framework) {
279
+ logger.error("Framework selection is required.");
280
+ process.exit(1);
260
281
  }
261
- if (!options.metaframework) {
262
- const { metaframework } = await prompts({
263
- type: "select",
264
- name: "metaframework",
265
- message: "Which metaframework would you like to use (optional)?",
266
- choices: [
267
- {
268
- title: "None",
269
- value: "none"
270
- },
271
- {
272
- title: "Next.js",
273
- value: "next"
274
- },
275
- {
276
- title: "Vite",
277
- value: "vite"
278
- },
279
- {
280
- title: "Nuxt",
281
- value: "nuxt"
282
- },
283
- {
284
- title: "SvelteKit",
285
- value: "sveltekit"
286
- }
287
- ],
288
- initial: 0
289
- });
290
- options.metaframework = metaframework === "none" ? void 0 : metaframework;
282
+ options.framework = framework;
283
+ }
284
+ if (!options.preset) {
285
+ const { preset } = await prompts({
286
+ type: "select",
287
+ name: "preset",
288
+ message: "Which preset would you like to use?",
289
+ choices: Object.entries(PRESETS).map(([key, preset]) => ({
290
+ title: preset.name,
291
+ value: key,
292
+ disabled: !preset.frameworks.includes(options.framework)
293
+ })),
294
+ initial: 0
295
+ });
296
+ if (!preset) {
297
+ logger.error("Preset selection is required.");
298
+ process.exit(1);
299
+ }
300
+ options.preset = preset;
301
+ }
302
+ if (!options.metaframework) {
303
+ const { metaframework } = await prompts({
304
+ type: "select",
305
+ name: "metaframework",
306
+ message: "Which metaframework would you like to use (optional)?",
307
+ choices: [{
308
+ title: "None",
309
+ value: "none"
310
+ }, ...Object.entries(METAFRAMEWORKS).filter(([_, meta]) => meta.frameworks.includes(options.framework)).map(([key, meta]) => ({
311
+ title: meta.title,
312
+ value: key
313
+ }))],
314
+ initial: 0
315
+ });
316
+ options.metaframework = metaframework === "none" ? void 0 : metaframework;
317
+ }
318
+ }
319
+ async function ensureOverwriteAllowed(options) {
320
+ if (!fsExtra.existsSync(path.resolve(options.cwd, "components.json")) || options.override) return;
321
+ const { overwrite } = await prompts({
322
+ type: "confirm",
323
+ name: "overwrite",
324
+ message: `A ${highlighter.info("components.json")} file already exists. Would you like to overwrite it?`,
325
+ initial: false
326
+ });
327
+ if (!overwrite) {
328
+ logger.info(` To start over, remove the ${highlighter.info("components.json")} file and run ${highlighter.info("init")} again.`);
329
+ logger.break();
330
+ process.exit(1);
331
+ }
332
+ options.override = true;
333
+ }
334
+ //#endregion
335
+ //#region src/templates/create-template.ts
336
+ function createTemplate(config) {
337
+ return {
338
+ ...config,
339
+ frameworks: config.frameworks || []
340
+ };
341
+ }
342
+ //#endregion
343
+ //#region src/templates/index.ts
344
+ const templates = {
345
+ react: createTemplate({ files: [{
346
+ path: "tsconfig.app.json",
347
+ content: dedent`{
348
+ "compilerOptions": {
349
+ "tsBuildInfoFile": "./node_modules/.tmp/tsconfig.app.tsbuildinfo",
350
+ "target": "ES2023",
351
+ "useDefineForClassFields": true,
352
+ "lib": ["ES2023", "DOM", "DOM.Iterable"],
353
+ "module": "ESNext",
354
+ "types": ["vite/client"],
355
+ "skipLibCheck": true,
356
+ /* Bundler mode */
357
+ "moduleResolution": "bundler",
358
+ "allowImportingTsExtensions": true,
359
+ "verbatimModuleSyntax": true,
360
+ "moduleDetection": "force",
361
+ "noEmit": true,
362
+ "jsx": "react-jsx",
363
+ /* Linting */
364
+ "strict": true,
365
+ "noUnusedLocals": true,
366
+ "noUnusedParameters": true,
367
+ "erasableSyntaxOnly": true,
368
+ "noFallthroughCasesInSwitch": true,
369
+ "noUncheckedSideEffectImports": true
370
+ },
371
+ "include": ["src"]
372
+ }
373
+ `
374
+ }, {
375
+ path: "tsconfig.json",
376
+ content: dedent`{
377
+ "files": [],
378
+ "references": [
379
+ { "path": "./tsconfig.app.json" },
380
+ { "path": "./tsconfig.node.json" }
381
+ ]
382
+ }`
383
+ }] }),
384
+ solid: createTemplate({}),
385
+ svelte: createTemplate({}),
386
+ vue: createTemplate({})
387
+ };
388
+ //#endregion
389
+ //#region src/commands/init/run-init.ts
390
+ async function runInit(options) {
391
+ let cwd = options.cwd;
392
+ if (options.name && options.name !== ".") {
393
+ cwd = path.resolve(options.cwd, options.name);
394
+ await fsExtra.ensureDir(cwd);
395
+ options.cwd = cwd;
396
+ logger.info(`Using project directory: ${highlighter.info(options.name)}`);
397
+ }
398
+ const selectedTemplate = options.framework && options.framework in templates ? templates[options.framework] : void 0;
399
+ if (selectedTemplate?.files.length) for (const file of selectedTemplate.files) {
400
+ const targetPath = path.resolve(cwd, file.path);
401
+ if (fsExtra.existsSync(targetPath) && !options.override) {
402
+ logger.warn(`Skipping ${highlighter.info(file.path)} because it already exists. Use ${highlighter.info("--override")} to replace it.`);
403
+ continue;
291
404
  }
405
+ await fsExtra.ensureDir(path.dirname(targetPath));
406
+ await fsExtra.writeFile(targetPath, file.content, "utf8");
407
+ logger.success(`Created ${highlighter.info(file.path)} template file.`);
292
408
  }
293
- if (options.framework) options.framework;
409
+ const componentsJsonPath = path.resolve(cwd, "components.json");
410
+ logger.info("Starting project initialization...");
294
411
  const config = {
295
412
  $schema: "https://ui.primitives.com/schema.json",
296
- framework: options.framework || "react",
297
- preset: options.preset || "monochrome",
413
+ framework: options.framework,
414
+ preset: options.preset,
298
415
  metaframework: options.metaframework,
299
416
  aliases: {
300
417
  components: "@/components",
@@ -322,63 +439,23 @@ async function runInit(options) {
322
439
  throw error;
323
440
  }
324
441
  }
325
- 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=vanilla", false).option("-f, --force", "force overwrite of 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) => {
326
- let reinstallComponents = [];
327
- const restoreBackupOnExit = () => {};
328
- process.on("exit", restoreBackupOnExit);
442
+ //#endregion
443
+ //#region src/commands/init.ts
444
+ 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=vanilla", 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) => {
329
445
  try {
330
- const options = initOptionsSchema.parse({
331
- ...opts,
332
- reinstall: opts.reinstall,
333
- cwd: path.resolve(opts.cwd)
334
- });
335
- if (options.defaults) {
336
- options.preset = options.preset || "monochrome";
337
- options.framework = options.framework || "react";
338
- options.reinstall = options.reinstall ?? false;
339
- }
340
- if (options.preset && !(options.preset in PRESETS)) {
341
- logger.error(`Invalid preset: ${highlighter.info(options.preset)}. Available presets: ${Object.keys(PRESETS).map((f) => highlighter.info(f)).join(", ")}.`);
342
- logger.break();
343
- process.exit(1);
344
- }
345
- if (options.framework && !(options.framework in FRAMEWORKS)) {
346
- logger.error(`Invalid framework: ${highlighter.info(options.framework)}. Available frameworks: ${Object.keys(FRAMEWORKS).map((f) => highlighter.info(f)).join(", ")}.`);
347
- logger.break();
348
- process.exit(1);
349
- }
350
- if (options.metaframework && !(options.metaframework in METAFRAMEWORKS)) {
351
- logger.error(`Invalid metaframework: ${highlighter.info(options.metaframework)}. Available metaframeworks: ${Object.keys(METAFRAMEWORKS).map((f) => highlighter.info(f)).join(", ")}.`);
352
- logger.break();
353
- process.exit(1);
354
- }
355
- const cwd = options.cwd;
356
- if (fsExtra.existsSync(path.resolve(cwd, "components.json")) && !options.force) {
357
- const { overwrite } = await prompts({
358
- type: "confirm",
359
- name: "overwrite",
360
- message: `A ${highlighter.info("components.json")} file already exists. Would you like to overwrite it?`,
361
- initial: false
362
- });
363
- if (!overwrite) {
364
- logger.info(` To start over, remove the ${highlighter.info("components.json")} file and run ${highlighter.info("init")} again.`);
365
- logger.break();
366
- process.exit(1);
367
- }
368
- options.force = true;
369
- }
370
- if (reinstallComponents.length) components = [...components, ...reinstallComponents];
446
+ const options = parseInitOptions(opts);
447
+ applyDefaultInitOptions(options);
448
+ validateExplicitOptions(options);
449
+ await promptForMissingSelections(options);
450
+ await ensureOverwriteAllowed(options);
371
451
  options.components = components;
372
452
  await loadEnvFiles(options.cwd);
373
453
  await runInit(options);
374
454
  logger.break();
375
- logger.log(`Project initialization completed.\nYou may now add components.`);
376
- process.removeListener("exit", restoreBackupOnExit);
377
- deleteFileBackup(path.resolve(cwd, "components.json"));
455
+ logger.log(`Project initialization completed in ${highlighter.info(options.cwd)}.\nYou may now add components.`);
456
+ deleteFileBackup(path.resolve(options.cwd, "components.json"));
378
457
  logger.break();
379
458
  } catch (error) {
380
- process.removeListener("exit", restoreBackupOnExit);
381
- restoreBackupOnExit();
382
459
  logger.break();
383
460
  handleError(error);
384
461
  } finally {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "sprawlify",
3
- "version": "0.0.90",
3
+ "version": "0.0.92",
4
4
  "type": "module",
5
5
  "description": "A command-line interface for Sprawlify.",
6
6
  "author": "sprawlify <npm@sprawlify.com>",
@@ -19,6 +19,7 @@
19
19
  "dependencies": {
20
20
  "@dotenvx/dotenvx": "^1.55.1",
21
21
  "commander": "^14.0.3",
22
+ "dedent": "^1.7.2",
22
23
  "fs-extra": "^11.3.4",
23
24
  "kleur": "^4.1.5",
24
25
  "prompts": "^2.4.2",