@remix-run/cli 0.0.0 → 0.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (159) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +77 -3
  3. package/bootstrap/.agents/skills/remix/SKILL.md +501 -0
  4. package/bootstrap/.agents/skills/remix/references/animate-elements.md +195 -0
  5. package/bootstrap/.agents/skills/remix/references/assets-and-browser-modules.md +122 -0
  6. package/bootstrap/.agents/skills/remix/references/auth-and-sessions.md +420 -0
  7. package/bootstrap/.agents/skills/remix/references/component-model.md +282 -0
  8. package/bootstrap/.agents/skills/remix/references/create-mixins.md +158 -0
  9. package/bootstrap/.agents/skills/remix/references/data-and-validation.md +363 -0
  10. package/bootstrap/.agents/skills/remix/references/hydration-frames-navigation.md +297 -0
  11. package/bootstrap/.agents/skills/remix/references/middleware-and-server.md +243 -0
  12. package/bootstrap/.agents/skills/remix/references/mixins-styling-events.md +213 -0
  13. package/bootstrap/.agents/skills/remix/references/routing-and-controllers.md +324 -0
  14. package/bootstrap/.agents/skills/remix/references/testing-patterns.md +156 -0
  15. package/bootstrap/AGENTS.md +39 -0
  16. package/bootstrap/README.md +27 -0
  17. package/bootstrap/app/assets/entry.ts +19 -0
  18. package/bootstrap/app/assets.ts +18 -0
  19. package/bootstrap/app/controllers/auth.tsx +21 -0
  20. package/bootstrap/app/controllers/home.tsx +11 -0
  21. package/bootstrap/app/router.ts +16 -0
  22. package/bootstrap/app/routes.ts +7 -0
  23. package/bootstrap/app/ui/document.tsx +26 -0
  24. package/bootstrap/app/ui/layout.tsx +22 -0
  25. package/bootstrap/app/ui/prompt-button.tsx +162 -0
  26. package/bootstrap/app/ui/scaffold-home-page.tsx +526 -0
  27. package/bootstrap/app/utils/render.tsx +26 -0
  28. package/bootstrap/package.json +22 -0
  29. package/bootstrap/server.ts +37 -0
  30. package/bootstrap/tsconfig.json +18 -0
  31. package/dist/index.d.ts +2 -0
  32. package/dist/index.d.ts.map +1 -0
  33. package/dist/index.js +1 -0
  34. package/dist/lib/bootstrap-project.d.ts +17 -0
  35. package/dist/lib/bootstrap-project.d.ts.map +1 -0
  36. package/dist/lib/bootstrap-project.js +137 -0
  37. package/dist/lib/cli-context.d.ts +11 -0
  38. package/dist/lib/cli-context.d.ts.map +1 -0
  39. package/dist/lib/cli-context.js +19 -0
  40. package/dist/lib/cli.d.ts +6 -0
  41. package/dist/lib/cli.d.ts.map +1 -0
  42. package/dist/lib/cli.js +84 -0
  43. package/dist/lib/commands/completion.d.ts +3 -0
  44. package/dist/lib/commands/completion.d.ts.map +1 -0
  45. package/dist/lib/commands/completion.js +51 -0
  46. package/dist/lib/commands/doctor.d.ts +4 -0
  47. package/dist/lib/commands/doctor.d.ts.map +1 -0
  48. package/dist/lib/commands/doctor.js +293 -0
  49. package/dist/lib/commands/help.d.ts +4 -0
  50. package/dist/lib/commands/help.d.ts.map +1 -0
  51. package/dist/lib/commands/help.js +100 -0
  52. package/dist/lib/commands/new.d.ts +4 -0
  53. package/dist/lib/commands/new.d.ts.map +1 -0
  54. package/dist/lib/commands/new.js +70 -0
  55. package/dist/lib/commands/routes.d.ts +4 -0
  56. package/dist/lib/commands/routes.d.ts.map +1 -0
  57. package/dist/lib/commands/routes.js +161 -0
  58. package/dist/lib/commands/test.d.ts +4 -0
  59. package/dist/lib/commands/test.d.ts.map +1 -0
  60. package/dist/lib/commands/test.js +23 -0
  61. package/dist/lib/commands/version.d.ts +4 -0
  62. package/dist/lib/commands/version.d.ts.map +1 -0
  63. package/dist/lib/commands/version.js +29 -0
  64. package/dist/lib/completion.d.ts +12 -0
  65. package/dist/lib/completion.d.ts.map +1 -0
  66. package/dist/lib/completion.js +310 -0
  67. package/dist/lib/contained-path.d.ts +2 -0
  68. package/dist/lib/contained-path.d.ts.map +1 -0
  69. package/dist/lib/contained-path.js +10 -0
  70. package/dist/lib/controller-files.d.ts +14 -0
  71. package/dist/lib/controller-files.d.ts.map +1 -0
  72. package/dist/lib/controller-files.js +53 -0
  73. package/dist/lib/controller-ownership.d.ts +41 -0
  74. package/dist/lib/controller-ownership.d.ts.map +1 -0
  75. package/dist/lib/controller-ownership.js +224 -0
  76. package/dist/lib/display-path.d.ts +2 -0
  77. package/dist/lib/display-path.d.ts.map +1 -0
  78. package/dist/lib/display-path.js +12 -0
  79. package/dist/lib/doctor/controller-findings.d.ts +4 -0
  80. package/dist/lib/doctor/controller-findings.d.ts.map +1 -0
  81. package/dist/lib/doctor/controller-findings.js +132 -0
  82. package/dist/lib/doctor/controller-fix-plans.d.ts +4 -0
  83. package/dist/lib/doctor/controller-fix-plans.d.ts.map +1 -0
  84. package/dist/lib/doctor/controller-fix-plans.js +130 -0
  85. package/dist/lib/doctor/controller-placeholders.d.ts +4 -0
  86. package/dist/lib/doctor/controller-placeholders.d.ts.map +1 -0
  87. package/dist/lib/doctor/controller-placeholders.js +183 -0
  88. package/dist/lib/doctor/controllers.d.ts +8 -0
  89. package/dist/lib/doctor/controllers.d.ts.map +1 -0
  90. package/dist/lib/doctor/controllers.js +13 -0
  91. package/dist/lib/doctor/environment.d.ts +17 -0
  92. package/dist/lib/doctor/environment.d.ts.map +1 -0
  93. package/dist/lib/doctor/environment.js +226 -0
  94. package/dist/lib/doctor/fixes.d.ts +3 -0
  95. package/dist/lib/doctor/fixes.d.ts.map +1 -0
  96. package/dist/lib/doctor/fixes.js +65 -0
  97. package/dist/lib/doctor/project.d.ts +10 -0
  98. package/dist/lib/doctor/project.d.ts.map +1 -0
  99. package/dist/lib/doctor/project.js +371 -0
  100. package/dist/lib/doctor/types.d.ts +48 -0
  101. package/dist/lib/doctor/types.d.ts.map +1 -0
  102. package/dist/lib/doctor/types.js +15 -0
  103. package/dist/lib/errors.d.ts +167 -0
  104. package/dist/lib/errors.d.ts.map +1 -0
  105. package/dist/lib/errors.js +307 -0
  106. package/dist/lib/help-text.d.ts +13 -0
  107. package/dist/lib/help-text.d.ts.map +1 -0
  108. package/dist/lib/help-text.js +43 -0
  109. package/dist/lib/load-route-map-worker.d.ts +2 -0
  110. package/dist/lib/load-route-map-worker.d.ts.map +1 -0
  111. package/dist/lib/load-route-map-worker.js +92 -0
  112. package/dist/lib/parse-args.d.ts +22 -0
  113. package/dist/lib/parse-args.d.ts.map +1 -0
  114. package/dist/lib/parse-args.js +35 -0
  115. package/dist/lib/remix-version.d.ts +2 -0
  116. package/dist/lib/remix-version.d.ts.map +1 -0
  117. package/dist/lib/remix-version.js +58 -0
  118. package/dist/lib/reporter.d.ts +56 -0
  119. package/dist/lib/reporter.d.ts.map +1 -0
  120. package/dist/lib/reporter.js +357 -0
  121. package/dist/lib/route-map.d.ts +38 -0
  122. package/dist/lib/route-map.d.ts.map +1 -0
  123. package/dist/lib/route-map.js +195 -0
  124. package/dist/lib/terminal.d.ts +17 -0
  125. package/dist/lib/terminal.d.ts.map +1 -0
  126. package/dist/lib/terminal.js +66 -0
  127. package/package.json +47 -5
  128. package/src/index.ts +1 -0
  129. package/src/lib/bootstrap-project.ts +222 -0
  130. package/src/lib/cli-context.ts +38 -0
  131. package/src/lib/cli.ts +116 -0
  132. package/src/lib/commands/completion.ts +74 -0
  133. package/src/lib/commands/doctor.ts +393 -0
  134. package/src/lib/commands/help.ts +123 -0
  135. package/src/lib/commands/new.ts +104 -0
  136. package/src/lib/commands/routes.ts +251 -0
  137. package/src/lib/commands/test.ts +31 -0
  138. package/src/lib/commands/version.ts +39 -0
  139. package/src/lib/completion.ts +418 -0
  140. package/src/lib/contained-path.ts +13 -0
  141. package/src/lib/controller-files.ts +79 -0
  142. package/src/lib/controller-ownership.ts +397 -0
  143. package/src/lib/display-path.ts +16 -0
  144. package/src/lib/doctor/controller-findings.ts +157 -0
  145. package/src/lib/doctor/controller-fix-plans.ts +184 -0
  146. package/src/lib/doctor/controller-placeholders.ts +242 -0
  147. package/src/lib/doctor/controllers.ts +23 -0
  148. package/src/lib/doctor/environment.ts +289 -0
  149. package/src/lib/doctor/fixes.ts +84 -0
  150. package/src/lib/doctor/project.ts +435 -0
  151. package/src/lib/doctor/types.ts +95 -0
  152. package/src/lib/errors.ts +367 -0
  153. package/src/lib/help-text.ts +88 -0
  154. package/src/lib/load-route-map-worker.ts +110 -0
  155. package/src/lib/parse-args.ts +82 -0
  156. package/src/lib/remix-version.ts +80 -0
  157. package/src/lib/reporter.ts +535 -0
  158. package/src/lib/route-map.ts +303 -0
  159. package/src/lib/terminal.ts +86 -0
