sprawlify 0.0.91 → 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 +232 -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.92";
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,189 @@ 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 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
225
390
  async function runInit(options) {
226
- const cwd = options.cwd;
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;
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.`);
408
+ }
227
409
  const componentsJsonPath = path.resolve(cwd, "components.json");
228
410
  logger.info("Starting project initialization...");
229
411
  const config = {
@@ -257,114 +439,23 @@ async function runInit(options) {
257
439
  throw error;
258
440
  }
259
441
  }
442
+ //#endregion
443
+ //#region src/commands/init.ts
260
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) => {
261
- let reinstallComponents = [];
262
- const restoreBackupOnExit = () => {};
263
- process.on("exit", restoreBackupOnExit);
264
445
  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];
446
+ const options = parseInitOptions(opts);
447
+ applyDefaultInitOptions(options);
448
+ validateExplicitOptions(options);
449
+ await promptForMissingSelections(options);
450
+ await ensureOverwriteAllowed(options);
357
451
  options.components = components;
358
452
  await loadEnvFiles(options.cwd);
359
453
  await runInit(options);
360
454
  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"));
455
+ logger.log(`Project initialization completed in ${highlighter.info(options.cwd)}.\nYou may now add components.`);
456
+ deleteFileBackup(path.resolve(options.cwd, "components.json"));
364
457
  logger.break();
365
458
  } catch (error) {
366
- process.removeListener("exit", restoreBackupOnExit);
367
- restoreBackupOnExit();
368
459
  logger.break();
369
460
  handleError(error);
370
461
  } finally {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "sprawlify",
3
- "version": "0.0.91",
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",