sprawlify 0.0.91 → 0.0.93

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 +360 -141
  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.91";
11
+ var version = "0.0.93";
11
12
  //#endregion
12
13
  //#region src/utils/file-helper.ts
13
14
  const FILE_BACKUP_SUFFIX = ".bak";
@@ -148,22 +149,27 @@ 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
- ]
152
+ //#region src/utils/env-loader.ts
153
+ async function loadEnvFiles(cwd = process.cwd()) {
154
+ try {
155
+ const { config } = await import("@dotenvx/dotenvx");
156
+ for (const envFile of [
157
+ ".env.local",
158
+ ".env.development.local",
159
+ ".env.development",
160
+ ".env"
161
+ ]) {
162
+ const envPath = join(cwd, envFile);
163
+ if (existsSync(envPath)) config({
164
+ path: envPath,
165
+ overload: false,
166
+ quiet: true
167
+ });
168
+ }
169
+ } catch (error) {
170
+ logger.warn("Failed to load env files:", error);
165
171
  }
166
- };
172
+ }
167
173
  //#endregion
168
174
  //#region src/frameworks/index.ts
169
175
  const FRAMEWORKS = {
@@ -187,29 +193,24 @@ const METAFRAMEWORKS = {
187
193
  }
188
194
  };
189
195
  //#endregion
190
- //#region src/utils/env-loader.ts
191
- async function loadEnvFiles(cwd = process.cwd()) {
192
- try {
193
- const { config } = await import("@dotenvx/dotenvx");
194
- for (const envFile of [
195
- ".env.local",
196
- ".env.development.local",
197
- ".env.development",
198
- ".env"
199
- ]) {
200
- const envPath = join(cwd, envFile);
201
- if (existsSync(envPath)) config({
202
- path: envPath,
203
- overload: false,
204
- quiet: true
205
- });
206
- }
207
- } catch (error) {
208
- logger.warn("Failed to load env files:", error);
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
+ ]
209
210
  }
210
- }
211
+ };
211
212
  //#endregion
