create-gridland 0.2.1 → 0.2.2

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.
@@ -25,43 +25,7 @@ var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__ge
25
25
  mod
26
26
  ));
27
27
 
28
- // src/constants.ts
29
- var DEFAULT_NAME = "my-gridland-app";
30
-
31
- // src/helpers/validate.ts
32
- import fs from "fs";
33
- import path from "path";
34
- var validNameRegex = /^(?:@[a-z0-9-*~][a-z0-9-*._~]*\/)?[a-z0-9-~][a-z0-9-._~]*$/;
35
- function validateProjectName(name) {
36
- if (!name) {
37
- return { valid: false, message: "Project name cannot be empty" };
38
- }
39
- if (!validNameRegex.test(name)) {
40
- return {
41
- valid: false,
42
- message: "Project name must be lowercase and URL-friendly (a-z, 0-9, hyphens, dots)"
43
- };
44
- }
45
- return { valid: true };
46
- }
47
- function checkDirectory(targetDir) {
48
- try {
49
- const files = fs.readdirSync(targetDir);
50
- const isEmpty = files.length === 0 || files.length === 1 && files[0] === ".git";
51
- return { exists: true, empty: isEmpty };
52
- } catch {
53
- return { exists: false, empty: true };
54
- }
55
- }
56
- function resolveTargetDir(projectName) {
57
- return path.resolve(process.cwd(), projectName);
58
- }
59
-
60
28
  export {
61
29
  __commonJS,
62
- __toESM,
63
- DEFAULT_NAME,
64
- validateProjectName,
65
- checkDirectory,
66
- resolveTargetDir
30
+ __toESM
67
31
  };
package/dist/index.js CHANGED
@@ -1,12 +1,8 @@
1
1
  #!/usr/bin/env node
2
2
  import {
3
- DEFAULT_NAME,
4
3
  __commonJS,
5
- __toESM,
6
- checkDirectory,
7
- resolveTargetDir,
8
- validateProjectName
9
- } from "./chunk-NWV7K4UF.js";
4
+ __toESM
5
+ } from "./chunk-FYS2JH42.js";
10
6
 
11
7
  // ../../node_modules/.bun/picocolors@1.1.1/node_modules/picocolors/picocolors.js
