@collie-lang/cli 1.1.1 → 1.3.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/index.js +391 -387
- package/dist/index.js.map +1 -1
- package/package.json +18 -22
- package/LICENSE +0 -21
- package/dist/index.cjs +0 -2588
- package/dist/index.cjs.map +0 -1
- package/dist/index.d.cts +0 -2
package/dist/index.cjs
DELETED
|
@@ -1,2588 +0,0 @@
|
|
|
1
|
-
#!/usr/bin/env node
|
|
2
|
-
"use strict";
|
|
3
|
-
var __create = Object.create;
|
|
4
|
-
var __defProp = Object.defineProperty;
|
|
5
|
-
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
6
|
-
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
7
|
-
var __getProtoOf = Object.getPrototypeOf;
|
|
8
|
-
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
9
|
-
var __copyProps = (to, from, except, desc) => {
|
|
10
|
-
if (from && typeof from === "object" || typeof from === "function") {
|
|
11
|
-
for (let key of __getOwnPropNames(from))
|
|
12
|
-
if (!__hasOwnProp.call(to, key) && key !== except)
|
|
13
|
-
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
|
|
14
|
-
}
|
|
15
|
-
return to;
|
|
16
|
-
};
|
|
17
|
-
var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(
|
|
18
|
-
// If the importer is in node compatibility mode or this is not an ESM
|
|
19
|
-
// file that has been converted to a CommonJS file using a Babel-
|
|
20
|
-
// compatible transform (i.e. "__esModule" has not been set), then set
|
|
21
|
-
// "default" to the CommonJS "module.exports" for node compatibility.
|
|
22
|
-
isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target,
|
|
23
|
-
mod
|
|
24
|
-
));
|
|
25
|
-
|
|
26
|
-
// src/index.ts
|
|
27
|
-
var import_node_child_process2 = require("child_process");
|
|
28
|
-
var import_promises9 = __toESM(require("fs/promises"), 1);
|
|
29
|
-
var import_node_fs4 = require("fs");
|
|
30
|
-
var import_node_path9 = __toESM(require("path"), 1);
|
|
31
|
-
var import_node_url2 = require("url");
|
|
32
|
-
var import_typescript = __toESM(require("typescript"), 1);
|
|
33
|
-
var import_fast_glob4 = __toESM(require("fast-glob"), 1);
|
|
34
|
-
var import_diff = require("diff");
|
|
35
|
-
var import_picocolors8 = __toESM(require("picocolors"), 1);
|
|
36
|
-
var import_prompts2 = __toESM(require("prompts"), 1);
|
|
37
|
-
|
|
38
|
-
// src/formatter.ts
|
|
39
|
-
var import_promises = __toESM(require("fs/promises"), 1);
|
|
40
|
-
var import_compiler = require("@collie-lang/compiler");
|
|
41
|
-
function formatSource(source, options = {}) {
|
|
42
|
-
return (0, import_compiler.formatCollie)(source, options);
|
|
43
|
-
}
|
|
44
|
-
|
|
45
|
-
// src/watcher.ts
|
|
46
|
-
var import_chokidar = __toESM(require("chokidar"), 1);
|
|
47
|
-
var import_compiler2 = require("@collie-lang/compiler");
|
|
48
|
-
var import_promises2 = __toESM(require("fs/promises"), 1);
|
|
49
|
-
var import_node_path2 = __toESM(require("path"), 1);
|
|
50
|
-
var import_picocolors = __toESM(require("picocolors"), 1);
|
|
51
|
-
|
|
52
|
-
// src/fs-utils.ts
|
|
53
|
-
var import_node_path = __toESM(require("path"), 1);
|
|
54
|
-
function resolveOutputPath(filepath, baseDir, outDir) {
|
|
55
|
-
const outputBase = outDir ?? baseDir;
|
|
56
|
-
const relative = import_node_path.default.relative(baseDir, filepath);
|
|
57
|
-
const ext = import_node_path.default.extname(relative);
|
|
58
|
-
const withoutExt = ext ? relative.slice(0, -ext.length) : relative;
|
|
59
|
-
return import_node_path.default.join(outputBase, `${withoutExt}.tsx`);
|
|
60
|
-
}
|
|
61
|
-
function toDisplayPath(target) {
|
|
62
|
-
const relative = import_node_path.default.relative(process.cwd(), target);
|
|
63
|
-
if (!relative || relative.startsWith("..") || relative.startsWith("..\\")) {
|
|
64
|
-
return target;
|
|
65
|
-
}
|
|
66
|
-
return relative;
|
|
67
|
-
}
|
|
68
|
-
|
|
69
|
-
// src/watcher.ts
|
|
70
|
-
async function watch(inputPath, options = {}) {
|
|
71
|
-
const resolvedInput = import_node_path2.default.resolve(process.cwd(), inputPath);
|
|
72
|
-
let stats;
|
|
73
|
-
try {
|
|
74
|
-
stats = await import_promises2.default.stat(resolvedInput);
|
|
75
|
-
} catch {
|
|
76
|
-
throw new Error(`Input path does not exist: ${inputPath}`);
|
|
77
|
-
}
|
|
78
|
-
const isDirectory = stats.isDirectory();
|
|
79
|
-
const ext = normalizeExtension(options.ext);
|
|
80
|
-
const baseDir = isDirectory ? resolvedInput : import_node_path2.default.dirname(resolvedInput);
|
|
81
|
-
const outDir = options.outDir ? import_node_path2.default.resolve(process.cwd(), options.outDir) : void 0;
|
|
82
|
-
const pattern = isDirectory ? import_node_path2.default.join(resolvedInput, `**/*${ext}`) : resolvedInput;
|
|
83
|
-
console.log(import_picocolors.default.cyan(`Watching ${toDisplayPath(resolvedInput)} for changes...
|
|
84
|
-
`));
|
|
85
|
-
const watcher = import_chokidar.default.watch(pattern, {
|
|
86
|
-
ignored: /node_modules/,
|
|
87
|
-
persistent: true,
|
|
88
|
-
ignoreInitial: false
|
|
89
|
-
});
|
|
90
|
-
watcher.on("add", (file) => {
|
|
91
|
-
void compileFile(file, baseDir, outDir, options);
|
|
92
|
-
});
|
|
93
|
-
watcher.on("change", (file) => {
|
|
94
|
-
if (options.verbose) {
|
|
95
|
-
console.log(import_picocolors.default.gray(`[${getTimestamp()}] Changed: ${toDisplayPath(file)}`));
|
|
96
|
-
}
|
|
97
|
-
void compileFile(file, baseDir, outDir, options);
|
|
98
|
-
});
|
|
99
|
-
watcher.on("unlink", (file) => {
|
|
100
|
-
void deleteCompiledFile(file, baseDir, outDir, options);
|
|
101
|
-
});
|
|
102
|
-
watcher.on("ready", () => {
|
|
103
|
-
console.log(import_picocolors.default.green("\nWatching for file changes...\n"));
|
|
104
|
-
});
|
|
105
|
-
await new Promise((resolve, reject) => {
|
|
106
|
-
watcher.on("close", resolve);
|
|
107
|
-
watcher.on("error", (error) => {
|
|
108
|
-
const err = error instanceof Error ? error : new Error(String(error));
|
|
109
|
-
console.error(import_picocolors.default.red(`[collie] Watcher error: ${err.message}`));
|
|
110
|
-
reject(err);
|
|
111
|
-
});
|
|
112
|
-
process.once("SIGINT", () => {
|
|
113
|
-
console.log(import_picocolors.default.yellow("\nStopping watch mode..."));
|
|
114
|
-
watcher.close().catch((error) => {
|
|
115
|
-
const err = error instanceof Error ? error : new Error(String(error));
|
|
116
|
-
console.error(import_picocolors.default.red(`[collie] Failed to stop watcher: ${err.message}`));
|
|
117
|
-
reject(err);
|
|
118
|
-
});
|
|
119
|
-
});
|
|
120
|
-
});
|
|
121
|
-
}
|
|
122
|
-
async function compileFile(filepath, baseDir, outDir, options) {
|
|
123
|
-
try {
|
|
124
|
-
const source = await import_promises2.default.readFile(filepath, "utf8");
|
|
125
|
-
const componentName = import_node_path2.default.basename(filepath, import_node_path2.default.extname(filepath));
|
|
126
|
-
const compileOptions = {
|
|
127
|
-
filename: filepath,
|
|
128
|
-
componentNameHint: componentName,
|
|
129
|
-
jsxRuntime: options.jsxRuntime ?? "automatic"
|
|
130
|
-
};
|
|
131
|
-
const result = (0, import_compiler2.compileToTsx)(source, compileOptions);
|
|
132
|
-
const errors = result.diagnostics.filter((d) => d.severity === "error");
|
|
133
|
-
if (errors.length) {
|
|
134
|
-
logDiagnostics(filepath, errors);
|
|
135
|
-
return;
|
|
136
|
-
}
|
|
137
|
-
const outputPath = resolveOutputPath(filepath, baseDir, outDir);
|
|
138
|
-
await import_promises2.default.mkdir(import_node_path2.default.dirname(outputPath), { recursive: true });
|
|
139
|
-
await import_promises2.default.writeFile(outputPath, result.code, "utf8");
|
|
140
|
-
if (options.sourcemap && result.map) {
|
|
141
|
-
await import_promises2.default.writeFile(`${outputPath}.map`, JSON.stringify(result.map), "utf8");
|
|
142
|
-
}
|
|
143
|
-
console.log(import_picocolors.default.green(`[${getTimestamp()}] Compiled ${toDisplayPath(filepath)} \u2192 ${toDisplayPath(outputPath)}`));
|
|
144
|
-
const warnings = result.diagnostics.filter((d) => d.severity === "warning");
|
|
145
|
-
if (warnings.length) {
|
|
146
|
-
logDiagnostics(filepath, warnings);
|
|
147
|
-
}
|
|
148
|
-
} catch (error) {
|
|
149
|
-
const message = error instanceof Error ? error.message : String(error);
|
|
150
|
-
console.error(import_picocolors.default.red(`[collie] Failed to compile ${toDisplayPath(filepath)}: ${message}`));
|
|
151
|
-
}
|
|
152
|
-
}
|
|
153
|
-
async function deleteCompiledFile(filepath, baseDir, outDir, options) {
|
|
154
|
-
try {
|
|
155
|
-
const outputPath = resolveOutputPath(filepath, baseDir, outDir);
|
|
156
|
-
await import_promises2.default.unlink(outputPath);
|
|
157
|
-
if (options.sourcemap) {
|
|
158
|
-
await import_promises2.default.unlink(`${outputPath}.map`).catch(() => {
|
|
159
|
-
});
|
|
160
|
-
}
|
|
161
|
-
console.log(import_picocolors.default.yellow(`[${getTimestamp()}] Deleted ${toDisplayPath(outputPath)}`));
|
|
162
|
-
} catch {
|
|
163
|
-
}
|
|
164
|
-
}
|
|
165
|
-
function logDiagnostics(file, diagnostics) {
|
|
166
|
-
for (const diag of diagnostics) {
|
|
167
|
-
const range = diag.range ?? diag.span;
|
|
168
|
-
const fileLabel = diag.filePath ?? diag.file ?? file;
|
|
169
|
-
const location = range ? `${range.start.line}:${range.start.col}` : "";
|
|
170
|
-
const prefix = location ? `${toDisplayPath(fileLabel)}:${location}` : toDisplayPath(fileLabel);
|
|
171
|
-
const code = diag.code ? ` (${diag.code})` : "";
|
|
172
|
-
const writer = diag.severity === "warning" ? import_picocolors.default.yellow : import_picocolors.default.red;
|
|
173
|
-
console[diag.severity === "warning" ? "warn" : "error"](
|
|
174
|
-
writer(`${prefix ? `${prefix}: ` : ""}${diag.severity}${code}: ${diag.message}`)
|
|
175
|
-
);
|
|
176
|
-
}
|
|
177
|
-
}
|
|
178
|
-
function normalizeExtension(ext) {
|
|
179
|
-
if (!ext || !ext.trim()) {
|
|
180
|
-
return ".collie";
|
|
181
|
-
}
|
|
182
|
-
return ext.startsWith(".") ? ext : `.${ext}`;
|
|
183
|
-
}
|
|
184
|
-
function getTimestamp() {
|
|
185
|
-
return (/* @__PURE__ */ new Date()).toLocaleTimeString("en-US", { hour12: false });
|
|
186
|
-
}
|
|
187
|
-
|
|
188
|
-
// src/builder.ts
|
|
189
|
-
var import_fast_glob = __toESM(require("fast-glob"), 1);
|
|
190
|
-
var import_compiler3 = require("@collie-lang/compiler");
|
|
191
|
-
var import_promises3 = __toESM(require("fs/promises"), 1);
|
|
192
|
-
var import_node_path3 = __toESM(require("path"), 1);
|
|
193
|
-
var import_picocolors3 = __toESM(require("picocolors"), 1);
|
|
194
|
-
|
|
195
|
-
// src/output.ts
|
|
196
|
-
var import_picocolors2 = __toESM(require("picocolors"), 1);
|
|
197
|
-
var SUMMARY_STYLES = {
|
|
198
|
-
success: { icon: "\u2714", color: import_picocolors2.default.green },
|
|
199
|
-
warning: { icon: "\u26A0", color: import_picocolors2.default.yellow },
|
|
200
|
-
error: { icon: "\u2716", color: import_picocolors2.default.red }
|
|
201
|
-
};
|
|
202
|
-
function printSummary(kind, message, detail, nextStep) {
|
|
203
|
-
const style = SUMMARY_STYLES[kind];
|
|
204
|
-
console.log(style.color(`${style.icon} ${message}`));
|
|
205
|
-
if (detail) {
|
|
206
|
-
console.log(import_picocolors2.default.dim(`Changed: ${detail}`));
|
|
207
|
-
}
|
|
208
|
-
if (nextStep) {
|
|
209
|
-
console.log(import_picocolors2.default.dim(`Next: ${nextStep}`));
|
|
210
|
-
}
|
|
211
|
-
}
|
|
212
|
-
function formatDiagnosticLine(diag, fallbackFile) {
|
|
213
|
-
const fileLabel = diag.filePath ?? diag.file ?? fallbackFile ?? "<unknown>";
|
|
214
|
-
const range = diag.range ?? diag.span;
|
|
215
|
-
const location = range ? `${fileLabel}:${range.start.line}:${range.start.col}` : fileLabel;
|
|
216
|
-
const code = diag.code ? ` (${diag.code})` : "";
|
|
217
|
-
return `${location}: ${diag.severity}${code}: ${diag.message}`;
|
|
218
|
-
}
|
|
219
|
-
|
|
220
|
-
// src/builder.ts
|
|
221
|
-
async function build(input, options = {}) {
|
|
222
|
-
const resolvedInput = import_node_path3.default.resolve(process.cwd(), input);
|
|
223
|
-
let stats;
|
|
224
|
-
try {
|
|
225
|
-
stats = await import_promises3.default.stat(resolvedInput);
|
|
226
|
-
} catch {
|
|
227
|
-
throw new Error(`Input path does not exist: ${input}`);
|
|
228
|
-
}
|
|
229
|
-
const isDirectory = stats.isDirectory();
|
|
230
|
-
const baseDir = isDirectory ? resolvedInput : import_node_path3.default.dirname(resolvedInput);
|
|
231
|
-
const outDir = options.outDir ? import_node_path3.default.resolve(process.cwd(), options.outDir) : void 0;
|
|
232
|
-
const files = isDirectory ? (await (0, import_fast_glob.default)("**/*.collie", { cwd: resolvedInput, absolute: true })).sort() : [resolvedInput];
|
|
233
|
-
if (!files.length) {
|
|
234
|
-
if (!options.quiet) {
|
|
235
|
-
printSummary(
|
|
236
|
-
"warning",
|
|
237
|
-
`No .collie files found under ${toDisplayPath(resolvedInput)}`,
|
|
238
|
-
void 0,
|
|
239
|
-
"add a .collie file or adjust the input path"
|
|
240
|
-
);
|
|
241
|
-
}
|
|
242
|
-
return { totalFiles: 0, successfulFiles: 0, errors: [] };
|
|
243
|
-
}
|
|
244
|
-
if (!options.quiet) {
|
|
245
|
-
console.log(import_picocolors3.default.cyan(`Compiling ${toDisplayPath(resolvedInput)}...`));
|
|
246
|
-
console.log("");
|
|
247
|
-
}
|
|
248
|
-
const result = {
|
|
249
|
-
totalFiles: files.length,
|
|
250
|
-
successfulFiles: 0,
|
|
251
|
-
errors: []
|
|
252
|
-
};
|
|
253
|
-
for (const file of files) {
|
|
254
|
-
const compileResult = await compileSingleFile(file, baseDir, outDir, options);
|
|
255
|
-
if (compileResult.success) {
|
|
256
|
-
result.successfulFiles++;
|
|
257
|
-
if (!options.quiet) {
|
|
258
|
-
console.log(import_picocolors3.default.green(`\u2714 ${toDisplayPath(file)} \u2192 ${toDisplayPath(compileResult.outputPath)}`));
|
|
259
|
-
}
|
|
260
|
-
if (options.verbose) {
|
|
261
|
-
logDiagnostics2(file, compileResult.diagnostics.filter((d) => d.severity === "warning"));
|
|
262
|
-
}
|
|
263
|
-
} else {
|
|
264
|
-
result.errors.push({ file, diagnostics: compileResult.diagnostics });
|
|
265
|
-
logDiagnostics2(file, compileResult.diagnostics, true);
|
|
266
|
-
}
|
|
267
|
-
}
|
|
268
|
-
if (!options.quiet) {
|
|
269
|
-
console.log("");
|
|
270
|
-
if (result.errors.length === 0) {
|
|
271
|
-
const changeDetail = outDir ? `wrote ${result.successfulFiles} .tsx file${result.successfulFiles === 1 ? "" : "s"} to ${toDisplayPath(outDir)}` : `wrote ${result.successfulFiles} .tsx file${result.successfulFiles === 1 ? "" : "s"} next to the source files`;
|
|
272
|
-
printSummary(
|
|
273
|
-
"success",
|
|
274
|
-
`Compiled ${result.totalFiles} .collie file${result.totalFiles === 1 ? "" : "s"}`,
|
|
275
|
-
changeDetail,
|
|
276
|
-
"import the generated .tsx files in your app"
|
|
277
|
-
);
|
|
278
|
-
} else {
|
|
279
|
-
const changeDetail = result.successfulFiles > 0 ? `wrote ${result.successfulFiles} .tsx file${result.successfulFiles === 1 ? "" : "s"} before failing` : void 0;
|
|
280
|
-
printSummary(
|
|
281
|
-
"error",
|
|
282
|
-
`Build failed with ${result.errors.length} error${result.errors.length === 1 ? "" : "s"}`,
|
|
283
|
-
changeDetail,
|
|
284
|
-
"fix the errors above and rerun collie build"
|
|
285
|
-
);
|
|
286
|
-
}
|
|
287
|
-
}
|
|
288
|
-
return result;
|
|
289
|
-
}
|
|
290
|
-
async function compileSingleFile(filepath, baseDir, outDir, options) {
|
|
291
|
-
try {
|
|
292
|
-
const source = await import_promises3.default.readFile(filepath, "utf8");
|
|
293
|
-
const componentName = import_node_path3.default.basename(filepath, import_node_path3.default.extname(filepath));
|
|
294
|
-
const compileOptions = {
|
|
295
|
-
filename: filepath,
|
|
296
|
-
componentNameHint: componentName,
|
|
297
|
-
jsxRuntime: options.jsxRuntime ?? "automatic"
|
|
298
|
-
};
|
|
299
|
-
const result = (0, import_compiler3.compileToTsx)(source, compileOptions);
|
|
300
|
-
const errors = result.diagnostics.filter((d) => d.severity === "error");
|
|
301
|
-
if (errors.length) {
|
|
302
|
-
return { success: false, diagnostics: errors };
|
|
303
|
-
}
|
|
304
|
-
const outputPath = resolveOutputPath(filepath, baseDir, outDir);
|
|
305
|
-
await import_promises3.default.mkdir(import_node_path3.default.dirname(outputPath), { recursive: true });
|
|
306
|
-
await import_promises3.default.writeFile(outputPath, result.code, "utf8");
|
|
307
|
-
if (options.sourcemap && result.map) {
|
|
308
|
-
await import_promises3.default.writeFile(`${outputPath}.map`, JSON.stringify(result.map), "utf8");
|
|
309
|
-
}
|
|
310
|
-
return { success: true, outputPath, diagnostics: result.diagnostics };
|
|
311
|
-
} catch (error) {
|
|
312
|
-
const message = error instanceof Error ? error.message : String(error);
|
|
313
|
-
return {
|
|
314
|
-
success: false,
|
|
315
|
-
diagnostics: [
|
|
316
|
-
{
|
|
317
|
-
severity: "error",
|
|
318
|
-
message,
|
|
319
|
-
file: filepath
|
|
320
|
-
}
|
|
321
|
-
]
|
|
322
|
-
};
|
|
323
|
-
}
|
|
324
|
-
}
|
|
325
|
-
function logDiagnostics2(file, diagnostics, force = false) {
|
|
326
|
-
if (!diagnostics.length) {
|
|
327
|
-
return;
|
|
328
|
-
}
|
|
329
|
-
for (const diag of diagnostics) {
|
|
330
|
-
if (!force && diag.severity !== "warning") {
|
|
331
|
-
continue;
|
|
332
|
-
}
|
|
333
|
-
const displayFile = diag.file ? toDisplayPath(diag.file) : toDisplayPath(file);
|
|
334
|
-
const message = formatDiagnosticLine({ ...diag, file: displayFile }, toDisplayPath(file));
|
|
335
|
-
const writer = diag.severity === "warning" ? import_picocolors3.default.yellow : import_picocolors3.default.red;
|
|
336
|
-
console[diag.severity === "warning" ? "warn" : "error"](writer(message));
|
|
337
|
-
}
|
|
338
|
-
}
|
|
339
|
-
|
|
340
|
-
// src/checker.ts
|
|
341
|
-
var import_fast_glob2 = __toESM(require("fast-glob"), 1);
|
|
342
|
-
var import_compiler4 = require("@collie-lang/compiler");
|
|
343
|
-
var import_promises4 = __toESM(require("fs/promises"), 1);
|
|
344
|
-
var import_node_path4 = __toESM(require("path"), 1);
|
|
345
|
-
var import_picocolors4 = __toESM(require("picocolors"), 1);
|
|
346
|
-
async function scanTemplates(patterns) {
|
|
347
|
-
const files = await (0, import_fast_glob2.default)(patterns, {
|
|
348
|
-
absolute: true,
|
|
349
|
-
onlyFiles: true,
|
|
350
|
-
dot: false,
|
|
351
|
-
unique: true
|
|
352
|
-
});
|
|
353
|
-
if (files.length === 0) {
|
|
354
|
-
throw new Error("No .collie files found for the provided patterns.");
|
|
355
|
-
}
|
|
356
|
-
const lineCache = /* @__PURE__ */ new Map();
|
|
357
|
-
const diagnostics = [];
|
|
358
|
-
const templates = [];
|
|
359
|
-
for (const file of files) {
|
|
360
|
-
const displayPath = toDisplayPath(file);
|
|
361
|
-
try {
|
|
362
|
-
const source = await import_promises4.default.readFile(file, "utf8");
|
|
363
|
-
lineCache.set(displayPath, source.split(/\r?\n/));
|
|
364
|
-
const parseResult = (0, import_compiler4.parseCollie)(source, { filename: file });
|
|
365
|
-
for (const diag of parseResult.diagnostics) {
|
|
366
|
-
const range = diag.range ?? diag.span;
|
|
367
|
-
const normalized = {
|
|
368
|
-
...diag,
|
|
369
|
-
file: diag.file ? toDisplayPath(import_node_path4.default.isAbsolute(diag.file) ? diag.file : import_node_path4.default.resolve(import_node_path4.default.dirname(file), diag.file)) : displayPath,
|
|
370
|
-
filePath: diag.filePath ? toDisplayPath(
|
|
371
|
-
import_node_path4.default.isAbsolute(diag.filePath) ? diag.filePath : import_node_path4.default.resolve(import_node_path4.default.dirname(file), diag.filePath)
|
|
372
|
-
) : displayPath,
|
|
373
|
-
range
|
|
374
|
-
};
|
|
375
|
-
diagnostics.push(normalized);
|
|
376
|
-
}
|
|
377
|
-
for (const template of parseResult.templates) {
|
|
378
|
-
templates.push({
|
|
379
|
-
id: template.id,
|
|
380
|
-
filePath: file,
|
|
381
|
-
displayPath,
|
|
382
|
-
span: template.span
|
|
383
|
-
});
|
|
384
|
-
}
|
|
385
|
-
} catch (error) {
|
|
386
|
-
const message = error instanceof Error ? error.message : String(error);
|
|
387
|
-
diagnostics.push({
|
|
388
|
-
severity: "error",
|
|
389
|
-
message,
|
|
390
|
-
file: displayPath,
|
|
391
|
-
filePath: displayPath
|
|
392
|
-
});
|
|
393
|
-
}
|
|
394
|
-
}
|
|
395
|
-
return { files, templates, diagnostics, lineCache };
|
|
396
|
-
}
|
|
397
|
-
function buildDuplicateDiagnostics(templates) {
|
|
398
|
-
const duplicates = /* @__PURE__ */ new Map();
|
|
399
|
-
for (const template of templates) {
|
|
400
|
-
const list = duplicates.get(template.id) ?? [];
|
|
401
|
-
list.push(template);
|
|
402
|
-
duplicates.set(template.id, list);
|
|
403
|
-
}
|
|
404
|
-
const diagnostics = [];
|
|
405
|
-
for (const [id, locations] of duplicates) {
|
|
406
|
-
const uniqueFiles = new Set(locations.map((location) => location.filePath));
|
|
407
|
-
if (uniqueFiles.size <= 1) {
|
|
408
|
-
continue;
|
|
409
|
-
}
|
|
410
|
-
for (const location of locations) {
|
|
411
|
-
const otherLocations = locations.filter((entry) => entry !== location).map((entry) => formatTemplateLocation(entry)).join(", ");
|
|
412
|
-
const suffix = otherLocations ? ` Also defined in ${otherLocations}.` : "";
|
|
413
|
-
diagnostics.push({
|
|
414
|
-
severity: "error",
|
|
415
|
-
code: "COLLIE703",
|
|
416
|
-
message: `Duplicate template id "${id}".${suffix}`,
|
|
417
|
-
file: location.displayPath,
|
|
418
|
-
filePath: location.displayPath,
|
|
419
|
-
range: location.span
|
|
420
|
-
});
|
|
421
|
-
}
|
|
422
|
-
}
|
|
423
|
-
return diagnostics;
|
|
424
|
-
}
|
|
425
|
-
function formatTemplateLocation(template) {
|
|
426
|
-
const span = template.span;
|
|
427
|
-
if (span) {
|
|
428
|
-
return `${template.displayPath}:${span.start.line}:${span.start.col}`;
|
|
429
|
-
}
|
|
430
|
-
return template.displayPath;
|
|
431
|
-
}
|
|
432
|
-
async function check(patterns, options = {}) {
|
|
433
|
-
const scan = await scanTemplates(patterns);
|
|
434
|
-
const diagnostics = [];
|
|
435
|
-
const filesWithErrors = /* @__PURE__ */ new Set();
|
|
436
|
-
const filesWithWarnings = /* @__PURE__ */ new Set();
|
|
437
|
-
let errorCount = 0;
|
|
438
|
-
let warningCount = 0;
|
|
439
|
-
for (const diag of scan.diagnostics) {
|
|
440
|
-
const fileLabel = diag.filePath ?? diag.file;
|
|
441
|
-
if (diag.severity === "error") {
|
|
442
|
-
if (fileLabel) {
|
|
443
|
-
filesWithErrors.add(fileLabel);
|
|
444
|
-
}
|
|
445
|
-
errorCount++;
|
|
446
|
-
diagnostics.push(diag);
|
|
447
|
-
} else {
|
|
448
|
-
warningCount++;
|
|
449
|
-
if (fileLabel) {
|
|
450
|
-
filesWithWarnings.add(fileLabel);
|
|
451
|
-
}
|
|
452
|
-
if (!options.noWarnings) {
|
|
453
|
-
diagnostics.push(diag);
|
|
454
|
-
}
|
|
455
|
-
}
|
|
456
|
-
}
|
|
457
|
-
const duplicateDiagnostics = buildDuplicateDiagnostics(scan.templates);
|
|
458
|
-
for (const diag of duplicateDiagnostics) {
|
|
459
|
-
const fileLabel = diag.filePath ?? diag.file;
|
|
460
|
-
if (fileLabel) {
|
|
461
|
-
filesWithErrors.add(fileLabel);
|
|
462
|
-
}
|
|
463
|
-
errorCount++;
|
|
464
|
-
diagnostics.push(diag);
|
|
465
|
-
}
|
|
466
|
-
const result = {
|
|
467
|
-
totalFiles: scan.files.length,
|
|
468
|
-
filesWithErrors: filesWithErrors.size,
|
|
469
|
-
filesWithWarnings: filesWithWarnings.size,
|
|
470
|
-
diagnostics,
|
|
471
|
-
errorCount,
|
|
472
|
-
warningCount
|
|
473
|
-
};
|
|
474
|
-
if ((options.format ?? "text") === "json") {
|
|
475
|
-
console.log(JSON.stringify(result, null, 2));
|
|
476
|
-
} else {
|
|
477
|
-
console.log(import_picocolors4.default.cyan(`Checking ${scan.files.length} file${scan.files.length === 1 ? "" : "s"}...
|
|
478
|
-
`));
|
|
479
|
-
printTextDiagnostics(result, options, scan.lineCache);
|
|
480
|
-
}
|
|
481
|
-
return result;
|
|
482
|
-
}
|
|
483
|
-
function printTextDiagnostics(result, options, lineCache) {
|
|
484
|
-
const hasDiagnostics = result.diagnostics.length > 0;
|
|
485
|
-
if (hasDiagnostics) {
|
|
486
|
-
for (const diag of result.diagnostics) {
|
|
487
|
-
const message = formatDiagnosticLine(diag);
|
|
488
|
-
const writer = diag.severity === "warning" ? import_picocolors4.default.yellow : import_picocolors4.default.red;
|
|
489
|
-
console.log(writer(message));
|
|
490
|
-
const range = diag.range ?? diag.span;
|
|
491
|
-
const fileLabel = diag.filePath ?? diag.file;
|
|
492
|
-
if (options.verbose && range && fileLabel) {
|
|
493
|
-
const lines = lineCache.get(fileLabel);
|
|
494
|
-
if (lines) {
|
|
495
|
-
const index = Math.max(0, range.start.line - 1);
|
|
496
|
-
const text = lines[index] ?? "";
|
|
497
|
-
const markerStart = Math.max(0, range.start.col - 1);
|
|
498
|
-
const width = Math.max(1, range.end.col - range.start.col);
|
|
499
|
-
const indicator = `${" ".repeat(markerStart)}${"^".repeat(width)}`;
|
|
500
|
-
console.log(import_picocolors4.default.dim(` ${text}`));
|
|
501
|
-
console.log(import_picocolors4.default.dim(` ${indicator}`));
|
|
502
|
-
}
|
|
503
|
-
}
|
|
504
|
-
if (options.verbose) {
|
|
505
|
-
console.log("");
|
|
506
|
-
}
|
|
507
|
-
}
|
|
508
|
-
if (!options.verbose) {
|
|
509
|
-
console.log("");
|
|
510
|
-
}
|
|
511
|
-
}
|
|
512
|
-
const warningCount = options.noWarnings ? 0 : result.warningCount;
|
|
513
|
-
const hasWarnings = warningCount > 0;
|
|
514
|
-
const hasErrors = result.errorCount > 0;
|
|
515
|
-
const summaryParts = [];
|
|
516
|
-
if (hasErrors) {
|
|
517
|
-
summaryParts.push(`${result.errorCount} error${result.errorCount === 1 ? "" : "s"}`);
|
|
518
|
-
}
|
|
519
|
-
if (hasWarnings) {
|
|
520
|
-
summaryParts.push(`${warningCount} warning${warningCount === 1 ? "" : "s"}`);
|
|
521
|
-
}
|
|
522
|
-
const summarySuffix = summaryParts.length > 0 ? ` with ${summaryParts.join(" and ")}` : " with no issues";
|
|
523
|
-
const summary = `Checked ${result.totalFiles} file${result.totalFiles === 1 ? "" : "s"}${summarySuffix}`;
|
|
524
|
-
if (hasErrors) {
|
|
525
|
-
printSummary("error", summary, "no files changed", "fix the errors above and rerun collie check");
|
|
526
|
-
} else if (hasWarnings) {
|
|
527
|
-
printSummary("warning", summary, "no files changed", "review the warnings above");
|
|
528
|
-
} else {
|
|
529
|
-
printSummary("success", summary, "no files changed", "run collie build when you are ready to compile");
|
|
530
|
-
}
|
|
531
|
-
}
|
|
532
|
-
|
|
533
|
-
// src/creator.ts
|
|
534
|
-
var import_prompts = __toESM(require("prompts"), 1);
|
|
535
|
-
var import_promises5 = __toESM(require("fs/promises"), 1);
|
|
536
|
-
var import_node_fs = require("fs");
|
|
537
|
-
var import_node_path5 = __toESM(require("path"), 1);
|
|
538
|
-
var import_node_child_process = require("child_process");
|
|
539
|
-
var import_node_url = require("url");
|
|
540
|
-
var import_picocolors5 = __toESM(require("picocolors"), 1);
|
|
541
|
-
var import_meta = {};
|
|
542
|
-
var TEMPLATE_MAP = {
|
|
543
|
-
vite: {
|
|
544
|
-
label: "Vite + React",
|
|
545
|
-
description: "Vite 5 + React 18 starter with Collie support",
|
|
546
|
-
variants: {
|
|
547
|
-
ts: "vite-react-ts",
|
|
548
|
-
js: "vite-react-js"
|
|
549
|
-
}
|
|
550
|
-
},
|
|
551
|
-
"nextjs-app-router": {
|
|
552
|
-
label: "Next.js App Router",
|
|
553
|
-
description: "Next.js 14 App Router starter wired with @collie-lang/next",
|
|
554
|
-
variants: {
|
|
555
|
-
ts: "nextjs-app-router-ts",
|
|
556
|
-
js: "nextjs-app-router-js"
|
|
557
|
-
}
|
|
558
|
-
}
|
|
559
|
-
};
|
|
560
|
-
var TEMPLATE_ALIASES = {
|
|
561
|
-
next: { template: "nextjs-app-router" },
|
|
562
|
-
nextjs: { template: "nextjs-app-router", forcedTypescript: true },
|
|
563
|
-
"nextjs-app": { template: "nextjs-app-router", forcedTypescript: true }
|
|
564
|
-
};
|
|
565
|
-
function formatTemplateList() {
|
|
566
|
-
return Object.entries(TEMPLATE_MAP).map(([key, meta]) => {
|
|
567
|
-
const variantInfo = [
|
|
568
|
-
`${meta.variants.ts} (TypeScript)`,
|
|
569
|
-
`${meta.variants.js} (JavaScript)`
|
|
570
|
-
].join(", ");
|
|
571
|
-
return [
|
|
572
|
-
` \u2022 ${key} \u2013 ${meta.label}`,
|
|
573
|
-
` ${meta.description}`,
|
|
574
|
-
` Variants: ${variantInfo}`
|
|
575
|
-
].join("\n");
|
|
576
|
-
}).join("\n");
|
|
577
|
-
}
|
|
578
|
-
async function create(options = {}) {
|
|
579
|
-
const resolved = await promptForOptions(options);
|
|
580
|
-
const targetDir = import_node_path5.default.resolve(process.cwd(), resolved.projectName);
|
|
581
|
-
if ((0, import_node_fs.existsSync)(targetDir)) {
|
|
582
|
-
const overwrite = await confirmOverwrite(resolved.projectName);
|
|
583
|
-
if (!overwrite) {
|
|
584
|
-
console.log(import_picocolors5.default.yellow("Cancelled"));
|
|
585
|
-
return;
|
|
586
|
-
}
|
|
587
|
-
await import_promises5.default.rm(targetDir, { recursive: true, force: true });
|
|
588
|
-
}
|
|
589
|
-
console.log(import_picocolors5.default.cyan(`
|
|
590
|
-
Creating project in ${targetDir}...
|
|
591
|
-
`));
|
|
592
|
-
const templateDir = getTemplateDir(resolved.template, resolved.typescript);
|
|
593
|
-
await copyTemplate(templateDir, targetDir, resolved.projectName);
|
|
594
|
-
console.log(import_picocolors5.default.green("\u2714 Copied template files"));
|
|
595
|
-
if (!resolved.noGit) {
|
|
596
|
-
try {
|
|
597
|
-
await runCommand("git", ["init"], targetDir);
|
|
598
|
-
console.log(import_picocolors5.default.green("\u2714 Initialized git repository"));
|
|
599
|
-
} catch (error) {
|
|
600
|
-
const message = error instanceof Error ? error.message : String(error);
|
|
601
|
-
console.log(import_picocolors5.default.yellow(`\u26A0 Failed to initialize git: ${message}`));
|
|
602
|
-
}
|
|
603
|
-
}
|
|
604
|
-
if (!resolved.noInstall) {
|
|
605
|
-
console.log(import_picocolors5.default.cyan(`\u2714 Installing dependencies with ${resolved.packageManager}...`));
|
|
606
|
-
try {
|
|
607
|
-
await installDependencies(resolved.packageManager, targetDir);
|
|
608
|
-
} catch (error) {
|
|
609
|
-
const message = error instanceof Error ? error.message : String(error);
|
|
610
|
-
console.log(import_picocolors5.default.yellow(`\u26A0 Failed to install dependencies: ${message}`));
|
|
611
|
-
console.log(import_picocolors5.default.yellow(" Run the install command manually once you're ready."));
|
|
612
|
-
}
|
|
613
|
-
}
|
|
614
|
-
printSuccessMessage(resolved);
|
|
615
|
-
}
|
|
616
|
-
async function promptForOptions(options) {
|
|
617
|
-
const detected = detectPackageManager();
|
|
618
|
-
const questions = [];
|
|
619
|
-
if (!options.projectName) {
|
|
620
|
-
questions.push({
|
|
621
|
-
type: "text",
|
|
622
|
-
name: "projectName",
|
|
623
|
-
message: "Project name:",
|
|
624
|
-
initial: "my-collie-app",
|
|
625
|
-
validate: (value) => {
|
|
626
|
-
if (!value.trim()) return "Project name is required";
|
|
627
|
-
if (!/^[a-z0-9-_]+$/i.test(value.trim())) return "Use letters, numbers, hyphens, or underscores.";
|
|
628
|
-
return true;
|
|
629
|
-
}
|
|
630
|
-
});
|
|
631
|
-
}
|
|
632
|
-
if (!options.template) {
|
|
633
|
-
const choices = Object.entries(TEMPLATE_MAP).map(([value, meta]) => ({
|
|
634
|
-
title: meta.label,
|
|
635
|
-
value
|
|
636
|
-
}));
|
|
637
|
-
questions.push({
|
|
638
|
-
type: "select",
|
|
639
|
-
name: "template",
|
|
640
|
-
message: "Select a template:",
|
|
641
|
-
choices,
|
|
642
|
-
initial: 0
|
|
643
|
-
});
|
|
644
|
-
}
|
|
645
|
-
if (options.typescript === void 0) {
|
|
646
|
-
questions.push({
|
|
647
|
-
type: "confirm",
|
|
648
|
-
name: "typescript",
|
|
649
|
-
message: "Use TypeScript?",
|
|
650
|
-
initial: true
|
|
651
|
-
});
|
|
652
|
-
}
|
|
653
|
-
if (!options.packageManager) {
|
|
654
|
-
questions.push({
|
|
655
|
-
type: "select",
|
|
656
|
-
name: "packageManager",
|
|
657
|
-
message: "Package manager:",
|
|
658
|
-
choices: [
|
|
659
|
-
{ title: "pnpm", value: "pnpm" },
|
|
660
|
-
{ title: "npm", value: "npm" },
|
|
661
|
-
{ title: "yarn", value: "yarn" }
|
|
662
|
-
],
|
|
663
|
-
initial: detected === "pnpm" ? 0 : detected === "npm" ? 1 : 2
|
|
664
|
-
});
|
|
665
|
-
}
|
|
666
|
-
const answers = questions.length > 0 ? await (0, import_prompts.default)(questions, {
|
|
667
|
-
onCancel: () => {
|
|
668
|
-
console.log(import_picocolors5.default.yellow("\nCancelled"));
|
|
669
|
-
process.exit(0);
|
|
670
|
-
}
|
|
671
|
-
}) : {};
|
|
672
|
-
const templateResult = resolveTemplate((options.template || answers.template || "vite").trim());
|
|
673
|
-
const typescript = templateResult.forcedTypescript ?? (options.typescript !== void 0 ? options.typescript : answers.typescript ?? true);
|
|
674
|
-
const packageManager = options.packageManager || answers.packageManager || detected;
|
|
675
|
-
if (!["npm", "yarn", "pnpm"].includes(packageManager)) {
|
|
676
|
-
throw new Error(`Unsupported package manager: ${packageManager}`);
|
|
677
|
-
}
|
|
678
|
-
return {
|
|
679
|
-
projectName: (options.projectName || answers.projectName).trim(),
|
|
680
|
-
template: templateResult.template,
|
|
681
|
-
typescript,
|
|
682
|
-
packageManager,
|
|
683
|
-
noInstall: options.noInstall ?? false,
|
|
684
|
-
noGit: options.noGit ?? false
|
|
685
|
-
};
|
|
686
|
-
}
|
|
687
|
-
function resolveTemplate(input) {
|
|
688
|
-
const normalized = input.trim().toLowerCase();
|
|
689
|
-
if (TEMPLATE_MAP[normalized]) {
|
|
690
|
-
return { template: normalized };
|
|
691
|
-
}
|
|
692
|
-
const alias = TEMPLATE_ALIASES[normalized];
|
|
693
|
-
if (alias && TEMPLATE_MAP[alias.template]) {
|
|
694
|
-
return { template: alias.template, forcedTypescript: alias.forcedTypescript };
|
|
695
|
-
}
|
|
696
|
-
for (const key of Object.keys(TEMPLATE_MAP)) {
|
|
697
|
-
const meta = TEMPLATE_MAP[key];
|
|
698
|
-
if (meta.variants.ts === normalized) {
|
|
699
|
-
return { template: key, forcedTypescript: true };
|
|
700
|
-
}
|
|
701
|
-
if (meta.variants.js === normalized) {
|
|
702
|
-
return { template: key, forcedTypescript: false };
|
|
703
|
-
}
|
|
704
|
-
}
|
|
705
|
-
return throwInvalidTemplateError(input);
|
|
706
|
-
}
|
|
707
|
-
function throwInvalidTemplateError(input) {
|
|
708
|
-
const list = formatTemplateList();
|
|
709
|
-
throw new Error(
|
|
710
|
-
`Invalid template: '${input}'.
|
|
711
|
-
|
|
712
|
-
Available templates:
|
|
713
|
-
${list}
|
|
714
|
-
|
|
715
|
-
Usage: collie create <project-name> --template <template>`
|
|
716
|
-
);
|
|
717
|
-
}
|
|
718
|
-
async function confirmOverwrite(projectName) {
|
|
719
|
-
const { overwrite } = await (0, import_prompts.default)({
|
|
720
|
-
type: "confirm",
|
|
721
|
-
name: "overwrite",
|
|
722
|
-
message: `Directory ${projectName} already exists. Overwrite?`,
|
|
723
|
-
initial: false
|
|
724
|
-
});
|
|
725
|
-
return Boolean(overwrite);
|
|
726
|
-
}
|
|
727
|
-
function getTemplateDir(template, typescript) {
|
|
728
|
-
const meta = TEMPLATE_MAP[template];
|
|
729
|
-
const variant = typescript ? meta.variants.ts : meta.variants.js;
|
|
730
|
-
const dir = import_node_path5.default.resolve(import_node_path5.default.dirname((0, import_node_url.fileURLToPath)(import_meta.url)), "..", "templates", variant);
|
|
731
|
-
if (!(0, import_node_fs.existsSync)(dir)) {
|
|
732
|
-
throw new Error(
|
|
733
|
-
`Template '${template}' with ${typescript ? "TypeScript" : "JavaScript"} is not available.`
|
|
734
|
-
);
|
|
735
|
-
}
|
|
736
|
-
return dir;
|
|
737
|
-
}
|
|
738
|
-
async function copyTemplate(templateDir, targetDir, projectName) {
|
|
739
|
-
await import_promises5.default.mkdir(targetDir, { recursive: true });
|
|
740
|
-
await copyDirectory(templateDir, targetDir, { projectName });
|
|
741
|
-
}
|
|
742
|
-
async function copyDirectory(source, target, context) {
|
|
743
|
-
const entries = await import_promises5.default.readdir(source, { withFileTypes: true });
|
|
744
|
-
for (const entry of entries) {
|
|
745
|
-
const srcPath = import_node_path5.default.join(source, entry.name);
|
|
746
|
-
const destName = entry.name.endsWith(".template") ? entry.name.replace(/\.template$/, "") : entry.name;
|
|
747
|
-
const destPath = import_node_path5.default.join(target, destName);
|
|
748
|
-
if (entry.isDirectory()) {
|
|
749
|
-
await import_promises5.default.mkdir(destPath, { recursive: true });
|
|
750
|
-
await copyDirectory(srcPath, destPath, context);
|
|
751
|
-
} else {
|
|
752
|
-
const buffer = await import_promises5.default.readFile(srcPath);
|
|
753
|
-
if (entry.name.endsWith(".template")) {
|
|
754
|
-
const content = buffer.toString("utf8").replace(/__PROJECT_NAME__/g, context.projectName);
|
|
755
|
-
await import_promises5.default.writeFile(destPath, content, "utf8");
|
|
756
|
-
} else {
|
|
757
|
-
await import_promises5.default.writeFile(destPath, buffer);
|
|
758
|
-
}
|
|
759
|
-
}
|
|
760
|
-
}
|
|
761
|
-
}
|
|
762
|
-
async function installDependencies(packageManager, cwd) {
|
|
763
|
-
const args = packageManager === "npm" ? ["install"] : packageManager === "yarn" ? ["install"] : ["install"];
|
|
764
|
-
await runCommand(packageManager, args, cwd);
|
|
765
|
-
}
|
|
766
|
-
function runCommand(command, args, cwd) {
|
|
767
|
-
return new Promise((resolve, reject) => {
|
|
768
|
-
const child = (0, import_node_child_process.spawn)(command, args, { cwd, stdio: "inherit" });
|
|
769
|
-
child.on("error", reject);
|
|
770
|
-
child.on("close", (code) => {
|
|
771
|
-
if (code === 0) resolve();
|
|
772
|
-
else reject(new Error(`${command} ${args.join(" ")} exited with code ${code}`));
|
|
773
|
-
});
|
|
774
|
-
});
|
|
775
|
-
}
|
|
776
|
-
function detectPackageManager() {
|
|
777
|
-
const userAgent = process.env.npm_config_user_agent ?? "";
|
|
778
|
-
if (userAgent.startsWith("pnpm")) return "pnpm";
|
|
779
|
-
if (userAgent.startsWith("yarn")) return "yarn";
|
|
780
|
-
if (userAgent.startsWith("npm")) return "npm";
|
|
781
|
-
return "pnpm";
|
|
782
|
-
}
|
|
783
|
-
function printSuccessMessage(config) {
|
|
784
|
-
const cdCommand = `cd ${config.projectName}`;
|
|
785
|
-
const installCommand = config.packageManager === "npm" ? "npm install" : `${config.packageManager} install`;
|
|
786
|
-
const devCommand = config.packageManager === "npm" ? "npm run dev" : `${config.packageManager} dev`;
|
|
787
|
-
console.log(import_picocolors5.default.green(`
|
|
788
|
-
\u{1F389} Success! Created ${config.projectName}
|
|
789
|
-
`));
|
|
790
|
-
console.log("Next steps:");
|
|
791
|
-
console.log(import_picocolors5.default.cyan(` ${cdCommand}`));
|
|
792
|
-
if (config.noInstall) {
|
|
793
|
-
console.log(import_picocolors5.default.cyan(` ${installCommand}`));
|
|
794
|
-
}
|
|
795
|
-
console.log(import_picocolors5.default.cyan(` ${devCommand}`));
|
|
796
|
-
console.log(import_picocolors5.default.gray("\nHappy coding with Collie! \u{1F415}\n"));
|
|
797
|
-
}
|
|
798
|
-
|
|
799
|
-
// src/nextjs-setup.ts
|
|
800
|
-
var import_promises6 = __toESM(require("fs/promises"), 1);
|
|
801
|
-
var import_node_fs2 = require("fs");
|
|
802
|
-
var import_node_path6 = __toESM(require("path"), 1);
|
|
803
|
-
var import_picocolors6 = __toESM(require("picocolors"), 1);
|
|
804
|
-
var import_next = require("@collie-lang/next");
|
|
805
|
-
function hasNextDependency(pkg) {
|
|
806
|
-
return Boolean(pkg?.dependencies?.next || pkg?.devDependencies?.next);
|
|
807
|
-
}
|
|
808
|
-
|
|
809
|
-
// src/converter.ts
|
|
810
|
-
var import_promises7 = __toESM(require("fs/promises"), 1);
|
|
811
|
-
var import_node_path7 = __toESM(require("path"), 1);
|
|
812
|
-
var import_compiler5 = require("@collie-lang/compiler");
|
|
813
|
-
async function convertFile(filepath, options = {}) {
|
|
814
|
-
const source = await import_promises7.default.readFile(filepath, "utf8");
|
|
815
|
-
const result = (0, import_compiler5.convertTsxToCollie)(source, { filename: filepath });
|
|
816
|
-
const { collie, warnings } = result;
|
|
817
|
-
let outputPath;
|
|
818
|
-
if (options.write) {
|
|
819
|
-
outputPath = resolveOutputPath2(filepath);
|
|
820
|
-
if (!options.overwrite) {
|
|
821
|
-
const exists = await fileExists(outputPath);
|
|
822
|
-
if (exists) {
|
|
823
|
-
throw new Error(`${import_node_path7.default.relative(process.cwd(), outputPath)} already exists. Use --overwrite to replace.`);
|
|
824
|
-
}
|
|
825
|
-
}
|
|
826
|
-
await import_promises7.default.mkdir(import_node_path7.default.dirname(outputPath), { recursive: true });
|
|
827
|
-
await import_promises7.default.writeFile(outputPath, collie, "utf8");
|
|
828
|
-
if (options.removeOriginal) {
|
|
829
|
-
await import_promises7.default.unlink(filepath);
|
|
830
|
-
}
|
|
831
|
-
}
|
|
832
|
-
return { collie, warnings, outputPath };
|
|
833
|
-
}
|
|
834
|
-
function resolveOutputPath2(filepath) {
|
|
835
|
-
return filepath.replace(/\.[tj]sx?$/, "") + ".collie";
|
|
836
|
-
}
|
|
837
|
-
async function fileExists(filepath) {
|
|
838
|
-
try {
|
|
839
|
-
await import_promises7.default.access(filepath);
|
|
840
|
-
return true;
|
|
841
|
-
} catch {
|
|
842
|
-
return false;
|
|
843
|
-
}
|
|
844
|
-
}
|
|
845
|
-
|
|
846
|
-
// src/doctor.ts
|
|
847
|
-
var import_compiler6 = require("@collie-lang/compiler");
|
|
848
|
-
var import_fast_glob3 = __toESM(require("fast-glob"), 1);
|
|
849
|
-
var import_promises8 = __toESM(require("fs/promises"), 1);
|
|
850
|
-
var import_node_fs3 = require("fs");
|
|
851
|
-
var import_node_path8 = __toESM(require("path"), 1);
|
|
852
|
-
var import_picocolors7 = __toESM(require("picocolors"), 1);
|
|
853
|
-
var VITE_CONFIG_FILES = ["vite.config.ts", "vite.config.js", "vite.config.mts", "vite.config.mjs"];
|
|
854
|
-
var NEXT_CONFIG_FILES = ["next.config.ts", "next.config.mjs", "next.config.js"];
|
|
855
|
-
var DECLARATION_CANDIDATES = ["src/collie.d.ts", "app/collie.d.ts", "collie.d.ts"];
|
|
856
|
-
async function runDoctor(options = {}) {
|
|
857
|
-
const cwd = options.cwd ?? process.cwd();
|
|
858
|
-
const context = {
|
|
859
|
-
cwd,
|
|
860
|
-
packageJson: await readPackageJson(cwd)
|
|
861
|
-
};
|
|
862
|
-
const results = [];
|
|
863
|
-
results.push(checkNodeVersion());
|
|
864
|
-
const compilerResult = checkCompilerDependency(context);
|
|
865
|
-
results.push(compilerResult);
|
|
866
|
-
const buildInfo = detectBuildSystem(context);
|
|
867
|
-
results.push(buildInfo.result);
|
|
868
|
-
if (buildInfo.type === "vite") {
|
|
869
|
-
const viteDependency = checkDependency(context, "@collie-lang/vite", "Collie Vite plugin", ["vite"]);
|
|
870
|
-
if (viteDependency) {
|
|
871
|
-
results.push(viteDependency);
|
|
872
|
-
}
|
|
873
|
-
results.push(await checkViteConfig(context));
|
|
874
|
-
} else if (buildInfo.type === "nextjs") {
|
|
875
|
-
const nextDependency = checkDependency(context, "@collie-lang/next", "Collie Next.js integration", ["nextjs"]);
|
|
876
|
-
if (nextDependency) {
|
|
877
|
-
results.push(nextDependency);
|
|
878
|
-
}
|
|
879
|
-
results.push(await checkNextConfig(context));
|
|
880
|
-
}
|
|
881
|
-
results.push(await checkTypeDeclarations(context));
|
|
882
|
-
results.push(await checkCollieFiles(context));
|
|
883
|
-
results.push(await testCompilation());
|
|
884
|
-
return results;
|
|
885
|
-
}
|
|
886
|
-
function filterDiagnostics(results, filter) {
|
|
887
|
-
if (!filter) {
|
|
888
|
-
return results;
|
|
889
|
-
}
|
|
890
|
-
const normalized = filter.trim().toLowerCase();
|
|
891
|
-
if (!normalized) {
|
|
892
|
-
return results;
|
|
893
|
-
}
|
|
894
|
-
return results.filter((result) => {
|
|
895
|
-
if (result.tags?.some((tag) => tag.toLowerCase() === normalized)) {
|
|
896
|
-
return true;
|
|
897
|
-
}
|
|
898
|
-
return result.check.toLowerCase().includes(normalized);
|
|
899
|
-
});
|
|
900
|
-
}
|
|
901
|
-
function printDoctorResults(results) {
|
|
902
|
-
console.log(import_picocolors7.default.bold("collie doctor"));
|
|
903
|
-
console.log(import_picocolors7.default.dim("Diagnosing your environment..."));
|
|
904
|
-
console.log("");
|
|
905
|
-
let errors = 0;
|
|
906
|
-
let warnings = 0;
|
|
907
|
-
for (const result of results) {
|
|
908
|
-
const icon = result.status === "pass" ? import_picocolors7.default.green("\u2714") : result.status === "warn" ? import_picocolors7.default.yellow("\u26A0") : import_picocolors7.default.red("\u2716");
|
|
909
|
-
console.log(`${icon} ${result.check}: ${result.message}`);
|
|
910
|
-
if (result.fix) {
|
|
911
|
-
console.log(import_picocolors7.default.dim(` Fix: ${result.fix}`));
|
|
912
|
-
}
|
|
913
|
-
console.log("");
|
|
914
|
-
if (result.status === "fail") {
|
|
915
|
-
errors++;
|
|
916
|
-
} else if (result.status === "warn") {
|
|
917
|
-
warnings++;
|
|
918
|
-
}
|
|
919
|
-
}
|
|
920
|
-
if (errors === 0 && warnings === 0) {
|
|
921
|
-
printSummary("success", "All checks passed", "no changes made", "continue with collie check or collie build");
|
|
922
|
-
return;
|
|
923
|
-
}
|
|
924
|
-
const summary = [];
|
|
925
|
-
if (errors > 0) summary.push(`${errors} error${errors === 1 ? "" : "s"}`);
|
|
926
|
-
if (warnings > 0) summary.push(`${warnings} warning${warnings === 1 ? "" : "s"}`);
|
|
927
|
-
printSummary(
|
|
928
|
-
errors > 0 ? "error" : "warning",
|
|
929
|
-
`Doctor found ${summary.join(" and ")}`,
|
|
930
|
-
"no changes made",
|
|
931
|
-
"address the items above and rerun collie doctor"
|
|
932
|
-
);
|
|
933
|
-
}
|
|
934
|
-
function checkNodeVersion() {
|
|
935
|
-
const version = process.version;
|
|
936
|
-
const major = Number(version.slice(1).split(".")[0]);
|
|
937
|
-
if (Number.isFinite(major) && major >= 18) {
|
|
938
|
-
return {
|
|
939
|
-
id: "node-version",
|
|
940
|
-
check: "Node.js version",
|
|
941
|
-
status: "pass",
|
|
942
|
-
message: `${version} (compatible)`,
|
|
943
|
-
tags: ["node"]
|
|
944
|
-
};
|
|
945
|
-
}
|
|
946
|
-
return {
|
|
947
|
-
id: "node-version",
|
|
948
|
-
check: "Node.js version",
|
|
949
|
-
status: "fail",
|
|
950
|
-
message: `${version} (incompatible)`,
|
|
951
|
-
fix: "Upgrade to Node.js 18 or newer.",
|
|
952
|
-
tags: ["node"]
|
|
953
|
-
};
|
|
954
|
-
}
|
|
955
|
-
function checkCompilerDependency(context) {
|
|
956
|
-
const spec = getDependencySpec(context.packageJson, "@collie-lang/compiler");
|
|
957
|
-
if (!spec) {
|
|
958
|
-
return {
|
|
959
|
-
id: "compiler-dependency",
|
|
960
|
-
check: "Collie compiler",
|
|
961
|
-
status: "fail",
|
|
962
|
-
message: context.packageJson ? "Not found in package.json" : "package.json not found",
|
|
963
|
-
fix: "Install @collie-lang/compiler (e.g. npm install --save-dev @collie-lang/compiler)",
|
|
964
|
-
tags: ["compiler"]
|
|
965
|
-
};
|
|
966
|
-
}
|
|
967
|
-
return {
|
|
968
|
-
id: "compiler-dependency",
|
|
969
|
-
check: "Collie compiler",
|
|
970
|
-
status: "pass",
|
|
971
|
-
message: `@collie-lang/compiler@${formatDependencyVersion(spec)}`,
|
|
972
|
-
tags: ["compiler"]
|
|
973
|
-
};
|
|
974
|
-
}
|
|
975
|
-
function detectBuildSystem(context) {
|
|
976
|
-
if (!context.packageJson) {
|
|
977
|
-
return {
|
|
978
|
-
type: null,
|
|
979
|
-
result: {
|
|
980
|
-
id: "build-system",
|
|
981
|
-
check: "Build system",
|
|
982
|
-
status: "warn",
|
|
983
|
-
message: "package.json not found",
|
|
984
|
-
fix: "Initialize a project (npm init) and install Vite or Next.js.",
|
|
985
|
-
tags: ["build"]
|
|
986
|
-
}
|
|
987
|
-
};
|
|
988
|
-
}
|
|
989
|
-
const hasVite = hasDependency(context.packageJson, "vite");
|
|
990
|
-
const hasNext = hasDependency(context.packageJson, "next");
|
|
991
|
-
if (hasVite && hasNext) {
|
|
992
|
-
return {
|
|
993
|
-
type: "vite",
|
|
994
|
-
result: {
|
|
995
|
-
id: "build-system",
|
|
996
|
-
check: "Build system",
|
|
997
|
-
status: "warn",
|
|
998
|
-
message: "Both Vite and Next.js detected (defaulting to Vite)",
|
|
999
|
-
tags: ["build"]
|
|
1000
|
-
}
|
|
1001
|
-
};
|
|
1002
|
-
}
|
|
1003
|
-
if (hasVite) {
|
|
1004
|
-
return {
|
|
1005
|
-
type: "vite",
|
|
1006
|
-
result: {
|
|
1007
|
-
id: "build-system",
|
|
1008
|
-
check: "Build system",
|
|
1009
|
-
status: "pass",
|
|
1010
|
-
message: "Vite detected",
|
|
1011
|
-
tags: ["build", "vite"]
|
|
1012
|
-
}
|
|
1013
|
-
};
|
|
1014
|
-
}
|
|
1015
|
-
if (hasNext) {
|
|
1016
|
-
return {
|
|
1017
|
-
type: "nextjs",
|
|
1018
|
-
result: {
|
|
1019
|
-
id: "build-system",
|
|
1020
|
-
check: "Build system",
|
|
1021
|
-
status: "pass",
|
|
1022
|
-
message: "Next.js detected",
|
|
1023
|
-
tags: ["build", "nextjs"]
|
|
1024
|
-
}
|
|
1025
|
-
};
|
|
1026
|
-
}
|
|
1027
|
-
return {
|
|
1028
|
-
type: null,
|
|
1029
|
-
result: {
|
|
1030
|
-
id: "build-system",
|
|
1031
|
-
check: "Build system",
|
|
1032
|
-
status: "warn",
|
|
1033
|
-
message: "No Vite or Next.js dependency found",
|
|
1034
|
-
fix: "Install Vite or Next.js for the best Collie experience.",
|
|
1035
|
-
tags: ["build"]
|
|
1036
|
-
}
|
|
1037
|
-
};
|
|
1038
|
-
}
|
|
1039
|
-
function checkDependency(context, dependency, label, tags) {
|
|
1040
|
-
if (!context.packageJson) {
|
|
1041
|
-
return null;
|
|
1042
|
-
}
|
|
1043
|
-
const spec = getDependencySpec(context.packageJson, dependency);
|
|
1044
|
-
if (!spec) {
|
|
1045
|
-
return {
|
|
1046
|
-
id: `${dependency}-dependency`,
|
|
1047
|
-
check: label,
|
|
1048
|
-
status: "fail",
|
|
1049
|
-
message: `${dependency} missing from package.json`,
|
|
1050
|
-
fix: `Install ${dependency} (e.g. npm install --save-dev ${dependency})`,
|
|
1051
|
-
tags
|
|
1052
|
-
};
|
|
1053
|
-
}
|
|
1054
|
-
return {
|
|
1055
|
-
id: `${dependency}-dependency`,
|
|
1056
|
-
check: label,
|
|
1057
|
-
status: "pass",
|
|
1058
|
-
message: `${dependency}@${formatDependencyVersion(spec)}`,
|
|
1059
|
-
tags
|
|
1060
|
-
};
|
|
1061
|
-
}
|
|
1062
|
-
async function checkViteConfig(context) {
|
|
1063
|
-
for (const filename of VITE_CONFIG_FILES) {
|
|
1064
|
-
const configPath = import_node_path8.default.join(context.cwd, filename);
|
|
1065
|
-
if (!(0, import_node_fs3.existsSync)(configPath)) continue;
|
|
1066
|
-
const contents = await import_promises8.default.readFile(configPath, "utf8");
|
|
1067
|
-
const hasPlugin = /@collie-lang\/vite/.test(contents) && /collie\s*\(/.test(contents);
|
|
1068
|
-
if (hasPlugin) {
|
|
1069
|
-
return {
|
|
1070
|
-
id: "vite-config",
|
|
1071
|
-
check: "Vite config",
|
|
1072
|
-
status: "pass",
|
|
1073
|
-
message: `Collie plugin configured in ${filename}`,
|
|
1074
|
-
tags: ["vite", "config"]
|
|
1075
|
-
};
|
|
1076
|
-
}
|
|
1077
|
-
return {
|
|
1078
|
-
id: "vite-config",
|
|
1079
|
-
check: "Vite config",
|
|
1080
|
-
status: "fail",
|
|
1081
|
-
message: `Found ${filename} but Collie plugin not configured`,
|
|
1082
|
-
fix: "Add collie() to your Vite plugins array or run collie init.",
|
|
1083
|
-
tags: ["vite", "config"]
|
|
1084
|
-
};
|
|
1085
|
-
}
|
|
1086
|
-
return {
|
|
1087
|
-
id: "vite-config",
|
|
1088
|
-
check: "Vite config",
|
|
1089
|
-
status: "fail",
|
|
1090
|
-
message: "Vite config not found",
|
|
1091
|
-
fix: "Create vite.config.ts and add the Collie plugin.",
|
|
1092
|
-
tags: ["vite", "config"]
|
|
1093
|
-
};
|
|
1094
|
-
}
|
|
1095
|
-
async function checkNextConfig(context) {
|
|
1096
|
-
for (const filename of NEXT_CONFIG_FILES) {
|
|
1097
|
-
const configPath = import_node_path8.default.join(context.cwd, filename);
|
|
1098
|
-
if (!(0, import_node_fs3.existsSync)(configPath)) continue;
|
|
1099
|
-
const contents = await import_promises8.default.readFile(configPath, "utf8");
|
|
1100
|
-
const hasLoader = /@collie-lang\/next/.test(contents) || /withCollie\s*\(/.test(contents);
|
|
1101
|
-
if (hasLoader) {
|
|
1102
|
-
return {
|
|
1103
|
-
id: "next-config",
|
|
1104
|
-
check: "Next.js config",
|
|
1105
|
-
status: "pass",
|
|
1106
|
-
message: `Collie loader configured in ${filename}`,
|
|
1107
|
-
tags: ["nextjs", "config"]
|
|
1108
|
-
};
|
|
1109
|
-
}
|
|
1110
|
-
return {
|
|
1111
|
-
id: "next-config",
|
|
1112
|
-
check: "Next.js config",
|
|
1113
|
-
status: "fail",
|
|
1114
|
-
message: `Found ${filename} but Collie loader not configured`,
|
|
1115
|
-
fix: "Wrap your Next.js config with withCollie() or run collie init --nextjs.",
|
|
1116
|
-
tags: ["nextjs", "config"]
|
|
1117
|
-
};
|
|
1118
|
-
}
|
|
1119
|
-
return {
|
|
1120
|
-
id: "next-config",
|
|
1121
|
-
check: "Next.js config",
|
|
1122
|
-
status: "fail",
|
|
1123
|
-
message: "Next.js config not found",
|
|
1124
|
-
fix: "Create next.config.js and configure withCollie().",
|
|
1125
|
-
tags: ["nextjs", "config"]
|
|
1126
|
-
};
|
|
1127
|
-
}
|
|
1128
|
-
async function checkTypeDeclarations(context) {
|
|
1129
|
-
for (const relativePath of DECLARATION_CANDIDATES) {
|
|
1130
|
-
const fullPath = import_node_path8.default.join(context.cwd, relativePath);
|
|
1131
|
-
if ((0, import_node_fs3.existsSync)(fullPath)) {
|
|
1132
|
-
return {
|
|
1133
|
-
id: "type-declarations",
|
|
1134
|
-
check: "Type declarations",
|
|
1135
|
-
status: "pass",
|
|
1136
|
-
message: `Found ${relativePath}`,
|
|
1137
|
-
tags: ["types"]
|
|
1138
|
-
};
|
|
1139
|
-
}
|
|
1140
|
-
}
|
|
1141
|
-
return {
|
|
1142
|
-
id: "type-declarations",
|
|
1143
|
-
check: "Type declarations",
|
|
1144
|
-
status: "warn",
|
|
1145
|
-
message: "collie.d.ts not found",
|
|
1146
|
-
fix: "Create src/collie.d.ts so TypeScript recognizes .collie imports.",
|
|
1147
|
-
tags: ["types"]
|
|
1148
|
-
};
|
|
1149
|
-
}
|
|
1150
|
-
async function checkCollieFiles(context) {
|
|
1151
|
-
const files = await (0, import_fast_glob3.default)("**/*.collie", {
|
|
1152
|
-
cwd: context.cwd,
|
|
1153
|
-
ignore: ["node_modules/**", "dist/**", ".next/**"],
|
|
1154
|
-
absolute: false
|
|
1155
|
-
});
|
|
1156
|
-
if (files.length === 0) {
|
|
1157
|
-
return {
|
|
1158
|
-
id: "collie-files",
|
|
1159
|
-
check: "Collie files",
|
|
1160
|
-
status: "warn",
|
|
1161
|
-
message: "No .collie files found",
|
|
1162
|
-
fix: "Create a .collie template to start using Collie.",
|
|
1163
|
-
tags: ["templates"]
|
|
1164
|
-
};
|
|
1165
|
-
}
|
|
1166
|
-
return {
|
|
1167
|
-
id: "collie-files",
|
|
1168
|
-
check: "Collie files",
|
|
1169
|
-
status: "pass",
|
|
1170
|
-
message: `Found ${files.length} .collie file${files.length === 1 ? "" : "s"}`,
|
|
1171
|
-
tags: ["templates"]
|
|
1172
|
-
};
|
|
1173
|
-
}
|
|
1174
|
-
async function testCompilation() {
|
|
1175
|
-
const template = ["props", ' name: string = "world"', "", 'div class="doctor-check"', " h1", " Hello {{ name }}"].join(
|
|
1176
|
-
"\n"
|
|
1177
|
-
);
|
|
1178
|
-
try {
|
|
1179
|
-
const result = (0, import_compiler6.compileToJsx)(template, { componentNameHint: "DoctorCheck" });
|
|
1180
|
-
const hasError = result.diagnostics.some((diag) => diag.severity === "error");
|
|
1181
|
-
if (hasError) {
|
|
1182
|
-
return {
|
|
1183
|
-
id: "compiler-test",
|
|
1184
|
-
check: "Test compilation",
|
|
1185
|
-
status: "fail",
|
|
1186
|
-
message: "Compiler produced errors",
|
|
1187
|
-
fix: "Reinstall @collie-lang/compiler or inspect diagnostics.",
|
|
1188
|
-
tags: ["compiler"]
|
|
1189
|
-
};
|
|
1190
|
-
}
|
|
1191
|
-
return {
|
|
1192
|
-
id: "compiler-test",
|
|
1193
|
-
check: "Test compilation",
|
|
1194
|
-
status: "pass",
|
|
1195
|
-
message: "Successful",
|
|
1196
|
-
tags: ["compiler"]
|
|
1197
|
-
};
|
|
1198
|
-
} catch {
|
|
1199
|
-
return {
|
|
1200
|
-
id: "compiler-test",
|
|
1201
|
-
check: "Test compilation",
|
|
1202
|
-
status: "fail",
|
|
1203
|
-
message: "Failed to compile sample template",
|
|
1204
|
-
fix: "Verify @collie-lang/compiler is installed correctly.",
|
|
1205
|
-
tags: ["compiler"]
|
|
1206
|
-
};
|
|
1207
|
-
}
|
|
1208
|
-
}
|
|
1209
|
-
async function readPackageJson(cwd) {
|
|
1210
|
-
try {
|
|
1211
|
-
const pkgPath = import_node_path8.default.join(cwd, "package.json");
|
|
1212
|
-
const raw = await import_promises8.default.readFile(pkgPath, "utf8");
|
|
1213
|
-
return JSON.parse(raw);
|
|
1214
|
-
} catch {
|
|
1215
|
-
return null;
|
|
1216
|
-
}
|
|
1217
|
-
}
|
|
1218
|
-
function getDependencySpec(pkg, name) {
|
|
1219
|
-
if (!pkg) {
|
|
1220
|
-
return void 0;
|
|
1221
|
-
}
|
|
1222
|
-
return pkg.dependencies?.[name] ?? pkg.devDependencies?.[name];
|
|
1223
|
-
}
|
|
1224
|
-
function hasDependency(pkg, name) {
|
|
1225
|
-
return Boolean(pkg.dependencies?.[name] ?? pkg.devDependencies?.[name]);
|
|
1226
|
-
}
|
|
1227
|
-
function formatDependencyVersion(spec) {
|
|
1228
|
-
return spec.replace(/^workspace:/, "").replace(/^[~^]/, "");
|
|
1229
|
-
}
|
|
1230
|
-
|
|
1231
|
-
// src/index.ts
|
|
1232
|
-
var import_meta2 = {};
|
|
1233
|
-
var VITE_CONFIG_FILES2 = ["vite.config.ts", "vite.config.mts", "vite.config.js", "vite.config.mjs"];
|
|
1234
|
-
var TAILWIND_CONFIG_FILES = [
|
|
1235
|
-
"tailwind.config.js",
|
|
1236
|
-
"tailwind.config.cjs",
|
|
1237
|
-
"tailwind.config.mjs",
|
|
1238
|
-
"tailwind.config.ts"
|
|
1239
|
-
];
|
|
1240
|
-
var POSTCSS_CONFIG_FILES = [
|
|
1241
|
-
"postcss.config.js",
|
|
1242
|
-
"postcss.config.cjs",
|
|
1243
|
-
"postcss.config.mjs",
|
|
1244
|
-
"postcss.config.ts"
|
|
1245
|
-
];
|
|
1246
|
-
var COLLIE_CONFIG_FILES = [
|
|
1247
|
-
"collie.config.ts",
|
|
1248
|
-
"collie.config.js",
|
|
1249
|
-
"collie.config.mjs",
|
|
1250
|
-
"collie.config.cjs",
|
|
1251
|
-
"collie.config.json"
|
|
1252
|
-
];
|
|
1253
|
-
var CLI_PACKAGE_INFO = readCliPackageInfo();
|
|
1254
|
-
var CLI_PACKAGE_VERSION = CLI_PACKAGE_INFO.version;
|
|
1255
|
-
var CLI_DEPENDENCY_SPECS = CLI_PACKAGE_INFO.dependencies;
|
|
1256
|
-
var DEFAULT_DEPENDENCY_RANGE = CLI_PACKAGE_VERSION === "latest" ? "latest" : `^${CLI_PACKAGE_VERSION}`;
|
|
1257
|
-
var COLLIE_COMPILER_DEPENDENCY = formatCollieDependency("@collie-lang/compiler");
|
|
1258
|
-
var COLLIE_VITE_DEPENDENCY = formatCollieDependency("@collie-lang/vite");
|
|
1259
|
-
var COLLIE_NEXT_DEPENDENCY = formatCollieDependency("@collie-lang/next");
|
|
1260
|
-
var COLLIE_NEXT_VERSION_RANGE = normalizeDependencyRange(
|
|
1261
|
-
CLI_DEPENDENCY_SPECS["@collie-lang/next"],
|
|
1262
|
-
DEFAULT_DEPENDENCY_RANGE
|
|
1263
|
-
);
|
|
1264
|
-
var COLLIE_CORE_PACKAGES = ["@collie-lang/compiler", "@collie-lang/config"];
|
|
1265
|
-
var COLLIE_VITE_PACKAGES = [
|
|
1266
|
-
...COLLIE_CORE_PACKAGES,
|
|
1267
|
-
"@collie-lang/vite",
|
|
1268
|
-
"@collie-lang/html-runtime"
|
|
1269
|
-
];
|
|
1270
|
-
var COLLIE_NEXT_PACKAGES = [...COLLIE_CORE_PACKAGES, "@collie-lang/next"];
|
|
1271
|
-
var PROMPT_OPTIONS = {
|
|
1272
|
-
onCancel: () => {
|
|
1273
|
-
console.log(import_picocolors8.default.yellow("\nCancelled"));
|
|
1274
|
-
process.exit(0);
|
|
1275
|
-
}
|
|
1276
|
-
};
|
|
1277
|
-
var preflightCompleted = false;
|
|
1278
|
-
async function main() {
|
|
1279
|
-
const args = process.argv.slice(2);
|
|
1280
|
-
const cmd = args[0];
|
|
1281
|
-
if (!cmd || cmd === "help" || cmd === "--help" || cmd === "-h") {
|
|
1282
|
-
printHelp();
|
|
1283
|
-
return;
|
|
1284
|
-
}
|
|
1285
|
-
if (cmd === "format") {
|
|
1286
|
-
try {
|
|
1287
|
-
await runFormat(args.slice(1));
|
|
1288
|
-
} catch (error) {
|
|
1289
|
-
const message = error instanceof Error ? error.message : String(error);
|
|
1290
|
-
printCliError(message);
|
|
1291
|
-
process.exit(1);
|
|
1292
|
-
}
|
|
1293
|
-
return;
|
|
1294
|
-
}
|
|
1295
|
-
if (cmd === "check") {
|
|
1296
|
-
const rest = args.slice(1);
|
|
1297
|
-
const patterns = rest.filter((arg) => !arg.startsWith("-"));
|
|
1298
|
-
if (patterns.length === 0) {
|
|
1299
|
-
throw new Error("No file patterns provided. Usage: collie check <files...>");
|
|
1300
|
-
}
|
|
1301
|
-
const formatValue = getFlag(rest, "--format");
|
|
1302
|
-
const format = formatValue ? validateFormatFlag(formatValue) : "text";
|
|
1303
|
-
const maxWarningsValue = getFlag(rest, "--max-warnings");
|
|
1304
|
-
let maxWarnings = -1;
|
|
1305
|
-
if (maxWarningsValue !== void 0) {
|
|
1306
|
-
const parsed = Number(maxWarningsValue);
|
|
1307
|
-
if (!Number.isInteger(parsed) || parsed < 0) {
|
|
1308
|
-
throw new Error("--max-warnings expects a non-negative integer.");
|
|
1309
|
-
}
|
|
1310
|
-
maxWarnings = parsed;
|
|
1311
|
-
}
|
|
1312
|
-
const options = {
|
|
1313
|
-
verbose: hasFlag(rest, "--verbose", "-v"),
|
|
1314
|
-
format,
|
|
1315
|
-
noWarnings: hasFlag(rest, "--no-warnings"),
|
|
1316
|
-
maxWarnings: maxWarnings >= 0 ? maxWarnings : void 0
|
|
1317
|
-
};
|
|
1318
|
-
const shouldContinue = await runPreflight("check");
|
|
1319
|
-
if (!shouldContinue) {
|
|
1320
|
-
return;
|
|
1321
|
-
}
|
|
1322
|
-
const result = await check(patterns, options);
|
|
1323
|
-
if (result.errorCount > 0) {
|
|
1324
|
-
process.exitCode = 1;
|
|
1325
|
-
} else if (maxWarnings >= 0 && result.warningCount > maxWarnings) {
|
|
1326
|
-
printCliError(
|
|
1327
|
-
`Exceeded maximum warnings: ${result.warningCount} warning${result.warningCount === 1 ? "" : "s"} (limit ${maxWarnings})`
|
|
1328
|
-
);
|
|
1329
|
-
console.error(import_picocolors8.default.dim("Next: fix warnings or raise --max-warnings."));
|
|
1330
|
-
process.exitCode = 1;
|
|
1331
|
-
}
|
|
1332
|
-
return;
|
|
1333
|
-
}
|
|
1334
|
-
if (cmd === "ids") {
|
|
1335
|
-
const rest = args.slice(1);
|
|
1336
|
-
const patterns = rest.filter((arg) => !arg.startsWith("-"));
|
|
1337
|
-
const resolvedPatterns = patterns.length ? patterns : ["**/*.collie"];
|
|
1338
|
-
try {
|
|
1339
|
-
await runIds(resolvedPatterns);
|
|
1340
|
-
} catch (error) {
|
|
1341
|
-
const message = error instanceof Error ? error.message : String(error);
|
|
1342
|
-
printCliError(message);
|
|
1343
|
-
process.exit(1);
|
|
1344
|
-
}
|
|
1345
|
-
return;
|
|
1346
|
-
}
|
|
1347
|
-
if (cmd === "explain") {
|
|
1348
|
-
const rest = args.slice(1);
|
|
1349
|
-
const { id, patterns } = parseExplainArgs(rest);
|
|
1350
|
-
if (!id) {
|
|
1351
|
-
throw new Error("No template id provided. Usage: collie explain <id> [files...]");
|
|
1352
|
-
}
|
|
1353
|
-
const resolvedPatterns = patterns.length ? patterns : ["**/*.collie"];
|
|
1354
|
-
try {
|
|
1355
|
-
await runExplain(id, resolvedPatterns);
|
|
1356
|
-
} catch (error) {
|
|
1357
|
-
const message = error instanceof Error ? error.message : String(error);
|
|
1358
|
-
printCliError(message);
|
|
1359
|
-
process.exit(1);
|
|
1360
|
-
}
|
|
1361
|
-
return;
|
|
1362
|
-
}
|
|
1363
|
-
if (cmd === "config") {
|
|
1364
|
-
const rest = args.slice(1);
|
|
1365
|
-
const shouldPrint = hasFlag(rest, "--print");
|
|
1366
|
-
if (!shouldPrint) {
|
|
1367
|
-
throw new Error('Missing required flag: --print (e.g. "collie config --print").');
|
|
1368
|
-
}
|
|
1369
|
-
const filePath = getFlag(rest, "--file");
|
|
1370
|
-
const cwdFlag = getFlag(rest, "--cwd");
|
|
1371
|
-
const cwd = cwdFlag ? import_node_path9.default.resolve(process.cwd(), cwdFlag) : filePath ? import_node_path9.default.dirname(import_node_path9.default.resolve(process.cwd(), filePath)) : process.cwd();
|
|
1372
|
-
let loadAndNormalizeConfig;
|
|
1373
|
-
try {
|
|
1374
|
-
({ loadAndNormalizeConfig } = await import("@collie-lang/config"));
|
|
1375
|
-
} catch (error) {
|
|
1376
|
-
const message = error instanceof Error ? error.message : String(error);
|
|
1377
|
-
throw new Error(`Failed to load @collie-lang/config (${message}).`);
|
|
1378
|
-
}
|
|
1379
|
-
const normalized = await loadAndNormalizeConfig({ cwd });
|
|
1380
|
-
if (!normalized) {
|
|
1381
|
-
throw new Error(`No Collie config found under ${cwd}.`);
|
|
1382
|
-
}
|
|
1383
|
-
console.log(JSON.stringify(normalized, null, 2));
|
|
1384
|
-
return;
|
|
1385
|
-
}
|
|
1386
|
-
if (cmd === "create") {
|
|
1387
|
-
const rest = args.slice(1);
|
|
1388
|
-
let projectName;
|
|
1389
|
-
const flagArgs = [];
|
|
1390
|
-
for (const arg of rest) {
|
|
1391
|
-
if (!projectName && !arg.startsWith("-")) {
|
|
1392
|
-
projectName = arg;
|
|
1393
|
-
} else {
|
|
1394
|
-
flagArgs.push(arg);
|
|
1395
|
-
}
|
|
1396
|
-
}
|
|
1397
|
-
const templateListRequested = hasFlag(flagArgs, "--list-templates");
|
|
1398
|
-
if (templateListRequested) {
|
|
1399
|
-
console.log(import_picocolors8.default.bold("Available templates:\n"));
|
|
1400
|
-
console.log(formatTemplateList());
|
|
1401
|
-
console.log("\nRun collie create <project-name> --template <template> to scaffold with a specific option.\n");
|
|
1402
|
-
return;
|
|
1403
|
-
}
|
|
1404
|
-
const template = getFlag(flagArgs, "--template");
|
|
1405
|
-
const typescriptFlag = hasFlag(flagArgs, "--typescript");
|
|
1406
|
-
const javascriptFlag = hasFlag(flagArgs, "--javascript");
|
|
1407
|
-
if (typescriptFlag && javascriptFlag) {
|
|
1408
|
-
throw new Error("Use only one of --typescript or --javascript.");
|
|
1409
|
-
}
|
|
1410
|
-
const options = {
|
|
1411
|
-
projectName,
|
|
1412
|
-
template,
|
|
1413
|
-
typescript: typescriptFlag ? true : javascriptFlag ? false : void 0,
|
|
1414
|
-
packageManager: getFlag(flagArgs, "--package-manager"),
|
|
1415
|
-
noInstall: hasFlag(flagArgs, "--no-install"),
|
|
1416
|
-
noGit: hasFlag(flagArgs, "--no-git")
|
|
1417
|
-
};
|
|
1418
|
-
await create(options);
|
|
1419
|
-
return;
|
|
1420
|
-
}
|
|
1421
|
-
if (cmd === "convert") {
|
|
1422
|
-
const rest = args.slice(1);
|
|
1423
|
-
const patterns = rest.filter((arg) => !arg.startsWith("-"));
|
|
1424
|
-
if (patterns.length === 0) {
|
|
1425
|
-
throw new Error("No files provided. Usage: collie convert <files...>");
|
|
1426
|
-
}
|
|
1427
|
-
const write = hasFlag(rest, "--write", "-w");
|
|
1428
|
-
const overwrite = hasFlag(rest, "--overwrite");
|
|
1429
|
-
const removeOriginal = hasFlag(rest, "--remove-original");
|
|
1430
|
-
if (removeOriginal && !write) {
|
|
1431
|
-
throw new Error("--remove-original can only be used with --write.");
|
|
1432
|
-
}
|
|
1433
|
-
const files = await (0, import_fast_glob4.default)(patterns, {
|
|
1434
|
-
absolute: false,
|
|
1435
|
-
onlyFiles: true,
|
|
1436
|
-
unique: true
|
|
1437
|
-
});
|
|
1438
|
-
if (!files.length) {
|
|
1439
|
-
printSummary("warning", "No files matched the provided patterns", void 0, "check the paths and try again");
|
|
1440
|
-
return;
|
|
1441
|
-
}
|
|
1442
|
-
files.sort();
|
|
1443
|
-
const options = { write, overwrite, removeOriginal };
|
|
1444
|
-
let converted = 0;
|
|
1445
|
-
let failed = 0;
|
|
1446
|
-
for (const file of files) {
|
|
1447
|
-
try {
|
|
1448
|
-
const result = await convertFile(file, options);
|
|
1449
|
-
if (write) {
|
|
1450
|
-
const target = result.outputPath ?? file.replace(/\.[tj]sx?$/, ".collie");
|
|
1451
|
-
console.log(import_picocolors8.default.green(`\u2714 Converted ${file} \u2192 ${target}`));
|
|
1452
|
-
converted++;
|
|
1453
|
-
} else {
|
|
1454
|
-
console.log(import_picocolors8.default.gray(`// Converted from ${file}
|
|
1455
|
-
`));
|
|
1456
|
-
process.stdout.write(result.collie);
|
|
1457
|
-
if (!result.collie.endsWith("\n")) {
|
|
1458
|
-
process.stdout.write("\n");
|
|
1459
|
-
}
|
|
1460
|
-
console.log("");
|
|
1461
|
-
}
|
|
1462
|
-
for (const warning of result.warnings) {
|
|
1463
|
-
console.warn(import_picocolors8.default.yellow(`\u26A0 ${file}: ${warning}`));
|
|
1464
|
-
}
|
|
1465
|
-
} catch (error) {
|
|
1466
|
-
const message = error instanceof Error ? error.message : String(error);
|
|
1467
|
-
console.error(import_picocolors8.default.red(`\u2716 Failed to convert ${file}: ${message}`));
|
|
1468
|
-
process.exitCode = 1;
|
|
1469
|
-
failed++;
|
|
1470
|
-
}
|
|
1471
|
-
}
|
|
1472
|
-
if (write) {
|
|
1473
|
-
if (failed === 0) {
|
|
1474
|
-
printSummary(
|
|
1475
|
-
"success",
|
|
1476
|
-
`Converted ${converted} file${converted === 1 ? "" : "s"}`,
|
|
1477
|
-
`wrote ${converted} .collie file${converted === 1 ? "" : "s"}`,
|
|
1478
|
-
"review the generated templates or run collie check"
|
|
1479
|
-
);
|
|
1480
|
-
} else {
|
|
1481
|
-
const changeDetail = converted > 0 ? `wrote ${converted} .collie file${converted === 1 ? "" : "s"} before failing` : void 0;
|
|
1482
|
-
printSummary(
|
|
1483
|
-
"error",
|
|
1484
|
-
`Converted ${converted} file${converted === 1 ? "" : "s"} with ${failed} failure${failed === 1 ? "" : "s"}`,
|
|
1485
|
-
changeDetail,
|
|
1486
|
-
"fix the errors above and rerun collie convert"
|
|
1487
|
-
);
|
|
1488
|
-
}
|
|
1489
|
-
}
|
|
1490
|
-
return;
|
|
1491
|
-
}
|
|
1492
|
-
if (cmd === "doctor") {
|
|
1493
|
-
const rest = args.slice(1);
|
|
1494
|
-
const jsonOutput = hasFlag(rest, "--json");
|
|
1495
|
-
const subsystem = getFlag(rest, "--check");
|
|
1496
|
-
const results = await runDoctor({ cwd: process.cwd() });
|
|
1497
|
-
const filtered = filterDiagnostics(results, subsystem);
|
|
1498
|
-
if (subsystem && filtered.length === 0) {
|
|
1499
|
-
printCliError(`Unknown subsystem for --check: ${subsystem}`);
|
|
1500
|
-
console.error(import_picocolors8.default.dim("Next: run collie doctor to list available checks."));
|
|
1501
|
-
process.exit(1);
|
|
1502
|
-
}
|
|
1503
|
-
if (jsonOutput) {
|
|
1504
|
-
console.log(JSON.stringify(filtered, null, 2));
|
|
1505
|
-
} else {
|
|
1506
|
-
printDoctorResults(filtered);
|
|
1507
|
-
}
|
|
1508
|
-
if (filtered.some((result) => result.status === "fail")) {
|
|
1509
|
-
process.exitCode = 1;
|
|
1510
|
-
}
|
|
1511
|
-
return;
|
|
1512
|
-
}
|
|
1513
|
-
if (cmd === "watch") {
|
|
1514
|
-
const watchArgs = args.slice(1);
|
|
1515
|
-
const inputPath = watchArgs[0];
|
|
1516
|
-
if (!inputPath) {
|
|
1517
|
-
throw new Error("No input path provided. Usage: collie watch <path>");
|
|
1518
|
-
}
|
|
1519
|
-
const flagArgs = watchArgs.slice(1);
|
|
1520
|
-
const verboseFlag = hasFlag(flagArgs, "--verbose", "-v");
|
|
1521
|
-
const sourcemapFlag = hasFlag(flagArgs, "--sourcemap");
|
|
1522
|
-
const jsxFlag = getFlag(flagArgs, "--jsx");
|
|
1523
|
-
await watch(inputPath, {
|
|
1524
|
-
outDir: getFlag(flagArgs, "--outDir"),
|
|
1525
|
-
sourcemap: sourcemapFlag ? true : void 0,
|
|
1526
|
-
ext: getFlag(flagArgs, "--ext"),
|
|
1527
|
-
jsxRuntime: jsxFlag ? parseJsxRuntime(jsxFlag) : void 0,
|
|
1528
|
-
verbose: verboseFlag ? true : void 0
|
|
1529
|
-
});
|
|
1530
|
-
return;
|
|
1531
|
-
}
|
|
1532
|
-
if (cmd === "build") {
|
|
1533
|
-
const buildArgs = args.slice(1);
|
|
1534
|
-
const inputPath = buildArgs[0];
|
|
1535
|
-
if (!inputPath) {
|
|
1536
|
-
throw new Error("No input path provided. Usage: collie build <path>");
|
|
1537
|
-
}
|
|
1538
|
-
const flagArgs = buildArgs.slice(1);
|
|
1539
|
-
const verbose = hasFlag(flagArgs, "--verbose", "-v");
|
|
1540
|
-
const quiet = hasFlag(flagArgs, "--quiet", "-q");
|
|
1541
|
-
if (verbose && quiet) {
|
|
1542
|
-
throw new Error("Cannot use --quiet and --verbose together.");
|
|
1543
|
-
}
|
|
1544
|
-
const sourcemapFlag = hasFlag(flagArgs, "--sourcemap");
|
|
1545
|
-
const jsxFlag = getFlag(flagArgs, "--jsx");
|
|
1546
|
-
const result = await build(inputPath, {
|
|
1547
|
-
outDir: getFlag(flagArgs, "--outDir"),
|
|
1548
|
-
sourcemap: sourcemapFlag ? true : void 0,
|
|
1549
|
-
jsxRuntime: jsxFlag ? parseJsxRuntime(jsxFlag) : void 0,
|
|
1550
|
-
verbose: verbose ? true : void 0,
|
|
1551
|
-
quiet: quiet ? true : void 0
|
|
1552
|
-
});
|
|
1553
|
-
if (result.errors.length > 0) {
|
|
1554
|
-
process.exitCode = 1;
|
|
1555
|
-
}
|
|
1556
|
-
return;
|
|
1557
|
-
}
|
|
1558
|
-
if (cmd === "init") {
|
|
1559
|
-
const rest = args.slice(1);
|
|
1560
|
-
const flagsNeedingValue = /* @__PURE__ */ new Set(["--project", "--package-manager", "--framework"]);
|
|
1561
|
-
let pendingFlag = null;
|
|
1562
|
-
let positionalProject;
|
|
1563
|
-
const extraArgs = [];
|
|
1564
|
-
for (const arg of rest) {
|
|
1565
|
-
if (pendingFlag) {
|
|
1566
|
-
pendingFlag = null;
|
|
1567
|
-
continue;
|
|
1568
|
-
}
|
|
1569
|
-
if (arg.startsWith("-")) {
|
|
1570
|
-
if (flagsNeedingValue.has(arg)) {
|
|
1571
|
-
pendingFlag = arg;
|
|
1572
|
-
}
|
|
1573
|
-
continue;
|
|
1574
|
-
}
|
|
1575
|
-
if (!positionalProject) {
|
|
1576
|
-
positionalProject = arg;
|
|
1577
|
-
} else {
|
|
1578
|
-
extraArgs.push(arg);
|
|
1579
|
-
}
|
|
1580
|
-
}
|
|
1581
|
-
if (pendingFlag) {
|
|
1582
|
-
throw new Error(`${pendingFlag} flag expects a value.`);
|
|
1583
|
-
}
|
|
1584
|
-
if (extraArgs.length > 0) {
|
|
1585
|
-
throw new Error(`Unexpected argument(s): ${extraArgs.join(", ")}`);
|
|
1586
|
-
}
|
|
1587
|
-
const frameworkValue = getFlag(rest, "--framework");
|
|
1588
|
-
const framework = (frameworkValue === "vite" || frameworkValue === "nextjs" ? frameworkValue : void 0) ?? (hasFlag(rest, "--nextjs") ? "nextjs" : void 0) ?? (hasFlag(rest, "--vite") ? "vite" : void 0);
|
|
1589
|
-
const typescriptFlag = hasFlag(rest, "--typescript");
|
|
1590
|
-
const javascriptFlag = hasFlag(rest, "--javascript");
|
|
1591
|
-
if (typescriptFlag && javascriptFlag) {
|
|
1592
|
-
throw new Error("Use only one of --typescript or --javascript.");
|
|
1593
|
-
}
|
|
1594
|
-
const packageManagerValue = getFlag(rest, "--package-manager");
|
|
1595
|
-
let packageManager;
|
|
1596
|
-
if (packageManagerValue) {
|
|
1597
|
-
if (packageManagerValue !== "npm" && packageManagerValue !== "yarn" && packageManagerValue !== "pnpm") {
|
|
1598
|
-
throw new Error('Invalid --package-manager value. Use "npm", "yarn", or "pnpm".');
|
|
1599
|
-
}
|
|
1600
|
-
packageManager = packageManagerValue;
|
|
1601
|
-
}
|
|
1602
|
-
const projectName = getFlag(rest, "--project") ?? positionalProject;
|
|
1603
|
-
const initOptions = {
|
|
1604
|
-
framework,
|
|
1605
|
-
projectName,
|
|
1606
|
-
typescript: typescriptFlag ? true : javascriptFlag ? false : void 0,
|
|
1607
|
-
packageManager,
|
|
1608
|
-
noInstall: hasFlag(rest, "--no-install")
|
|
1609
|
-
};
|
|
1610
|
-
try {
|
|
1611
|
-
const shouldContinue = await runPreflight("init", {
|
|
1612
|
-
framework: initOptions.framework,
|
|
1613
|
-
packageManager: initOptions.packageManager
|
|
1614
|
-
});
|
|
1615
|
-
if (!shouldContinue) {
|
|
1616
|
-
return;
|
|
1617
|
-
}
|
|
1618
|
-
await runInit(initOptions);
|
|
1619
|
-
} catch (error) {
|
|
1620
|
-
const message = error instanceof Error ? error.message : String(error);
|
|
1621
|
-
printCliError(message);
|
|
1622
|
-
process.exit(1);
|
|
1623
|
-
}
|
|
1624
|
-
return;
|
|
1625
|
-
}
|
|
1626
|
-
printCliError(`Unknown command: ${cmd}`);
|
|
1627
|
-
process.exit(1);
|
|
1628
|
-
}
|
|
1629
|
-
function printHelp() {
|
|
1630
|
-
console.log(`${import_picocolors8.default.bold("collie")}
|
|
1631
|
-
|
|
1632
|
-
Usage:
|
|
1633
|
-
collie <command> [options]
|
|
1634
|
-
|
|
1635
|
-
Commands:
|
|
1636
|
-
collie build Compile .collie templates to output files
|
|
1637
|
-
collie check Validate .collie templates
|
|
1638
|
-
collie config Print resolved Collie config (json)
|
|
1639
|
-
collie ids List template ids and their locations
|
|
1640
|
-
collie explain Find the file + location for a template id
|
|
1641
|
-
collie format Format .collie templates
|
|
1642
|
-
collie convert Convert JSX/TSX to .collie templates
|
|
1643
|
-
collie doctor Diagnose setup issues
|
|
1644
|
-
collie init Create a Collie config and wire Vite when possible
|
|
1645
|
-
collie watch Watch and compile templates
|
|
1646
|
-
collie create Scaffold a new Collie project
|
|
1647
|
-
`);
|
|
1648
|
-
}
|
|
1649
|
-
async function runIds(patterns) {
|
|
1650
|
-
const scan = await scanTemplates(patterns);
|
|
1651
|
-
const diagnostics = [...scan.diagnostics, ...buildDuplicateDiagnostics(scan.templates)];
|
|
1652
|
-
const errors = diagnostics.filter((diag) => diag.severity === "error");
|
|
1653
|
-
if (diagnostics.length) {
|
|
1654
|
-
printTemplateDiagnostics(diagnostics);
|
|
1655
|
-
}
|
|
1656
|
-
if (errors.length) {
|
|
1657
|
-
process.exitCode = 1;
|
|
1658
|
-
return;
|
|
1659
|
-
}
|
|
1660
|
-
const templates = [...scan.templates].sort((a, b) => {
|
|
1661
|
-
const byId = a.id.localeCompare(b.id);
|
|
1662
|
-
if (byId !== 0) return byId;
|
|
1663
|
-
return a.displayPath.localeCompare(b.displayPath);
|
|
1664
|
-
});
|
|
1665
|
-
if (!templates.length) {
|
|
1666
|
-
printSummary(
|
|
1667
|
-
"warning",
|
|
1668
|
-
"No template ids found",
|
|
1669
|
-
`checked ${scan.files.length} file${scan.files.length === 1 ? "" : "s"}`,
|
|
1670
|
-
"add #id blocks then rerun collie ids"
|
|
1671
|
-
);
|
|
1672
|
-
return;
|
|
1673
|
-
}
|
|
1674
|
-
for (const template of templates) {
|
|
1675
|
-
console.log(`${template.id} ${formatTemplateLocation2(template)}`);
|
|
1676
|
-
}
|
|
1677
|
-
}
|
|
1678
|
-
async function runExplain(id, patterns) {
|
|
1679
|
-
const scan = await scanTemplates(patterns);
|
|
1680
|
-
const diagnostics = [...scan.diagnostics, ...buildDuplicateDiagnostics(scan.templates)];
|
|
1681
|
-
const errors = diagnostics.filter((diag) => diag.severity === "error");
|
|
1682
|
-
if (diagnostics.length) {
|
|
1683
|
-
printTemplateDiagnostics(diagnostics);
|
|
1684
|
-
}
|
|
1685
|
-
if (errors.length) {
|
|
1686
|
-
process.exitCode = 1;
|
|
1687
|
-
return;
|
|
1688
|
-
}
|
|
1689
|
-
const matches = scan.templates.filter((template) => template.id === id);
|
|
1690
|
-
if (!matches.length) {
|
|
1691
|
-
const knownIds = Array.from(new Set(scan.templates.map((template) => template.id))).sort();
|
|
1692
|
-
const preview = knownIds.slice(0, 5);
|
|
1693
|
-
const suffix = knownIds.length > preview.length ? "..." : "";
|
|
1694
|
-
const details = preview.length ? `Known ids: ${preview.join(", ")}${suffix}` : "No template ids found.";
|
|
1695
|
-
printCliError(`Unknown template id "${id}". ${details}`);
|
|
1696
|
-
process.exitCode = 1;
|
|
1697
|
-
return;
|
|
1698
|
-
}
|
|
1699
|
-
const sorted = matches.sort((a, b) => a.displayPath.localeCompare(b.displayPath));
|
|
1700
|
-
for (const template of sorted) {
|
|
1701
|
-
console.log(`${template.id} ${formatTemplateLocation2(template)}`);
|
|
1702
|
-
}
|
|
1703
|
-
}
|
|
1704
|
-
function parseExplainArgs(args) {
|
|
1705
|
-
let id;
|
|
1706
|
-
const patterns = [];
|
|
1707
|
-
for (const arg of args) {
|
|
1708
|
-
if (arg.startsWith("-")) {
|
|
1709
|
-
continue;
|
|
1710
|
-
}
|
|
1711
|
-
if (!id) {
|
|
1712
|
-
id = arg;
|
|
1713
|
-
continue;
|
|
1714
|
-
}
|
|
1715
|
-
patterns.push(arg);
|
|
1716
|
-
}
|
|
1717
|
-
return { id, patterns };
|
|
1718
|
-
}
|
|
1719
|
-
function formatTemplateLocation2(template) {
|
|
1720
|
-
const span = template.span;
|
|
1721
|
-
if (span) {
|
|
1722
|
-
return `${template.displayPath}:${span.start.line}:${span.start.col}`;
|
|
1723
|
-
}
|
|
1724
|
-
return template.displayPath;
|
|
1725
|
-
}
|
|
1726
|
-
function printTemplateDiagnostics(diagnostics) {
|
|
1727
|
-
for (const diag of diagnostics) {
|
|
1728
|
-
const message = formatDiagnosticLine(diag);
|
|
1729
|
-
const writer = diag.severity === "warning" ? import_picocolors8.default.yellow : import_picocolors8.default.red;
|
|
1730
|
-
console.log(writer(message));
|
|
1731
|
-
}
|
|
1732
|
-
if (diagnostics.length) {
|
|
1733
|
-
console.log("");
|
|
1734
|
-
}
|
|
1735
|
-
}
|
|
1736
|
-
async function runInit(options = {}) {
|
|
1737
|
-
const projectRoot = process.cwd();
|
|
1738
|
-
const packageJson = await readProjectPackage(projectRoot);
|
|
1739
|
-
const detectedFramework = packageJson ? detectFrameworkFromPackage(packageJson) : null;
|
|
1740
|
-
const cssDetection = await detectCssStrategy(projectRoot, packageJson);
|
|
1741
|
-
const framework = options.framework ?? detectedFramework;
|
|
1742
|
-
const projectType = framework ? mapFrameworkToProjectType(framework) : await promptProjectType();
|
|
1743
|
-
const existingConfig = findExistingCollieConfig(projectRoot);
|
|
1744
|
-
const targetPath = existingConfig ?? import_node_path9.default.join(projectRoot, "collie.config.ts");
|
|
1745
|
-
const relativeTarget = import_node_path9.default.relative(projectRoot, targetPath) || import_node_path9.default.basename(targetPath);
|
|
1746
|
-
console.log(import_picocolors8.default.bold("collie init"));
|
|
1747
|
-
console.log(import_picocolors8.default.dim("This creates a Collie config and applies framework wiring when possible."));
|
|
1748
|
-
if (framework) {
|
|
1749
|
-
console.log(import_picocolors8.default.dim(`Detected ${formatFrameworkLabel(framework)} project.`));
|
|
1750
|
-
} else {
|
|
1751
|
-
console.log(import_picocolors8.default.dim("No framework detected."));
|
|
1752
|
-
}
|
|
1753
|
-
console.log(import_picocolors8.default.dim(`CSS strategy: ${formatCssDetection(cssDetection)}.`));
|
|
1754
|
-
console.log(import_picocolors8.default.dim(`Override in ${relativeTarget} via css.strategy and css.diagnostics.unknownClass.`));
|
|
1755
|
-
console.log(import_picocolors8.default.dim(`Project type: ${describeProjectType(projectType)}.`));
|
|
1756
|
-
console.log("");
|
|
1757
|
-
const configLabel = framework === "vite" ? "Vite-ready" : "Collie";
|
|
1758
|
-
const confirmMessage = existingConfig ? `${relativeTarget} already exists. Replace it with a ${configLabel} config?` : `Create ${relativeTarget}?`;
|
|
1759
|
-
const shouldWrite = await promptForConfirmation(confirmMessage, !existingConfig);
|
|
1760
|
-
if (!shouldWrite) {
|
|
1761
|
-
const detail = existingConfig ? `left ${relativeTarget} unchanged` : "no files created";
|
|
1762
|
-
printSummary("warning", "No changes made", detail, "run collie init when you are ready");
|
|
1763
|
-
return;
|
|
1764
|
-
}
|
|
1765
|
-
const contents = buildInitConfig(projectType, import_node_path9.default.extname(targetPath).toLowerCase(), cssDetection);
|
|
1766
|
-
await import_promises9.default.writeFile(targetPath, contents, "utf8");
|
|
1767
|
-
const typeDeclarationsPath = import_node_path9.default.join(projectRoot, "src", "collie.d.ts");
|
|
1768
|
-
let typeDeclarationsStatus = "skipped";
|
|
1769
|
-
if (projectType !== "html" && shouldWriteTypeDeclarations(projectRoot, options)) {
|
|
1770
|
-
if ((0, import_node_fs4.existsSync)(typeDeclarationsPath)) {
|
|
1771
|
-
typeDeclarationsStatus = "exists";
|
|
1772
|
-
} else {
|
|
1773
|
-
await ensureCollieDeclaration(projectRoot);
|
|
1774
|
-
typeDeclarationsStatus = "created";
|
|
1775
|
-
}
|
|
1776
|
-
}
|
|
1777
|
-
let viteConfigStatus = "skipped";
|
|
1778
|
-
let viteConfigPath = null;
|
|
1779
|
-
if (framework === "vite") {
|
|
1780
|
-
viteConfigPath = findViteConfigFile(projectRoot);
|
|
1781
|
-
if (viteConfigPath) {
|
|
1782
|
-
viteConfigStatus = await patchViteConfig(viteConfigPath);
|
|
1783
|
-
} else {
|
|
1784
|
-
viteConfigStatus = "not-found";
|
|
1785
|
-
}
|
|
1786
|
-
}
|
|
1787
|
-
printSummary("success", "Initialized Collie config", `created ${relativeTarget}`);
|
|
1788
|
-
if (typeDeclarationsStatus !== "skipped") {
|
|
1789
|
-
const declarationLabel = import_node_path9.default.relative(projectRoot, typeDeclarationsPath) || import_node_path9.default.basename(typeDeclarationsPath);
|
|
1790
|
-
if (typeDeclarationsStatus === "created") {
|
|
1791
|
-
console.log(import_picocolors8.default.green(`\u2714 Added ${declarationLabel} for .collie typings`));
|
|
1792
|
-
} else {
|
|
1793
|
-
console.log(import_picocolors8.default.dim(`- ${declarationLabel} already exists`));
|
|
1794
|
-
}
|
|
1795
|
-
} else if (projectType !== "html") {
|
|
1796
|
-
console.log(
|
|
1797
|
-
import_picocolors8.default.dim("Skipping .collie typings (no TypeScript config found). Add src/collie.d.ts if you enable TypeScript.")
|
|
1798
|
-
);
|
|
1799
|
-
}
|
|
1800
|
-
if (framework === "vite") {
|
|
1801
|
-
if (viteConfigStatus === "patched" && viteConfigPath) {
|
|
1802
|
-
console.log(import_picocolors8.default.green(`\u2714 Updated ${import_node_path9.default.relative(projectRoot, viteConfigPath) || import_node_path9.default.basename(viteConfigPath)}`));
|
|
1803
|
-
} else if (viteConfigStatus === "already-configured" && viteConfigPath) {
|
|
1804
|
-
console.log(
|
|
1805
|
-
import_picocolors8.default.dim(`- ${import_node_path9.default.relative(projectRoot, viteConfigPath) || import_node_path9.default.basename(viteConfigPath)} already includes collie()`)
|
|
1806
|
-
);
|
|
1807
|
-
} else if (viteConfigStatus === "manual" && viteConfigPath) {
|
|
1808
|
-
console.log(
|
|
1809
|
-
import_picocolors8.default.yellow(
|
|
1810
|
-
`\u26A0 Could not patch ${import_node_path9.default.relative(projectRoot, viteConfigPath) || import_node_path9.default.basename(viteConfigPath)}. Add collie() manually to the Vite plugins array.`
|
|
1811
|
-
)
|
|
1812
|
-
);
|
|
1813
|
-
} else if (viteConfigStatus === "not-found") {
|
|
1814
|
-
console.log(import_picocolors8.default.yellow("\u26A0 Vite config not found. Add the Collie plugin to vite.config.ts manually."));
|
|
1815
|
-
}
|
|
1816
|
-
}
|
|
1817
|
-
if (framework === "vite") {
|
|
1818
|
-
const pkgManager = options.packageManager ?? detectPackageManager2(projectRoot);
|
|
1819
|
-
printNextSteps(pkgManager, targetPath);
|
|
1820
|
-
}
|
|
1821
|
-
if (!framework) {
|
|
1822
|
-
console.log(import_picocolors8.default.dim(`Tip: update the project type in ${relativeTarget} if needed.`));
|
|
1823
|
-
}
|
|
1824
|
-
}
|
|
1825
|
-
async function readProjectPackage(projectRoot) {
|
|
1826
|
-
const packageJsonPath = import_node_path9.default.join(projectRoot, "package.json");
|
|
1827
|
-
if (!(0, import_node_fs4.existsSync)(packageJsonPath)) {
|
|
1828
|
-
return null;
|
|
1829
|
-
}
|
|
1830
|
-
const raw = await import_promises9.default.readFile(packageJsonPath, "utf8");
|
|
1831
|
-
return JSON.parse(raw);
|
|
1832
|
-
}
|
|
1833
|
-
function detectFrameworkFromPackage(pkg) {
|
|
1834
|
-
if (hasNextDependency(pkg)) {
|
|
1835
|
-
return "nextjs";
|
|
1836
|
-
}
|
|
1837
|
-
if (getViteDependencyInfo(pkg)) {
|
|
1838
|
-
return "vite";
|
|
1839
|
-
}
|
|
1840
|
-
return null;
|
|
1841
|
-
}
|
|
1842
|
-
function mapFrameworkToProjectType(framework) {
|
|
1843
|
-
return framework === "nextjs" ? "react-next" : "react-vite";
|
|
1844
|
-
}
|
|
1845
|
-
function formatFrameworkLabel(framework) {
|
|
1846
|
-
return framework === "nextjs" ? "Next.js" : "Vite";
|
|
1847
|
-
}
|
|
1848
|
-
function describeProjectType(projectType) {
|
|
1849
|
-
const labels = {
|
|
1850
|
-
"react-vite": "React (Vite)",
|
|
1851
|
-
"react-next": "React (Next.js)",
|
|
1852
|
-
"react-generic": "React (generic)",
|
|
1853
|
-
html: "HTML (no framework)"
|
|
1854
|
-
};
|
|
1855
|
-
return labels[projectType];
|
|
1856
|
-
}
|
|
1857
|
-
function shouldWriteTypeDeclarations(projectRoot, options) {
|
|
1858
|
-
if (options.typescript === false) {
|
|
1859
|
-
return false;
|
|
1860
|
-
}
|
|
1861
|
-
if (options.typescript === true) {
|
|
1862
|
-
return true;
|
|
1863
|
-
}
|
|
1864
|
-
return (0, import_node_fs4.existsSync)(import_node_path9.default.join(projectRoot, "tsconfig.json"));
|
|
1865
|
-
}
|
|
1866
|
-
async function detectCssStrategy(projectRoot, packageJson) {
|
|
1867
|
-
const reasons = [];
|
|
1868
|
-
try {
|
|
1869
|
-
let tailwindDetected = false;
|
|
1870
|
-
for (const filename of TAILWIND_CONFIG_FILES) {
|
|
1871
|
-
if ((0, import_node_fs4.existsSync)(import_node_path9.default.join(projectRoot, filename))) {
|
|
1872
|
-
reasons.push(`${filename} found`);
|
|
1873
|
-
tailwindDetected = true;
|
|
1874
|
-
}
|
|
1875
|
-
}
|
|
1876
|
-
if (packageJson && hasTailwindDependency(packageJson)) {
|
|
1877
|
-
reasons.push("package.json includes tailwindcss");
|
|
1878
|
-
tailwindDetected = true;
|
|
1879
|
-
}
|
|
1880
|
-
const postcssHit = await scanPostcssForTailwind(projectRoot);
|
|
1881
|
-
if (postcssHit) {
|
|
1882
|
-
reasons.push(`${postcssHit} mentions tailwindcss`);
|
|
1883
|
-
tailwindDetected = true;
|
|
1884
|
-
}
|
|
1885
|
-
const cssHit = await scanTopLevelCssForTailwind(projectRoot);
|
|
1886
|
-
if (cssHit) {
|
|
1887
|
-
reasons.push(`${cssHit} contains @tailwind`);
|
|
1888
|
-
tailwindDetected = true;
|
|
1889
|
-
}
|
|
1890
|
-
if (tailwindDetected) {
|
|
1891
|
-
return { strategy: "tailwind", unknownClass: "off", reasons };
|
|
1892
|
-
}
|
|
1893
|
-
return {
|
|
1894
|
-
strategy: "global",
|
|
1895
|
-
unknownClass: "warn",
|
|
1896
|
-
reasons: ["no Tailwind signals found"]
|
|
1897
|
-
};
|
|
1898
|
-
} catch (error) {
|
|
1899
|
-
const message = error instanceof Error ? error.message : String(error);
|
|
1900
|
-
return {
|
|
1901
|
-
strategy: "unknown",
|
|
1902
|
-
unknownClass: "off",
|
|
1903
|
-
reasons: [`detection error: ${message}`]
|
|
1904
|
-
};
|
|
1905
|
-
}
|
|
1906
|
-
}
|
|
1907
|
-
function formatCssDetection(result) {
|
|
1908
|
-
const reason = result.reasons.length ? ` (${result.reasons.join(", ")})` : "";
|
|
1909
|
-
const label = result.strategy === "tailwind" ? "Tailwind" : result.strategy === "global" ? "Global CSS" : "Unknown";
|
|
1910
|
-
return `${label}${reason} => unknownClass ${result.unknownClass}`;
|
|
1911
|
-
}
|
|
1912
|
-
function hasTailwindDependency(pkg) {
|
|
1913
|
-
return Boolean(
|
|
1914
|
-
pkg.dependencies && pkg.dependencies.tailwindcss || pkg.devDependencies && pkg.devDependencies.tailwindcss || pkg.peerDependencies && pkg.peerDependencies.tailwindcss
|
|
1915
|
-
);
|
|
1916
|
-
}
|
|
1917
|
-
async function scanPostcssForTailwind(projectRoot) {
|
|
1918
|
-
for (const filename of POSTCSS_CONFIG_FILES) {
|
|
1919
|
-
const fullPath = import_node_path9.default.join(projectRoot, filename);
|
|
1920
|
-
if (!(0, import_node_fs4.existsSync)(fullPath)) {
|
|
1921
|
-
continue;
|
|
1922
|
-
}
|
|
1923
|
-
const contents = await import_promises9.default.readFile(fullPath, "utf8");
|
|
1924
|
-
if (contents.includes("tailwindcss")) {
|
|
1925
|
-
return filename;
|
|
1926
|
-
}
|
|
1927
|
-
}
|
|
1928
|
-
return null;
|
|
1929
|
-
}
|
|
1930
|
-
async function scanTopLevelCssForTailwind(projectRoot) {
|
|
1931
|
-
const files = await (0, import_fast_glob4.default)("*.css", { cwd: projectRoot, onlyFiles: true });
|
|
1932
|
-
for (const filename of files) {
|
|
1933
|
-
const fullPath = import_node_path9.default.join(projectRoot, filename);
|
|
1934
|
-
const contents = await import_promises9.default.readFile(fullPath, "utf8");
|
|
1935
|
-
if (/\@tailwind\s+(base|components|utilities)\b/.test(contents)) {
|
|
1936
|
-
return filename;
|
|
1937
|
-
}
|
|
1938
|
-
}
|
|
1939
|
-
return null;
|
|
1940
|
-
}
|
|
1941
|
-
function buildInitConfig(projectType, ext, cssDetection) {
|
|
1942
|
-
const config = {
|
|
1943
|
-
css: {
|
|
1944
|
-
strategy: cssDetection.strategy,
|
|
1945
|
-
diagnostics: {
|
|
1946
|
-
unknownClass: cssDetection.unknownClass
|
|
1947
|
-
}
|
|
1948
|
-
},
|
|
1949
|
-
projects: [
|
|
1950
|
-
{
|
|
1951
|
-
type: projectType,
|
|
1952
|
-
input: "src/**/*.collie"
|
|
1953
|
-
}
|
|
1954
|
-
]
|
|
1955
|
-
};
|
|
1956
|
-
if (ext === ".json") {
|
|
1957
|
-
return `${JSON.stringify(config, null, 2)}
|
|
1958
|
-
`;
|
|
1959
|
-
}
|
|
1960
|
-
const commentLines = projectType === "react-vite" ? [
|
|
1961
|
-
"// Collie config for Vite.",
|
|
1962
|
-
"// Templates are compiled in-memory by @collie-lang/vite."
|
|
1963
|
-
] : ["// Collie config. Update the project type or input as needed."];
|
|
1964
|
-
const comment = `${commentLines.join("\n")}
|
|
1965
|
-
`;
|
|
1966
|
-
const body = JSON.stringify(config, null, 2);
|
|
1967
|
-
if (ext === ".mjs" || ext === ".ts") {
|
|
1968
|
-
return `${comment}export default ${body};
|
|
1969
|
-
`;
|
|
1970
|
-
}
|
|
1971
|
-
return `${comment}module.exports = ${body};
|
|
1972
|
-
`;
|
|
1973
|
-
}
|
|
1974
|
-
function findExistingCollieConfig(root) {
|
|
1975
|
-
for (const filename of COLLIE_CONFIG_FILES) {
|
|
1976
|
-
const candidate = import_node_path9.default.join(root, filename);
|
|
1977
|
-
if ((0, import_node_fs4.existsSync)(candidate)) {
|
|
1978
|
-
return candidate;
|
|
1979
|
-
}
|
|
1980
|
-
}
|
|
1981
|
-
return null;
|
|
1982
|
-
}
|
|
1983
|
-
async function promptForConfirmation(message, initial) {
|
|
1984
|
-
const response = await (0, import_prompts2.default)(
|
|
1985
|
-
{
|
|
1986
|
-
type: "confirm",
|
|
1987
|
-
name: "confirmed",
|
|
1988
|
-
message,
|
|
1989
|
-
initial
|
|
1990
|
-
},
|
|
1991
|
-
PROMPT_OPTIONS
|
|
1992
|
-
);
|
|
1993
|
-
return Boolean(response.confirmed);
|
|
1994
|
-
}
|
|
1995
|
-
async function promptProjectType() {
|
|
1996
|
-
const response = await (0, import_prompts2.default)(
|
|
1997
|
-
{
|
|
1998
|
-
type: "select",
|
|
1999
|
-
name: "projectType",
|
|
2000
|
-
message: "What type of project should this config describe?",
|
|
2001
|
-
choices: [
|
|
2002
|
-
{ title: "React (Vite)", value: "react-vite" },
|
|
2003
|
-
{ title: "React (Next.js)", value: "react-next" },
|
|
2004
|
-
{ title: "React (generic)", value: "react-generic" },
|
|
2005
|
-
{ title: "HTML (no framework)", value: "html" }
|
|
2006
|
-
],
|
|
2007
|
-
initial: 0
|
|
2008
|
-
},
|
|
2009
|
-
PROMPT_OPTIONS
|
|
2010
|
-
);
|
|
2011
|
-
return response.projectType ?? "react-generic";
|
|
2012
|
-
}
|
|
2013
|
-
async function runPreflight(command, options = {}) {
|
|
2014
|
-
if (preflightCompleted) {
|
|
2015
|
-
return true;
|
|
2016
|
-
}
|
|
2017
|
-
preflightCompleted = true;
|
|
2018
|
-
const projectRoot = findProjectRoot(process.cwd());
|
|
2019
|
-
if (!projectRoot) {
|
|
2020
|
-
console.log(import_picocolors8.default.dim("Skipping dependency preflight (no package.json found)."));
|
|
2021
|
-
return true;
|
|
2022
|
-
}
|
|
2023
|
-
let packageJson = null;
|
|
2024
|
-
try {
|
|
2025
|
-
packageJson = await readProjectPackage(projectRoot);
|
|
2026
|
-
} catch (error) {
|
|
2027
|
-
const message = error instanceof Error ? error.message : String(error);
|
|
2028
|
-
console.log(import_picocolors8.default.yellow(`Skipping dependency preflight: failed to read package.json (${message}).`));
|
|
2029
|
-
return true;
|
|
2030
|
-
}
|
|
2031
|
-
if (!packageJson) {
|
|
2032
|
-
console.log(import_picocolors8.default.dim("Skipping dependency preflight (package.json not found)."));
|
|
2033
|
-
return true;
|
|
2034
|
-
}
|
|
2035
|
-
const detectedFramework = detectFrameworkFromPackage(packageJson);
|
|
2036
|
-
const resolvedFramework = options.framework ?? detectedFramework ?? (command === "init" ? "vite" : void 0);
|
|
2037
|
-
const requiredPackages = getRequiredPackages(command, resolvedFramework);
|
|
2038
|
-
if (requiredPackages.length === 0) {
|
|
2039
|
-
return true;
|
|
2040
|
-
}
|
|
2041
|
-
const missing = collectMissingDependencies(projectRoot, packageJson, requiredPackages);
|
|
2042
|
-
if (missing.length === 0) {
|
|
2043
|
-
return true;
|
|
2044
|
-
}
|
|
2045
|
-
const packageManager = options.packageManager ?? detectPackageManager2(projectRoot);
|
|
2046
|
-
const prompt = `Missing required Collie packages: ${missing.join(", ")}. Install now?`;
|
|
2047
|
-
const shouldInstall = await promptForConfirmation(prompt, true);
|
|
2048
|
-
if (!shouldInstall) {
|
|
2049
|
-
console.log(import_picocolors8.default.yellow("Skipped installing Collie dependencies."));
|
|
2050
|
-
console.log(
|
|
2051
|
-
import_picocolors8.default.dim(`Next: ${formatInstallCommand(packageManager, missing)} && collie ${command}`)
|
|
2052
|
-
);
|
|
2053
|
-
return false;
|
|
2054
|
-
}
|
|
2055
|
-
const specs = missing.map((dep) => resolveDependencySpec(dep));
|
|
2056
|
-
console.log(import_picocolors8.default.cyan(`Installing ${missing.length} Collie package${missing.length === 1 ? "" : "s"}...`));
|
|
2057
|
-
await installDevDependencies(packageManager, projectRoot, specs);
|
|
2058
|
-
console.log(import_picocolors8.default.green(`\u2714 Installed ${missing.length} Collie package${missing.length === 1 ? "" : "s"}.`));
|
|
2059
|
-
return true;
|
|
2060
|
-
}
|
|
2061
|
-
function findProjectRoot(startDir) {
|
|
2062
|
-
let current = startDir;
|
|
2063
|
-
while (true) {
|
|
2064
|
-
if ((0, import_node_fs4.existsSync)(import_node_path9.default.join(current, "package.json"))) {
|
|
2065
|
-
return current;
|
|
2066
|
-
}
|
|
2067
|
-
const parent = import_node_path9.default.dirname(current);
|
|
2068
|
-
if (parent === current) {
|
|
2069
|
-
return null;
|
|
2070
|
-
}
|
|
2071
|
-
current = parent;
|
|
2072
|
-
}
|
|
2073
|
-
}
|
|
2074
|
-
function getRequiredPackages(command, framework) {
|
|
2075
|
-
if (command === "check") {
|
|
2076
|
-
return ["@collie-lang/compiler"];
|
|
2077
|
-
}
|
|
2078
|
-
if (framework === "nextjs") {
|
|
2079
|
-
return [...COLLIE_NEXT_PACKAGES];
|
|
2080
|
-
}
|
|
2081
|
-
if (framework === "vite") {
|
|
2082
|
-
return [...COLLIE_VITE_PACKAGES];
|
|
2083
|
-
}
|
|
2084
|
-
return [...COLLIE_CORE_PACKAGES];
|
|
2085
|
-
}
|
|
2086
|
-
function collectMissingDependencies(projectRoot, packageJson, required) {
|
|
2087
|
-
return required.filter((dependency) => !isDependencySatisfied(projectRoot, packageJson, dependency));
|
|
2088
|
-
}
|
|
2089
|
-
function isDependencySatisfied(projectRoot, packageJson, dependency) {
|
|
2090
|
-
const listed = Boolean(
|
|
2091
|
-
packageJson?.dependencies?.[dependency] || packageJson?.devDependencies?.[dependency]
|
|
2092
|
-
);
|
|
2093
|
-
if (listed) {
|
|
2094
|
-
return true;
|
|
2095
|
-
}
|
|
2096
|
-
const modulePath = import_node_path9.default.join(projectRoot, "node_modules", ...dependency.split("/"));
|
|
2097
|
-
return (0, import_node_fs4.existsSync)(modulePath);
|
|
2098
|
-
}
|
|
2099
|
-
function resolveDependencySpec(packageName) {
|
|
2100
|
-
const range = normalizeDependencyRange(CLI_DEPENDENCY_SPECS[packageName], "latest");
|
|
2101
|
-
return `${packageName}@${range}`;
|
|
2102
|
-
}
|
|
2103
|
-
function formatInstallCommand(packageManager, dependencies) {
|
|
2104
|
-
const specs = dependencies.map((dep) => resolveDependencySpec(dep));
|
|
2105
|
-
if (packageManager === "pnpm") {
|
|
2106
|
-
return `pnpm add -D ${specs.join(" ")}`;
|
|
2107
|
-
}
|
|
2108
|
-
if (packageManager === "yarn") {
|
|
2109
|
-
return `yarn add -D ${specs.join(" ")}`;
|
|
2110
|
-
}
|
|
2111
|
-
return `npm install -D ${specs.join(" ")}`;
|
|
2112
|
-
}
|
|
2113
|
-
function detectPackageManager2(root) {
|
|
2114
|
-
if ((0, import_node_fs4.existsSync)(import_node_path9.default.join(root, "pnpm-lock.yaml"))) return "pnpm";
|
|
2115
|
-
if ((0, import_node_fs4.existsSync)(import_node_path9.default.join(root, "yarn.lock"))) return "yarn";
|
|
2116
|
-
return "npm";
|
|
2117
|
-
}
|
|
2118
|
-
async function installDevDependencies(packageManager, cwd, deps) {
|
|
2119
|
-
const argsByManager = {
|
|
2120
|
-
pnpm: ["add", "-D", ...deps],
|
|
2121
|
-
yarn: ["add", "-D", ...deps],
|
|
2122
|
-
npm: ["install", "-D", ...deps]
|
|
2123
|
-
};
|
|
2124
|
-
await runCommand2(packageManager, argsByManager[packageManager], cwd);
|
|
2125
|
-
}
|
|
2126
|
-
async function patchViteConfig(configPath) {
|
|
2127
|
-
const original = await import_promises9.default.readFile(configPath, "utf8");
|
|
2128
|
-
const hasImport = original.includes("@collie-lang/vite");
|
|
2129
|
-
const hasPlugin = /\bcollie\s*\(/.test(original);
|
|
2130
|
-
if (hasImport && hasPlugin) {
|
|
2131
|
-
return "already-configured";
|
|
2132
|
-
}
|
|
2133
|
-
try {
|
|
2134
|
-
const result = transformViteConfig(original);
|
|
2135
|
-
if (!result.changed) {
|
|
2136
|
-
return "already-configured";
|
|
2137
|
-
}
|
|
2138
|
-
await import_promises9.default.writeFile(configPath, result.code, "utf8");
|
|
2139
|
-
return "patched";
|
|
2140
|
-
} catch (error) {
|
|
2141
|
-
return "manual";
|
|
2142
|
-
}
|
|
2143
|
-
}
|
|
2144
|
-
function transformViteConfig(source) {
|
|
2145
|
-
const sourceFile = import_typescript.default.createSourceFile(
|
|
2146
|
-
"vite.config.ts",
|
|
2147
|
-
source,
|
|
2148
|
-
import_typescript.default.ScriptTarget.Latest,
|
|
2149
|
-
true,
|
|
2150
|
-
import_typescript.default.ScriptKind.TS
|
|
2151
|
-
);
|
|
2152
|
-
let needsImport = !source.includes("@collie-lang/vite");
|
|
2153
|
-
let needsPlugin = !/\bcollie\s*\(/.test(source);
|
|
2154
|
-
let changed = false;
|
|
2155
|
-
const printer = import_typescript.default.createPrinter({ newLine: import_typescript.default.NewLineKind.LineFeed });
|
|
2156
|
-
const transformer = (context) => {
|
|
2157
|
-
return (rootNode) => {
|
|
2158
|
-
function visit(node) {
|
|
2159
|
-
if (needsImport && import_typescript.default.isImportDeclaration(node)) {
|
|
2160
|
-
return node;
|
|
2161
|
-
}
|
|
2162
|
-
if (import_typescript.default.isCallExpression(node) && import_typescript.default.isIdentifier(node.expression) && node.expression.text === "defineConfig" && node.arguments.length > 0) {
|
|
2163
|
-
const configArg = node.arguments[0];
|
|
2164
|
-
if (import_typescript.default.isObjectLiteralExpression(configArg)) {
|
|
2165
|
-
const updatedConfig = updateConfigObject(configArg);
|
|
2166
|
-
if (updatedConfig !== configArg) {
|
|
2167
|
-
changed = true;
|
|
2168
|
-
return import_typescript.default.factory.updateCallExpression(
|
|
2169
|
-
node,
|
|
2170
|
-
node.expression,
|
|
2171
|
-
node.typeArguments,
|
|
2172
|
-
[updatedConfig]
|
|
2173
|
-
);
|
|
2174
|
-
}
|
|
2175
|
-
}
|
|
2176
|
-
}
|
|
2177
|
-
return import_typescript.default.visitEachChild(node, visit, context);
|
|
2178
|
-
}
|
|
2179
|
-
function updateConfigObject(configObj) {
|
|
2180
|
-
let pluginsProperty;
|
|
2181
|
-
const otherProperties = [];
|
|
2182
|
-
for (const prop of configObj.properties) {
|
|
2183
|
-
if (import_typescript.default.isPropertyAssignment(prop) && import_typescript.default.isIdentifier(prop.name) && prop.name.text === "plugins") {
|
|
2184
|
-
pluginsProperty = prop;
|
|
2185
|
-
} else {
|
|
2186
|
-
otherProperties.push(prop);
|
|
2187
|
-
}
|
|
2188
|
-
}
|
|
2189
|
-
let updatedPluginsArray;
|
|
2190
|
-
if (pluginsProperty && import_typescript.default.isArrayLiteralExpression(pluginsProperty.initializer)) {
|
|
2191
|
-
updatedPluginsArray = ensurePluginOrdering(pluginsProperty.initializer);
|
|
2192
|
-
if (updatedPluginsArray === pluginsProperty.initializer) {
|
|
2193
|
-
return configObj;
|
|
2194
|
-
}
|
|
2195
|
-
} else if (needsPlugin) {
|
|
2196
|
-
updatedPluginsArray = import_typescript.default.factory.createArrayLiteralExpression(
|
|
2197
|
-
[createCollieCall()],
|
|
2198
|
-
false
|
|
2199
|
-
);
|
|
2200
|
-
} else {
|
|
2201
|
-
return configObj;
|
|
2202
|
-
}
|
|
2203
|
-
const updatedPluginsProperty = import_typescript.default.factory.createPropertyAssignment(
|
|
2204
|
-
"plugins",
|
|
2205
|
-
updatedPluginsArray
|
|
2206
|
-
);
|
|
2207
|
-
if (pluginsProperty) {
|
|
2208
|
-
const allProperties = [...otherProperties, updatedPluginsProperty];
|
|
2209
|
-
return import_typescript.default.factory.updateObjectLiteralExpression(configObj, allProperties);
|
|
2210
|
-
} else {
|
|
2211
|
-
const allProperties = [...otherProperties, updatedPluginsProperty];
|
|
2212
|
-
return import_typescript.default.factory.updateObjectLiteralExpression(configObj, allProperties);
|
|
2213
|
-
}
|
|
2214
|
-
}
|
|
2215
|
-
function ensurePluginOrdering(array) {
|
|
2216
|
-
const elements = Array.from(array.elements);
|
|
2217
|
-
let hasReact = false;
|
|
2218
|
-
let hasCollie = false;
|
|
2219
|
-
let reactIndex = -1;
|
|
2220
|
-
let collieIndex = -1;
|
|
2221
|
-
elements.forEach((elem, idx) => {
|
|
2222
|
-
if (import_typescript.default.isCallExpression(elem) && import_typescript.default.isIdentifier(elem.expression)) {
|
|
2223
|
-
if (elem.expression.text === "react") {
|
|
2224
|
-
hasReact = true;
|
|
2225
|
-
reactIndex = idx;
|
|
2226
|
-
} else if (elem.expression.text === "collie") {
|
|
2227
|
-
hasCollie = true;
|
|
2228
|
-
collieIndex = idx;
|
|
2229
|
-
}
|
|
2230
|
-
}
|
|
2231
|
-
});
|
|
2232
|
-
const isMultiLine = elements.length > 1;
|
|
2233
|
-
if (hasCollie && !needsPlugin) {
|
|
2234
|
-
if (hasReact && reactIndex > collieIndex) {
|
|
2235
|
-
const newElements = [...elements];
|
|
2236
|
-
const reactPlugin = newElements[reactIndex];
|
|
2237
|
-
const colliePlugin = newElements[collieIndex];
|
|
2238
|
-
newElements[collieIndex] = reactPlugin;
|
|
2239
|
-
newElements[reactIndex] = colliePlugin;
|
|
2240
|
-
return import_typescript.default.factory.createArrayLiteralExpression(newElements, isMultiLine);
|
|
2241
|
-
}
|
|
2242
|
-
return array;
|
|
2243
|
-
}
|
|
2244
|
-
if (!hasCollie && needsPlugin) {
|
|
2245
|
-
const newElements = [...elements, createCollieCall()];
|
|
2246
|
-
return import_typescript.default.factory.createArrayLiteralExpression(newElements, isMultiLine);
|
|
2247
|
-
}
|
|
2248
|
-
return array;
|
|
2249
|
-
}
|
|
2250
|
-
function createCollieCall() {
|
|
2251
|
-
return import_typescript.default.factory.createCallExpression(
|
|
2252
|
-
import_typescript.default.factory.createIdentifier("collie"),
|
|
2253
|
-
void 0,
|
|
2254
|
-
[]
|
|
2255
|
-
);
|
|
2256
|
-
}
|
|
2257
|
-
const visited = import_typescript.default.visitNode(rootNode, visit);
|
|
2258
|
-
if (needsImport && changed) {
|
|
2259
|
-
const collieImport = import_typescript.default.factory.createImportDeclaration(
|
|
2260
|
-
void 0,
|
|
2261
|
-
import_typescript.default.factory.createImportClause(
|
|
2262
|
-
false,
|
|
2263
|
-
import_typescript.default.factory.createIdentifier("collie"),
|
|
2264
|
-
void 0
|
|
2265
|
-
),
|
|
2266
|
-
import_typescript.default.factory.createStringLiteral("@collie-lang/vite", true)
|
|
2267
|
-
);
|
|
2268
|
-
let lastImportIndex = -1;
|
|
2269
|
-
for (let i = 0; i < visited.statements.length; i++) {
|
|
2270
|
-
if (import_typescript.default.isImportDeclaration(visited.statements[i])) {
|
|
2271
|
-
lastImportIndex = i;
|
|
2272
|
-
}
|
|
2273
|
-
}
|
|
2274
|
-
const statements = Array.from(visited.statements);
|
|
2275
|
-
if (lastImportIndex >= 0) {
|
|
2276
|
-
statements.splice(lastImportIndex + 1, 0, collieImport);
|
|
2277
|
-
} else {
|
|
2278
|
-
statements.unshift(collieImport);
|
|
2279
|
-
}
|
|
2280
|
-
return import_typescript.default.factory.updateSourceFile(visited, statements);
|
|
2281
|
-
}
|
|
2282
|
-
return visited;
|
|
2283
|
-
};
|
|
2284
|
-
};
|
|
2285
|
-
const result = import_typescript.default.transform(sourceFile, [transformer]);
|
|
2286
|
-
const transformedSourceFile = result.transformed[0];
|
|
2287
|
-
result.dispose();
|
|
2288
|
-
if (!changed) {
|
|
2289
|
-
return { code: source, changed: false };
|
|
2290
|
-
}
|
|
2291
|
-
const output = printer.printFile(transformedSourceFile);
|
|
2292
|
-
return { code: output, changed: true };
|
|
2293
|
-
}
|
|
2294
|
-
async function ensureCollieDeclaration(root) {
|
|
2295
|
-
const target = import_node_path9.default.join(root, "src", "collie.d.ts");
|
|
2296
|
-
if ((0, import_node_fs4.existsSync)(target)) return;
|
|
2297
|
-
await import_promises9.default.mkdir(import_node_path9.default.dirname(target), { recursive: true });
|
|
2298
|
-
const declaration = `// Allows importing Collie templates as React components.
|
|
2299
|
-
// Customize this typing if your templates expose specific props.
|
|
2300
|
-
declare module "*.collie" {
|
|
2301
|
-
import type { ComponentType } from "react";
|
|
2302
|
-
const component: ComponentType<Record<string, unknown>>;
|
|
2303
|
-
export default component;
|
|
2304
|
-
}
|
|
2305
|
-
`;
|
|
2306
|
-
await import_promises9.default.writeFile(target, declaration, "utf8");
|
|
2307
|
-
}
|
|
2308
|
-
function findViteConfigFile(root) {
|
|
2309
|
-
for (const file of VITE_CONFIG_FILES2) {
|
|
2310
|
-
const candidate = import_node_path9.default.join(root, file);
|
|
2311
|
-
if ((0, import_node_fs4.existsSync)(candidate)) {
|
|
2312
|
-
return candidate;
|
|
2313
|
-
}
|
|
2314
|
-
}
|
|
2315
|
-
return null;
|
|
2316
|
-
}
|
|
2317
|
-
function readCliPackageInfo() {
|
|
2318
|
-
try {
|
|
2319
|
-
const dir = import_node_path9.default.dirname((0, import_node_url2.fileURLToPath)(import_meta2.url));
|
|
2320
|
-
const pkgPath = import_node_path9.default.resolve(dir, "..", "package.json");
|
|
2321
|
-
const raw = (0, import_node_fs4.readFileSync)(pkgPath, "utf8");
|
|
2322
|
-
const pkg = JSON.parse(raw);
|
|
2323
|
-
const version = typeof pkg.version === "string" ? pkg.version : "latest";
|
|
2324
|
-
const dependencies = pkg && typeof pkg === "object" && pkg.dependencies && typeof pkg.dependencies === "object" ? pkg.dependencies : {};
|
|
2325
|
-
return { version, dependencies };
|
|
2326
|
-
} catch {
|
|
2327
|
-
return { version: "latest", dependencies: {} };
|
|
2328
|
-
}
|
|
2329
|
-
}
|
|
2330
|
-
function formatCollieDependency(packageName) {
|
|
2331
|
-
return CLI_PACKAGE_VERSION === "latest" ? packageName : `${packageName}@${CLI_PACKAGE_VERSION}`;
|
|
2332
|
-
}
|
|
2333
|
-
function normalizeDependencyRange(spec, fallback) {
|
|
2334
|
-
if (!spec) {
|
|
2335
|
-
return fallback;
|
|
2336
|
-
}
|
|
2337
|
-
if (spec.startsWith("workspace:")) {
|
|
2338
|
-
const trimmed = spec.replace(/^workspace:/, "");
|
|
2339
|
-
return trimmed && trimmed !== "*" ? trimmed : fallback;
|
|
2340
|
-
}
|
|
2341
|
-
return spec;
|
|
2342
|
-
}
|
|
2343
|
-
function getViteDependencyInfo(pkg) {
|
|
2344
|
-
const spec = pkg.devDependencies && pkg.devDependencies.vite || pkg.dependencies && pkg.dependencies.vite || null;
|
|
2345
|
-
if (!spec) return null;
|
|
2346
|
-
const normalized = spec.replace(/^workspace:/, "").replace(/^[~^>=<\s]*/, "");
|
|
2347
|
-
const match = normalized.match(/(\d+)(?:\.\d+)?/);
|
|
2348
|
-
return { range: spec, major: match ? Number(match[1]) : null };
|
|
2349
|
-
}
|
|
2350
|
-
function printNextSteps(pkgManager, configPath) {
|
|
2351
|
-
const devCommand = formatDevCommand(pkgManager);
|
|
2352
|
-
console.log("");
|
|
2353
|
-
console.log(import_picocolors8.default.green("Next steps:"));
|
|
2354
|
-
console.log(` - Create a Collie template under src (e.g. src/Hello.collie).`);
|
|
2355
|
-
console.log(` - Import it in your React app and run ${devCommand} to start Vite.`);
|
|
2356
|
-
console.log(` - Need to adjust plugins later? Edit ${import_node_path9.default.basename(configPath)}.`);
|
|
2357
|
-
}
|
|
2358
|
-
function formatDevCommand(pkgManager) {
|
|
2359
|
-
if (pkgManager === "pnpm") return "pnpm dev";
|
|
2360
|
-
if (pkgManager === "yarn") return "yarn dev";
|
|
2361
|
-
return "npm run dev";
|
|
2362
|
-
}
|
|
2363
|
-
function runCommand2(command, args, cwd) {
|
|
2364
|
-
return new Promise((resolve, reject) => {
|
|
2365
|
-
const child = (0, import_node_child_process2.spawn)(command, args, { cwd, stdio: "inherit" });
|
|
2366
|
-
child.on("error", reject);
|
|
2367
|
-
child.on("close", (code) => {
|
|
2368
|
-
if (code === 0) resolve();
|
|
2369
|
-
else reject(new Error(`${command} ${args.join(" ")} exited with code ${code}`));
|
|
2370
|
-
});
|
|
2371
|
-
});
|
|
2372
|
-
}
|
|
2373
|
-
async function runFormat(args) {
|
|
2374
|
-
const { patterns, flags } = parseFormatArgs(args);
|
|
2375
|
-
if (patterns.length === 0) {
|
|
2376
|
-
throw new Error("No file patterns provided. Usage: collie format <files...>");
|
|
2377
|
-
}
|
|
2378
|
-
const indent = flags.indent ?? 2;
|
|
2379
|
-
const cwd = process.cwd();
|
|
2380
|
-
const files = await (0, import_fast_glob4.default)(patterns, { cwd, onlyFiles: true, unique: true });
|
|
2381
|
-
if (!files.length) {
|
|
2382
|
-
printSummary("warning", "No files matched the provided patterns", void 0, "check the glob and try again");
|
|
2383
|
-
return;
|
|
2384
|
-
}
|
|
2385
|
-
files.sort();
|
|
2386
|
-
let written = 0;
|
|
2387
|
-
let needsFormatting = 0;
|
|
2388
|
-
let failures = 0;
|
|
2389
|
-
for (const file of files) {
|
|
2390
|
-
let contents;
|
|
2391
|
-
try {
|
|
2392
|
-
contents = await import_promises9.default.readFile(file, "utf8");
|
|
2393
|
-
} catch (error) {
|
|
2394
|
-
const message = error instanceof Error ? error.message : String(error);
|
|
2395
|
-
console.error(import_picocolors8.default.red(`\u2716 Failed to read ${file}: ${message}`));
|
|
2396
|
-
failures++;
|
|
2397
|
-
continue;
|
|
2398
|
-
}
|
|
2399
|
-
let result;
|
|
2400
|
-
try {
|
|
2401
|
-
result = formatSource(contents, { indent });
|
|
2402
|
-
} catch (error) {
|
|
2403
|
-
const message = error instanceof Error ? error.message : String(error);
|
|
2404
|
-
console.error(import_picocolors8.default.red(`\u2716 Failed to format ${file}: ${message}`));
|
|
2405
|
-
failures++;
|
|
2406
|
-
continue;
|
|
2407
|
-
}
|
|
2408
|
-
if (!result.success) {
|
|
2409
|
-
printDiagnostics(file, result.diagnostics);
|
|
2410
|
-
failures++;
|
|
2411
|
-
continue;
|
|
2412
|
-
}
|
|
2413
|
-
const changed = result.formatted !== contents;
|
|
2414
|
-
if (flags.diff && changed) {
|
|
2415
|
-
printDiff(file, contents, result.formatted);
|
|
2416
|
-
}
|
|
2417
|
-
if (flags.check) {
|
|
2418
|
-
if (changed) {
|
|
2419
|
-
console.log(import_picocolors8.default.red(`\u2716 ${file} needs formatting`));
|
|
2420
|
-
needsFormatting++;
|
|
2421
|
-
} else {
|
|
2422
|
-
console.log(import_picocolors8.default.green(`\u2714 ${file} is formatted`));
|
|
2423
|
-
}
|
|
2424
|
-
continue;
|
|
2425
|
-
}
|
|
2426
|
-
if (flags.write) {
|
|
2427
|
-
if (changed) {
|
|
2428
|
-
await import_promises9.default.writeFile(file, result.formatted, "utf8");
|
|
2429
|
-
written++;
|
|
2430
|
-
console.log(import_picocolors8.default.green(`\u2714 Formatted ${file}`));
|
|
2431
|
-
} else {
|
|
2432
|
-
console.log(import_picocolors8.default.dim(`- ${file} already formatted`));
|
|
2433
|
-
}
|
|
2434
|
-
continue;
|
|
2435
|
-
}
|
|
2436
|
-
if (!flags.diff) {
|
|
2437
|
-
process.stdout.write(result.formatted);
|
|
2438
|
-
}
|
|
2439
|
-
}
|
|
2440
|
-
if (flags.check) {
|
|
2441
|
-
if (needsFormatting > 0) {
|
|
2442
|
-
console.log("");
|
|
2443
|
-
printSummary(
|
|
2444
|
-
"error",
|
|
2445
|
-
`${needsFormatting} file${needsFormatting === 1 ? "" : "s"} need formatting`,
|
|
2446
|
-
"no files changed"
|
|
2447
|
-
);
|
|
2448
|
-
console.log(import_picocolors8.default.dim("Run: collie format --write to fix"));
|
|
2449
|
-
process.exitCode = 1;
|
|
2450
|
-
} else if (failures > 0) {
|
|
2451
|
-
printSummary(
|
|
2452
|
-
"error",
|
|
2453
|
-
`Failed to check ${failures} file${failures === 1 ? "" : "s"}`,
|
|
2454
|
-
"no files changed",
|
|
2455
|
-
"resolve the errors above and rerun collie format --check"
|
|
2456
|
-
);
|
|
2457
|
-
} else {
|
|
2458
|
-
printSummary(
|
|
2459
|
-
"success",
|
|
2460
|
-
`All ${files.length} file${files.length === 1 ? "" : "s"} formatted`,
|
|
2461
|
-
"no files changed",
|
|
2462
|
-
"run collie build when you are ready to compile"
|
|
2463
|
-
);
|
|
2464
|
-
}
|
|
2465
|
-
} else if (flags.write) {
|
|
2466
|
-
if (failures > 0) {
|
|
2467
|
-
printSummary(
|
|
2468
|
-
"error",
|
|
2469
|
-
`Formatted ${written} file${written === 1 ? "" : "s"} with ${failures} failure${failures === 1 ? "" : "s"}`,
|
|
2470
|
-
`wrote ${written} file${written === 1 ? "" : "s"} to disk`,
|
|
2471
|
-
"fix the errors above and rerun collie format --write"
|
|
2472
|
-
);
|
|
2473
|
-
} else {
|
|
2474
|
-
printSummary(
|
|
2475
|
-
"success",
|
|
2476
|
-
`Formatted ${written} file${written === 1 ? "" : "s"}`,
|
|
2477
|
-
`wrote ${written} file${written === 1 ? "" : "s"} to disk`,
|
|
2478
|
-
"review the changes or run collie check"
|
|
2479
|
-
);
|
|
2480
|
-
}
|
|
2481
|
-
}
|
|
2482
|
-
if (failures > 0) {
|
|
2483
|
-
process.exitCode = 1;
|
|
2484
|
-
}
|
|
2485
|
-
}
|
|
2486
|
-
function parseFormatArgs(args) {
|
|
2487
|
-
const flags = { write: false, check: false, diff: false };
|
|
2488
|
-
const patterns = [];
|
|
2489
|
-
for (let i = 0; i < args.length; i++) {
|
|
2490
|
-
const arg = args[i];
|
|
2491
|
-
if (arg === "--write" || arg === "-w") {
|
|
2492
|
-
flags.write = true;
|
|
2493
|
-
continue;
|
|
2494
|
-
}
|
|
2495
|
-
if (arg === "--check" || arg === "-c") {
|
|
2496
|
-
flags.check = true;
|
|
2497
|
-
continue;
|
|
2498
|
-
}
|
|
2499
|
-
if (arg === "--diff" || arg === "-d") {
|
|
2500
|
-
flags.diff = true;
|
|
2501
|
-
continue;
|
|
2502
|
-
}
|
|
2503
|
-
if (arg === "--indent") {
|
|
2504
|
-
const value = args[i + 1];
|
|
2505
|
-
if (!value) {
|
|
2506
|
-
throw new Error("--indent flag expects a number.");
|
|
2507
|
-
}
|
|
2508
|
-
const parsed = Number(value);
|
|
2509
|
-
if (!Number.isFinite(parsed) || parsed < 1) {
|
|
2510
|
-
throw new Error("Indent width must be a positive integer.");
|
|
2511
|
-
}
|
|
2512
|
-
flags.indent = Math.floor(parsed);
|
|
2513
|
-
i++;
|
|
2514
|
-
continue;
|
|
2515
|
-
}
|
|
2516
|
-
if (arg.startsWith("-")) {
|
|
2517
|
-
throw new Error(`Unknown option: ${arg}`);
|
|
2518
|
-
}
|
|
2519
|
-
patterns.push(arg);
|
|
2520
|
-
}
|
|
2521
|
-
if (flags.write && flags.check) {
|
|
2522
|
-
throw new Error("Cannot use --write and --check together.");
|
|
2523
|
-
}
|
|
2524
|
-
return { patterns, flags };
|
|
2525
|
-
}
|
|
2526
|
-
function printDiagnostics(file, diagnostics) {
|
|
2527
|
-
for (const diag of diagnostics) {
|
|
2528
|
-
const message = formatDiagnosticLine({ ...diag, file }, file);
|
|
2529
|
-
if (diag.severity === "warning") {
|
|
2530
|
-
console.warn(import_picocolors8.default.yellow(message));
|
|
2531
|
-
} else {
|
|
2532
|
-
console.error(import_picocolors8.default.red(message));
|
|
2533
|
-
}
|
|
2534
|
-
}
|
|
2535
|
-
}
|
|
2536
|
-
function printDiff(file, before, after) {
|
|
2537
|
-
console.log(import_picocolors8.default.cyan(`diff -- ${file}`));
|
|
2538
|
-
const diff = (0, import_diff.diffLines)(before, after);
|
|
2539
|
-
for (const part of diff) {
|
|
2540
|
-
const prefix = part.added ? "+" : part.removed ? "-" : " ";
|
|
2541
|
-
const color = part.added ? import_picocolors8.default.green : part.removed ? import_picocolors8.default.red : import_picocolors8.default.dim;
|
|
2542
|
-
const lines = part.value.split("\n");
|
|
2543
|
-
for (let i = 0; i < lines.length; i++) {
|
|
2544
|
-
const line = lines[i];
|
|
2545
|
-
if (i === lines.length - 1 && line === "") {
|
|
2546
|
-
continue;
|
|
2547
|
-
}
|
|
2548
|
-
console.log(color(`${prefix}${line}`));
|
|
2549
|
-
}
|
|
2550
|
-
}
|
|
2551
|
-
}
|
|
2552
|
-
function getFlag(args, flag) {
|
|
2553
|
-
const index = args.indexOf(flag);
|
|
2554
|
-
if (index === -1) {
|
|
2555
|
-
return void 0;
|
|
2556
|
-
}
|
|
2557
|
-
const value = args[index + 1];
|
|
2558
|
-
if (!value || value.startsWith("-")) {
|
|
2559
|
-
throw new Error(`${flag} flag expects a value.`);
|
|
2560
|
-
}
|
|
2561
|
-
return value;
|
|
2562
|
-
}
|
|
2563
|
-
function hasFlag(args, ...names) {
|
|
2564
|
-
return names.some((name) => args.includes(name));
|
|
2565
|
-
}
|
|
2566
|
-
function parseJsxRuntime(value) {
|
|
2567
|
-
if (!value) {
|
|
2568
|
-
throw new Error("--jsx flag expects a value.");
|
|
2569
|
-
}
|
|
2570
|
-
if (value === "automatic" || value === "classic") {
|
|
2571
|
-
return value;
|
|
2572
|
-
}
|
|
2573
|
-
throw new Error('Invalid --jsx flag. Use "automatic" or "classic".');
|
|
2574
|
-
}
|
|
2575
|
-
function validateFormatFlag(value) {
|
|
2576
|
-
if (value === "text" || value === "json") {
|
|
2577
|
-
return value;
|
|
2578
|
-
}
|
|
2579
|
-
throw new Error('Invalid --format flag. Use "text" or "json".');
|
|
2580
|
-
}
|
|
2581
|
-
function printCliError(message) {
|
|
2582
|
-
console.error(import_picocolors8.default.red(`\u2716 ${message}`));
|
|
2583
|
-
}
|
|
2584
|
-
main().catch((error) => {
|
|
2585
|
-
printCliError(error instanceof Error ? error.message : String(error));
|
|
2586
|
-
process.exit(1);
|
|
2587
|
-
});
|
|
2588
|
-
//# sourceMappingURL=index.cjs.map
|