create-gridland 0.2.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.
- package/dist/app-BRWRSR5Q.js +317 -0
- package/dist/chunk-HO6BR4JK.js +74 -0
- package/dist/index.js +3605 -0
- package/package.json +31 -0
- package/templates/next/app/layout.tsx +18 -0
- package/templates/next/app/page.tsx +74 -0
- package/templates/next/next-env.d.ts +5 -0
- package/templates/next/next.config.ts +3 -0
- package/templates/next/package.json +21 -0
- package/templates/next/tsconfig.json +20 -0
- package/templates/shared/_gitignore +6 -0
- package/templates/shared/gridland-jsx.d.ts +36 -0
- package/templates/vite/index.html +16 -0
- package/templates/vite/package.json +22 -0
- package/templates/vite/src/App.tsx +68 -0
- package/templates/vite/src/main.tsx +4 -0
- package/templates/vite/tsconfig.json +13 -0
- package/templates/vite/vite.config.ts +13 -0
|
@@ -0,0 +1,317 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
import {
|
|
3
|
+
DEFAULT_NAME,
|
|
4
|
+
validateProjectName
|
|
5
|
+
} from "./chunk-HO6BR4JK.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
|
+
};
|
|
@@ -0,0 +1,74 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
var __create = Object.create;
|
|
3
|
+
var __defProp = Object.defineProperty;
|
|
4
|
+
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
5
|
+
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
6
|
+
var __getProtoOf = Object.getPrototypeOf;
|
|
7
|
+
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
8
|
+
var __require = /* @__PURE__ */ ((x) => typeof require !== "undefined" ? require : typeof Proxy !== "undefined" ? new Proxy(x, {
|
|
9
|
+
get: (a, b) => (typeof require !== "undefined" ? require : a)[b]
|
|
10
|
+
}) : x)(function(x) {
|
|
11
|
+
if (typeof require !== "undefined") return require.apply(this, arguments);
|
|
12
|
+
throw Error('Dynamic require of "' + x + '" is not supported');
|
|
13
|
+
});
|
|
14
|
+
var __commonJS = (cb, mod) => function __require2() {
|
|
15
|
+
return mod || (0, cb[__getOwnPropNames(cb)[0]])((mod = { exports: {} }).exports, mod), mod.exports;
|
|
16
|
+
};
|
|
17
|
+
var __copyProps = (to, from, except, desc) => {
|
|
18
|
+
if (from && typeof from === "object" || typeof from === "function") {
|
|
19
|
+
for (let key of __getOwnPropNames(from))
|
|
20
|
+
if (!__hasOwnProp.call(to, key) && key !== except)
|
|
21
|
+
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
|
|
22
|
+
}
|
|
23
|
+
return to;
|
|
24
|
+
};
|
|
25
|
+
var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(
|
|
26
|
+
// If the importer is in node compatibility mode or this is not an ESM
|
|
27
|
+
// file that has been converted to a CommonJS file using a Babel-
|
|
28
|
+
// compatible transform (i.e. "__esModule" has not been set), then set
|
|
29
|
+
// "default" to the CommonJS "module.exports" for node compatibility.
|
|
30
|
+
isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target,
|
|
31
|
+
mod
|
|
32
|
+
));
|
|
33
|
+
|
|
34
|
+
// src/constants.ts
|
|
35
|
+
var DEFAULT_NAME = "my-gridland-app";
|
|
36
|
+
|
|
37
|
+
// src/helpers/validate.ts
|
|
38
|
+
import fs from "fs";
|
|
39
|
+
import path from "path";
|
|
40
|
+
var validNameRegex = /^(?:@[a-z0-9-*~][a-z0-9-*._~]*\/)?[a-z0-9-~][a-z0-9-._~]*$/;
|
|
41
|
+
function validateProjectName(name) {
|
|
42
|
+
if (!name) {
|
|
43
|
+
return { valid: false, message: "Project name cannot be empty" };
|
|
44
|
+
}
|
|
45
|
+
if (!validNameRegex.test(name)) {
|
|
46
|
+
return {
|
|
47
|
+
valid: false,
|
|
48
|
+
message: "Project name must be lowercase and URL-friendly (a-z, 0-9, hyphens, dots)"
|
|
49
|
+
};
|
|
50
|
+
}
|
|
51
|
+
return { valid: true };
|
|
52
|
+
}
|
|
53
|
+
function checkDirectory(targetDir) {
|
|
54
|
+
try {
|
|
55
|
+
const files = fs.readdirSync(targetDir);
|
|
56
|
+
const isEmpty = files.length === 0 || files.length === 1 && files[0] === ".git";
|
|
57
|
+
return { exists: true, empty: isEmpty };
|
|
58
|
+
} catch {
|
|
59
|
+
return { exists: false, empty: true };
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
function resolveTargetDir(projectName) {
|
|
63
|
+
return path.resolve(process.cwd(), projectName);
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
export {
|
|
67
|
+
__require,
|
|
68
|
+
__commonJS,
|
|
69
|
+
__toESM,
|
|
70
|
+
DEFAULT_NAME,
|
|
71
|
+
validateProjectName,
|
|
72
|
+
checkDirectory,
|
|
73
|
+
resolveTargetDir
|
|
74
|
+
};
|