@remix-run/cli 0.0.0 → 0.1.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.
Files changed (155) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +80 -3
  3. package/bootstrap/AGENTS.md +35 -0
  4. package/bootstrap/README.md +27 -0
  5. package/bootstrap/app/controllers/auth.tsx +21 -0
  6. package/bootstrap/app/controllers/home.tsx +26 -0
  7. package/bootstrap/app/router.ts +10 -0
  8. package/bootstrap/app/routes.ts +6 -0
  9. package/bootstrap/app/ui/document.tsx +21 -0
  10. package/bootstrap/app/ui/layout.tsx +22 -0
  11. package/bootstrap/app/utils/render.tsx +7 -0
  12. package/bootstrap/package.json +22 -0
  13. package/bootstrap/server.ts +37 -0
  14. package/bootstrap/tsconfig.json +19 -0
  15. package/dist/index.d.ts +2 -0
  16. package/dist/index.d.ts.map +1 -0
  17. package/dist/index.js +1 -0
  18. package/dist/lib/bootstrap-project.d.ts +17 -0
  19. package/dist/lib/bootstrap-project.d.ts.map +1 -0
  20. package/dist/lib/bootstrap-project.js +137 -0
  21. package/dist/lib/cli-context.d.ts +11 -0
  22. package/dist/lib/cli-context.d.ts.map +1 -0
  23. package/dist/lib/cli-context.js +19 -0
  24. package/dist/lib/cli.d.ts +6 -0
  25. package/dist/lib/cli.d.ts.map +1 -0
  26. package/dist/lib/cli.js +87 -0
  27. package/dist/lib/commands/completion.d.ts +3 -0
  28. package/dist/lib/commands/completion.d.ts.map +1 -0
  29. package/dist/lib/commands/completion.js +51 -0
  30. package/dist/lib/commands/doctor.d.ts +4 -0
  31. package/dist/lib/commands/doctor.d.ts.map +1 -0
  32. package/dist/lib/commands/doctor.js +293 -0
  33. package/dist/lib/commands/help.d.ts +4 -0
  34. package/dist/lib/commands/help.d.ts.map +1 -0
  35. package/dist/lib/commands/help.js +124 -0
  36. package/dist/lib/commands/new.d.ts +4 -0
  37. package/dist/lib/commands/new.d.ts.map +1 -0
  38. package/dist/lib/commands/new.js +70 -0
  39. package/dist/lib/commands/routes.d.ts +4 -0
  40. package/dist/lib/commands/routes.d.ts.map +1 -0
  41. package/dist/lib/commands/routes.js +161 -0
  42. package/dist/lib/commands/skills.d.ts +6 -0
  43. package/dist/lib/commands/skills.d.ts.map +1 -0
  44. package/dist/lib/commands/skills.js +222 -0
  45. package/dist/lib/commands/test.d.ts +4 -0
  46. package/dist/lib/commands/test.d.ts.map +1 -0
  47. package/dist/lib/commands/test.js +19 -0
  48. package/dist/lib/commands/version.d.ts +4 -0
  49. package/dist/lib/commands/version.d.ts.map +1 -0
  50. package/dist/lib/commands/version.js +29 -0
  51. package/dist/lib/completion.d.ts +12 -0
  52. package/dist/lib/completion.d.ts.map +1 -0
  53. package/dist/lib/completion.js +408 -0
  54. package/dist/lib/contained-path.d.ts +2 -0
  55. package/dist/lib/contained-path.d.ts.map +1 -0
  56. package/dist/lib/contained-path.js +10 -0
  57. package/dist/lib/controller-files.d.ts +14 -0
  58. package/dist/lib/controller-files.d.ts.map +1 -0
  59. package/dist/lib/controller-files.js +53 -0
  60. package/dist/lib/controller-ownership.d.ts +41 -0
  61. package/dist/lib/controller-ownership.d.ts.map +1 -0
  62. package/dist/lib/controller-ownership.js +224 -0
  63. package/dist/lib/display-path.d.ts +2 -0
  64. package/dist/lib/display-path.d.ts.map +1 -0
  65. package/dist/lib/display-path.js +12 -0
  66. package/dist/lib/doctor/controller-findings.d.ts +4 -0
  67. package/dist/lib/doctor/controller-findings.d.ts.map +1 -0
  68. package/dist/lib/doctor/controller-findings.js +132 -0
  69. package/dist/lib/doctor/controller-fix-plans.d.ts +4 -0
  70. package/dist/lib/doctor/controller-fix-plans.d.ts.map +1 -0
  71. package/dist/lib/doctor/controller-fix-plans.js +130 -0
  72. package/dist/lib/doctor/controller-placeholders.d.ts +4 -0
  73. package/dist/lib/doctor/controller-placeholders.d.ts.map +1 -0
  74. package/dist/lib/doctor/controller-placeholders.js +183 -0
  75. package/dist/lib/doctor/controllers.d.ts +8 -0
  76. package/dist/lib/doctor/controllers.d.ts.map +1 -0
  77. package/dist/lib/doctor/controllers.js +13 -0
  78. package/dist/lib/doctor/environment.d.ts +17 -0
  79. package/dist/lib/doctor/environment.d.ts.map +1 -0
  80. package/dist/lib/doctor/environment.js +226 -0
  81. package/dist/lib/doctor/fixes.d.ts +3 -0
  82. package/dist/lib/doctor/fixes.d.ts.map +1 -0
  83. package/dist/lib/doctor/fixes.js +65 -0
  84. package/dist/lib/doctor/project.d.ts +10 -0
  85. package/dist/lib/doctor/project.d.ts.map +1 -0
  86. package/dist/lib/doctor/project.js +371 -0
  87. package/dist/lib/doctor/types.d.ts +48 -0
  88. package/dist/lib/doctor/types.d.ts.map +1 -0
  89. package/dist/lib/doctor/types.js +15 -0
  90. package/dist/lib/errors.d.ts +173 -0
  91. package/dist/lib/errors.d.ts.map +1 -0
  92. package/dist/lib/errors.js +318 -0
  93. package/dist/lib/help-text.d.ts +13 -0
  94. package/dist/lib/help-text.d.ts.map +1 -0
  95. package/dist/lib/help-text.js +43 -0
  96. package/dist/lib/load-route-map-worker.d.ts +2 -0
  97. package/dist/lib/load-route-map-worker.d.ts.map +1 -0
  98. package/dist/lib/load-route-map-worker.js +92 -0
  99. package/dist/lib/parse-args.d.ts +22 -0
  100. package/dist/lib/parse-args.d.ts.map +1 -0
  101. package/dist/lib/parse-args.js +35 -0
  102. package/dist/lib/remix-version.d.ts +2 -0
  103. package/dist/lib/remix-version.d.ts.map +1 -0
  104. package/dist/lib/remix-version.js +58 -0
  105. package/dist/lib/reporter.d.ts +56 -0
  106. package/dist/lib/reporter.d.ts.map +1 -0
  107. package/dist/lib/reporter.js +357 -0
  108. package/dist/lib/route-map.d.ts +38 -0
  109. package/dist/lib/route-map.d.ts.map +1 -0
  110. package/dist/lib/route-map.js +195 -0
  111. package/dist/lib/skills-cache.d.ts +19 -0
  112. package/dist/lib/skills-cache.d.ts.map +1 -0
  113. package/dist/lib/skills-cache.js +89 -0
  114. package/dist/lib/skills.d.ts +30 -0
  115. package/dist/lib/skills.d.ts.map +1 -0
  116. package/dist/lib/skills.js +441 -0
  117. package/dist/lib/terminal.d.ts +17 -0
  118. package/dist/lib/terminal.d.ts.map +1 -0
  119. package/dist/lib/terminal.js +66 -0
  120. package/package.json +48 -5
  121. package/src/index.ts +1 -0
  122. package/src/lib/bootstrap-project.ts +222 -0
  123. package/src/lib/cli-context.ts +38 -0
  124. package/src/lib/cli.ts +120 -0
  125. package/src/lib/commands/completion.ts +74 -0
  126. package/src/lib/commands/doctor.ts +393 -0
  127. package/src/lib/commands/help.ts +157 -0
  128. package/src/lib/commands/new.ts +104 -0
  129. package/src/lib/commands/routes.ts +251 -0
  130. package/src/lib/commands/skills.ts +306 -0
  131. package/src/lib/commands/test.ts +25 -0
  132. package/src/lib/commands/version.ts +39 -0
  133. package/src/lib/completion.ts +561 -0
  134. package/src/lib/contained-path.ts +13 -0
  135. package/src/lib/controller-files.ts +79 -0
  136. package/src/lib/controller-ownership.ts +397 -0
  137. package/src/lib/display-path.ts +16 -0
  138. package/src/lib/doctor/controller-findings.ts +157 -0
  139. package/src/lib/doctor/controller-fix-plans.ts +184 -0
  140. package/src/lib/doctor/controller-placeholders.ts +242 -0
  141. package/src/lib/doctor/controllers.ts +23 -0
  142. package/src/lib/doctor/environment.ts +289 -0
  143. package/src/lib/doctor/fixes.ts +84 -0
  144. package/src/lib/doctor/project.ts +435 -0
  145. package/src/lib/doctor/types.ts +95 -0
  146. package/src/lib/errors.ts +379 -0
  147. package/src/lib/help-text.ts +88 -0
  148. package/src/lib/load-route-map-worker.ts +110 -0
  149. package/src/lib/parse-args.ts +82 -0
  150. package/src/lib/remix-version.ts +80 -0
  151. package/src/lib/reporter.ts +535 -0
  152. package/src/lib/route-map.ts +303 -0
  153. package/src/lib/skills-cache.ts +140 -0
  154. package/src/lib/skills.ts +706 -0
  155. package/src/lib/terminal.ts +86 -0
