lattice-ui 0.4.3
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/LICENSE +7 -0
- package/README.md +45 -0
- package/dist/cli.d.ts +1 -0
- package/dist/cli.js +426 -0
- package/dist/commands/add.d.ts +3 -0
- package/dist/commands/add.js +156 -0
- package/dist/commands/create.d.ts +23 -0
- package/dist/commands/create.js +351 -0
- package/dist/commands/doctor.d.ts +2 -0
- package/dist/commands/doctor.js +164 -0
- package/dist/commands/init.d.ts +21 -0
- package/dist/commands/init.js +547 -0
- package/dist/commands/remove.d.ts +3 -0
- package/dist/commands/remove.js +113 -0
- package/dist/commands/selection.d.ts +6 -0
- package/dist/commands/selection.js +27 -0
- package/dist/commands/upgrade.d.ts +3 -0
- package/dist/commands/upgrade.js +150 -0
- package/dist/core/errors.d.ts +21 -0
- package/dist/core/errors.js +52 -0
- package/dist/core/fs/copy.d.ts +13 -0
- package/dist/core/fs/copy.js +129 -0
- package/dist/core/fs/json.d.ts +3 -0
- package/dist/core/fs/json.js +163 -0
- package/dist/core/fs/patch.d.ts +16 -0
- package/dist/core/fs/patch.js +89 -0
- package/dist/core/logger.d.ts +27 -0
- package/dist/core/logger.js +166 -0
- package/dist/core/npm/latest.d.ts +1 -0
- package/dist/core/npm/latest.js +40 -0
- package/dist/core/output.d.ts +8 -0
- package/dist/core/output.js +20 -0
- package/dist/core/pm/detect.d.ts +18 -0
- package/dist/core/pm/detect.js +147 -0
- package/dist/core/pm/npm.d.ts +2 -0
- package/dist/core/pm/npm.js +48 -0
- package/dist/core/pm/pnpm.d.ts +2 -0
- package/dist/core/pm/pnpm.js +48 -0
- package/dist/core/pm/types.d.ts +8 -0
- package/dist/core/pm/types.js +2 -0
- package/dist/core/pm/yarn.d.ts +2 -0
- package/dist/core/pm/yarn.js +48 -0
- package/dist/core/project/findRoot.d.ts +1 -0
- package/dist/core/project/findRoot.js +60 -0
- package/dist/core/project/readPackageJson.d.ts +13 -0
- package/dist/core/project/readPackageJson.js +41 -0
- package/dist/core/project/writePackageJson.d.ts +2 -0
- package/dist/core/project/writePackageJson.js +41 -0
- package/dist/core/prompt.d.ts +23 -0
- package/dist/core/prompt.js +217 -0
- package/dist/core/registry/load.d.ts +3 -0
- package/dist/core/registry/load.js +59 -0
- package/dist/core/registry/schema.d.ts +18 -0
- package/dist/core/registry/schema.js +87 -0
- package/dist/ctx.d.ts +27 -0
- package/dist/ctx.js +75 -0
- package/dist/index.d.ts +2 -0
- package/dist/index.js +21 -0
- package/package.json +27 -0
- package/registry/components.json +118 -0
- package/registry/presets.json +6 -0
- package/templates/init/default.project.json.template +67 -0
- package/templates/init/package.json +22 -0
- package/templates/init/src/client/App.tsx +21 -0
- package/templates/init/src/client/main.client.tsx +26 -0
- package/templates/init/src/server/main.server.ts +3 -0
- package/templates/init/src/shared/constants.ts +1 -0
- package/templates/init/tsconfig.json +27 -0
- package/templates/init-lint/.prettierrc +7 -0
- package/templates/init-lint/eslint.config.mjs +51 -0
- package/templates/init-lint/package.json +19 -0
|
@@ -0,0 +1,351 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
3
|
+
if (k2 === undefined) k2 = k;
|
|
4
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
5
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
6
|
+
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
7
|
+
}
|
|
8
|
+
Object.defineProperty(o, k2, desc);
|
|
9
|
+
}) : (function(o, m, k, k2) {
|
|
10
|
+
if (k2 === undefined) k2 = k;
|
|
11
|
+
o[k2] = m[k];
|
|
12
|
+
}));
|
|
13
|
+
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
|
14
|
+
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
|
15
|
+
}) : function(o, v) {
|
|
16
|
+
o["default"] = v;
|
|
17
|
+
});
|
|
18
|
+
var __importStar = (this && this.__importStar) || (function () {
|
|
19
|
+
var ownKeys = function(o) {
|
|
20
|
+
ownKeys = Object.getOwnPropertyNames || function (o) {
|
|
21
|
+
var ar = [];
|
|
22
|
+
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
|
|
23
|
+
return ar;
|
|
24
|
+
};
|
|
25
|
+
return ownKeys(o);
|
|
26
|
+
};
|
|
27
|
+
return function (mod) {
|
|
28
|
+
if (mod && mod.__esModule) return mod;
|
|
29
|
+
var result = {};
|
|
30
|
+
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
|
|
31
|
+
__setModuleDefault(result, mod);
|
|
32
|
+
return result;
|
|
33
|
+
};
|
|
34
|
+
})();
|
|
35
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
36
|
+
exports.runCreateCommand = runCreateCommand;
|
|
37
|
+
const node_child_process_1 = require("node:child_process");
|
|
38
|
+
const node_fs_1 = require("node:fs");
|
|
39
|
+
const path = __importStar(require("node:path"));
|
|
40
|
+
const errors_1 = require("../core/errors");
|
|
41
|
+
const copy_1 = require("../core/fs/copy");
|
|
42
|
+
const logger_1 = require("../core/logger");
|
|
43
|
+
const latest_1 = require("../core/npm/latest");
|
|
44
|
+
const output_1 = require("../core/output");
|
|
45
|
+
const detect_1 = require("../core/pm/detect");
|
|
46
|
+
const prompt_1 = require("../core/prompt");
|
|
47
|
+
const SUPPORTED_TEMPLATE = "rbxts";
|
|
48
|
+
const CORE_VERSION_PACKAGES = {
|
|
49
|
+
latticeStyle: "@lattice-ui/style",
|
|
50
|
+
latticeCli: "@lattice-ui/cli",
|
|
51
|
+
rbxtsReact: "@rbxts/react",
|
|
52
|
+
rbxtsReactRoblox: "@rbxts/react-roblox",
|
|
53
|
+
rbxtsCompilerTypes: "@rbxts/compiler-types",
|
|
54
|
+
rbxtsTypes: "@rbxts/types",
|
|
55
|
+
robloxTs: "roblox-ts",
|
|
56
|
+
typescript: "typescript",
|
|
57
|
+
};
|
|
58
|
+
const LINT_VERSION_PACKAGES = {
|
|
59
|
+
eslint: "eslint",
|
|
60
|
+
eslintEslintrc: "@eslint/eslintrc",
|
|
61
|
+
eslintJs: "@eslint/js",
|
|
62
|
+
eslintConfigPrettier: "eslint-config-prettier",
|
|
63
|
+
eslintPluginPrettier: "eslint-plugin-prettier",
|
|
64
|
+
eslintPluginRobloxTs: "eslint-plugin-roblox-ts",
|
|
65
|
+
typescriptEslintPlugin: "@typescript-eslint/eslint-plugin",
|
|
66
|
+
typescriptEslintParser: "@typescript-eslint/parser",
|
|
67
|
+
prettier: "prettier",
|
|
68
|
+
};
|
|
69
|
+
const GITIGNORE_ENTRIES = [
|
|
70
|
+
"node_modules",
|
|
71
|
+
"out",
|
|
72
|
+
"include",
|
|
73
|
+
"*.rbxl",
|
|
74
|
+
"*.rbxlx",
|
|
75
|
+
"*.rbxm",
|
|
76
|
+
"*.rbxmx",
|
|
77
|
+
"*.rbxl.lock",
|
|
78
|
+
"*.rbxlx.lock",
|
|
79
|
+
"*.rbxm.lock",
|
|
80
|
+
"*.rbxmx.lock",
|
|
81
|
+
"*.tsbuildinfo",
|
|
82
|
+
".pnpm-store",
|
|
83
|
+
".DS_Store",
|
|
84
|
+
];
|
|
85
|
+
const GITIGNORE_CONTENT = `${GITIGNORE_ENTRIES.join("\n")}\n`;
|
|
86
|
+
const PROJECT_DIRECTORIES = ["include", "out/shared", "out/server", "out/client"];
|
|
87
|
+
const PNPM_NPMRC_PATH = ".npmrc";
|
|
88
|
+
const PNPM_NODE_LINKER_LINE = "node-linker=hoisted";
|
|
89
|
+
function inferPackageName(projectRoot) {
|
|
90
|
+
const baseName = path.basename(projectRoot);
|
|
91
|
+
const normalized = baseName
|
|
92
|
+
.trim()
|
|
93
|
+
.toLowerCase()
|
|
94
|
+
.replace(/[^a-z0-9-]+/g, "-")
|
|
95
|
+
.replace(/^-+/, "")
|
|
96
|
+
.replace(/-+$/, "")
|
|
97
|
+
.replace(/-{2,}/g, "-");
|
|
98
|
+
return normalized.length > 0 ? normalized : "lattice-app";
|
|
99
|
+
}
|
|
100
|
+
function normalizeProjectPath(projectPath) {
|
|
101
|
+
const trimmed = projectPath.trim();
|
|
102
|
+
if (trimmed.length === 0) {
|
|
103
|
+
throw (0, errors_1.usageError)("create requires a non-empty <project-path>.");
|
|
104
|
+
}
|
|
105
|
+
if (path.isAbsolute(trimmed)) {
|
|
106
|
+
throw (0, errors_1.usageError)("create only accepts relative project paths.");
|
|
107
|
+
}
|
|
108
|
+
const normalized = path.normalize(trimmed);
|
|
109
|
+
const parts = normalized.split(path.sep).filter((part) => part.length > 0);
|
|
110
|
+
if (parts.length === 0 || parts.some((part) => part === "." || part === "..")) {
|
|
111
|
+
throw (0, errors_1.usageError)("create does not allow '.', '..', or parent-relative paths.");
|
|
112
|
+
}
|
|
113
|
+
return normalized;
|
|
114
|
+
}
|
|
115
|
+
async function ensureEmptyDirectory(targetRoot) {
|
|
116
|
+
try {
|
|
117
|
+
const stat = await node_fs_1.promises.stat(targetRoot);
|
|
118
|
+
if (!stat.isDirectory()) {
|
|
119
|
+
throw (0, errors_1.usageError)(`Target path exists and is not a directory: ${targetRoot}`);
|
|
120
|
+
}
|
|
121
|
+
const entries = await node_fs_1.promises.readdir(targetRoot);
|
|
122
|
+
if (entries.length > 0) {
|
|
123
|
+
throw (0, errors_1.usageError)(`Target directory must be empty: ${targetRoot}`);
|
|
124
|
+
}
|
|
125
|
+
}
|
|
126
|
+
catch (error) {
|
|
127
|
+
const nodeError = error;
|
|
128
|
+
if (nodeError.code !== "ENOENT") {
|
|
129
|
+
throw error;
|
|
130
|
+
}
|
|
131
|
+
await node_fs_1.promises.mkdir(targetRoot, { recursive: true });
|
|
132
|
+
}
|
|
133
|
+
}
|
|
134
|
+
async function runProcess(command, args, cwd) {
|
|
135
|
+
await new Promise((resolve, reject) => {
|
|
136
|
+
const child = (0, node_child_process_1.spawn)(command, args, {
|
|
137
|
+
cwd,
|
|
138
|
+
stdio: "inherit",
|
|
139
|
+
shell: process.platform === "win32",
|
|
140
|
+
});
|
|
141
|
+
child.on("error", (error) => {
|
|
142
|
+
reject((0, errors_1.packageManagerFailedError)(`Failed to run ${command}: ${error.message}`, error));
|
|
143
|
+
});
|
|
144
|
+
child.on("close", (code) => {
|
|
145
|
+
if (code === 0) {
|
|
146
|
+
resolve();
|
|
147
|
+
return;
|
|
148
|
+
}
|
|
149
|
+
reject((0, errors_1.packageManagerFailedError)(`${command} ${args.join(" ")} exited with code ${code ?? "unknown"}.`));
|
|
150
|
+
});
|
|
151
|
+
});
|
|
152
|
+
}
|
|
153
|
+
async function ensureGitignoreExists(targetRoot) {
|
|
154
|
+
const gitignorePath = path.join(targetRoot, ".gitignore");
|
|
155
|
+
try {
|
|
156
|
+
await node_fs_1.promises.access(gitignorePath);
|
|
157
|
+
}
|
|
158
|
+
catch {
|
|
159
|
+
await node_fs_1.promises.writeFile(gitignorePath, GITIGNORE_CONTENT, "utf8");
|
|
160
|
+
}
|
|
161
|
+
}
|
|
162
|
+
async function ensureProjectDirectories(targetRoot) {
|
|
163
|
+
await Promise.all(PROJECT_DIRECTORIES.map((directory) => node_fs_1.promises.mkdir(path.join(targetRoot, directory), { recursive: true })));
|
|
164
|
+
}
|
|
165
|
+
function normalizeTemplate(template) {
|
|
166
|
+
const value = template?.trim();
|
|
167
|
+
if (!value || value.length === 0) {
|
|
168
|
+
return SUPPORTED_TEMPLATE;
|
|
169
|
+
}
|
|
170
|
+
return value;
|
|
171
|
+
}
|
|
172
|
+
async function ensurePnpmNodeLinkerConfig(targetRoot, packageManager) {
|
|
173
|
+
if (packageManager !== "pnpm") {
|
|
174
|
+
return;
|
|
175
|
+
}
|
|
176
|
+
const npmrcPath = path.join(targetRoot, PNPM_NPMRC_PATH);
|
|
177
|
+
try {
|
|
178
|
+
const currentContent = await node_fs_1.promises.readFile(npmrcPath, "utf8");
|
|
179
|
+
const eol = currentContent.includes("\r\n") ? "\r\n" : "\n";
|
|
180
|
+
const lines = currentContent.split(/\r?\n/);
|
|
181
|
+
const nodeLinkerIndex = lines.findIndex((line) => /^\s*node-linker\s*=/.test(line));
|
|
182
|
+
if (nodeLinkerIndex >= 0) {
|
|
183
|
+
if (lines[nodeLinkerIndex].trim() === PNPM_NODE_LINKER_LINE) {
|
|
184
|
+
return;
|
|
185
|
+
}
|
|
186
|
+
lines[nodeLinkerIndex] = PNPM_NODE_LINKER_LINE;
|
|
187
|
+
await node_fs_1.promises.writeFile(npmrcPath, lines.join(eol), "utf8");
|
|
188
|
+
return;
|
|
189
|
+
}
|
|
190
|
+
const nextContent = currentContent.endsWith("\n")
|
|
191
|
+
? `${currentContent}${PNPM_NODE_LINKER_LINE}${eol}`
|
|
192
|
+
: `${currentContent}${eol}${PNPM_NODE_LINKER_LINE}${eol}`;
|
|
193
|
+
await node_fs_1.promises.writeFile(npmrcPath, nextContent, "utf8");
|
|
194
|
+
}
|
|
195
|
+
catch (error) {
|
|
196
|
+
const nodeError = error;
|
|
197
|
+
if (nodeError.code !== "ENOENT") {
|
|
198
|
+
throw error;
|
|
199
|
+
}
|
|
200
|
+
await node_fs_1.promises.writeFile(npmrcPath, `${PNPM_NODE_LINKER_LINE}\n`, "utf8");
|
|
201
|
+
}
|
|
202
|
+
}
|
|
203
|
+
async function selectTemplate(providedTemplate) {
|
|
204
|
+
const normalized = normalizeTemplate(providedTemplate);
|
|
205
|
+
if (normalized !== SUPPORTED_TEMPLATE) {
|
|
206
|
+
throw (0, errors_1.usageError)(`Unknown template: ${normalized}. Supported template: ${SUPPORTED_TEMPLATE}.`);
|
|
207
|
+
}
|
|
208
|
+
return normalized;
|
|
209
|
+
}
|
|
210
|
+
async function selectGitEnabled(runtime, providedGit) {
|
|
211
|
+
if (providedGit !== undefined) {
|
|
212
|
+
return providedGit;
|
|
213
|
+
}
|
|
214
|
+
return (0, prompt_1.promptConfirm)(runtime, "Initialize a git repository?", { defaultValue: false });
|
|
215
|
+
}
|
|
216
|
+
async function selectLintEnabled(runtime, providedLint) {
|
|
217
|
+
if (providedLint !== undefined) {
|
|
218
|
+
return providedLint;
|
|
219
|
+
}
|
|
220
|
+
if (runtime.yes) {
|
|
221
|
+
return true;
|
|
222
|
+
}
|
|
223
|
+
return (0, prompt_1.promptConfirm)(runtime, "Set up ESLint + Prettier?", { defaultValue: true });
|
|
224
|
+
}
|
|
225
|
+
async function resolveProjectPath(runtime, projectPath, promptInputFn) {
|
|
226
|
+
const value = projectPath?.trim();
|
|
227
|
+
if (value && value.length > 0) {
|
|
228
|
+
return value;
|
|
229
|
+
}
|
|
230
|
+
if (runtime.yes) {
|
|
231
|
+
throw (0, errors_1.usageError)("create requires [project-path] when using --yes.");
|
|
232
|
+
}
|
|
233
|
+
return promptInputFn(runtime, "What is your project named?", { required: true });
|
|
234
|
+
}
|
|
235
|
+
async function runCreateCommand(input, runtimeOverrides) {
|
|
236
|
+
const detectPackageManagerFn = runtimeOverrides?.detectPackageManagerFn ?? detect_1.detectPackageManager;
|
|
237
|
+
const resolveLatestVersionsFn = runtimeOverrides?.resolveLatestVersionsFn ?? latest_1.resolveLatestVersions;
|
|
238
|
+
const runProcessFn = runtimeOverrides?.runProcessFn ?? runProcess;
|
|
239
|
+
const createLoggerFn = runtimeOverrides?.createLoggerFn ?? logger_1.createLogger;
|
|
240
|
+
const promptInputFn = runtimeOverrides?.promptInputFn ?? prompt_1.promptInput;
|
|
241
|
+
const promptSelectFn = runtimeOverrides?.promptSelectFn ?? prompt_1.promptSelect;
|
|
242
|
+
const runtime = { yes: input.yes };
|
|
243
|
+
const template = await selectTemplate(input.template);
|
|
244
|
+
if (template !== SUPPORTED_TEMPLATE) {
|
|
245
|
+
throw (0, errors_1.usageError)(`Unsupported template: ${template}`);
|
|
246
|
+
}
|
|
247
|
+
const gitEnabled = await selectGitEnabled(runtime, input.git);
|
|
248
|
+
const lintEnabled = await selectLintEnabled(runtime, input.lint);
|
|
249
|
+
const providedProjectPath = await resolveProjectPath(runtime, input.projectPath, promptInputFn);
|
|
250
|
+
const relativeProjectPath = normalizeProjectPath(providedProjectPath);
|
|
251
|
+
const targetRoot = path.resolve(input.cwd, relativeProjectPath);
|
|
252
|
+
await ensureEmptyDirectory(targetRoot);
|
|
253
|
+
const logger = createLoggerFn({
|
|
254
|
+
verbose: false,
|
|
255
|
+
yes: input.yes,
|
|
256
|
+
});
|
|
257
|
+
const resolvedPm = await detectPackageManagerFn(targetRoot, input.pm, {
|
|
258
|
+
runtime,
|
|
259
|
+
promptSelectFn,
|
|
260
|
+
});
|
|
261
|
+
let packageManagerSourceLabel;
|
|
262
|
+
switch (resolvedPm.source) {
|
|
263
|
+
case "override":
|
|
264
|
+
packageManagerSourceLabel = "explicit --pm";
|
|
265
|
+
break;
|
|
266
|
+
case "lockfile":
|
|
267
|
+
packageManagerSourceLabel = "lockfile";
|
|
268
|
+
break;
|
|
269
|
+
case "installed":
|
|
270
|
+
packageManagerSourceLabel = "only installed package manager";
|
|
271
|
+
break;
|
|
272
|
+
case "prompt":
|
|
273
|
+
packageManagerSourceLabel = "interactive selection";
|
|
274
|
+
break;
|
|
275
|
+
}
|
|
276
|
+
logger.section("Creating a new Lattice app");
|
|
277
|
+
logger.kv("Location", targetRoot);
|
|
278
|
+
logger.kv("Template", template);
|
|
279
|
+
logger.kv("Resolved package manager", resolvedPm.name);
|
|
280
|
+
logger.kv("Package manager source", packageManagerSourceLabel);
|
|
281
|
+
logger.kv("Git", gitEnabled ? "enabled" : "disabled (use --git to enable)");
|
|
282
|
+
logger.kv("Lint/format", lintEnabled ? "enabled" : "disabled");
|
|
283
|
+
logger.section("Resolving");
|
|
284
|
+
const versionSpinner = logger.spinner("Resolving latest package versions...");
|
|
285
|
+
const packagesToResolve = [
|
|
286
|
+
...Object.values(CORE_VERSION_PACKAGES),
|
|
287
|
+
...(lintEnabled ? Object.values(LINT_VERSION_PACKAGES) : []),
|
|
288
|
+
];
|
|
289
|
+
const versions = await resolveLatestVersionsFn(packagesToResolve);
|
|
290
|
+
versionSpinner.succeed("Package versions resolved.");
|
|
291
|
+
const templateDir = path.resolve(__dirname, "../../templates/init");
|
|
292
|
+
logger.section("Scaffolding");
|
|
293
|
+
const scaffoldSpinner = logger.spinner(`Scaffolding ${template} template...`);
|
|
294
|
+
const report = await (0, copy_1.copyTemplateSafe)(templateDir, targetRoot, {
|
|
295
|
+
dryRun: false,
|
|
296
|
+
logger,
|
|
297
|
+
replacements: {
|
|
298
|
+
__PROJECT_NAME__: inferPackageName(targetRoot),
|
|
299
|
+
__LATTICE_STYLE_VERSION__: versions[CORE_VERSION_PACKAGES.latticeStyle],
|
|
300
|
+
__LATTICE_CLI_VERSION__: versions[CORE_VERSION_PACKAGES.latticeCli],
|
|
301
|
+
__RBXTS_REACT_VERSION__: versions[CORE_VERSION_PACKAGES.rbxtsReact],
|
|
302
|
+
__RBXTS_REACT_ROBLOX_VERSION__: versions[CORE_VERSION_PACKAGES.rbxtsReactRoblox],
|
|
303
|
+
__RBXTS_COMPILER_TYPES_VERSION__: versions[CORE_VERSION_PACKAGES.rbxtsCompilerTypes],
|
|
304
|
+
__RBXTS_TYPES_VERSION__: versions[CORE_VERSION_PACKAGES.rbxtsTypes],
|
|
305
|
+
__ROBLOX_TS_VERSION__: versions[CORE_VERSION_PACKAGES.robloxTs],
|
|
306
|
+
__TYPESCRIPT_VERSION__: versions[CORE_VERSION_PACKAGES.typescript],
|
|
307
|
+
},
|
|
308
|
+
});
|
|
309
|
+
scaffoldSpinner.succeed(`Scaffold complete (${report.created.length} created, ${report.merged.length} merged).`);
|
|
310
|
+
if (lintEnabled) {
|
|
311
|
+
logger.section("Configuring");
|
|
312
|
+
const lintTemplateDir = path.resolve(__dirname, "../../templates/init-lint");
|
|
313
|
+
const lintSpinner = logger.spinner("Applying ESLint + Prettier configuration...");
|
|
314
|
+
await (0, copy_1.copyTemplateSafe)(lintTemplateDir, targetRoot, {
|
|
315
|
+
dryRun: false,
|
|
316
|
+
logger,
|
|
317
|
+
replacements: {
|
|
318
|
+
__ESLINT_VERSION__: versions[LINT_VERSION_PACKAGES.eslint],
|
|
319
|
+
__ESLINT_ESLINTRC_VERSION__: versions[LINT_VERSION_PACKAGES.eslintEslintrc],
|
|
320
|
+
__ESLINT_JS_VERSION__: versions[LINT_VERSION_PACKAGES.eslintJs],
|
|
321
|
+
__ESLINT_CONFIG_PRETTIER_VERSION__: versions[LINT_VERSION_PACKAGES.eslintConfigPrettier],
|
|
322
|
+
__ESLINT_PLUGIN_PRETTIER_VERSION__: versions[LINT_VERSION_PACKAGES.eslintPluginPrettier],
|
|
323
|
+
__ESLINT_PLUGIN_ROBLOX_TS_VERSION__: versions[LINT_VERSION_PACKAGES.eslintPluginRobloxTs],
|
|
324
|
+
__TYPESCRIPT_ESLINT_PLUGIN_VERSION__: versions[LINT_VERSION_PACKAGES.typescriptEslintPlugin],
|
|
325
|
+
__TYPESCRIPT_ESLINT_PARSER_VERSION__: versions[LINT_VERSION_PACKAGES.typescriptEslintParser],
|
|
326
|
+
__PRETTIER_VERSION__: versions[LINT_VERSION_PACKAGES.prettier],
|
|
327
|
+
},
|
|
328
|
+
});
|
|
329
|
+
lintSpinner.succeed("Lint and format configuration applied.");
|
|
330
|
+
}
|
|
331
|
+
await ensureProjectDirectories(targetRoot);
|
|
332
|
+
await ensureGitignoreExists(targetRoot);
|
|
333
|
+
await ensurePnpmNodeLinkerConfig(targetRoot, resolvedPm.name);
|
|
334
|
+
logger.section("Installing");
|
|
335
|
+
const installSpinner = logger.spinner(`Installing dependencies with ${resolvedPm.name}...`);
|
|
336
|
+
await resolvedPm.manager.install(targetRoot);
|
|
337
|
+
installSpinner.succeed("Dependencies installed.");
|
|
338
|
+
if (gitEnabled) {
|
|
339
|
+
logger.section("Git");
|
|
340
|
+
const gitSpinner = logger.spinner("Initializing git repository...");
|
|
341
|
+
await runProcessFn("git", ["init"], targetRoot);
|
|
342
|
+
gitSpinner.succeed("Git repository initialized.");
|
|
343
|
+
}
|
|
344
|
+
logger.success(`Success! Created ${path.basename(targetRoot)} at ${targetRoot}`);
|
|
345
|
+
logger.kv("Files created", String(report.created.length));
|
|
346
|
+
logger.kv("Files merged", String(report.merged.length));
|
|
347
|
+
logger.section("Next Steps");
|
|
348
|
+
logger.step(`cd ${relativeProjectPath}`);
|
|
349
|
+
logger.step(`${resolvedPm.name} run build`);
|
|
350
|
+
logger.step(`${(0, output_1.resolveLocalLatticeCommand)(resolvedPm.name)} add --preset form`);
|
|
351
|
+
}
|
|
@@ -0,0 +1,164 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.runDoctorCommand = runDoctorCommand;
|
|
4
|
+
const errors_1 = require("../core/errors");
|
|
5
|
+
const patch_1 = require("../core/fs/patch");
|
|
6
|
+
const output_1 = require("../core/output");
|
|
7
|
+
const readPackageJson_1 = require("../core/project/readPackageJson");
|
|
8
|
+
const schema_1 = require("../core/registry/schema");
|
|
9
|
+
const LATTICE_TOOLING_PACKAGES = new Set(["@lattice-ui/cli"]);
|
|
10
|
+
function collectRecommendations(issues, pmName) {
|
|
11
|
+
const recommendationSet = new Set();
|
|
12
|
+
const localLattice = (0, output_1.resolveLocalLatticeCommand)(pmName);
|
|
13
|
+
for (const issue of issues) {
|
|
14
|
+
switch (issue.code) {
|
|
15
|
+
case "missing-lockfile":
|
|
16
|
+
recommendationSet.add(`${pmName} install`);
|
|
17
|
+
break;
|
|
18
|
+
case "multiple-lockfiles":
|
|
19
|
+
recommendationSet.add("Remove extra lockfiles and keep exactly one package manager lockfile.");
|
|
20
|
+
break;
|
|
21
|
+
case "package-manager-mismatch":
|
|
22
|
+
recommendationSet.add("Update package.json packageManager to match the active lockfile manager.");
|
|
23
|
+
break;
|
|
24
|
+
case "missing-lattice-packages":
|
|
25
|
+
recommendationSet.add(`${localLattice} add <component>`);
|
|
26
|
+
break;
|
|
27
|
+
case "unknown-lattice-package":
|
|
28
|
+
recommendationSet.add(`${localLattice} upgrade`);
|
|
29
|
+
break;
|
|
30
|
+
case "missing-peer":
|
|
31
|
+
case "missing-required-provider":
|
|
32
|
+
recommendationSet.add(`${localLattice} add <component>`);
|
|
33
|
+
break;
|
|
34
|
+
case "missing-optional-provider":
|
|
35
|
+
recommendationSet.add(`${localLattice} add <component>`);
|
|
36
|
+
break;
|
|
37
|
+
default:
|
|
38
|
+
break;
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
return [...recommendationSet];
|
|
42
|
+
}
|
|
43
|
+
async function runDoctorCommand(ctx) {
|
|
44
|
+
ctx.logger.section("Checking");
|
|
45
|
+
ctx.logger.kv("Project", ctx.projectRoot);
|
|
46
|
+
ctx.logger.kv("Resolved package manager", ctx.pmName);
|
|
47
|
+
const manifest = await (0, readPackageJson_1.readPackageJson)(ctx.projectRoot);
|
|
48
|
+
const installedDependencies = (0, patch_1.getDependencyNames)(manifest);
|
|
49
|
+
const issues = [];
|
|
50
|
+
if (ctx.detectedLockfiles.length === 0) {
|
|
51
|
+
issues.push({
|
|
52
|
+
code: "missing-lockfile",
|
|
53
|
+
level: "warn",
|
|
54
|
+
message: `No lockfile found. Resolved package manager is ${ctx.pmName}.`,
|
|
55
|
+
});
|
|
56
|
+
}
|
|
57
|
+
if (ctx.detectedLockfiles.length > 1) {
|
|
58
|
+
issues.push({
|
|
59
|
+
code: "multiple-lockfiles",
|
|
60
|
+
level: "warn",
|
|
61
|
+
message: `Multiple lockfiles detected: ${ctx.detectedLockfiles.join(", ")}. Using ${ctx.pmName}.`,
|
|
62
|
+
});
|
|
63
|
+
}
|
|
64
|
+
if (typeof manifest.packageManager === "string" && manifest.packageManager.length > 0) {
|
|
65
|
+
const normalized = manifest.packageManager.split("@")[0];
|
|
66
|
+
if (normalized !== ctx.pmName) {
|
|
67
|
+
issues.push({
|
|
68
|
+
code: "package-manager-mismatch",
|
|
69
|
+
level: "warn",
|
|
70
|
+
message: `packageManager field is ${manifest.packageManager} but resolved ${ctx.pmName}.`,
|
|
71
|
+
});
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
const installedLattice = [...installedDependencies]
|
|
75
|
+
.filter((name) => name.startsWith("@lattice-ui/"))
|
|
76
|
+
.sort((left, right) => left.localeCompare(right));
|
|
77
|
+
const installedLatticeComponents = installedLattice.filter((name) => !LATTICE_TOOLING_PACKAGES.has(name));
|
|
78
|
+
if (installedLatticeComponents.length === 0) {
|
|
79
|
+
issues.push({
|
|
80
|
+
code: "missing-lattice-packages",
|
|
81
|
+
level: "warn",
|
|
82
|
+
message: "No @lattice-ui component packages are installed.",
|
|
83
|
+
});
|
|
84
|
+
}
|
|
85
|
+
const componentByNpm = new Map();
|
|
86
|
+
for (const [componentName, entry] of Object.entries(ctx.registry.packages)) {
|
|
87
|
+
componentByNpm.set(entry.npm, componentName);
|
|
88
|
+
}
|
|
89
|
+
for (const npmPackage of installedLatticeComponents) {
|
|
90
|
+
const componentName = componentByNpm.get(npmPackage);
|
|
91
|
+
if (!componentName) {
|
|
92
|
+
issues.push({
|
|
93
|
+
code: "unknown-lattice-package",
|
|
94
|
+
level: "warn",
|
|
95
|
+
message: `${npmPackage} is installed but not found in CLI registry.`,
|
|
96
|
+
});
|
|
97
|
+
continue;
|
|
98
|
+
}
|
|
99
|
+
const entry = ctx.registry.packages[componentName];
|
|
100
|
+
for (const peer of entry.peers ?? []) {
|
|
101
|
+
if (!installedDependencies.has(peer)) {
|
|
102
|
+
issues.push({
|
|
103
|
+
code: "missing-peer",
|
|
104
|
+
level: "warn",
|
|
105
|
+
message: `${componentName} expects peer ${peer}, but it is not installed.`,
|
|
106
|
+
});
|
|
107
|
+
}
|
|
108
|
+
}
|
|
109
|
+
for (const rawProvider of entry.providers ?? []) {
|
|
110
|
+
const provider = (0, schema_1.parseProviderRequirement)(rawProvider);
|
|
111
|
+
if (installedDependencies.has(provider.packageName)) {
|
|
112
|
+
continue;
|
|
113
|
+
}
|
|
114
|
+
if (provider.optional) {
|
|
115
|
+
issues.push({
|
|
116
|
+
code: "missing-optional-provider",
|
|
117
|
+
level: "warn",
|
|
118
|
+
message: `${componentName} optional provider not found: ${provider.raw}`,
|
|
119
|
+
});
|
|
120
|
+
}
|
|
121
|
+
else {
|
|
122
|
+
issues.push({
|
|
123
|
+
code: "missing-required-provider",
|
|
124
|
+
level: "error",
|
|
125
|
+
message: `${componentName} required provider missing: ${provider.raw}`,
|
|
126
|
+
});
|
|
127
|
+
}
|
|
128
|
+
}
|
|
129
|
+
}
|
|
130
|
+
const warnings = issues.filter((issue) => issue.level === "warn");
|
|
131
|
+
const errors = issues.filter((issue) => issue.level === "error");
|
|
132
|
+
ctx.logger.section("Summary");
|
|
133
|
+
ctx.logger.kv("Errors", String(errors.length));
|
|
134
|
+
ctx.logger.kv("Warnings", String(warnings.length));
|
|
135
|
+
if (warnings.length > 0) {
|
|
136
|
+
ctx.logger.section("Warnings");
|
|
137
|
+
ctx.logger.list(warnings.map((issue) => issue.message));
|
|
138
|
+
}
|
|
139
|
+
if (errors.length > 0) {
|
|
140
|
+
ctx.logger.section("Errors");
|
|
141
|
+
ctx.logger.list(errors.map((issue) => issue.message));
|
|
142
|
+
}
|
|
143
|
+
const recommendations = collectRecommendations(issues, ctx.pmName);
|
|
144
|
+
if (recommendations.length > 0) {
|
|
145
|
+
ctx.logger.section("Recommended Commands");
|
|
146
|
+
const recommendationSummary = (0, output_1.summarizeItems)(recommendations);
|
|
147
|
+
ctx.logger.list(recommendationSummary.visible);
|
|
148
|
+
if (recommendationSummary.hidden > 0) {
|
|
149
|
+
ctx.logger.step(`...and ${recommendationSummary.hidden} more`);
|
|
150
|
+
}
|
|
151
|
+
}
|
|
152
|
+
ctx.logger.section("Result");
|
|
153
|
+
if (errors.length > 0) {
|
|
154
|
+
const message = `doctor found ${errors.length} error(s) and ${warnings.length} warning(s).`;
|
|
155
|
+
ctx.logger.error(message);
|
|
156
|
+
throw (0, errors_1.validationError)(message);
|
|
157
|
+
}
|
|
158
|
+
if (warnings.length > 0) {
|
|
159
|
+
ctx.logger.success(`doctor completed with ${warnings.length} warning(s).`);
|
|
160
|
+
}
|
|
161
|
+
else {
|
|
162
|
+
ctx.logger.success("doctor found no issues.");
|
|
163
|
+
}
|
|
164
|
+
}
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
import { createLogger } from "../core/logger";
|
|
2
|
+
import { resolveLatestVersions } from "../core/npm/latest";
|
|
3
|
+
import { detectPackageManager } from "../core/pm/detect";
|
|
4
|
+
import { promptConfirm, promptSelect } from "../core/prompt";
|
|
5
|
+
export interface InitCommandInput {
|
|
6
|
+
cwd: string;
|
|
7
|
+
pm?: string;
|
|
8
|
+
yes: boolean;
|
|
9
|
+
dryRun: boolean;
|
|
10
|
+
template?: string;
|
|
11
|
+
lint?: boolean;
|
|
12
|
+
}
|
|
13
|
+
interface InitCommandRuntimeOverrides {
|
|
14
|
+
detectPackageManagerFn?: typeof detectPackageManager;
|
|
15
|
+
resolveLatestVersionsFn?: typeof resolveLatestVersions;
|
|
16
|
+
createLoggerFn?: typeof createLogger;
|
|
17
|
+
promptSelectFn?: typeof promptSelect;
|
|
18
|
+
promptConfirmFn?: typeof promptConfirm;
|
|
19
|
+
}
|
|
20
|
+
export declare function runInitCommand(input: InitCommandInput, runtimeOverrides?: InitCommandRuntimeOverrides): Promise<void>;
|
|
21
|
+
export {};
|