@@ -0,0 +1,92 @@
1
+ var __rewriteRelativeImportExtension = (this && this.__rewriteRelativeImportExtension) || function (path, preserveJsx) {
2
+ if (typeof path === "string" && /^\.\.?\//.test(path)) {
3
+ return path.replace(/\.(tsx)$|((?:\.d)?)((?:\.[^./]+?)?)\.([cm]?)ts$/i, function (m, tsx, d, ext, cm) {
4
+ return tsx ? preserveJsx ? ".jsx" : ".js" : d && (!ext || !cm) ? m : (d + ext + "." + cm.toLowerCase() + "js");
5
+ });
6
+ }
7
+ return path;
8
+ };
9
+ import * as process from 'node:process';
10
+ import { pathToFileURL } from 'node:url';
11
+ void run().catch((error) => {
12
+ if (error instanceof Error) {
13
+ process.stderr.write(`${error.message}\n`);
14
+ }
15
+ else {
16
+ process.stderr.write(`${String(error)}\n`);
17
+ }
18
+ setExitCode(1);
19
+ });
20
+ async function run() {
21
+ let routesFile = process.argv[2];
22
+ if (typeof routesFile !== 'string' || routesFile.length === 0) {
23
+ throw new Error('Missing app/routes.ts path.');
24
+ }
25
+ let tree = await loadRawRouteTree(routesFile);
26
+ process.stdout.write(JSON.stringify(tree));
27
+ }
28
+ async function loadRawRouteTree(routesFile) {
29
+ let routeModule = await import(__rewriteRelativeImportExtension(pathToFileURL(routesFile).href));
30
+ if (!('routes' in routeModule)) {
31
+ throw new Error(`Route module ${routesFile} must export a named "routes" value.`);
32
+ }
33
+ return normalizeRouteGroup(Reflect.get(routeModule, 'routes'), [], new Map());
34
+ }
35
+ function normalizeRouteGroup(value, parentSegments, seen) {
36
+ if (!isPlainObject(value)) {
37
+ let location = parentSegments.length === 0 ? 'routes' : parentSegments.join('.');
38
+ throw new Error(`Invalid route map value at "${location}". Expected a nested route object.`);
39
+ }
40
+ let existingPath = seen.get(value);
41
+ let currentPath = parentSegments.length === 0 ? 'routes' : parentSegments.join('.');
42
+ if (existingPath != null) {
43
+ throw new Error(`Detected a route map cycle at "${currentPath}" via "${existingPath}".`);
44
+ }
45
+ seen.set(value, currentPath);
46
+ try {
47
+ return Object.entries(value).map(([key, entryValue]) => {
48
+ let nameSegments = [...parentSegments, key];
49
+ let name = nameSegments.join('.');
50
+ if (isRouteLeaf(entryValue)) {
51
+ return {
52
+ children: [],
53
+ key,
54
+ kind: 'route',
55
+ method: entryValue.method,
56
+ name,
57
+ pattern: entryValue.pattern.source,
58
+ };
59
+ }
60
+ if (isPlainObject(entryValue)) {
61
+ return {
62
+ children: normalizeRouteGroup(entryValue, nameSegments, seen),
63
+ key,
64
+ kind: 'group',
65
+ name,
66
+ };
67
+ }
68
+ throw new Error(`Invalid route map value at "${name}". Expected a route or nested route object.`);
69
+ });
70
+ }
71
+ finally {
72
+ seen.delete(value);
73
+ }
74
+ }
75
+ function isRouteLeaf(value) {
76
+ return (typeof value === 'object' &&
77
+ value != null &&
78
+ typeof Reflect.get(value, 'method') === 'string' &&
79
+ typeof Reflect.get(value, 'pattern') === 'object' &&
80
+ Reflect.get(value, 'pattern') != null &&
81
+ typeof Reflect.get(Reflect.get(value, 'pattern'), 'source') === 'string');
82
+ }
83
+ function isPlainObject(value) {
84
+ if (typeof value !== 'object' || value == null) {
85
+ return false;
86
+ }
87
+ let prototype = Object.getPrototypeOf(value);
88
+ return prototype === Object.prototype || prototype === null;
89
+ }
90
+ function setExitCode(exitCode) {
91
+ globalThis.process.exitCode = exitCode;
92
+ }
@@ -0,0 +1,22 @@
1
+ export interface ParseArgsBooleanOptionSpec {
2
+ flag: string;
3
+ type: 'boolean';
4
+ }
5
+ export interface ParseArgsStringOptionSpec {
6
+ flag: string;
7
+ type: 'string';
8
+ }
9
+ export type ParseArgsOptionSpec = ParseArgsBooleanOptionSpec | ParseArgsStringOptionSpec;
10
+ export type ParseArgsOptionDefinitions = Record<string, ParseArgsOptionSpec>;
11
+ export interface ParseArgsOptions {
12
+ maxPositionals?: number;
13
+ }
14
+ export type ParsedArgsValues<definitions extends ParseArgsOptionDefinitions> = {
15
+ [key in keyof definitions]: definitions[key]['type'] extends 'boolean' ? boolean : string | undefined;
16
+ };
17
+ export interface ParsedArgsResult<definitions extends ParseArgsOptionDefinitions> {
18
+ options: ParsedArgsValues<definitions>;
19
+ positionals: string[];
20
+ }
21
+ export declare function parseArgs<const definitions extends ParseArgsOptionDefinitions>(argv: string[], definitions: definitions, options?: ParseArgsOptions): ParsedArgsResult<definitions>;
22
+ //# sourceMappingURL=parse-args.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"parse-args.d.ts","sourceRoot":"","sources":["../../src/lib/parse-args.ts"],"names":[],"mappings":"AAEA,MAAM,WAAW,0BAA0B;IACzC,IAAI,EAAE,MAAM,CAAA;IACZ,IAAI,EAAE,SAAS,CAAA;CAChB;AAED,MAAM,WAAW,yBAAyB;IACxC,IAAI,EAAE,MAAM,CAAA;IACZ,IAAI,EAAE,QAAQ,CAAA;CACf;AAED,MAAM,MAAM,mBAAmB,GAAG,0BAA0B,GAAG,yBAAyB,CAAA;AAExF,MAAM,MAAM,0BAA0B,GAAG,MAAM,CAAC,MAAM,EAAE,mBAAmB,CAAC,CAAA;AAE5E,MAAM,WAAW,gBAAgB;IAC/B,cAAc,CAAC,EAAE,MAAM,CAAA;CACxB;AAED,MAAM,MAAM,gBAAgB,CAAC,WAAW,SAAS,0BAA0B,IAAI;KAC5E,GAAG,IAAI,MAAM,WAAW,GAAG,WAAW,CAAC,GAAG,CAAC,CAAC,MAAM,CAAC,SAAS,SAAS,GAClE,OAAO,GACP,MAAM,GAAG,SAAS;CACvB,CAAA;AAED,MAAM,WAAW,gBAAgB,CAAC,WAAW,SAAS,0BAA0B;IAC9E,OAAO,EAAE,gBAAgB,CAAC,WAAW,CAAC,CAAA;IACtC,WAAW,EAAE,MAAM,EAAE,CAAA;CACtB;AAED,wBAAgB,SAAS,CAAC,KAAK,CAAC,WAAW,SAAS,0BAA0B,EAC5E,IAAI,EAAE,MAAM,EAAE,EACd,WAAW,EAAE,WAAW,EACxB,OAAO,GAAE,gBAAqB,GAC7B,gBAAgB,CAAC,WAAW,CAAC,CA8C/B"}
@@ -0,0 +1,35 @@
1
+ import { missingOptionValue, unexpectedExtraArgument, unknownArgument } from "./errors.js";
2
+ export function parseArgs(argv, definitions, options = {}) {
3
+ let values = {};
4
+ let specsByFlag = new Map();
5
+ let positionals = [];
6
+ for (let [key, spec] of Object.entries(definitions)) {
7
+ values[key] = (spec.type === 'boolean' ? false : undefined);
8
+ specsByFlag.set(spec.flag, { key, spec });
9
+ }
10
+ for (let index = 0; index < argv.length; index++) {
11
+ let arg = argv[index];
12
+ let resolved = specsByFlag.get(arg);
13
+ if (resolved != null) {
14
+ if (resolved.spec.type === 'boolean') {
15
+ values[resolved.key] = true;
16
+ continue;
17
+ }
18
+ let next = argv[index + 1];
19
+ if (next == null || next.startsWith('-')) {
20
+ throw missingOptionValue(arg);
21
+ }
22
+ values[resolved.key] = next;
23
+ index += 1;
24
+ continue;
25
+ }
26
+ if (arg.startsWith('-')) {
27
+ throw unknownArgument(arg);
28
+ }
29
+ if (options.maxPositionals != null && positionals.length >= options.maxPositionals) {
30
+ throw unexpectedExtraArgument(arg);
31
+ }
32
+ positionals.push(arg);
33
+ }
34
+ return { options: values, positionals };
35
+ }
@@ -0,0 +1,2 @@
1
+ export declare function resolveDefaultRemixVersion(cwd?: string): Promise<string | undefined>;
2
+ //# sourceMappingURL=remix-version.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"remix-version.d.ts","sourceRoot":"","sources":["../../src/lib/remix-version.ts"],"names":[],"mappings":"AAWA,wBAAsB,0BAA0B,CAC9C,GAAG,GAAE,MAAsB,GAC1B,OAAO,CAAC,MAAM,GAAG,SAAS,CAAC,CAO7B"}
@@ -0,0 +1,58 @@
1
+ import * as fs from 'node:fs/promises';
2
+ import { createRequire } from 'node:module';
3
+ import * as path from 'node:path';
4
+ import * as process from 'node:process';
5
+ import { fileURLToPath } from 'node:url';
6
+ export async function resolveDefaultRemixVersion(cwd = process.cwd()) {
7
+ for (let packageJsonPath of getRemixPackageJsonCandidates(cwd)) {
8
+ let version = await readRemixPackageVersion(packageJsonPath);
9
+ if (version != null) {
10
+ return version;
11
+ }
12
+ }
13
+ }
14
+ function getRemixPackageJsonCandidates(cwd) {
15
+ let candidates = [];
16
+ let seen = new Set();
17
+ function addCandidate(filePath) {
18
+ if (filePath == null) {
19
+ return;
20
+ }
21
+ let resolvedPath = path.resolve(filePath);
22
+ if (seen.has(resolvedPath)) {
23
+ return;
24
+ }
25
+ seen.add(resolvedPath);
26
+ candidates.push(resolvedPath);
27
+ }
28
+ function addResolvedPackageJson(basePath) {
29
+ try {
30
+ addCandidate(createRequire(basePath).resolve('remix/package.json'));
31
+ }
32
+ catch { }
33
+ }
34
+ addResolvedPackageJson(path.join(cwd, 'package.json'));
35
+ if (process.argv[1] != null) {
36
+ addResolvedPackageJson(path.resolve(process.argv[1]));
37
+ }
38
+ addResolvedPackageJson(import.meta.url);
39
+ addCandidate(path.resolve(path.dirname(fileURLToPath(import.meta.url)), '../../../remix/package.json'));
40
+ return candidates;
41
+ }
42
+ async function readRemixPackageVersion(packageJsonPath) {
43
+ let packageJson;
44
+ try {
45
+ packageJson = JSON.parse(await fs.readFile(packageJsonPath, 'utf8'));
46
+ }
47
+ catch {
48
+ return undefined;
49
+ }
50
+ if (packageJson.name !== 'remix') {
51
+ return undefined;
52
+ }
53
+ if (typeof packageJson.version !== 'string') {
54
+ return undefined;
55
+ }
56
+ let version = packageJson.version.trim();
57
+ return version.length === 0 ? undefined : version;
58
+ }
@@ -0,0 +1,56 @@
1
+ interface StepProgressLabel {
2
+ complete: string;
3
+ running?: string;
4
+ }
5
+ export interface CommandReporter {
6
+ finish(): void;
7
+ out: TextChannel;
8
+ status: StatusChannel;
9
+ }
10
+ export interface TableOptions {
11
+ headers: string[];
12
+ noHeaders?: boolean;
13
+ rows: string[][];
14
+ formatRow?: (line: string, rowIndex: number) => string;
15
+ }
16
+ export interface TextChannel {
17
+ blank(): void;
18
+ bullet(text: string): void;
19
+ bullets(items: string[]): void;
20
+ dedent(): void;
21
+ indent(): void;
22
+ label(tag: string, text: string, options?: LabelOptions): string;
23
+ line(text?: string): void;
24
+ section(title: string, callback?: () => void): void;
25
+ table(options: TableOptions): void;
26
+ withIndent<result>(callback: () => result): result;
27
+ }
28
+ export interface StatusChannel extends TextChannel {
29
+ commandHeader(commandLabel: string): Promise<void>;
30
+ failStep(label?: string): void;
31
+ skipStep(label: string, reason?: string): void;
32
+ startStep(label: string): void;
33
+ succeedStep(label?: string): void;
34
+ summaryGap(): void;
35
+ }
36
+ export interface LabelOptions {
37
+ tone?: 'error' | 'warn';
38
+ }
39
+ export interface CreateCommandReporterOptions {
40
+ remixVersion?: string;
41
+ stderr?: NodeJS.WriteStream;
42
+ statusFrameIntervalMs?: number;
43
+ stdout?: NodeJS.WriteStream;
44
+ }
45
+ export interface StepProgressReporter<step extends string> {
46
+ fail(step: step): void;
47
+ skip(step: step, reason?: string): void;
48
+ start(step: step): void;
49
+ succeed(step: step): void;
50
+ writeSummaryGap(): void;
51
+ }
52
+ export declare function createCommandReporter(options?: CreateCommandReporterOptions): CommandReporter;
53
+ export declare function createStepProgressReporter<step extends string>(status: StatusChannel, labels: Record<step, string | StepProgressLabel>): StepProgressReporter<step>;
54
+ export declare function runProgressStep<step extends string, result>(progress: StepProgressReporter<step> | null | undefined, step: step, callback: () => Promise<result>): Promise<result>;
55
+ export {};
56
+ //# sourceMappingURL=reporter.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"reporter.d.ts","sourceRoot":"","sources":["../../src/lib/reporter.ts"],"names":[],"mappings":"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"}
@@ -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"}