@@ -0,0 +1,357 @@
1
+ import * as process from 'node:process';
2
+ import { bold, canUseAnsi, clearCurrentLine, lightGray, lightGreen, lightRed, lightYellow, remixWordmark, } from "./terminal.js";
3
+ const BULLET = '•';
4
+ const DOT_FRAMES = ['.', '..', '...'];
5
+ const DEFAULT_INDENT = ' ';
6
+ const DEFAULT_STATUS_FRAME_INTERVAL_MS = 250;
7
+ export function createCommandReporter(options = {}) {
8
+ let stdout = options.stdout ?? process.stdout;
9
+ let stderr = options.stderr ?? process.stderr;
10
+ let session = new ReporterSessionState();
11
+ let states = new Map();
12
+ let getState = (stream) => {
13
+ let state = states.get(stream);
14
+ if (state != null) {
15
+ return state;
16
+ }
17
+ state = new ReporterStreamState(stream, session);
18
+ states.set(stream, state);
19
+ return state;
20
+ };
21
+ return {
22
+ finish() {
23
+ for (let state of states.values()) {
24
+ state.finish();
25
+ }
26
+ },
27
+ out: new ReporterTextChannel(stdout, getState(stdout)),
28
+ status: new ReporterStatusChannel(stderr, getState(stderr), options.statusFrameIntervalMs ?? DEFAULT_STATUS_FRAME_INTERVAL_MS, options.remixVersion),
29
+ };
30
+ }
31
+ export function createStepProgressReporter(status, labels) {
32
+ return {
33
+ fail(step) {
34
+ status.failStep(getCompleteLabel(labels[step]));
35
+ },
36
+ skip(step, reason) {
37
+ status.skipStep(getCompleteLabel(labels[step]), reason);
38
+ },
39
+ start(step) {
40
+ status.startStep(getRunningLabel(labels[step]));
41
+ },
42
+ succeed(step) {
43
+ status.succeedStep(getCompleteLabel(labels[step]));
44
+ },
45
+ writeSummaryGap() {
46
+ status.summaryGap();
47
+ },
48
+ };
49
+ }
50
+ export async function runProgressStep(progress, step, callback) {
51
+ progress?.start(step);
52
+ try {
53
+ let result = await callback();
54
+ progress?.succeed(step);
55
+ return result;
56
+ }
57
+ catch (error) {
58
+ progress?.fail(step);
59
+ throw error;
60
+ }
61
+ }
62
+ class ReporterTextChannel {
63
+ #indentLevel = 0;
64
+ #state;
65
+ #stream;
66
+ constructor(stream, state) {
67
+ this.#stream = stream;
68
+ this.#state = state;
69
+ }
70
+ blank() {
71
+ if (!this.#state.hasWrittenContent()) {
72
+ return;
73
+ }
74
+ this.#state.queueBlank();
75
+ }
76
+ bullet(text) {
77
+ this.line(`${BULLET} ${text}`);
78
+ }
79
+ bullets(items) {
80
+ for (let item of items) {
81
+ this.bullet(item);
82
+ }
83
+ }
84
+ dedent() {
85
+ this.#indentLevel = Math.max(0, this.#indentLevel - 1);
86
+ }
87
+ indent() {
88
+ this.#indentLevel += 1;
89
+ }
90
+ label(tag, text, options) {
91
+ let line = text.length === 0 ? formatLabel(tag) : `${formatLabel(tag)} ${text}`;
92
+ if (options?.tone === 'warn') {
93
+ return lightYellow(line, this.#stream);
94
+ }
95
+ if (options?.tone === 'error') {
96
+ return lightRed(line, this.#stream);
97
+ }
98
+ return line;
99
+ }
100
+ line(text = '') {
101
+ if (text.length === 0) {
102
+ this.blank();
103
+ return;
104
+ }
105
+ this.#flushPendingBlank();
106
+ this.#write(`${this.#getIndent()}${text}\n`);
107
+ }
108
+ section(title, callback) {
109
+ this.line(title);
110
+ if (callback == null) {
111
+ return;
112
+ }
113
+ this.withIndent(callback);
114
+ }
115
+ table({ formatRow, headers, noHeaders = false, rows }) {
116
+ let columnCount = Math.max(headers.length, ...rows.map((row) => row.length));
117
+ let widths = Array.from({ length: columnCount }, (_, columnIndex) => {
118
+ let headerWidth = headers[columnIndex]?.length ?? 0;
119
+ let rowWidth = rows.reduce((width, row) => Math.max(width, row[columnIndex]?.length ?? 0), 0);
120
+ return Math.max(headerWidth, rowWidth);
121
+ });
122
+ if (!noHeaders) {
123
+ this.line(headers
124
+ .map((header, columnIndex) => bold(header.padEnd(widths[columnIndex] ?? header.length), this.#stream))
125
+ .join(' '));
126
+ }
127
+ rows.forEach((row, rowIndex) => {
128
+ let line = row
129
+ .map((cell, columnIndex) => cell.padEnd(widths[columnIndex] ?? cell.length))
130
+ .join(' ');
131
+ this.line(formatRow == null ? line : formatRow(line, rowIndex));
132
+ });
133
+ }
134
+ withIndent(callback) {
135
+ this.indent();
136
+ try {
137
+ return callback();
138
+ }
139
+ finally {
140
+ this.dedent();
141
+ }
142
+ }
143
+ getStream() {
144
+ return this.#stream;
145
+ }
146
+ writeImmediate(text) {
147
+ this.#state.writeImmediate(text);
148
+ }
149
+ getIndentText() {
150
+ return this.#getIndent();
151
+ }
152
+ #flushPendingBlank() {
153
+ this.#state.flushPendingBlank();
154
+ }
155
+ #getIndent() {
156
+ return DEFAULT_INDENT.repeat(this.#indentLevel);
157
+ }
158
+ #write(text) {
159
+ this.#state.write(text);
160
+ }
161
+ }
162
+ class ReporterStatusChannel extends ReporterTextChannel {
163
+ #activeLabel = null;
164
+ #activeTimer = null;
165
+ #frameIndex = 0;
166
+ #frameIntervalMs;
167
+ #hasRenderedStep = false;
168
+ #hasWrittenSummaryGap = false;
169
+ #remixVersion;
170
+ constructor(stream, state, frameIntervalMs, remixVersion) {
171
+ super(stream, state);
172
+ this.#frameIntervalMs = frameIntervalMs;
173
+ this.#remixVersion = remixVersion;
174
+ }
175
+ async commandHeader(commandLabel) {
176
+ if (!this.getStream().isTTY || this.#remixVersion == null) {
177
+ return;
178
+ }
179
+ this.writeImmediate(`${remixWordmark(this.getStream())} v${this.#remixVersion} - ${commandLabel}\n\n`);
180
+ }
181
+ failStep(label = this.#activeLabel ?? '') {
182
+ this.#stopAnimation();
183
+ this.#activeLabel = null;
184
+ this.#noteStepOutput();
185
+ this.#writeFinalLine(this.#formatFailedLine(label));
186
+ }
187
+ skipStep(label, reason) {
188
+ this.#stopAnimation();
189
+ if (this.#activeLabel === label) {
190
+ this.#activeLabel = null;
191
+ }
192
+ this.#noteStepOutput();
193
+ this.#writeFinalLine(this.#formatSkippedLine(label, reason));
194
+ }
195
+ startStep(label) {
196
+ this.#stopAnimation();
197
+ this.#activeLabel = label;
198
+ this.#noteStepOutput();
199
+ if (this.#isInteractive()) {
200
+ this.#frameIndex = 0;
201
+ this.writeImmediate(this.#renderLiveLine(this.#formatRunningLine(label, DOT_FRAMES[0])));
202
+ this.#activeTimer = setInterval(() => {
203
+ if (this.#activeLabel == null) {
204
+ return;
205
+ }
206
+ this.#frameIndex = (this.#frameIndex + 1) % DOT_FRAMES.length;
207
+ this.writeImmediate(this.#renderLiveLine(this.#formatRunningLine(this.#activeLabel, DOT_FRAMES[this.#frameIndex])));
208
+ }, this.#frameIntervalMs);
209
+ this.#activeTimer.unref?.();
210
+ return;
211
+ }
212
+ this.writeImmediate(`${this.#formatRunningLine(label, '...')}\n`);
213
+ }
214
+ succeedStep(label = this.#activeLabel ?? '') {
215
+ this.#stopAnimation();
216
+ this.#activeLabel = null;
217
+ this.#noteStepOutput();
218
+ this.#writeFinalLine(this.#formatSucceededLine(label));
219
+ }
220
+ summaryGap() {
221
+ if (!this.#hasRenderedStep || this.#hasWrittenSummaryGap) {
222
+ return;
223
+ }
224
+ this.writeImmediate('\n');
225
+ this.#hasWrittenSummaryGap = true;
226
+ }
227
+ #formatFailedLine(label) {
228
+ return `${this.getIndentText()}${lightRed('✗', this.getStream())} ${lightGray(label, this.getStream())}`;
229
+ }
230
+ #formatRunningLine(label, dots) {
231
+ return lightGray(`${this.getIndentText()}${BULLET} ${label}${dots}`, this.getStream());
232
+ }
233
+ #formatSkippedLine(label, reason) {
234
+ let line = reason == null ? `${BULLET} ${label}` : `${BULLET} ${label} (skipped: ${reason})`;
235
+ return lightGray(`${this.getIndentText()}${line}`, this.getStream());
236
+ }
237
+ #formatSucceededLine(label) {
238
+ return `${this.getIndentText()}${lightGreen('✓', this.getStream())} ${lightGray(label, this.getStream())}`;
239
+ }
240
+ #isInteractive() {
241
+ return canUseAnsi(this.getStream());
242
+ }
243
+ #noteStepOutput() {
244
+ this.#hasRenderedStep = true;
245
+ this.#hasWrittenSummaryGap = false;
246
+ }
247
+ #renderLiveLine(line) {
248
+ return `${clearCurrentLine()}${line}`;
249
+ }
250
+ #stopAnimation() {
251
+ if (this.#activeTimer != null) {
252
+ clearInterval(this.#activeTimer);
253
+ this.#activeTimer = null;
254
+ }
255
+ }
256
+ #writeFinalLine(line) {
257
+ if (this.#isInteractive()) {
258
+ this.writeImmediate(`${this.#renderLiveLine(line)}\n`);
259
+ return;
260
+ }
261
+ this.writeImmediate(`${line}\n`);
262
+ }
263
+ }
264
+ class ReporterStreamState {
265
+ #hasWrittenContent = false;
266
+ #pendingBlank = false;
267
+ #session;
268
+ #stream;
269
+ #trailingNewlineCount = 0;
270
+ constructor(stream, session) {
271
+ this.#session = session;
272
+ this.#stream = stream;
273
+ }
274
+ finish() {
275
+ if (!this.#hasWrittenContent) {
276
+ return;
277
+ }
278
+ if (this.#trailingNewlineCount >= 2) {
279
+ return;
280
+ }
281
+ this.#writeRaw('\n');
282
+ this.#pendingBlank = false;
283
+ }
284
+ flushPendingBlank() {
285
+ if (!this.#pendingBlank) {
286
+ return;
287
+ }
288
+ this.#writeRaw('\n');
289
+ this.#pendingBlank = false;
290
+ }
291
+ hasWrittenContent() {
292
+ return this.#hasWrittenContent;
293
+ }
294
+ queueBlank() {
295
+ this.#pendingBlank = true;
296
+ }
297
+ write(text) {
298
+ if (!this.#session.hasWrittenPreamble()) {
299
+ this.#writeRaw('\n');
300
+ this.#session.markPreambleWritten();
301
+ }
302
+ this.#writeRaw(text);
303
+ this.#hasWrittenContent = true;
304
+ }
305
+ writeImmediate(text) {
306
+ if (!this.#session.hasWrittenPreamble()) {
307
+ this.#writeRaw('\n');
308
+ this.#session.markPreambleWritten();
309
+ }
310
+ this.#writeRaw(text);
311
+ this.#hasWrittenContent = true;
312
+ this.#pendingBlank = false;
313
+ }
314
+ #writeRaw(text) {
315
+ this.#stream.write(text);
316
+ this.#updateTrailingNewlines(text);
317
+ }
318
+ #updateTrailingNewlines(text) {
319
+ let trailingNewlines = 0;
320
+ for (let index = text.length - 1; index >= 0; index--) {
321
+ if (text[index] !== '\n') {
322
+ break;
323
+ }
324
+ trailingNewlines += 1;
325
+ }
326
+ if (trailingNewlines === 0) {
327
+ this.#trailingNewlineCount = 0;
328
+ return;
329
+ }
330
+ if (trailingNewlines === text.length) {
331
+ this.#trailingNewlineCount += trailingNewlines;
332
+ return;
333
+ }
334
+ this.#trailingNewlineCount = trailingNewlines;
335
+ }
336
+ }
337
+ class ReporterSessionState {
338
+ #hasWrittenPreamble = false;
339
+ hasWrittenPreamble() {
340
+ return this.#hasWrittenPreamble;
341
+ }
342
+ markPreambleWritten() {
343
+ this.#hasWrittenPreamble = true;
344
+ }
345
+ }
346
+ function formatLabel(tag) {
347
+ if (tag.startsWith('[') && tag.endsWith(']')) {
348
+ return tag;
349
+ }
350
+ return `[${tag}]`;
351
+ }
352
+ function getCompleteLabel(label) {
353
+ return typeof label === 'string' ? label : label.complete;
354
+ }
355
+ function getRunningLabel(label) {
356
+ return typeof label === 'string' ? label : (label.running ?? label.complete);
357
+ }
@@ -0,0 +1,38 @@
1
+ export type RouteOwnerKind = 'action' | 'controller';
2
+ export type RouteTreeNodeKind = 'group' | 'route';
3
+ export interface RouteTreeOwner {
4
+ exists: boolean;
5
+ kind: RouteOwnerKind;
6
+ path: string;
7
+ }
8
+ export interface RouteTreeNode {
9
+ children: RouteTreeNode[];
10
+ key: string;
11
+ kind: RouteTreeNodeKind;
12
+ method?: string;
13
+ name: string;
14
+ owner: RouteTreeOwner;
15
+ pattern?: string;
16
+ }
17
+ export interface LoadedRouteMap {
18
+ appRoot: string;
19
+ routesFile: string;
20
+ tree: RouteTreeNode[];
21
+ }
22
+ export interface LoadedRouteManifest {
23
+ appRoot: string;
24
+ routesFile: string;
25
+ tree: RawRouteTreeNode[];
26
+ }
27
+ export interface RawRouteTreeNode {
28
+ children: RawRouteTreeNode[];
29
+ key: string;
30
+ kind: RouteTreeNodeKind;
31
+ method?: string;
32
+ name: string;
33
+ pattern?: string;
34
+ }
35
+ export declare function loadRouteMap(cwd?: string): Promise<LoadedRouteMap>;
36
+ export declare function loadRouteManifest(cwd?: string): Promise<LoadedRouteManifest>;
37
+ export declare function loadRouteManifestFromAppRoot(appRoot: string): Promise<LoadedRouteManifest>;
38
+ //# sourceMappingURL=route-map.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"route-map.d.ts","sourceRoot":"","sources":["../../src/lib/route-map.ts"],"names":[],"mappings":"AAeA,MAAM,MAAM,cAAc,GAAG,QAAQ,GAAG,YAAY,CAAA;AACpD,MAAM,MAAM,iBAAiB,GAAG,OAAO,GAAG,OAAO,CAAA;AAEjD,MAAM,WAAW,cAAc;IAC7B,MAAM,EAAE,OAAO,CAAA;IACf,IAAI,EAAE,cAAc,CAAA;IACpB,IAAI,EAAE,MAAM,CAAA;CACb;AAED,MAAM,WAAW,aAAa;IAC5B,QAAQ,EAAE,aAAa,EAAE,CAAA;IACzB,GAAG,EAAE,MAAM,CAAA;IACX,IAAI,EAAE,iBAAiB,CAAA;IACvB,MAAM,CAAC,EAAE,MAAM,CAAA;IACf,IAAI,EAAE,MAAM,CAAA;IACZ,KAAK,EAAE,cAAc,CAAA;IACrB,OAAO,CAAC,EAAE,MAAM,CAAA;CACjB;AAED,MAAM,WAAW,cAAc;IAC7B,OAAO,EAAE,MAAM,CAAA;IACf,UAAU,EAAE,MAAM,CAAA;IAClB,IAAI,EAAE,aAAa,EAAE,CAAA;CACtB;AAED,MAAM,WAAW,mBAAmB;IAClC,OAAO,EAAE,MAAM,CAAA;IACf,UAAU,EAAE,MAAM,CAAA;IAClB,IAAI,EAAE,gBAAgB,EAAE,CAAA;CACzB;AAED,MAAM,WAAW,gBAAgB;IAC/B,QAAQ,EAAE,gBAAgB,EAAE,CAAA;IAC5B,GAAG,EAAE,MAAM,CAAA;IACX,IAAI,EAAE,iBAAiB,CAAA;IACvB,MAAM,CAAC,EAAE,MAAM,CAAA;IACf,IAAI,EAAE,MAAM,CAAA;IACZ,OAAO,CAAC,EAAE,MAAM,CAAA;CACjB;AAED,wBAAsB,YAAY,CAAC,GAAG,GAAE,MAAsB,GAAG,OAAO,CAAC,cAAc,CAAC,CAUvF;AAED,wBAAsB,iBAAiB,CAAC,GAAG,GAAE,MAAsB,GAAG,OAAO,CAAC,mBAAmB,CAAC,CAGjG;AAED,wBAAsB,4BAA4B,CAAC,OAAO,EAAE,MAAM,GAAG,OAAO,CAAC,mBAAmB,CAAC,CAShG"}
@@ -0,0 +1,195 @@
1
+ import * as fs from 'node:fs/promises';
2
+ import * as path from 'node:path';
3
+ import * as process from 'node:process';
4
+ import { spawn } from 'node:child_process';
5
+ import { fileURLToPath } from 'node:url';
6
+ import { inspectControllerOwnership } from "./controller-ownership.js";
7
+ import { routeMapLoaderFailed, routeMapLoaderInvalidJson, routeMapLoaderSignal, routeOwnerPlanUnresolved, routesFileNotFound, } from "./errors.js";
8
+ export async function loadRouteMap(cwd = process.cwd()) {
9
+ let manifest = await loadRouteManifest(cwd);
10
+ let ownership = await inspectControllerOwnership(manifest.appRoot, manifest.tree);
11
+ let tree = decorateRouteTree(manifest.tree, ownership.subtrees);
12
+ return {
13
+ appRoot: manifest.appRoot,
14
+ routesFile: manifest.routesFile,
15
+ tree,
16
+ };
17
+ }
18
+ export async function loadRouteManifest(cwd = process.cwd()) {
19
+ let appRoot = await findRemixAppRoot(cwd);
20
+ return loadRouteManifestFromAppRoot(appRoot);
21
+ }
22
+ export async function loadRouteManifestFromAppRoot(appRoot) {
23
+ let routesFile = path.join(appRoot, 'app', 'routes.ts');
24
+ let tree = await loadRawRouteMap(appRoot, routesFile);
25
+ return {
26
+ appRoot,
27
+ routesFile,
28
+ tree,
29
+ };
30
+ }
31
+ async function loadRawRouteMap(appRoot, routesFile) {
32
+ let workerPath = getRouteMapWorkerPath();
33
+ let child = spawn(process.execPath, [workerPath, routesFile], {
34
+ cwd: appRoot,
35
+ env: createRouteMapWorkerEnv(),
36
+ stdio: ['ignore', 'pipe', 'pipe'],
37
+ });
38
+ let stdout = '';
39
+ let stderr = '';
40
+ child.stdout.setEncoding('utf8');
41
+ child.stderr.setEncoding('utf8');
42
+ child.stdout.on('data', (chunk) => {
43
+ stdout += chunk;
44
+ });
45
+ child.stderr.on('data', (chunk) => {
46
+ stderr += chunk;
47
+ });
48
+ let exitResult = await new Promise((resolve, reject) => {
49
+ child.once('error', reject);
50
+ child.once('close', (code, signal) => {
51
+ resolve({ code, signal });
52
+ });
53
+ });
54
+ if (exitResult.signal != null) {
55
+ throw routeMapLoaderSignal(exitResult.signal);
56
+ }
57
+ if (exitResult.code !== 0) {
58
+ let message = stderr.trim();
59
+ if (message.length === 0) {
60
+ message = 'Route-map loader failed.';
61
+ }
62
+ throw routeMapLoaderFailed(message);
63
+ }
64
+ let parsed;
65
+ try {
66
+ parsed = JSON.parse(stdout);
67
+ }
68
+ catch {
69
+ throw routeMapLoaderInvalidJson();
70
+ }
71
+ return assertRawRouteTree(parsed);
72
+ }
73
+ function decorateRouteTree(rawTree, subtrees, parentSegments = []) {
74
+ let subtreesByRouteName = new Map(subtrees.map((subtree) => [subtree.routeName, subtree]));
75
+ return decorateRouteTreeWithLookup(rawTree, subtreesByRouteName, parentSegments);
76
+ }
77
+ function decorateRouteTreeWithLookup(rawTree, subtreesByRouteName, parentSegments = []) {
78
+ return rawTree.map((rawNode) => {
79
+ let owner = getRouteOwner(rawNode, parentSegments, subtreesByRouteName);
80
+ let nextParentSegments = rawNode.kind === 'group' ? [...parentSegments, rawNode.key] : parentSegments;
81
+ return {
82
+ children: rawNode.kind === 'group'
83
+ ? decorateRouteTreeWithLookup(rawNode.children, subtreesByRouteName, nextParentSegments)
84
+ : [],
85
+ key: rawNode.key,
86
+ kind: rawNode.kind,
87
+ method: rawNode.method,
88
+ name: rawNode.name,
89
+ owner,
90
+ pattern: rawNode.pattern,
91
+ };
92
+ });
93
+ }
94
+ function getRouteOwner(rawNode, parentSegments, subtreesByRouteName) {
95
+ let ownerRouteName = rawNode.kind === 'group'
96
+ ? rawNode.name
97
+ : parentSegments.length === 0
98
+ ? rawNode.name
99
+ : parentSegments.join('.');
100
+ let subtree = subtreesByRouteName.get(ownerRouteName);
101
+ if (subtree == null) {
102
+ throw routeOwnerPlanUnresolved(rawNode.name);
103
+ }
104
+ return {
105
+ exists: subtree.actualEntryPath != null,
106
+ kind: subtree.kind,
107
+ path: subtree.actualEntryPath ?? subtree.entryDisplayPath,
108
+ };
109
+ }
110
+ function assertRawRouteTree(value) {
111
+ if (!Array.isArray(value)) {
112
+ throw new Error('Route-map loader returned an invalid tree.');
113
+ }
114
+ return value.map((entry) => assertRawRouteTreeNode(entry));
115
+ }
116
+ function assertRawRouteTreeNode(value) {
117
+ if (typeof value !== 'object' || value == null) {
118
+ throw new Error('Route-map loader returned an invalid route node.');
119
+ }
120
+ let key = Reflect.get(value, 'key');
121
+ let name = Reflect.get(value, 'name');
122
+ let kind = Reflect.get(value, 'kind');
123
+ let children = Reflect.get(value, 'children');
124
+ if (typeof key !== 'string' || typeof name !== 'string') {
125
+ throw new Error('Route-map loader returned a route node without a valid name.');
126
+ }
127
+ if (kind !== 'group' && kind !== 'route') {
128
+ throw new Error(`Route-map loader returned an unknown node kind for "${name}".`);
129
+ }
130
+ if (!Array.isArray(children)) {
131
+ throw new Error(`Route-map loader returned invalid children for "${name}".`);
132
+ }
133
+ if (kind === 'group') {
134
+ return {
135
+ children: children.map((child) => assertRawRouteTreeNode(child)),
136
+ key,
137
+ kind,
138
+ name,
139
+ };
140
+ }
141
+ let method = Reflect.get(value, 'method');
142
+ let pattern = Reflect.get(value, 'pattern');
143
+ if (typeof method !== 'string' || typeof pattern !== 'string') {
144
+ throw new Error(`Route-map loader returned an invalid route leaf for "${name}".`);
145
+ }
146
+ return {
147
+ children: [],
148
+ key,
149
+ kind,
150
+ method,
151
+ name,
152
+ pattern,
153
+ };
154
+ }
155
+ async function findRemixAppRoot(startDir) {
156
+ let currentDir = path.resolve(startDir);
157
+ while (true) {
158
+ if (await pathExists(path.join(currentDir, 'app', 'routes.ts'))) {
159
+ return currentDir;
160
+ }
161
+ let parentDir = path.dirname(currentDir);
162
+ if (parentDir === currentDir) {
163
+ break;
164
+ }
165
+ currentDir = parentDir;
166
+ }
167
+ throw routesFileNotFound(startDir);
168
+ }
169
+ async function pathExists(filePath) {
170
+ try {
171
+ await fs.access(filePath);
172
+ return true;
173
+ }
174
+ catch (error) {
175
+ let nodeError = error;
176
+ if (nodeError.code === 'ENOENT') {
177
+ return false;
178
+ }
179
+ throw error;
180
+ }
181
+ }
182
+ function getRouteMapWorkerPath() {
183
+ let currentFilePath = fileURLToPath(import.meta.url);
184
+ let extension = currentFilePath.endsWith('.ts') ? '.ts' : '.js';
185
+ return fileURLToPath(new URL(`./load-route-map-worker${extension}`, import.meta.url));
186
+ }
187
+ function createRouteMapWorkerEnv() {
188
+ let env = { ...process.env };
189
+ for (let key of Object.keys(env)) {
190
+ if (key.startsWith('NODE_TEST_')) {
191
+ delete env[key];
192
+ }
193
+ }
194
+ return env;
195
+ }
@@ -0,0 +1,19 @@
1
+ export interface SkillsCacheFileEntry {
2
+ localHash: string;
3
+ remoteSha: string;
4
+ }
5
+ export interface SkillsCacheSkillEntry {
6
+ files: Record<string, SkillsCacheFileEntry>;
7
+ }
8
+ export interface SkillsCacheManifest {
9
+ ref: string;
10
+ repo: string;
11
+ skills: Record<string, SkillsCacheSkillEntry>;
12
+ skillsDir: string;
13
+ version: number;
14
+ }
15
+ export declare function createSkillsCacheManifest(skillsDir: string, skills: Record<string, SkillsCacheSkillEntry>): SkillsCacheManifest;
16
+ export declare function getSkillsCacheFilePath(skillsDir: string): string;
17
+ export declare function readSkillsCache(skillsDir: string): Promise<SkillsCacheManifest | null>;
18
+ export declare function writeSkillsCache(skillsDir: string, manifest: SkillsCacheManifest): Promise<void>;
19
+ //# sourceMappingURL=skills-cache.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"skills-cache.d.ts","sourceRoot":"","sources":["../../src/lib/skills-cache.ts"],"names":[],"mappings":"AAUA,MAAM,WAAW,oBAAoB;IACnC,SAAS,EAAE,MAAM,CAAA;IACjB,SAAS,EAAE,MAAM,CAAA;CAClB;AAED,MAAM,WAAW,qBAAqB;IACpC,KAAK,EAAE,MAAM,CAAC,MAAM,EAAE,oBAAoB,CAAC,CAAA;CAC5C;AAED,MAAM,WAAW,mBAAmB;IAClC,GAAG,EAAE,MAAM,CAAA;IACX,IAAI,EAAE,MAAM,CAAA;IACZ,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,qBAAqB,CAAC,CAAA;IAC7C,SAAS,EAAE,MAAM,CAAA;IACjB,OAAO,EAAE,MAAM,CAAA;CAChB;AAED,wBAAgB,yBAAyB,CACvC,SAAS,EAAE,MAAM,EACjB,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,qBAAqB,CAAC,GAC5C,mBAAmB,CAQrB;AAED,wBAAgB,sBAAsB,CAAC,SAAS,EAAE,MAAM,GAAG,MAAM,CAGhE;AAED,wBAAsB,eAAe,CAAC,SAAS,EAAE,MAAM,GAAG,OAAO,CAAC,mBAAmB,GAAG,IAAI,CAAC,CAiB5F;AAED,wBAAsB,gBAAgB,CACpC,SAAS,EAAE,MAAM,EACjB,QAAQ,EAAE,mBAAmB,GAC5B,OAAO,CAAC,IAAI,CAAC,CAOf"}