12
8
  var require_picocolors = __commonJS({
@@ -136,6 +132,9 @@ function scaffold({ projectName, framework, targetDir }) {
136
132
  copyDir(frameworkDir, targetDir, replacements);
137
133
  }
138
134
 
135
+ // src/constants.ts
136
+ var DEFAULT_NAME = "my-gridland-app";
137
+
139
138
  // src/helpers/package-manager.ts
140
139
  var commands = {
141
140
  npm: { install: "npm install", dev: "npm run dev" },
@@ -185,6 +184,35 @@ function gitInit(cwd) {
185
184
  }
186
185
  }
187
186
 
187
+ // src/helpers/validate.ts
188
+ import fs2 from "fs";
189
+ import path2 from "path";
190
+ var validNameRegex = /^(?:@[a-z0-9-*~][a-z0-9-*._~]*\/)?[a-z0-9-~][a-z0-9-._~]*$/;
191
+ function validateProjectName(name) {
192
+ if (!name) {
193
+ return { valid: false, message: "Project name cannot be empty" };
194
+ }
195
+ if (!validNameRegex.test(name)) {
196
+ return {
197
+ valid: false,
198
+ message: "Project name must be lowercase and URL-friendly (a-z, 0-9, hyphens, dots)"
199
+ };
200
+ }
201
+ return { valid: true };
202
+ }
203
+ function checkDirectory(targetDir) {
204
+ try {
205
+ const files = fs2.readdirSync(targetDir);
206
+ const isEmpty = files.length === 0 || files.length === 1 && files[0] === ".git";
207
+ return { exists: true, empty: isEmpty };
208
+ } catch {
209
+ return { exists: false, empty: true };
210
+ }
211
+ }
212
+ function resolveTargetDir(projectName) {
213
+ return path2.resolve(process.cwd(), projectName);
214
+ }
215
+
188
216
  // src/index.ts
189
217
  var program = new Command();
190
218
  program.name("create-gridland").description("Create a new Gridland project").version("0.1.0").argument("[project-name]", "Name of the project").option("--framework <framework>", "Framework to use (vite or next)").option("--no-install", "Skip dependency installation").option("--no-git", "Skip git initialization").option("--yes", "Use defaults for all prompts").option("--overwrite", "Overwrite existing directory").action(async (projectNameArg, options) => {
@@ -212,30 +240,25 @@ program.name("create-gridland").description("Create a new Gridland project").ver
212
240
  pm
213
241
  });
214
242
  } else {
215
- const [{ createCliRenderer }, { createRoot }, React, { CreateGridlandApp }] = await Promise.all([
216
- import("@opentui/core"),
217
- import("@opentui/react"),
218
- import("react"),
219
- import("./app-3JKMN57G.js")
220
- ]);
221
- const renderer = createCliRenderer();
222
- const root = createRoot(renderer);
223
- await new Promise((resolve) => {
224
- root.render(
225
- React.createElement(CreateGridlandApp, {
226
- initialName: projectNameArg,
227
- onComplete: async (result) => {
228
- root.unmount();
229
- renderer.cleanup();
230
- await runScaffold({
231
- ...result,
232
- overwrite: options.overwrite,
233
- pm
234
- });
235
- resolve();
236
- }
237
- })
238
- );
243
+ const { promptProjectName, promptFramework, promptInstallDeps, promptInitGit } = await import("./prompts-HFNHE2AO.js");
244
+ console.log("\n GRIDLAND\n");
245
+ console.log(" Create a new Gridland project\n");
246
+ const projectName = await promptProjectName(projectNameArg);
247
+ const validation = validateProjectName(projectName);
248
+ if (!validation.valid) {
249
+ console.error(import_picocolors.default.red(validation.message));
250
+ process.exit(1);
251
+ }
252
+ const framework = await promptFramework();
253
+ const installDeps = await promptInstallDeps();
254
+ const initGit = await promptInitGit();
255
+ await runScaffold({
256
+ projectName,
257
+ framework,
258
+ installDeps,
259
+ initGit,
260
+ overwrite: options.overwrite,
261
+ pm
239
262
  });
240
263
  }
241
264
  });
@@ -0,0 +1,52 @@
1
+ #!/usr/bin/env node
2
+ import "./chunk-FYS2JH42.js";
3
+
4
+ // src/prompts.ts
5
+ import * as readline from "readline";
6
+ function createInterface2() {
7
+ return readline.createInterface({
8
+ input: process.stdin,
9
+ output: process.stdout
10
+ });
11
+ }
12
+ function ask(question) {
13
+ const rl = createInterface2();
14
+ return new Promise((resolve) => {
15
+ rl.question(question, (answer) => {
16
+ rl.close();
17
+ resolve(answer.trim());
18
+ });
19
+ });
20
+ }
21
+ function askYesNo(question, defaultYes = true) {
22
+ const hint = defaultYes ? "Y/n" : "y/N";
23
+ return ask(`${question} (${hint}): `).then((answer) => {
24
+ if (!answer) return defaultYes;
25
+ return answer.toLowerCase().startsWith("y");
26
+ });
27
+ }
28
+ async function promptProjectName(initial) {
29
+ if (initial) return initial;
30
+ const answer = await ask("Project name (my-gridland-app): ");
31
+ return answer || "my-gridland-app";
32
+ }
33
+ async function promptFramework() {
34
+ console.log("\nFramework:");
35
+ console.log(" 1. Vite");
36
+ console.log(" 2. Next.js");
37
+ const answer = await ask("\nSelect framework (1): ");
38
+ if (answer === "2" || answer.toLowerCase() === "next") return "next";
39
+ return "vite";
40
+ }
41
+ async function promptInstallDeps() {
42
+ return askYesNo("\nInstall dependencies?");
43
+ }
44
+ async function promptInitGit() {
45
+ return askYesNo("Initialize git repository?");
46
+ }
47
+ export {
48
+ promptFramework,
49
+ promptInitGit,
50
+ promptInstallDeps,
51
+ promptProjectName
52
+ };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "create-gridland",
3
- "version": "0.2.1",
3
+ "version": "0.2.2",
4
4
  "description": "Create a new Gridland project",
5
5
  "type": "module",
6
6
  "bin": {
@@ -16,14 +16,9 @@
16
16
  "test:e2e": "bun test src/__tests__/e2e.test.ts --timeout 120000"
17
17
  },
18
18
  "dependencies": {
19
- "@opentui/core": "^0.1.86",
20
- "@opentui/react": "^0.1.86",
21
- "commander": "^13.0.0",
22
- "react": "^19.0.0"
19
+ "commander": "^13.0.0"
23
20
  },
24
21
  "devDependencies": {
25
- "@gridland/ui": "workspace:*",
26
- "@types/react": "^19.0.0",
27
22
  "picocolors": "^1.1.0",
28
23
  "tsup": "^8.0.0",
29
24
  "typescript": "^5.7.0"
@@ -1,317 +0,0 @@
1
- #!/usr/bin/env node
2
- import {
3
- DEFAULT_NAME,
4
- validateProjectName
5
- } from "./chunk-NWV7K4UF.js";
6
-
7
- // src/app.tsx
8
- import { useState as useState7 } from "react";
9
-
10
- // ../ui/dist/index.js
11
- import { jsx } from "react/jsx-runtime";
12
- import { useState } from "react";
13
- import { jsx as jsx2 } from "react/jsx-runtime";
14
- import { jsx as jsx3, jsxs } from "react/jsx-runtime";
15
- import { jsx as jsx4 } from "react/jsx-runtime";
16
- import { useEffect, useState as useState2 } from "react";
17
- import { createContext, useContext } from "react";
18
- import { jsx as jsx5 } from "react/jsx-runtime";
19
- import { jsx as jsx6, jsxs as jsxs2 } from "react/jsx-runtime";
20
- import { useState as useState3 } from "react";
21
- import { jsx as jsx7, jsxs as jsxs3 } from "react/jsx-runtime";
22
- import { useState as useState4, useCallback } from "react";
23
- import { jsx as jsx8, jsxs as jsxs4 } from "react/jsx-runtime";
24
- import { useCallback as useCallback2 } from "react";
25
- import { jsx as jsx9 } from "react/jsx-runtime";
26
- import { useState as useState5, useCallback as useCallback3 } from "react";
27
- import { jsx as jsx10, jsxs as jsxs5 } from "react/jsx-runtime";
28
- import { Fragment } from "react";
29
- import { jsx as jsx11, jsxs as jsxs6 } from "react/jsx-runtime";
30
- import { Fragment as Fragment2, jsx as jsx12 } from "react/jsx-runtime";
31
- import { jsx as jsx13 } from "react/jsx-runtime";
32
- import { Fragment as Fragment3, jsx as jsx14, jsxs as jsxs7 } from "react/jsx-runtime";
33
- import { useState as useState6, useRef } from "react";
34
- import { Fragment as Fragment4, jsx as jsx15, jsxs as jsxs8 } from "react/jsx-runtime";
35
- import { jsx as jsx16, jsxs as jsxs9 } from "react/jsx-runtime";
36
- import { jsx as jsx17, jsxs as jsxs10 } from "react/jsx-runtime";
37
- import { jsx as jsx18, jsxs as jsxs11 } from "react/jsx-runtime";
38
- import { useTerminalDimensions } from "@opentui/react";
39
- var UNDERLINE = 1 << 3;
40
- var UNDERLINE_DASHED = 1 << 4;
41
- var UNDERLINE_DOTTED = 1 << 6;
42
- var BOLD = 1 << 0;
43
- var DIM = 1 << 1;
44
- var ITALIC = 1 << 2;
45
- var UNDERLINE2 = 1 << 3;
46
- var INVERSE = 1 << 5;
47
- function Ascii({ text, font, color }) {
48
- return /* @__PURE__ */ jsx4("ascii-font", { text, font, style: { fg: color } });
49
- }
50
- var darkTheme = {
51
- primary: "#FF71CE",
52
- accent: "#01CDFE",
53
- secondary: "#B967FF",
54
- muted: "#7B6F8E",
55
- border: "#B967FF",
56
- text: "#F0E6FF",
57
- success: "#05FFA1",
58
- error: "#FF6B6B",
59
- warning: "#FFC164"
60
- };
61
- var ThemeContext = createContext(null);
62
- function useTheme() {
63
- return useContext(ThemeContext) ?? darkTheme;
64
- }
65
- var VARIANTS = {
66
- dots: { frames: ["\u280B", "\u2819", "\u2839", "\u2838", "\u283C", "\u2834", "\u2826", "\u2827", "\u2807", "\u280F"], interval: 83 },
67
- pulse: { frames: ["\xB7", "\u2219", "\u25CF", "\u2219", "\xB7", "\xB7", "\xB7"], interval: 180 },
68
- meter: { frames: ["\u25B1\u25B1\u25B1", "\u25B0\u25B1\u25B1", "\u25B0\u25B0\u25B1", "\u25B0\u25B0\u25B0", "\u25B0\u25B0\u25B1", "\u25B0\u25B1\u25B1", "\u25B1\u25B1\u25B1"], interval: 143 },
69
- bloom: { frames: ["\xB7", "\u2726", "\u2727", "\u2739", "\u273A", "\u274B", "\u2738", "\u2735", "\u2738", "\u274B", "\u273A", "\u2739", "\u2727", "\u2726", "\xB7", "\xB7"], interval: 100 },
70
- ellipsis: { frames: [" ", ". ", ".. ", "..."], interval: 333 }
71
- };
72
- var VARIANT_NAMES = Object.keys(VARIANTS);
73
- function Spinner({ variant = "dots", text, color }) {
74
- const theme = useTheme();
75
- const resolvedColor = color ?? theme.accent;
76
- const { frames, interval } = VARIANTS[variant];
77
- const [frame, setFrame] = useState2(0);
78
- useEffect(() => {
79
- setFrame(0);
80
- const timer = setInterval(() => {
81
- setFrame((prev) => (prev + 1) % frames.length);
82
- }, interval);
83
- return () => clearInterval(timer);
84
- }, [variant]);
85
- return /* @__PURE__ */ jsxs2("text", { children: [
86
- /* @__PURE__ */ jsx6("span", { style: { fg: resolvedColor }, children: frames[frame] }),
87
- text ? /* @__PURE__ */ jsxs2("span", { children: [
88
- " ",
89
- text
90
- ] }) : null
91
- ] });
92
- }
93
- function TextInput({
94
- value: controlledValue,
95
- onChange,
96
- onSubmit,
97
- placeholder = "",
98
- prompt = "> ",
99
- promptColor,
100
- focus = true,
101
- maxLength
102
- }) {
103
- const theme = useTheme();
104
- const resolvedPromptColor = promptColor ?? theme.accent;
105
- const [internalValue, setInternalValue] = useState4("");
106
- const isControlled = controlledValue !== void 0;
107
- const displayValue = isControlled ? controlledValue : internalValue;
108
- const handleInput = useCallback(
109
- (newValue) => {
110
- if (!isControlled) setInternalValue(newValue);
111
- onChange?.(newValue);
112
- },
113
- [isControlled, onChange]
114
- );
115
- const handleSubmit = useCallback(
116
- (value) => {
117
- onSubmit?.(value);
118
- if (!isControlled) setInternalValue("");
119
- },
120
- [isControlled, onSubmit]
121
- );
122
- return /* @__PURE__ */ jsxs4("box", { children: [
123
- prompt && /* @__PURE__ */ jsx8("text", { style: { fg: resolvedPromptColor }, children: prompt }),
124
- /* @__PURE__ */ jsx8(
125
- "input",
126
- {
127
- value: displayValue,
128
- placeholder,
129
- maxLength,
130
- focused: focus,
131
- onInput: handleInput,
132
- onSubmit: handleSubmit
133
- }
134
- )
135
- ] });
136
- }
137
- function SelectInput({
138
- items: items2 = [],
139
- focus = true,
140
- initialIndex = 0,
141
- limit,
142
- onSelect,
143
- onHighlight,
144
- textColor,
145
- selectedTextColor,
146
- focusedTextColor,
147
- backgroundColor,
148
- selectedBackgroundColor,
149
- focusedBackgroundColor
150
- }) {
151
- const theme = useTheme();
152
- const resolvedTextColor = textColor ?? theme.text;
153
- const resolvedSelectedTextColor = selectedTextColor ?? theme.primary;
154
- const resolvedFocusedTextColor = focusedTextColor ?? theme.primary;
155
- const options = items2.map((item) => ({
156
- name: item.label,
157
- value: item.value
158
- }));
159
- const handleSelect = useCallback2(
160
- (index) => {
161
- const item = items2[index];
162
- if (item) onSelect?.(item);
163
- },
164
- [items2, onSelect]
165
- );
166
- const handleChange = useCallback2(
167
- (index) => {
168
- const item = items2[index];
169
- if (item) onHighlight?.(item);
170
- },
171
- [items2, onHighlight]
172
- );
173
- return /* @__PURE__ */ jsx9(
174
- "select",
175
- {
176
- options,
177
- selectedIndex: initialIndex,
178
- focused: focus,
179
- wrapSelection: true,
180
- onChange: handleChange,
181
- onSelect: handleSelect,
182
- height: limit ?? items2.length,
183
- showDescription: false,
184
- textColor: resolvedTextColor,
185
- selectedTextColor: resolvedSelectedTextColor,
186
- focusedTextColor: resolvedFocusedTextColor,
187
- backgroundColor: backgroundColor ?? "transparent",
188
- selectedBackgroundColor: selectedBackgroundColor ?? "transparent",
189
- focusedBackgroundColor: focusedBackgroundColor ?? "transparent"
190
- }
191
- );
192
- }
193
-
194
- // src/components/confirm.tsx
195
- import { jsx as jsx19 } from "react/jsx-runtime";
196
- var items = [
197
- { label: "Yes", value: true },
198
- { label: "No", value: false }
199
- ];
200
- function Confirm({ onConfirm, defaultValue = true, focus = true }) {
201
- return /* @__PURE__ */ jsx19(
202
- SelectInput,
203
- {
204
- items,
205
- initialIndex: defaultValue ? 0 : 1,
206
- onSelect: (item) => onConfirm(item.value),
207
- focus
208
- }
209
- );
210
- }
211
-
212
- // src/components/step.tsx
213
- import { jsx as jsx20, jsxs as jsxs12 } from "react/jsx-runtime";
214
- function Step({ label, active, completed, children }) {
215
- if (!active && !completed) return null;
216
- return /* @__PURE__ */ jsxs12("box", { flexDirection: "column", marginBottom: 1, children: [
217
- /* @__PURE__ */ jsxs12("text", { fg: completed ? "#a3be8c" : "#88c0d0", children: [
218
- completed ? "\u2713" : "\u203A",
219
- " ",
220
- label
221
- ] }),
222
- active && /* @__PURE__ */ jsx20("box", { marginLeft: 2, children })
223
- ] });
224
- }
225
-
226
- // src/app.tsx
227
- import { jsx as jsx21, jsxs as jsxs13 } from "react/jsx-runtime";
228
- var frameworkItems = [
229
- { label: "Vite", value: "vite" },
230
- { label: "Next.js", value: "next" }
231
- ];
232
- function CreateGridlandApp({ initialName, onComplete }) {
233
- const [step, setStep] = useState7(initialName ? "framework" : "name");
234
- const [nameInput, setNameInput] = useState7(initialName ?? "");
235
- const [framework, setFramework] = useState7(null);
236
- const [installDeps, setInstallDeps] = useState7(null);
237
- const [error, setError] = useState7(null);
238
- const resolvedName = nameInput || DEFAULT_NAME;
239
- const handleNameSubmit = (value) => {
240
- const name = value || DEFAULT_NAME;
241
- const validation = validateProjectName(name);
242
- if (!validation.valid) {
243
- setError(validation.message);
244
- return;
245
- }
246
- setError(null);
247
- setStep("framework");
248
- };
249
- const handleFrameworkSelect = (item) => {
250
- setFramework(item.value);
251
- setStep("install");
252
- };
253
- const handleInstallConfirm = (value) => {
254
- setInstallDeps(value);
255
- setStep("git");
256
- };
257
- const handleGitConfirm = (value) => {
258
- setStep("done");
259
- onComplete({
260
- projectName: resolvedName,
261
- framework,
262
- installDeps,
263
- initGit: value
264
- });
265
- };
266
- return /* @__PURE__ */ jsxs13("box", { flexDirection: "column", padding: 1, children: [
267
- /* @__PURE__ */ jsx21(Ascii, { text: "GRIDLAND", font: "tiny", color: "#88c0d0" }),
268
- /* @__PURE__ */ jsx21("box", { marginBottom: 1, children: /* @__PURE__ */ jsx21("text", { fg: "#81a1c1", children: "Create a new Gridland project" }) }),
269
- /* @__PURE__ */ jsx21(
270
- Step,
271
- {
272
- label: step === "name" ? "Project name" : `Project name: ${resolvedName}`,
273
- active: step === "name",
274
- completed: step !== "name",
275
- children: /* @__PURE__ */ jsxs13("box", { flexDirection: "column", children: [
276
- /* @__PURE__ */ jsx21(
277
- TextInput,
278
- {
279
- value: nameInput,
280
- onChange: setNameInput,
281
- onSubmit: handleNameSubmit,
282
- placeholder: DEFAULT_NAME,
283
- focus: step === "name"
284
- }
285
- ),
286
- error && /* @__PURE__ */ jsx21("text", { fg: "#bf616a", children: error })
287
- ] })
288
- }
289
- ),
290
- /* @__PURE__ */ jsx21(Step, { label: "Framework", active: step === "framework", completed: !!framework, children: /* @__PURE__ */ jsx21(
291
- SelectInput,
292
- {
293
- items: frameworkItems,
294
- onSelect: handleFrameworkSelect,
295
- focus: step === "framework"
296
- }
297
- ) }),
298
- /* @__PURE__ */ jsx21(Step, { label: "Install dependencies?", active: step === "install", completed: installDeps !== null, children: /* @__PURE__ */ jsx21(
299
- Confirm,
300
- {
301
- onConfirm: handleInstallConfirm,
302
- focus: step === "install"
303
- }
304
- ) }),
305
- /* @__PURE__ */ jsx21(Step, { label: "Initialize git repository?", active: step === "git", completed: step === "done", children: /* @__PURE__ */ jsx21(
306
- Confirm,
307
- {
308
- onConfirm: handleGitConfirm,
309
- focus: step === "git"
310
- }
311
- ) }),
312
- step === "done" && /* @__PURE__ */ jsx21("box", { marginTop: 1, children: /* @__PURE__ */ jsx21(Spinner, { variant: "dots", text: "Scaffolding project..." }) })
313
- ] });
314
- }
315
- export {
316
- CreateGridlandApp
317
- };