212
- //#region src/commands/init.ts
213
+ //#region src/commands/init/options.ts
213
214
  const initOptionsSchema = z.object({
214
215
  cwd: z.string(),
215
216
  name: z.string().optional(),
@@ -222,8 +223,317 @@ const initOptionsSchema = z.object({
222
223
  framework: z.string().optional(),
223
224
  metaframework: z.string().optional()
224
225
  });
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);
264
+ }
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);
281
+ }
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 \0@oxc-project+runtime@0.115.0/helpers/taggedTemplateLiteral.js
344
+ function _taggedTemplateLiteral(e, t) {
345
+ return t || (t = e.slice(0)), Object.freeze(Object.defineProperties(e, { raw: { value: Object.freeze(t) } }));
346
+ }
347
+ //#endregion
348
+ //#region src/templates/react.ts
349
+ var _templateObject;
350
+ //#endregion
351
+ //#region src/templates/index.ts
352
+ const templates = {
353
+ react: createTemplate({ files: [
354
+ {
355
+ path: "src/root.tsx",
356
+ content: dedent`export function Root() {
357
+ return (
358
+ <div>
359
+ <h1 className="text-2xl font-bold">Welcome to Sprawlify + React!</h1>
360
+ <p className="mt-4 text-lg">This is your root component.</p>
361
+ </div>
362
+ )
363
+ }`
364
+ },
365
+ {
366
+ path: "src/main.tsx",
367
+ content: dedent`import { StrictMode } from 'react'
368
+ import { createRoot } from 'react-dom/client'
369
+ import { Root } from './root'
370
+ import './globals.css'
371
+
372
+ createRoot(document.getElementById('root')!).render(
373
+ <StrictMode>
374
+ <Root />
375
+ </StrictMode>,
376
+ )`
377
+ },
378
+ {
379
+ path: ".gitignore",
380
+ content: dedent`# Logs
381
+ logs
382
+ *.log
383
+ npm-debug.log*
384
+ yarn-debug.log*
385
+ yarn-error.log*
386
+ pnpm-debug.log*
387
+ lerna-debug.log*
388
+
389
+ node_modules
390
+ dist
391
+ dist-ssr
392
+ *.local
393
+
394
+ # Editor directories and files
395
+ .vscode/*
396
+ !.vscode/extensions.json
397
+ .idea
398
+ .DS_Store
399
+ *.suo
400
+ *.ntvs*
401
+ *.njsproj
402
+ *.sln
403
+ *.sw?`
404
+ },
405
+ {
406
+ path: "index.html",
407
+ content: dedent(_templateObject || (_templateObject = _taggedTemplateLiteral(["<!DOCTYPE html>\n<html lang=\"en\">\n <head>\n <meta charset=\"UTF-8\" />\n <meta name=\"viewport\" content=\"width=device-width, initial-scale=1.0\" />\n <title>Sprawlify + React</title>\n </head>\n <body>\n <div id=\"root\"></div>\n <script type=\"module\" src=\"/src/main.tsx\"><\/script>\n </body>\n</html>"])))
408
+ },
409
+ {
410
+ path: "package.json",
411
+ content: dedent`{
412
+ "private": true,
413
+ "version": "0.0.0",
414
+ "type": "module",
415
+ "scripts": {
416
+ "dev": "vite",
417
+ "build": "tsc -b && vite build",
418
+ "preview": "vite preview"
419
+ },
420
+ "dependencies": {
421
+ "react": "^19.2.4",
422
+ "react-dom": "^19.2.4"
423
+ },
424
+ "devDependencies": {
425
+ "@types/node": "^24.12.0",
426
+ "@types/react": "^19.2.14",
427
+ "@types/react-dom": "^19.2.3",
428
+ "@vitejs/plugin-react": "^6.0.1",
429
+ "globals": "^17.4.0",
430
+ "typescript": "~5.9.3",
431
+ "vite": "^8.0.0"
432
+ }
433
+ }`
434
+ },
435
+ {
436
+ path: "tsconfig.app.json",
437
+ content: dedent`{
438
+ "compilerOptions": {
439
+ "tsBuildInfoFile": "./node_modules/.tmp/tsconfig.app.tsbuildinfo",
440
+ "target": "ES2023",
441
+ "useDefineForClassFields": true,
442
+ "lib": ["ES2023", "DOM", "DOM.Iterable"],
443
+ "module": "ESNext",
444
+ "types": ["vite/client"],
445
+ "skipLibCheck": true,
446
+ /* Bundler mode */
447
+ "moduleResolution": "bundler",
448
+ "allowImportingTsExtensions": true,
449
+ "verbatimModuleSyntax": true,
450
+ "moduleDetection": "force",
451
+ "noEmit": true,
452
+ "jsx": "react-jsx",
453
+ /* Linting */
454
+ "strict": true,
455
+ "noUnusedLocals": true,
456
+ "noUnusedParameters": true,
457
+ "erasableSyntaxOnly": true,
458
+ "noFallthroughCasesInSwitch": true,
459
+ "noUncheckedSideEffectImports": true
460
+ },
461
+ "include": ["src"]
462
+ }`
463
+ },
464
+ {
465
+ path: "tsconfig.json",
466
+ content: dedent`{
467
+ "files": [],
468
+ "references": [
469
+ { "path": "./tsconfig.app.json" },
470
+ { "path": "./tsconfig.node.json" }
471
+ ]
472
+ }`
473
+ },
474
+ {
475
+ path: "tsconfig.node.json",
476
+ content: dedent`{
477
+ "compilerOptions": {
478
+ "tsBuildInfoFile": "./node_modules/.tmp/tsconfig.node.tsbuildinfo",
479
+ "target": "ES2023",
480
+ "lib": ["ES2023"],
481
+ "module": "ESNext",
482
+ "types": ["node"],
483
+ "skipLibCheck": true,
484
+ /* Bundler mode */
485
+ "moduleResolution": "bundler",
486
+ "allowImportingTsExtensions": true,
487
+ "verbatimModuleSyntax": true,
488
+ "moduleDetection": "force",
489
+ "noEmit": true,
490
+ /* Linting */
491
+ "strict": true,
492
+ "noUnusedLocals": true,
493
+ "noUnusedParameters": true,
494
+ "erasableSyntaxOnly": true,
495
+ "noFallthroughCasesInSwitch": true,
496
+ "noUncheckedSideEffectImports": true
497
+ },
498
+ "include": ["vite.config.ts"]
499
+ }`
500
+ },
501
+ {
502
+ path: "vite.config.ts",
503
+ content: dedent`import { defineConfig } from 'vite'
504
+ import react from '@vitejs/plugin-react'
505
+
506
+ export default defineConfig({
507
+ plugins: [react()],
508
+ })
509
+ `
510
+ }
511
+ ] }),
512
+ solid: createTemplate({ files: [] }),
513
+ svelte: createTemplate({ files: [] }),
514
+ vue: createTemplate({ files: [] })
515
+ };
516
+ //#endregion
517
+ //#region src/commands/init/run-init.ts
225
518
  async function runInit(options) {
226
- const cwd = options.cwd;
519
+ let cwd = options.cwd;
520
+ if (options.name && options.name !== ".") {
521
+ cwd = path.resolve(options.cwd, options.name);
522
+ await fsExtra.ensureDir(cwd);
523
+ options.cwd = cwd;
524
+ logger.info(`Using project directory: ${highlighter.info(options.name)}`);
525
+ }
526
+ const selectedTemplate = options.framework && options.framework in templates ? templates[options.framework] : void 0;
527
+ if (selectedTemplate?.files.length) for (const file of selectedTemplate.files) {
528
+ const targetPath = path.resolve(cwd, file.path);
529
+ if (fsExtra.existsSync(targetPath) && !options.override) {
530
+ logger.warn(`Skipping ${highlighter.info(file.path)} because it already exists. Use ${highlighter.info("--override")} to replace it.`);
531
+ continue;
532
+ }
533
+ await fsExtra.ensureDir(path.dirname(targetPath));
534
+ await fsExtra.writeFile(targetPath, file.content, "utf8");
535
+ logger.success(`Created ${highlighter.info(file.path)} template file.`);
536
+ }
227
537
  const componentsJsonPath = path.resolve(cwd, "components.json");
228
538
  logger.info("Starting project initialization...");
229
539
  const config = {
@@ -257,114 +567,23 @@ async function runInit(options) {
257
567
  throw error;
258
568
  }
259
569
  }
570
+ //#endregion
571
+ //#region src/commands/init.ts
260
572
  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) => {
261
- let reinstallComponents = [];
262
- const restoreBackupOnExit = () => {};
263
- process.on("exit", restoreBackupOnExit);
264
573
  try {
265
- const options = initOptionsSchema.parse({
266
- ...opts,
267
- reinstall: opts.reinstall,
268
- cwd: path.resolve(opts.cwd)
269
- });
270
- if (options.defaults) {
271
- options.preset = options.preset || "monochrome";
272
- options.framework = options.framework || "react";
273
- options.reinstall = options.reinstall ?? false;
274
- }
275
- if (options.preset && !(options.preset in PRESETS)) {
276
- logger.error(`Invalid preset: ${highlighter.info(options.preset)}. Available presets: ${Object.keys(PRESETS).map((f) => highlighter.info(f)).join(", ")}.`);
277
- logger.break();
278
- process.exit(1);
279
- }
280
- if (options.framework && !(options.framework in FRAMEWORKS)) {
281
- logger.error(`Invalid framework: ${highlighter.info(options.framework)}. Available frameworks: ${Object.keys(FRAMEWORKS).map((f) => highlighter.info(f)).join(", ")}.`);
282
- logger.break();
283
- process.exit(1);
284
- }
285
- if (options.metaframework && !(options.metaframework in METAFRAMEWORKS)) {
286
- logger.error(`Invalid metaframework: ${highlighter.info(options.metaframework)}. Available metaframeworks: ${Object.keys(METAFRAMEWORKS).map((f) => highlighter.info(f)).join(", ")}.`);
287
- logger.break();
288
- process.exit(1);
289
- }
290
- if (!options.framework) {
291
- const { framework } = await prompts({
292
- type: "select",
293
- name: "framework",
294
- message: "Which framework would you like to use?",
295
- choices: Object.keys(FRAMEWORKS).map((framework) => ({
296
- title: FRAMEWORKS[framework].title,
297
- value: framework
298
- })),
299
- initial: 0
300
- });
301
- if (!framework) {
302
- logger.error("Framework selection is required.");
303
- process.exit(1);
304
- }
305
- options.framework = framework;
306
- }
307
- if (!options.preset) {
308
- const { preset } = await prompts({
309
- type: "select",
310
- name: "preset",
311
- message: "Which preset would you like to use?",
312
- choices: Object.entries(PRESETS).map(([key, preset]) => ({
313
- title: preset.name,
314
- value: key,
315
- disabled: !preset.frameworks.includes(options.framework)
316
- })),
317
- initial: 0
318
- });
319
- if (!preset) {
320
- logger.error("Preset selection is required.");
321
- process.exit(1);
322
- }
323
- options.preset = preset;
324
- }
325
- if (!options.metaframework) {
326
- const { metaframework } = await prompts({
327
- type: "select",
328
- name: "metaframework",
329
- message: "Which metaframework would you like to use (optional)?",
330
- choices: [{
331
- title: "None",
332
- value: "none"
333
- }, ...Object.entries(METAFRAMEWORKS).filter(([_, meta]) => meta.frameworks.includes(options.framework)).map(([key, meta]) => ({
334
- title: meta.title,
335
- value: key
336
- }))],
337
- initial: 0
338
- });
339
- options.metaframework = metaframework === "none" ? void 0 : metaframework;
340
- }
341
- const cwd = options.cwd;
342
- if (fsExtra.existsSync(path.resolve(cwd, "components.json")) && !options.override) {
343
- const { overwrite } = await prompts({
344
- type: "confirm",
345
- name: "overwrite",
346
- message: `A ${highlighter.info("components.json")} file already exists. Would you like to overwrite it?`,
347
- initial: false
348
- });
349
- if (!overwrite) {
350
- logger.info(` To start over, remove the ${highlighter.info("components.json")} file and run ${highlighter.info("init")} again.`);
351
- logger.break();
352
- process.exit(1);
353
- }
354
- options.override = true;
355
- }
356
- if (reinstallComponents.length) components = [...components, ...reinstallComponents];
574
+ const options = parseInitOptions(opts);
575
+ applyDefaultInitOptions(options);
576
+ validateExplicitOptions(options);
577
+ await promptForMissingSelections(options);
578
+ await ensureOverwriteAllowed(options);
357
579
  options.components = components;
358
580
  await loadEnvFiles(options.cwd);
359
581
  await runInit(options);
360
582
  logger.break();
361
- logger.log(`Project initialization completed.\nYou may now add components.`);
362
- process.removeListener("exit", restoreBackupOnExit);
363
- deleteFileBackup(path.resolve(cwd, "components.json"));
583
+ logger.log(`Project initialization completed in ${highlighter.info(options.cwd)}.\nYou may now add components.`);
584
+ deleteFileBackup(path.resolve(options.cwd, "components.json"));
364
585
  logger.break();
365
586
  } catch (error) {
366
- process.removeListener("exit", restoreBackupOnExit);
367
- restoreBackupOnExit();
368
587
  logger.break();
369
588
  handleError(error);
370
589
  } finally {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "sprawlify",
3
- "version": "0.0.91",
3
+ "version": "0.0.93",
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",