@remix-run/cli 0.2.0 → 0.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/lib/bootstrap-project.d.ts.map +1 -1
- package/dist/lib/bootstrap-project.js +32 -9
- package/dist/lib/cli.d.ts +29 -0
- package/dist/lib/cli.d.ts.map +1 -1
- package/dist/lib/cli.js +26 -4
- package/dist/lib/commands/completion.d.ts.map +1 -1
- package/dist/lib/commands/completion.js +5 -1
- package/dist/lib/commands/doctor.js +18 -18
- package/dist/lib/commands/routes.js +3 -3
- package/dist/lib/completion.js +1 -5
- package/dist/lib/controller-files.d.ts +0 -1
- package/dist/lib/controller-files.d.ts.map +1 -1
- package/dist/lib/controller-files.js +3 -5
- package/dist/lib/controller-ownership.d.ts +9 -9
- package/dist/lib/controller-ownership.d.ts.map +1 -1
- package/dist/lib/controller-ownership.js +56 -91
- package/dist/lib/doctor/controller-findings.d.ts +1 -1
- package/dist/lib/doctor/controller-findings.d.ts.map +1 -1
- package/dist/lib/doctor/controller-findings.js +15 -87
- package/dist/lib/doctor/controller-fix-plans.d.ts.map +1 -1
- package/dist/lib/doctor/controller-fix-plans.js +13 -24
- package/dist/lib/doctor/controller-placeholders.d.ts +2 -3
- package/dist/lib/doctor/controller-placeholders.d.ts.map +1 -1
- package/dist/lib/doctor/controller-placeholders.js +18 -149
- package/dist/lib/doctor/controllers.js +1 -1
- package/dist/lib/doctor/project.js +60 -52
- package/dist/lib/doctor/types.d.ts +2 -2
- package/dist/lib/doctor/types.d.ts.map +1 -1
- package/dist/lib/load-route-map-worker.js +17 -9
- package/dist/lib/route-map.d.ts +1 -1
- package/dist/lib/route-map.d.ts.map +1 -1
- package/dist/lib/route-map.js +29 -17
- package/package.json +3 -3
- package/src/lib/bootstrap-project.ts +39 -13
- package/src/lib/cli.ts +39 -4
- package/src/lib/commands/completion.ts +6 -1
- package/src/lib/commands/doctor.ts +18 -21
- package/src/lib/commands/routes.ts +3 -3
- package/src/lib/completion.ts +1 -5
- package/src/lib/controller-files.ts +4 -8
- package/src/lib/controller-ownership.ts +78 -141
- package/src/lib/doctor/controller-findings.ts +20 -97
- package/src/lib/doctor/controller-fix-plans.ts +13 -29
- package/src/lib/doctor/controller-placeholders.ts +17 -189
- package/src/lib/doctor/controllers.ts +1 -1
- package/src/lib/doctor/project.ts +60 -52
- package/src/lib/doctor/types.ts +1 -5
- package/src/lib/load-route-map-worker.ts +19 -10
- package/src/lib/route-map.ts +61 -16
- package/{bootstrap → template}/.agents/skills/remix/SKILL.md +160 -73
- package/{bootstrap → template}/.agents/skills/remix/references/assets-and-browser-modules.md +22 -14
- package/{bootstrap → template}/.agents/skills/remix/references/auth-and-sessions.md +41 -18
- package/{bootstrap → template}/.agents/skills/remix/references/data-and-validation.md +21 -5
- package/{bootstrap → template}/.agents/skills/remix/references/middleware-and-server.md +43 -53
- package/{bootstrap → template}/.agents/skills/remix/references/routing-and-controllers.md +111 -44
- package/{bootstrap → template}/.agents/skills/remix/references/testing-patterns.md +17 -1
- package/{bootstrap → template}/AGENTS.md +10 -9
- package/template/README.md +29 -0
- package/template/app/actions/controller.tsx +18 -0
- package/template/app/assets/entry.ts +8 -0
- package/{bootstrap/app/ui → template/app/assets}/prompt-button.tsx +2 -1
- package/{bootstrap → template}/app/assets.ts +5 -3
- package/template/app/middleware/render.tsx +43 -0
- package/template/app/router.ts +20 -0
- package/{bootstrap → template}/app/routes.ts +1 -2
- package/{bootstrap → template}/app/ui/document.tsx +11 -4
- package/{bootstrap → template}/app/ui/scaffold-home-page.tsx +39 -38
- package/template/gitignore +4 -0
- package/{bootstrap → template}/package.json +5 -6
- package/template/public/favicon.svg +11 -0
- package/{bootstrap → template}/server.ts +10 -11
- package/{bootstrap → template}/tsconfig.json +3 -2
- package/bootstrap/README.md +0 -27
- package/bootstrap/app/assets/entry.ts +0 -19
- package/bootstrap/app/controllers/auth.tsx +0 -21
- package/bootstrap/app/controllers/home.tsx +0 -11
- package/bootstrap/app/router.ts +0 -16
- package/bootstrap/app/ui/layout.tsx +0 -22
- package/bootstrap/app/utils/render.tsx +0 -26
- /package/{bootstrap → template}/.agents/skills/remix/references/animate-elements.md +0 -0
- /package/{bootstrap → template}/.agents/skills/remix/references/component-model.md +0 -0
- /package/{bootstrap → template}/.agents/skills/remix/references/create-mixins.md +0 -0
- /package/{bootstrap → template}/.agents/skills/remix/references/hydration-frames-navigation.md +0 -0
- /package/{bootstrap → template}/.agents/skills/remix/references/mixins-styling-events.md +0 -0
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"bootstrap-project.d.ts","sourceRoot":"","sources":["../../src/lib/bootstrap-project.ts"],"names":[],"mappings":"AAYA,OAAO,EAAmB,KAAK,oBAAoB,EAAE,MAAM,eAAe,CAAA;
|
|
1
|
+
{"version":3,"file":"bootstrap-project.d.ts","sourceRoot":"","sources":["../../src/lib/bootstrap-project.ts"],"names":[],"mappings":"AAYA,OAAO,EAAmB,KAAK,oBAAoB,EAAE,MAAM,eAAe,CAAA;AAM1E,eAAO,MAAM,8BAA8B,WAAW,CAAA;AAEtD,MAAM,WAAW,uBAAuB;IACtC,OAAO,EAAE,MAAM,GAAG,IAAI,CAAA;IACtB,GAAG,CAAC,EAAE,MAAM,CAAA;IACZ,KAAK,EAAE,OAAO,CAAA;IACd,YAAY,CAAC,EAAE,MAAM,CAAA;IACrB,SAAS,EAAE,MAAM,CAAA;CAClB;AAED,MAAM,MAAM,qBAAqB,GAC7B,0BAA0B,GAC1B,yBAAyB,GACzB,uBAAuB,CAAA;AAE3B,MAAM,WAAW,mBAAmB;IAClC,cAAc,EAAE,MAAM,CAAA;IACtB,SAAS,EAAE,MAAM,CAAA;CAClB;AAED,MAAM,MAAM,yBAAyB,GAAG,oBAAoB,CAAC,qBAAqB,CAAC,CAAA;AAUnF,wBAAsB,gBAAgB,CACpC,OAAO,EAAE,uBAAuB,EAChC,QAAQ,CAAC,EAAE,yBAAyB,GACnC,OAAO,CAAC,mBAAmB,CAAC,CAgC9B"}
|
|
@@ -5,8 +5,10 @@ import { fileURLToPath } from 'node:url';
|
|
|
5
5
|
import semver from 'semver';
|
|
6
6
|
import { appNameUnavailable, invalidPackageName, targetDirectoryNotEmpty, targetPathNotDirectory, } from "./errors.js";
|
|
7
7
|
import { runProgressStep } from "./reporter.js";
|
|
8
|
-
const
|
|
9
|
-
const
|
|
8
|
+
const MODULE_DIRECTORY = path.dirname(fileURLToPath(import.meta.url));
|
|
9
|
+
const PACKAGE_TEMPLATE_DIRECTORY = path.resolve(MODULE_DIRECTORY, '../../template');
|
|
10
|
+
const REPO_TEMPLATE_DIRECTORY = path.resolve(MODULE_DIRECTORY, '../../../../template');
|
|
11
|
+
const TEMPLATE_EXCLUDED_NAMES = new Set(['.gitkeep', 'node_modules']);
|
|
10
12
|
export const MINIMUM_SUPPORTED_NODE_VERSION = '24.3.0';
|
|
11
13
|
export async function bootstrapProject(options, progress) {
|
|
12
14
|
let cwd = options.cwd ?? process.cwd();
|
|
@@ -21,8 +23,8 @@ export async function bootstrapProject(options, progress) {
|
|
|
21
23
|
remixVersion: readDefaultRemixVersion(options.remixVersion),
|
|
22
24
|
};
|
|
23
25
|
await runProgressStep(progress, 'prepare-target-directory', () => ensureTargetDirectory(targetDir, options.force));
|
|
24
|
-
await runProgressStep(progress, 'generate-scaffold-files', () =>
|
|
25
|
-
sourceDir:
|
|
26
|
+
await runProgressStep(progress, 'generate-scaffold-files', async () => copyTemplateDirectory({
|
|
27
|
+
sourceDir: await resolveTemplateDirectory(),
|
|
26
28
|
targetDir,
|
|
27
29
|
templateValues: createTemplateValues(config),
|
|
28
30
|
}));
|
|
@@ -49,6 +51,28 @@ function createTemplateValues(config) {
|
|
|
49
51
|
'%%RMX_APP_DISPLAY_NAME_URI_COMPONENT%%': encodeURIComponent(config.appDisplayName),
|
|
50
52
|
};
|
|
51
53
|
}
|
|
54
|
+
async function resolveTemplateDirectory() {
|
|
55
|
+
if (await isDirectory(PACKAGE_TEMPLATE_DIRECTORY)) {
|
|
56
|
+
return PACKAGE_TEMPLATE_DIRECTORY;
|
|
57
|
+
}
|
|
58
|
+
if (await isDirectory(REPO_TEMPLATE_DIRECTORY)) {
|
|
59
|
+
return REPO_TEMPLATE_DIRECTORY;
|
|
60
|
+
}
|
|
61
|
+
throw new Error(`Could not find the Remix app template at ${PACKAGE_TEMPLATE_DIRECTORY} or ${REPO_TEMPLATE_DIRECTORY}.`);
|
|
62
|
+
}
|
|
63
|
+
async function isDirectory(directory) {
|
|
64
|
+
try {
|
|
65
|
+
let stats = await fs.stat(directory);
|
|
66
|
+
return stats.isDirectory();
|
|
67
|
+
}
|
|
68
|
+
catch (error) {
|
|
69
|
+
let nodeError = error;
|
|
70
|
+
if (nodeError.code === 'ENOENT') {
|
|
71
|
+
return false;
|
|
72
|
+
}
|
|
73
|
+
throw error;
|
|
74
|
+
}
|
|
75
|
+
}
|
|
52
76
|
async function writeScaffoldPackageJson(targetDir, config) {
|
|
53
77
|
let packageJsonPath = path.join(targetDir, 'package.json');
|
|
54
78
|
let packageJson = JSON.parse(await fs.readFile(packageJsonPath, 'utf8'));
|
|
@@ -56,7 +80,6 @@ async function writeScaffoldPackageJson(targetDir, config) {
|
|
|
56
80
|
packageJson.dependencies = {
|
|
57
81
|
...packageJson.dependencies,
|
|
58
82
|
remix: config.remixVersion,
|
|
59
|
-
tsx: 'latest',
|
|
60
83
|
};
|
|
61
84
|
packageJson.devDependencies = {
|
|
62
85
|
...packageJson.devDependencies,
|
|
@@ -88,17 +111,17 @@ async function ensureTargetDirectory(targetDir, force) {
|
|
|
88
111
|
}
|
|
89
112
|
await fs.mkdir(targetDir, { recursive: true });
|
|
90
113
|
}
|
|
91
|
-
async function
|
|
114
|
+
async function copyTemplateDirectory({ sourceDir, targetDir, templateValues, }) {
|
|
92
115
|
let entries = await fs.readdir(sourceDir, { withFileTypes: true });
|
|
93
116
|
for (let entry of entries) {
|
|
94
117
|
let sourcePath = path.join(sourceDir, entry.name);
|
|
95
|
-
let targetPath = path.join(targetDir, entry.name);
|
|
96
|
-
if (
|
|
118
|
+
let targetPath = path.join(targetDir, entry.name === 'gitignore' ? '.gitignore' : entry.name);
|
|
119
|
+
if (TEMPLATE_EXCLUDED_NAMES.has(entry.name)) {
|
|
97
120
|
continue;
|
|
98
121
|
}
|
|
99
122
|
if (entry.isDirectory()) {
|
|
100
123
|
await fs.mkdir(targetPath, { recursive: true });
|
|
101
|
-
await
|
|
124
|
+
await copyTemplateDirectory({ sourceDir: sourcePath, targetDir: targetPath, templateValues });
|
|
102
125
|
continue;
|
|
103
126
|
}
|
|
104
127
|
let content = await fs.readFile(sourcePath, 'utf8');
|
package/dist/lib/cli.d.ts
CHANGED
|
@@ -1,6 +1,35 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Options accepted by {@link runRemix}.
|
|
3
|
+
*/
|
|
1
4
|
export interface RunRemixOptions {
|
|
5
|
+
/**
|
|
6
|
+
* Directory used as the working root when resolving project files (default
|
|
7
|
+
* `process.cwd()`).
|
|
8
|
+
*/
|
|
2
9
|
cwd?: string;
|
|
10
|
+
/**
|
|
11
|
+
* Override for the Remix version reported by `remix --version`. Defaults to
|
|
12
|
+
* the version of the installed `@remix-run/cli` package.
|
|
13
|
+
*/
|
|
3
14
|
remixVersion?: string;
|
|
4
15
|
}
|
|
16
|
+
/**
|
|
17
|
+
* Entry point for the `remix` CLI. Parses `argv`, dispatches to the matching
|
|
18
|
+
* subcommand (`new`, `doctor`, `routes`, `test`, `version`, `completion`,
|
|
19
|
+
* `help`), and resolves with the exit code the process should use.
|
|
20
|
+
*
|
|
21
|
+
* @param argv Argument vector to parse, excluding the node and script paths
|
|
22
|
+
* (default `process.argv.slice(2)`).
|
|
23
|
+
* @param options Overrides for CLI context resolution.
|
|
24
|
+
* @returns The numeric exit code (`0` on success, non-zero on failure).
|
|
25
|
+
*
|
|
26
|
+
* @example
|
|
27
|
+
* ```ts
|
|
28
|
+
* import { runRemix } from '@remix-run/cli'
|
|
29
|
+
*
|
|
30
|
+
* let exitCode = await runRemix(process.argv.slice(2))
|
|
31
|
+
* process.exit(exitCode)
|
|
32
|
+
* ```
|
|
33
|
+
*/
|
|
5
34
|
export declare function runRemix(argv?: string[], options?: RunRemixOptions): Promise<number>;
|
|
6
35
|
//# sourceMappingURL=cli.d.ts.map
|
package/dist/lib/cli.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"cli.d.ts","sourceRoot":"","sources":["../../src/lib/cli.ts"],"names":[],"mappings":"AAOA,MAAM,WAAW,eAAe;IAC9B,GAAG,CAAC,EAAE,MAAM,CAAA;IACZ,YAAY,CAAC,EAAE,MAAM,CAAA;CACtB;AAED,wBAAsB,QAAQ,CAC5B,IAAI,GAAE,MAAM,EAA0B,EACtC,OAAO,GAAE,eAAoB,GAC5B,OAAO,CAAC,MAAM,CAAC,
|
|
1
|
+
{"version":3,"file":"cli.d.ts","sourceRoot":"","sources":["../../src/lib/cli.ts"],"names":[],"mappings":"AAOA;;GAEG;AACH,MAAM,WAAW,eAAe;IAC9B;;;OAGG;IACH,GAAG,CAAC,EAAE,MAAM,CAAA;IACZ;;;OAGG;IACH,YAAY,CAAC,EAAE,MAAM,CAAA;CACtB;AAED;;;;;;;;;;;;;;;;;GAiBG;AACH,wBAAsB,QAAQ,CAC5B,IAAI,GAAE,MAAM,EAA0B,EACtC,OAAO,GAAE,eAAoB,GAC5B,OAAO,CAAC,MAAM,CAAC,CA0BjB"}
|
package/dist/lib/cli.js
CHANGED
|
@@ -3,14 +3,30 @@ import { getCliHelpText, runHelpCommand } from "./commands/help.js";
|
|
|
3
3
|
import { resolveCliContext } from "./cli-context.js";
|
|
4
4
|
import { renderCliError, unknownCommand } from "./errors.js";
|
|
5
5
|
import { configureColors, restoreTerminalFormatting } from "./terminal.js";
|
|
6
|
+
/**
|
|
7
|
+
* Entry point for the `remix` CLI. Parses `argv`, dispatches to the matching
|
|
8
|
+
* subcommand (`new`, `doctor`, `routes`, `test`, `version`, `completion`,
|
|
9
|
+
* `help`), and resolves with the exit code the process should use.
|
|
10
|
+
*
|
|
11
|
+
* @param argv Argument vector to parse, excluding the node and script paths
|
|
12
|
+
* (default `process.argv.slice(2)`).
|
|
13
|
+
* @param options Overrides for CLI context resolution.
|
|
14
|
+
* @returns The numeric exit code (`0` on success, non-zero on failure).
|
|
15
|
+
*
|
|
16
|
+
* @example
|
|
17
|
+
* ```ts
|
|
18
|
+
* import { runRemix } from '@remix-run/cli'
|
|
19
|
+
*
|
|
20
|
+
* let exitCode = await runRemix(process.argv.slice(2))
|
|
21
|
+
* process.exit(exitCode)
|
|
22
|
+
* ```
|
|
23
|
+
*/
|
|
6
24
|
export async function runRemix(argv = process.argv.slice(2), options = {}) {
|
|
7
25
|
let context = await resolveCliContext(options);
|
|
8
26
|
try {
|
|
9
|
-
|
|
10
|
-
argv = argv.slice(1);
|
|
11
|
-
}
|
|
27
|
+
argv = stripLeadingSeparators(argv);
|
|
12
28
|
let globalOptions = extractGlobalOptions(argv);
|
|
13
|
-
argv = globalOptions.argv;
|
|
29
|
+
argv = stripLeadingSeparators(globalOptions.argv);
|
|
14
30
|
configureColors({ disabled: globalOptions.noColor });
|
|
15
31
|
if (argv.length === 0) {
|
|
16
32
|
process.stdout.write(getCliHelpText());
|
|
@@ -27,6 +43,12 @@ export async function runRemix(argv = process.argv.slice(2), options = {}) {
|
|
|
27
43
|
restoreTerminalFormatting();
|
|
28
44
|
}
|
|
29
45
|
}
|
|
46
|
+
function stripLeadingSeparators(argv) {
|
|
47
|
+
while (argv[0] === '--') {
|
|
48
|
+
argv = argv.slice(1);
|
|
49
|
+
}
|
|
50
|
+
return argv;
|
|
51
|
+
}
|
|
30
52
|
async function runCommand(command, argv, context) {
|
|
31
53
|
if (command === 'help') {
|
|
32
54
|
return runHelpCommand(argv);
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"completion.d.ts","sourceRoot":"","sources":["../../../src/lib/commands/completion.ts"],"names":[],"mappings":"AAiBA,wBAAsB,oBAAoB,CAAC,IAAI,EAAE,MAAM,EAAE,GAAG,OAAO,CAAC,MAAM,CAAC,
|
|
1
|
+
{"version":3,"file":"completion.d.ts","sourceRoot":"","sources":["../../../src/lib/commands/completion.ts"],"names":[],"mappings":"AAiBA,wBAAsB,oBAAoB,CAAC,IAAI,EAAE,MAAM,EAAE,GAAG,OAAO,CAAC,MAAM,CAAC,CAkC1E;AAED,wBAAgB,4BAA4B,CAAC,MAAM,GAAE,MAAM,CAAC,WAA4B,GAAG,MAAM,CAShG"}
|
|
@@ -4,13 +4,17 @@ import { invalidCompletionRequest, renderCliError, toCliError, unknownCompletion
|
|
|
4
4
|
import { formatHelpText } from "../help-text.js";
|
|
5
5
|
import { parseArgs } from "../parse-args.js";
|
|
6
6
|
export async function runCompletionCommand(argv) {
|
|
7
|
-
if (argv.length === 0
|
|
7
|
+
if (argv.length === 0) {
|
|
8
8
|
process.stdout.write(getCompletionCommandHelpText());
|
|
9
9
|
return 0;
|
|
10
10
|
}
|
|
11
11
|
if (argv[0] === '--') {
|
|
12
12
|
return runCompletionPlumbing(argv.slice(1));
|
|
13
13
|
}
|
|
14
|
+
if (argv.includes('-h') || argv.includes('--help')) {
|
|
15
|
+
process.stdout.write(getCompletionCommandHelpText());
|
|
16
|
+
return 0;
|
|
17
|
+
}
|
|
14
18
|
let [shell, ...rest] = argv;
|
|
15
19
|
try {
|
|
16
20
|
parseArgs(rest, {}, { maxPositionals: 0 });
|
|
@@ -10,9 +10,9 @@ import { formatHelpText } from "../help-text.js";
|
|
|
10
10
|
import { parseArgs } from "../parse-args.js";
|
|
11
11
|
import { createCommandReporter, createStepProgressReporter, } from "../reporter.js";
|
|
12
12
|
const DOCTOR_SUITE_LABELS = {
|
|
13
|
-
|
|
14
|
-
complete: '
|
|
15
|
-
running: 'Checking
|
|
13
|
+
actions: {
|
|
14
|
+
complete: 'actions',
|
|
15
|
+
running: 'Checking actions',
|
|
16
16
|
},
|
|
17
17
|
environment: {
|
|
18
18
|
complete: 'environment',
|
|
@@ -78,7 +78,7 @@ export function getDoctorCommandHelpText(target = process.stdout) {
|
|
|
78
78
|
description: 'Exit with status 1 when warning-level findings are present',
|
|
79
79
|
label: '--strict',
|
|
80
80
|
},
|
|
81
|
-
{ description: 'Apply low-risk project and
|
|
81
|
+
{ description: 'Apply low-risk project and action fixes', label: '--fix' },
|
|
82
82
|
],
|
|
83
83
|
usage: ['remix doctor [--json] [--strict] [--fix] [--no-color]'],
|
|
84
84
|
}, target);
|
|
@@ -132,13 +132,13 @@ async function collectDoctorReport(progress, options, reporter, context) {
|
|
|
132
132
|
progress?.writeSummaryGap();
|
|
133
133
|
if (hasWarningFindings(environment.suite.findings)) {
|
|
134
134
|
let projectSuite = createSkippedDoctorSuite('project', 'Blocked by environment warnings.');
|
|
135
|
-
let
|
|
136
|
-
suites.push(projectSuite,
|
|
135
|
+
let actionsSuite = createSkippedDoctorSuite('actions', 'Blocked by environment warnings.');
|
|
136
|
+
suites.push(projectSuite, actionsSuite);
|
|
137
137
|
progress?.skip(projectSuite.name, projectSuite.reason);
|
|
138
138
|
writeDoctorSuiteDetails(reporter, projectSuite);
|
|
139
139
|
progress?.writeSummaryGap();
|
|
140
|
-
progress?.skip(
|
|
141
|
-
writeDoctorSuiteDetails(reporter,
|
|
140
|
+
progress?.skip(actionsSuite.name, actionsSuite.reason);
|
|
141
|
+
writeDoctorSuiteDetails(reporter, actionsSuite);
|
|
142
142
|
progress?.writeSummaryGap();
|
|
143
143
|
return {
|
|
144
144
|
appRoot: environment.projectRoot,
|
|
@@ -181,10 +181,10 @@ async function collectDoctorReport(progress, options, reporter, context) {
|
|
|
181
181
|
writeDoctorSuiteDetails(reporter, project.suite);
|
|
182
182
|
progress?.writeSummaryGap();
|
|
183
183
|
if (hasWarningFindings(project.suite.findings)) {
|
|
184
|
-
let
|
|
185
|
-
suites.push(
|
|
186
|
-
progress?.skip(
|
|
187
|
-
writeDoctorSuiteDetails(reporter,
|
|
184
|
+
let actionsSuite = createSkippedDoctorSuite('actions', 'Blocked by project warnings.');
|
|
185
|
+
suites.push(actionsSuite);
|
|
186
|
+
progress?.skip(actionsSuite.name, actionsSuite.reason);
|
|
187
|
+
writeDoctorSuiteDetails(reporter, actionsSuite);
|
|
188
188
|
progress?.writeSummaryGap();
|
|
189
189
|
return {
|
|
190
190
|
appRoot: environment.projectRoot,
|
|
@@ -195,7 +195,7 @@ async function collectDoctorReport(progress, options, reporter, context) {
|
|
|
195
195
|
suites,
|
|
196
196
|
};
|
|
197
197
|
}
|
|
198
|
-
let
|
|
198
|
+
let actions = await runDoctorSuite(progress, 'actions', async () => {
|
|
199
199
|
let controllerResult = await checkControllerConventions(project.routeManifest.appRoot, project.routeManifest.tree);
|
|
200
200
|
let remainingFindings = controllerResult.suite.findings;
|
|
201
201
|
let suiteAppliedFixes = [];
|
|
@@ -204,16 +204,16 @@ async function collectDoctorReport(progress, options, reporter, context) {
|
|
|
204
204
|
let finalControllerResult = await checkControllerConventions(project.routeManifest.appRoot, project.routeManifest.tree);
|
|
205
205
|
remainingFindings = finalControllerResult.suite.findings;
|
|
206
206
|
}
|
|
207
|
-
let suite = createDoctorSuite('
|
|
207
|
+
let suite = createDoctorSuite('actions', remainingFindings);
|
|
208
208
|
if (suiteAppliedFixes.length > 0) {
|
|
209
209
|
suite.appliedFixes = suiteAppliedFixes;
|
|
210
210
|
}
|
|
211
211
|
return { appliedFixes: suiteAppliedFixes, suite };
|
|
212
212
|
});
|
|
213
|
-
findings.push(...
|
|
214
|
-
appliedFixes.push(...(
|
|
215
|
-
suites.push(
|
|
216
|
-
writeDoctorSuiteDetails(reporter,
|
|
213
|
+
findings.push(...actions.suite.findings);
|
|
214
|
+
appliedFixes.push(...(actions.appliedFixes ?? []));
|
|
215
|
+
suites.push(actions.suite);
|
|
216
|
+
writeDoctorSuiteDetails(reporter, actions.suite);
|
|
217
217
|
progress?.writeSummaryGap();
|
|
218
218
|
let report = {
|
|
219
219
|
appRoot: environment.projectRoot,
|
|
@@ -5,7 +5,7 @@ import { parseArgs } from "../parse-args.js";
|
|
|
5
5
|
import { createCommandReporter } from "../reporter.js";
|
|
6
6
|
import { loadRouteMap } from "../route-map.js";
|
|
7
7
|
import { lightRed } from "../terminal.js";
|
|
8
|
-
const
|
|
8
|
+
const ACTIONS_PATH_PREFIX = 'app/actions/';
|
|
9
9
|
export async function runRoutesCommand(argv, context) {
|
|
10
10
|
if (argv.includes('-h') || argv.includes('--help')) {
|
|
11
11
|
process.stdout.write(getRoutesCommandHelpText());
|
|
@@ -154,8 +154,8 @@ function colorRouteLine(line, node) {
|
|
|
154
154
|
return node.owner.exists ? line : lightRed(line);
|
|
155
155
|
}
|
|
156
156
|
function getCompactOwnerPath(ownerPath) {
|
|
157
|
-
if (ownerPath.startsWith(
|
|
158
|
-
return ownerPath.slice(
|
|
157
|
+
if (ownerPath.startsWith(ACTIONS_PATH_PREFIX)) {
|
|
158
|
+
return ownerPath.slice(ACTIONS_PATH_PREFIX.length);
|
|
159
159
|
}
|
|
160
160
|
return ownerPath;
|
|
161
161
|
}
|
package/dist/lib/completion.js
CHANGED
|
@@ -137,11 +137,7 @@ function completeCommand(command, tokens, currentWord, usedGlobalFlags) {
|
|
|
137
137
|
return completeSimpleFlags(tokens, currentWord, usedGlobalFlags, []);
|
|
138
138
|
}
|
|
139
139
|
if (command === 'test') {
|
|
140
|
-
return completeSimpleFlags(tokens, currentWord, usedGlobalFlags, [
|
|
141
|
-
'--coverage',
|
|
142
|
-
'--watch',
|
|
143
|
-
'--help',
|
|
144
|
-
]);
|
|
140
|
+
return completeSimpleFlags(tokens, currentWord, usedGlobalFlags, ['--coverage', '--watch']);
|
|
145
141
|
}
|
|
146
142
|
if (command === 'completion') {
|
|
147
143
|
return completeCompletionCommand(tokens, currentWord, usedGlobalFlags);
|
|
@@ -1,6 +1,5 @@
|
|
|
1
1
|
declare const OWNER_FILE_EXTENSIONS: readonly [".ts", ".tsx", ".js", ".jsx"];
|
|
2
2
|
export type OwnerFileExtension = (typeof OWNER_FILE_EXTENSIONS)[number];
|
|
3
|
-
export declare function getActionOwnerCandidates(segments: string[]): string[];
|
|
4
3
|
export declare function getControllerOwnerCandidates(segments: string[]): string[];
|
|
5
4
|
export declare function getRouteSubtreePath(segments: string[]): string;
|
|
6
5
|
export declare function getPreferredOwnerDisplayPath(candidates: string[]): string;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"controller-files.d.ts","sourceRoot":"","sources":["../../src/lib/controller-files.ts"],"names":[],"mappings":"AAEA,QAAA,MAAM,qBAAqB,yCAA0C,CAAA;
|
|
1
|
+
{"version":3,"file":"controller-files.d.ts","sourceRoot":"","sources":["../../src/lib/controller-files.ts"],"names":[],"mappings":"AAEA,QAAA,MAAM,qBAAqB,yCAA0C,CAAA;AAGrE,MAAM,MAAM,kBAAkB,GAAG,CAAC,OAAO,qBAAqB,CAAC,CAAC,MAAM,CAAC,CAAA;AAEvE,wBAAgB,4BAA4B,CAAC,QAAQ,EAAE,MAAM,EAAE,GAAG,MAAM,EAAE,CAIzE;AAED,wBAAgB,mBAAmB,CAAC,QAAQ,EAAE,MAAM,EAAE,GAAG,MAAM,CAE9D;AAED,wBAAgB,4BAA4B,CAAC,UAAU,EAAE,MAAM,EAAE,GAAG,MAAM,CAGzE;AAED,wBAAgB,6BAA6B,CAC3C,UAAU,EAAE,MAAM,EAAE,EACpB,SAAS,EAAE,kBAAkB,GAC5B,MAAM,GAAG,IAAI,CAEf;AAED,wBAAgB,qBAAqB,CAAC,QAAQ,EAAE,MAAM,GAAG,kBAAkB,GAAG,IAAI,CAEjF;AAED,wBAAgB,aAAa,CAAC,OAAO,EAAE,MAAM,GAAG,MAAM,CAYrD;AAED,wBAAgB,yBAAyB,CAAC,QAAQ,EAAE,MAAM,GAAG,OAAO,CAEnE;AAED,wBAAgB,sBAAsB,CAAC,QAAQ,EAAE,MAAM,GAAG,MAAM,GAAG,IAAI,CAQtE;AAED,wBAAgB,gBAAgB,CAAC,QAAQ,EAAE,MAAM,GAAG,OAAO,CAS1D"}
|
|
@@ -1,13 +1,11 @@
|
|
|
1
1
|
import * as path from 'node:path';
|
|
2
2
|
const OWNER_FILE_EXTENSIONS = ['.ts', '.tsx', '.js', '.jsx'];
|
|
3
|
-
|
|
4
|
-
return OWNER_FILE_EXTENSIONS.map((extension) => normalizeRelativePath(path.join('app', 'controllers', `${path.join(...segments)}${extension}`)));
|
|
5
|
-
}
|
|
3
|
+
const ACTIONS_DIRECTORY = path.join('app', 'actions');
|
|
6
4
|
export function getControllerOwnerCandidates(segments) {
|
|
7
|
-
return OWNER_FILE_EXTENSIONS.map((extension) => normalizeRelativePath(path.join(
|
|
5
|
+
return OWNER_FILE_EXTENSIONS.map((extension) => normalizeRelativePath(path.join(ACTIONS_DIRECTORY, ...segments, `controller${extension}`)));
|
|
8
6
|
}
|
|
9
7
|
export function getRouteSubtreePath(segments) {
|
|
10
|
-
return normalizeRelativePath(path.join(
|
|
8
|
+
return normalizeRelativePath(path.join(ACTIONS_DIRECTORY, ...segments));
|
|
11
9
|
}
|
|
12
10
|
export function getPreferredOwnerDisplayPath(candidates) {
|
|
13
11
|
let tsxCandidate = candidates.find((candidate) => candidate.endsWith('.tsx'));
|
|
@@ -1,4 +1,5 @@
|
|
|
1
|
-
import type {
|
|
1
|
+
import type { RouteTreeNodeKind } from './route-map.ts';
|
|
2
|
+
export declare const ROOT_ROUTE_NAME = "<root>";
|
|
2
3
|
export interface OwnershipRouteNode {
|
|
3
4
|
children: OwnershipRouteNode[];
|
|
4
5
|
key: string;
|
|
@@ -7,35 +8,34 @@ export interface OwnershipRouteNode {
|
|
|
7
8
|
name: string;
|
|
8
9
|
}
|
|
9
10
|
export interface ControllerDirectoryScan {
|
|
10
|
-
actionEntryPaths: Set<string>;
|
|
11
11
|
controllerEntryPaths: Set<string>;
|
|
12
|
-
|
|
12
|
+
routeDirectoryPaths: Set<string>;
|
|
13
13
|
routeLocalFilePaths: Set<string>;
|
|
14
14
|
}
|
|
15
|
+
export interface RouteDirectoryPlan {
|
|
16
|
+
directoryPath: string;
|
|
17
|
+
routeName: string;
|
|
18
|
+
}
|
|
15
19
|
export interface OwnedSubtreePlan {
|
|
16
|
-
alternateCandidates: string[];
|
|
17
|
-
alternateDisplayPath: string;
|
|
18
20
|
entryCandidates: string[];
|
|
19
21
|
entryDisplayPath: string;
|
|
20
|
-
kind: RouteOwnerKind;
|
|
21
22
|
routeName: string;
|
|
22
23
|
subtreePath: string;
|
|
23
24
|
}
|
|
24
25
|
export interface OwnedSubtree extends OwnedSubtreePlan {
|
|
25
|
-
actualAlternatePath: string | null;
|
|
26
|
-
actualAlternatePaths: string[];
|
|
27
26
|
actualEntryPath: string | null;
|
|
28
27
|
actualEntryPaths: string[];
|
|
29
28
|
claimedFilePaths: string[];
|
|
30
29
|
claimedRouteLocalFilePaths: string[];
|
|
31
30
|
}
|
|
32
31
|
export interface ControllerOwnership {
|
|
33
|
-
orphanActionPaths: string[];
|
|
34
32
|
orphanControllerPaths: string[];
|
|
35
33
|
orphanRouteDirectoryPaths: string[];
|
|
34
|
+
routeDirectories: RouteDirectoryPlan[];
|
|
36
35
|
scan: ControllerDirectoryScan;
|
|
37
36
|
subtrees: OwnedSubtree[];
|
|
38
37
|
}
|
|
39
38
|
export declare function inspectControllerOwnership(appRoot: string, tree: OwnershipRouteNode[]): Promise<ControllerOwnership>;
|
|
40
39
|
export declare function buildOwnedSubtrees(tree: OwnershipRouteNode[], parentSegments?: string[], subtrees?: OwnedSubtreePlan[]): OwnedSubtreePlan[];
|
|
40
|
+
export declare function buildRouteDirectories(tree: OwnershipRouteNode[], parentSegments?: string[], directories?: RouteDirectoryPlan[]): RouteDirectoryPlan[];
|
|
41
41
|
//# sourceMappingURL=controller-ownership.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"controller-ownership.d.ts","sourceRoot":"","sources":["../../src/lib/controller-ownership.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"controller-ownership.d.ts","sourceRoot":"","sources":["../../src/lib/controller-ownership.ts"],"names":[],"mappings":"AAWA,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,gBAAgB,CAAA;AAIvD,eAAO,MAAM,eAAe,WAAW,CAAA;AAEvC,MAAM,WAAW,kBAAkB;IACjC,QAAQ,EAAE,kBAAkB,EAAE,CAAA;IAC9B,GAAG,EAAE,MAAM,CAAA;IACX,IAAI,EAAE,iBAAiB,CAAA;IACvB,MAAM,CAAC,EAAE,MAAM,CAAA;IACf,IAAI,EAAE,MAAM,CAAA;CACb;AAED,MAAM,WAAW,uBAAuB;IACtC,oBAAoB,EAAE,GAAG,CAAC,MAAM,CAAC,CAAA;IACjC,mBAAmB,EAAE,GAAG,CAAC,MAAM,CAAC,CAAA;IAChC,mBAAmB,EAAE,GAAG,CAAC,MAAM,CAAC,CAAA;CACjC;AAED,MAAM,WAAW,kBAAkB;IACjC,aAAa,EAAE,MAAM,CAAA;IACrB,SAAS,EAAE,MAAM,CAAA;CAClB;AAED,MAAM,WAAW,gBAAgB;IAC/B,eAAe,EAAE,MAAM,EAAE,CAAA;IACzB,gBAAgB,EAAE,MAAM,CAAA;IACxB,SAAS,EAAE,MAAM,CAAA;IACjB,WAAW,EAAE,MAAM,CAAA;CACpB;AAED,MAAM,WAAW,YAAa,SAAQ,gBAAgB;IACpD,eAAe,EAAE,MAAM,GAAG,IAAI,CAAA;IAC9B,gBAAgB,EAAE,MAAM,EAAE,CAAA;IAC1B,gBAAgB,EAAE,MAAM,EAAE,CAAA;IAC1B,0BAA0B,EAAE,MAAM,EAAE,CAAA;CACrC;AAED,MAAM,WAAW,mBAAmB;IAClC,qBAAqB,EAAE,MAAM,EAAE,CAAA;IAC/B,yBAAyB,EAAE,MAAM,EAAE,CAAA;IACnC,gBAAgB,EAAE,kBAAkB,EAAE,CAAA;IACtC,IAAI,EAAE,uBAAuB,CAAA;IAC7B,QAAQ,EAAE,YAAY,EAAE,CAAA;CACzB;AAED,wBAAsB,0BAA0B,CAC9C,OAAO,EAAE,MAAM,EACf,IAAI,EAAE,kBAAkB,EAAE,GACzB,OAAO,CAAC,mBAAmB,CAAC,CAa9B;AAED,wBAAgB,kBAAkB,CAChC,IAAI,EAAE,kBAAkB,EAAE,EAC1B,cAAc,GAAE,MAAM,EAAO,EAC7B,QAAQ,GAAE,gBAAgB,EAAO,GAChC,gBAAgB,EAAE,CAsBpB;AAED,wBAAgB,qBAAqB,CACnC,IAAI,EAAE,kBAAkB,EAAE,EAC1B,cAAc,GAAE,MAAM,EAAO,EAC7B,WAAW,GAAE,kBAAkB,EAAO,GACrC,kBAAkB,EAAE,CAetB"}
|