@remix-run/cli 0.3.3 → 0.4.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 (121) hide show
  1. package/README.md +105 -1
  2. package/dist/index.js +1 -1
  3. package/dist/lib/app-root.d.ts +11 -0
  4. package/dist/lib/app-root.d.ts.map +1 -0
  5. package/dist/lib/app-root.js +38 -0
  6. package/dist/lib/bootstrap-project.js +2 -2
  7. package/dist/lib/cli-context.d.ts +6 -0
  8. package/dist/lib/cli-context.d.ts.map +1 -1
  9. package/dist/lib/cli-context.js +12 -1
  10. package/dist/lib/cli.d.ts +3 -2
  11. package/dist/lib/cli.d.ts.map +1 -1
  12. package/dist/lib/cli.js +42 -14
  13. package/dist/lib/commands/completion.js +4 -4
  14. package/dist/lib/commands/db.d.ts +4 -0
  15. package/dist/lib/commands/db.d.ts.map +1 -0
  16. package/dist/lib/commands/db.js +283 -0
  17. package/dist/lib/commands/doctor.d.ts +5 -0
  18. package/dist/lib/commands/doctor.d.ts.map +1 -1
  19. package/dist/lib/commands/doctor.js +26 -251
  20. package/dist/lib/commands/help.d.ts.map +1 -1
  21. package/dist/lib/commands/help.js +17 -9
  22. package/dist/lib/commands/new.js +6 -6
  23. package/dist/lib/commands/routes.js +6 -6
  24. package/dist/lib/commands/test.d.ts +20 -1
  25. package/dist/lib/commands/test.d.ts.map +1 -1
  26. package/dist/lib/commands/test.js +353 -10
  27. package/dist/lib/commands/version.js +3 -3
  28. package/dist/lib/completion.d.ts +1 -1
  29. package/dist/lib/completion.d.ts.map +1 -1
  30. package/dist/lib/completion.js +229 -6
  31. package/dist/lib/controller-files.d.ts +0 -8
  32. package/dist/lib/controller-files.d.ts.map +1 -1
  33. package/dist/lib/controller-files.js +0 -23
  34. package/dist/lib/controller-ownership.d.ts +4 -12
  35. package/dist/lib/controller-ownership.d.ts.map +1 -1
  36. package/dist/lib/controller-ownership.js +31 -127
  37. package/dist/lib/database-command.d.ts +20 -0
  38. package/dist/lib/database-command.d.ts.map +1 -0
  39. package/dist/lib/database-command.js +7 -0
  40. package/dist/lib/doctor/controller-findings.d.ts +2 -2
  41. package/dist/lib/doctor/controller-findings.d.ts.map +1 -1
  42. package/dist/lib/doctor/controller-findings.js +23 -39
  43. package/dist/lib/doctor/controllers.d.ts +1 -2
  44. package/dist/lib/doctor/controllers.d.ts.map +1 -1
  45. package/dist/lib/doctor/controllers.js +4 -7
  46. package/dist/lib/doctor/environment.js +2 -2
  47. package/dist/lib/doctor/fixes.d.ts.map +1 -1
  48. package/dist/lib/doctor/fixes.js +46 -7
  49. package/dist/lib/doctor/project.d.ts.map +1 -1
  50. package/dist/lib/doctor/project.js +3 -212
  51. package/dist/lib/doctor/run.d.ts +9 -0
  52. package/dist/lib/doctor/run.d.ts.map +1 -0
  53. package/dist/lib/doctor/run.js +237 -0
  54. package/dist/lib/doctor/types.d.ts +2 -1
  55. package/dist/lib/doctor/types.d.ts.map +1 -1
  56. package/dist/lib/doctor/types.js +4 -1
  57. package/dist/lib/errors.d.ts +30 -0
  58. package/dist/lib/errors.d.ts.map +1 -1
  59. package/dist/lib/errors.js +55 -0
  60. package/dist/lib/help-text.js +1 -1
  61. package/dist/lib/parse-args.d.ts.map +1 -1
  62. package/dist/lib/parse-args.js +17 -4
  63. package/dist/lib/remix-config.d.ts +75 -0
  64. package/dist/lib/remix-config.d.ts.map +1 -0
  65. package/dist/lib/remix-config.js +411 -0
  66. package/dist/lib/reporter.js +1 -1
  67. package/dist/lib/route-map.js +13 -33
  68. package/package.json +12 -6
  69. package/schema/remix.json +242 -0
  70. package/src/lib/app-root.ts +46 -0
  71. package/src/lib/cli-context.ts +17 -0
  72. package/src/lib/cli.ts +41 -6
  73. package/src/lib/commands/db.ts +377 -0
  74. package/src/lib/commands/doctor.ts +27 -329
  75. package/src/lib/commands/help.ts +10 -1
  76. package/src/lib/commands/test.ts +453 -12
  77. package/src/lib/completion.ts +321 -6
  78. package/src/lib/controller-files.ts +0 -38
  79. package/src/lib/controller-ownership.ts +50 -203
  80. package/src/lib/database-command.ts +31 -0
  81. package/src/lib/doctor/controller-findings.ts +30 -47
  82. package/src/lib/doctor/controllers.ts +2 -6
  83. package/src/lib/doctor/fixes.ts +55 -6
  84. package/src/lib/doctor/project.ts +0 -236
  85. package/src/lib/doctor/run.ts +311 -0
  86. package/src/lib/doctor/types.ts +6 -5
  87. package/src/lib/errors.ts +65 -0
  88. package/src/lib/parse-args.ts +19 -3
  89. package/src/lib/remix-config.ts +639 -0
  90. package/src/lib/route-map.ts +11 -48
  91. package/template/.agents/skills/remix/SKILL.md +39 -28
  92. package/template/.agents/skills/remix/references/assets-and-browser-modules.md +53 -10
  93. package/template/.agents/skills/remix/references/auth-and-sessions.md +5 -5
  94. package/template/.agents/skills/remix/references/data-and-validation.md +15 -15
  95. package/template/.agents/skills/remix/references/hydration-frames-navigation.md +54 -8
  96. package/template/.agents/skills/remix/references/middleware-and-server.md +68 -6
  97. package/template/.agents/skills/remix/references/mixins-styling-events.md +2 -0
  98. package/template/.agents/skills/remix/references/routing-and-controllers.md +8 -8
  99. package/template/.agents/skills/remix/references/testing-patterns.md +19 -18
  100. package/template/AGENTS.md +6 -3
  101. package/template/README.md +6 -3
  102. package/template/app/actions/controller.tsx +1 -1
  103. package/template/app/{ui → actions}/document.tsx +7 -5
  104. package/template/app/{ui/scaffold-home-page.tsx → actions/home-page.tsx} +2 -3
  105. package/template/app/actions/public/entry.ts +48 -0
  106. package/template/app/{assets → actions/public}/prompt-button.tsx +29 -25
  107. package/template/app/assets.ts +19 -8
  108. package/template/app/middleware/render.tsx +29 -1
  109. package/template/hmr.ts +46 -0
  110. package/template/package.json +4 -3
  111. package/template/server.ts +8 -1
  112. package/template/tsconfig.json +1 -1
  113. package/dist/lib/doctor/controller-fix-plans.d.ts +0 -4
  114. package/dist/lib/doctor/controller-fix-plans.d.ts.map +0 -1
  115. package/dist/lib/doctor/controller-fix-plans.js +0 -119
  116. package/dist/lib/doctor/controller-placeholders.d.ts +0 -3
  117. package/dist/lib/doctor/controller-placeholders.d.ts.map +0 -1
  118. package/dist/lib/doctor/controller-placeholders.js +0 -52
  119. package/src/lib/doctor/controller-fix-plans.ts +0 -168
  120. package/src/lib/doctor/controller-placeholders.ts +0 -70
  121. package/template/app/assets/entry.ts +0 -8
@@ -1,52 +1,36 @@
1
- import { ROOT_ROUTE_NAME, } from "../controller-ownership.js";
2
- export function getControllerFindings(ownership, fixPlans) {
1
+ import { ROOT_ROUTE_NAME, } from '../controller-ownership.js';
2
+ export function getControllerFindings(ownership) {
3
+ let missingRouteNames = new Set(ownership.routeDirectories
4
+ .filter((directory) => !directory.exists)
5
+ .map((directory) => directory.routeName));
3
6
  return [
4
- ...getSubtreeFindings(ownership.subtrees, fixPlans),
5
- ...ownership.orphanControllerPaths.map((actualPath) => ({
6
- actualPath,
7
- code: 'orphan-controller',
8
- message: `Action controller ${actualPath} does not match any route map.`,
9
- severity: 'warn',
10
- suite: 'actions',
11
- })),
12
- ...ownership.orphanRouteDirectoryPaths.map((actualPath) => ({
13
- actualPath,
14
- code: 'orphan-route-directory',
15
- message: `Directory ${actualPath} does not match any route-map key path.`,
16
- severity: 'warn',
17
- suite: 'actions',
18
- })),
7
+ ...getSubtreeFindings(ownership.subtrees, missingRouteNames),
8
+ ...getRouteDirectoryFindings(ownership.routeDirectories),
19
9
  ];
20
10
  }
21
- function getSubtreeFindings(subtrees, fixPlans) {
11
+ function getRouteDirectoryFindings(directories) {
12
+ return directories
13
+ .filter((directory) => !directory.exists)
14
+ .map((directory) => ({
15
+ code: 'missing-route-directory',
16
+ expectedPath: directory.directoryPath,
17
+ message: `Route map "${directory.routeName}" is missing action directory ${directory.directoryPath}.`,
18
+ routeName: directory.routeName,
19
+ severity: 'warn',
20
+ suite: 'actions',
21
+ }));
22
+ }
23
+ function getSubtreeFindings(subtrees, missingRouteNames) {
22
24
  let findings = [];
23
- let fixableFindingKeys = new Set(fixPlans.map((fixPlan) => `${fixPlan.code}:${fixPlan.routeName ?? ''}`));
24
25
  for (let subtree of subtrees) {
25
- let routeMapName = formatRouteMapName(subtree.routeName);
26
- if (subtree.actualEntryPaths.length > 1) {
27
- findings.push({
28
- actualPath: subtree.actualEntryPaths[0],
29
- code: 'duplicate-owner-file',
30
- expectedPath: subtree.entryDisplayPath,
31
- message: `${routeMapName} has multiple action controller files: ${subtree.actualEntryPaths.join(', ')}. Keep only one controller owner file.`,
32
- routeName: subtree.routeName,
33
- severity: 'warn',
34
- suite: 'actions',
35
- });
26
+ if (missingRouteNames.has(subtree.routeName)) {
36
27
  continue;
37
28
  }
38
29
  if (subtree.actualEntryPath == null) {
39
- let code = subtree.claimedFilePaths.length > 0
40
- ? 'incomplete-controller'
41
- : 'missing-owner';
42
30
  findings.push({
43
- actualPath: subtree.claimedFilePaths.length > 0 ? subtree.subtreePath : undefined,
44
- code,
31
+ code: 'missing-owner',
45
32
  expectedPath: subtree.entryDisplayPath,
46
- fixable: fixableFindingKeys.has(`${code}:${subtree.routeName}`),
47
- message: subtree.claimedFilePaths.length > 0
48
- ? `${routeMapName} has files under ${subtree.subtreePath}, but is missing action controller ${subtree.entryDisplayPath}.`
49
- : `${routeMapName} is missing action controller ${subtree.entryDisplayPath}.`,
33
+ message: `${formatRouteMapName(subtree.routeName)} is missing action controller ${subtree.entryDisplayPath}.`,
50
34
  routeName: subtree.routeName,
51
35
  severity: 'warn',
52
36
  suite: 'actions',
@@ -1,7 +1,6 @@
1
1
  import { type OwnershipRouteNode } from '../controller-ownership.ts';
2
- import { type DoctorFixPlan, type DoctorSuiteResult } from './types.ts';
2
+ import { type DoctorSuiteResult } from './types.ts';
3
3
  export interface ControllerDoctorResult {
4
- fixPlans: DoctorFixPlan[];
5
4
  suite: DoctorSuiteResult;
6
5
  }
7
6
  export declare function checkControllerConventions(appRoot: string, tree: OwnershipRouteNode[]): Promise<ControllerDoctorResult>;
@@ -1 +1 @@
1
- {"version":3,"file":"controllers.d.ts","sourceRoot":"","sources":["../../../src/lib/doctor/controllers.ts"],"names":[],"mappings":"AAAA,OAAO,EAA8B,KAAK,kBAAkB,EAAE,MAAM,4BAA4B,CAAA;AAGhG,OAAO,EAAqB,KAAK,aAAa,EAAE,KAAK,iBAAiB,EAAE,MAAM,YAAY,CAAA;AAE1F,MAAM,WAAW,sBAAsB;IACrC,QAAQ,EAAE,aAAa,EAAE,CAAA;IACzB,KAAK,EAAE,iBAAiB,CAAA;CACzB;AAED,wBAAsB,0BAA0B,CAC9C,OAAO,EAAE,MAAM,EACf,IAAI,EAAE,kBAAkB,EAAE,GACzB,OAAO,CAAC,sBAAsB,CAAC,CASjC"}
1
+ {"version":3,"file":"controllers.d.ts","sourceRoot":"","sources":["../../../src/lib/doctor/controllers.ts"],"names":[],"mappings":"AAAA,OAAO,EAA8B,KAAK,kBAAkB,EAAE,MAAM,4BAA4B,CAAA;AAEhG,OAAO,EAAqB,KAAK,iBAAiB,EAAE,MAAM,YAAY,CAAA;AAEtE,MAAM,WAAW,sBAAsB;IACrC,KAAK,EAAE,iBAAiB,CAAA;CACzB;AAED,wBAAsB,0BAA0B,CAC9C,OAAO,EAAE,MAAM,EACf,IAAI,EAAE,kBAAkB,EAAE,GACzB,OAAO,CAAC,sBAAsB,CAAC,CAOjC"}
@@ -1,13 +1,10 @@
1
- import { inspectControllerOwnership } from "../controller-ownership.js";
2
- import { getControllerFindings } from "./controller-findings.js";
3
- import { getControllerFixPlans } from "./controller-fix-plans.js";
4
- import { createDoctorSuite } from "./types.js";
1
+ import { inspectControllerOwnership } from '../controller-ownership.js';
2
+ import { getControllerFindings } from './controller-findings.js';
3
+ import { createDoctorSuite } from './types.js';
5
4
  export async function checkControllerConventions(appRoot, tree) {
6
5
  let ownership = await inspectControllerOwnership(appRoot, tree);
7
- let fixPlans = await getControllerFixPlans(appRoot, tree, ownership);
8
- let findings = getControllerFindings(ownership, fixPlans);
6
+ let findings = getControllerFindings(ownership);
9
7
  return {
10
- fixPlans,
11
8
  suite: createDoctorSuite('actions', findings),
12
9
  };
13
10
  }
@@ -3,8 +3,8 @@ import * as path from 'node:path';
3
3
  import * as process from 'node:process';
4
4
  import { createRequire } from 'node:module';
5
5
  import * as semver from 'semver';
6
- import { MINIMUM_SUPPORTED_NODE_VERSION } from "../bootstrap-project.js";
7
- import { createDoctorSuite, } from "./types.js";
6
+ import { MINIMUM_SUPPORTED_NODE_VERSION } from '../bootstrap-project.js';
7
+ import { createDoctorSuite, } from './types.js';
8
8
  export async function checkEnvironment(cwd = process.cwd()) {
9
9
  let projectRoot = await findNearestPackageRoot(cwd);
10
10
  if (projectRoot == null) {
@@ -1 +1 @@
1
- {"version":3,"file":"fixes.d.ts","sourceRoot":"","sources":["../../../src/lib/doctor/fixes.ts"],"names":[],"mappings":"AAIA,OAAO,KAAK,EAAE,gBAAgB,EAAE,aAAa,EAAE,MAAM,YAAY,CAAA;AAEjE,wBAAsB,mBAAmB,CACvC,OAAO,EAAE,MAAM,EACf,QAAQ,EAAE,aAAa,EAAE,GACxB,OAAO,CAAC,gBAAgB,EAAE,CAAC,CAmC7B"}
1
+ {"version":3,"file":"fixes.d.ts","sourceRoot":"","sources":["../../../src/lib/doctor/fixes.ts"],"names":[],"mappings":"AAIA,OAAO,KAAK,EAAE,gBAAgB,EAAE,aAAa,EAAE,MAAM,YAAY,CAAA;AAEjE,wBAAsB,mBAAmB,CACvC,OAAO,EAAE,MAAM,EACf,QAAQ,EAAE,aAAa,EAAE,GACxB,OAAO,CAAC,gBAAgB,EAAE,CAAC,CAkC7B"}
@@ -1,10 +1,10 @@
1
1
  import * as fs from 'node:fs/promises';
2
2
  import * as path from 'node:path';
3
- import { resolveContainedPath } from "../contained-path.js";
3
+ import { resolveContainedPath } from '../contained-path.js';
4
4
  export async function applyDoctorFixPlans(appRoot, fixPlans) {
5
5
  let appliedFixes = [];
6
6
  for (let fixPlan of dedupeDoctorFixPlans(fixPlans)) {
7
- let absolutePath = resolveDoctorFixPath(appRoot, fixPlan.path);
7
+ let absolutePath = await resolveDoctorFixPath(appRoot, fixPlan.path);
8
8
  if (fixPlan.kind === 'create-directory') {
9
9
  await fs.mkdir(absolutePath, { recursive: true });
10
10
  appliedFixes.push(toAppliedDoctorFix(fixPlan));
@@ -20,8 +20,7 @@ export async function applyDoctorFixPlans(appRoot, fixPlans) {
20
20
  await fs.writeFile(absolutePath, fixPlan.contents ?? '', { encoding: 'utf8', flag: 'wx' });
21
21
  }
22
22
  catch (error) {
23
- let nodeError = error;
24
- if (nodeError.code !== 'EEXIST') {
23
+ if (!isErrorWithCode(error, 'EEXIST')) {
25
24
  throw error;
26
25
  }
27
26
  continue;
@@ -52,14 +51,54 @@ function toAppliedDoctorFix(fixPlan) {
52
51
  suite: fixPlan.suite,
53
52
  };
54
53
  }
55
- function resolveDoctorFixPath(appRoot, fixPath) {
54
+ async function resolveDoctorFixPath(appRoot, fixPath) {
55
+ let absolutePath;
56
56
  try {
57
- return resolveContainedPath(appRoot, fixPath);
57
+ absolutePath = resolveContainedPath(appRoot, fixPath);
58
58
  }
59
59
  catch (error) {
60
60
  if (error instanceof Error && error.message.includes('escapes the allowed root')) {
61
- throw new Error(`Doctor fix path resolves outside the app root: ${fixPath}`);
61
+ throwDoctorFixPathOutsideRoot(fixPath);
62
62
  }
63
63
  throw error;
64
64
  }
65
+ let rootPath = path.resolve(appRoot);
66
+ let rootRealPath = await fs.realpath(rootPath);
67
+ let relativePath = path.relative(rootPath, absolutePath);
68
+ let pathParts = relativePath === '' ? [] : relativePath.split(path.sep);
69
+ let currentPath = rootPath;
70
+ let currentRealPath = rootRealPath;
71
+ for (let [index, pathPart] of pathParts.entries()) {
72
+ currentPath = path.join(currentPath, pathPart);
73
+ let stats = await lstatIfExists(currentPath);
74
+ if (stats == null) {
75
+ return path.join(currentRealPath, ...pathParts.slice(index));
76
+ }
77
+ currentRealPath = await fs.realpath(currentPath);
78
+ if (!isPathInsideRoot(rootRealPath, currentRealPath)) {
79
+ throwDoctorFixPathOutsideRoot(fixPath);
80
+ }
81
+ }
82
+ return currentRealPath;
83
+ }
84
+ async function lstatIfExists(filePath) {
85
+ try {
86
+ return await fs.lstat(filePath);
87
+ }
88
+ catch (error) {
89
+ if (isErrorWithCode(error, 'ENOENT')) {
90
+ return null;
91
+ }
92
+ throw error;
93
+ }
94
+ }
95
+ function isPathInsideRoot(rootPath, filePath) {
96
+ let relativePath = path.relative(rootPath, filePath);
97
+ return relativePath === '' || (!relativePath.startsWith('..') && !path.isAbsolute(relativePath));
98
+ }
99
+ function isErrorWithCode(error, code) {
100
+ return typeof error === 'object' && error !== null && 'code' in error && error.code === code;
101
+ }
102
+ function throwDoctorFixPathOutsideRoot(fixPath) {
103
+ throw new Error(`Doctor fix path resolves outside the app root: ${fixPath}`);
65
104
  }
@@ -1 +1 @@
1
- {"version":3,"file":"project.d.ts","sourceRoot":"","sources":["../../../src/lib/doctor/project.ts"],"names":[],"mappings":"AAUA,OAAO,EAAgC,KAAK,mBAAmB,EAAE,MAAM,iBAAiB,CAAA;AACxF,OAAO,EAGL,KAAK,aAAa,EAClB,KAAK,iBAAiB,EACvB,MAAM,YAAY,CAAA;AAEnB,MAAM,WAAW,mBAAmB;IAClC,UAAU,EAAE,MAAM,CAAA;IAClB,aAAa,CAAC,EAAE,mBAAmB,CAAA;IACnC,KAAK,EAAE,iBAAiB,CAAA;CACzB;AAED,wBAAsB,YAAY,CAAC,WAAW,EAAE,MAAM,GAAG,OAAO,CAAC,mBAAmB,CAAC,CAmCpF;AAED,wBAAsB,kBAAkB,CAAC,WAAW,EAAE,MAAM,GAAG,OAAO,CAAC,aAAa,EAAE,CAAC,CAoDtF"}
1
+ {"version":3,"file":"project.d.ts","sourceRoot":"","sources":["../../../src/lib/doctor/project.ts"],"names":[],"mappings":"AAIA,OAAO,EAAgC,KAAK,mBAAmB,EAAE,MAAM,iBAAiB,CAAA;AACxF,OAAO,EAGL,KAAK,aAAa,EAClB,KAAK,iBAAiB,EACvB,MAAM,YAAY,CAAA;AAEnB,MAAM,WAAW,mBAAmB;IAClC,UAAU,EAAE,MAAM,CAAA;IAClB,aAAa,CAAC,EAAE,mBAAmB,CAAA;IACnC,KAAK,EAAE,iBAAiB,CAAA;CACzB;AAED,wBAAsB,YAAY,CAAC,WAAW,EAAE,MAAM,GAAG,OAAO,CAAC,mBAAmB,CAAC,CAmCpF;AAED,wBAAsB,kBAAkB,CAAC,WAAW,EAAE,MAAM,GAAG,OAAO,CAAC,aAAa,EAAE,CAAC,CA6BtF"}
@@ -1,9 +1,8 @@
1
1
  import * as fs from 'node:fs/promises';
2
2
  import * as path from 'node:path';
3
- import { getOwnerFileExtension, isActionFileName, isControllerEntryFileName, } from "../controller-files.js";
4
- import { CliError } from "../errors.js";
5
- import { loadRouteManifestFromAppRoot } from "../route-map.js";
6
- import { createDoctorSuite, } from "./types.js";
3
+ import { CliError } from '../errors.js';
4
+ import { loadRouteManifestFromAppRoot } from '../route-map.js';
5
+ import { createDoctorSuite, } from './types.js';
7
6
  export async function checkProject(projectRoot) {
8
7
  let routesFile = path.join(projectRoot, 'app', 'routes.ts');
9
8
  if (!(await pathExists(routesFile))) {
@@ -39,7 +38,6 @@ export async function checkProject(projectRoot) {
39
38
  }
40
39
  export async function getProjectFixPlans(projectRoot) {
41
40
  let routesFile = path.join(projectRoot, 'app', 'routes.ts');
42
- let homeActionPath = normalizeRelativePath(path.join('app', 'actions', `controller${await inferHomeOwnerExtension(projectRoot)}`));
43
41
  if (await pathExists(routesFile)) {
44
42
  let routesSource = await fs.readFile(routesFile, 'utf8');
45
43
  if (!hasOnlyWhitespaceAndComments(routesSource)) {
@@ -53,18 +51,6 @@ export async function getProjectFixPlans(projectRoot) {
53
51
  path: 'app/routes.ts',
54
52
  suite: 'project',
55
53
  },
56
- ...((await pathExists(path.join(projectRoot, homeActionPath)))
57
- ? []
58
- : [
59
- {
60
- code: 'missing-owner',
61
- contents: renderDefaultHomeAction(homeActionPath),
62
- kind: 'create-file',
63
- path: homeActionPath,
64
- routeName: 'home',
65
- suite: 'project',
66
- },
67
- ]),
68
54
  ];
69
55
  }
70
56
  return [
@@ -75,14 +61,6 @@ export async function getProjectFixPlans(projectRoot) {
75
61
  path: 'app/routes.ts',
76
62
  suite: 'project',
77
63
  },
78
- {
79
- code: 'missing-owner',
80
- contents: renderDefaultHomeAction(homeActionPath),
81
- kind: 'create-file',
82
- path: homeActionPath,
83
- routeName: 'home',
84
- suite: 'project',
85
- },
86
64
  ];
87
65
  }
88
66
  function toProjectFinding(error) {
@@ -180,63 +158,6 @@ function hasOnlyWhitespaceAndComments(source) {
180
158
  }
181
159
  return true;
182
160
  }
183
- async function inferHomeOwnerExtension(projectRoot) {
184
- let actionsDir = path.join(projectRoot, 'app', 'actions');
185
- let extensions = await collectOwnerExtensions(actionsDir);
186
- if (extensions.length > 0) {
187
- return getMostCommonExtension(extensions);
188
- }
189
- if (await pathExists(path.join(projectRoot, 'tsconfig.json'))) {
190
- return '.tsx';
191
- }
192
- return '.js';
193
- }
194
- async function collectOwnerExtensions(directory) {
195
- try {
196
- let entries = await fs.readdir(directory, { withFileTypes: true });
197
- let extensions = [];
198
- for (let entry of entries) {
199
- let absolutePath = path.join(directory, entry.name);
200
- if (entry.isDirectory()) {
201
- extensions.push(...(await collectOwnerExtensions(absolutePath)));
202
- continue;
203
- }
204
- let fileName = path.basename(absolutePath);
205
- if (!isActionFileName(fileName) && !isControllerEntryFileName(fileName)) {
206
- continue;
207
- }
208
- let extension = getOwnerFileExtension(fileName);
209
- if (extension != null) {
210
- extensions.push(extension);
211
- }
212
- }
213
- return extensions;
214
- }
215
- catch (error) {
216
- let nodeError = error;
217
- if (nodeError.code === 'ENOENT') {
218
- return [];
219
- }
220
- throw error;
221
- }
222
- }
223
- function getMostCommonExtension(extensions) {
224
- let priority = ['.tsx', '.ts', '.jsx', '.js'];
225
- let counts = new Map();
226
- for (let extension of extensions) {
227
- counts.set(extension, (counts.get(extension) ?? 0) + 1);
228
- }
229
- let bestExtension = priority[0];
230
- let bestCount = -1;
231
- for (let extension of priority) {
232
- let count = counts.get(extension) ?? 0;
233
- if (count > bestCount) {
234
- bestCount = count;
235
- bestExtension = extension;
236
- }
237
- }
238
- return bestExtension;
239
- }
240
161
  function renderDefaultRoutesFile() {
241
162
  return [
242
163
  `import { route } from 'remix/routes'`,
@@ -247,133 +168,3 @@ function renderDefaultRoutesFile() {
247
168
  '',
248
169
  ].join('\n');
249
170
  }
250
- function normalizeRelativePath(filePath) {
251
- return filePath.split(path.sep).join('/');
252
- }
253
- function renderDefaultHomeAction(entryPath) {
254
- let extension = getOwnerFileExtension(entryPath);
255
- if (extension === '.js') {
256
- return [
257
- `import { html } from 'remix/html-template'`,
258
- `import { createHtmlResponse } from 'remix/response/html'`,
259
- '',
260
- 'export default {',
261
- ' actions: {',
262
- ' home() {',
263
- ' let page = html`',
264
- ' <html lang="en">',
265
- ' <head>',
266
- ' <meta charset="utf-8" />',
267
- ' <meta name="viewport" content="width=device-width, initial-scale=1" />',
268
- ' <title>Home</title>',
269
- ' </head>',
270
- ' <body>',
271
- ' <h1>Home</h1>',
272
- ' <p>Update app/routes.ts and app/actions/controller to keep building your app.</p>',
273
- ' </body>',
274
- ' </html>',
275
- ' `',
276
- ' return createHtmlResponse(page)',
277
- ' },',
278
- ' },',
279
- '}',
280
- '',
281
- ].join('\n');
282
- }
283
- if (extension === '.ts') {
284
- return [
285
- `import { createController } from 'remix/router'`,
286
- `import { html } from 'remix/html-template'`,
287
- `import { createHtmlResponse } from 'remix/response/html'`,
288
- '',
289
- `import { routes } from '../routes.ts'`,
290
- '',
291
- `export default createController(routes, {`,
292
- ' actions: {',
293
- ' home() {',
294
- ' let page = html`',
295
- ' <html lang="en">',
296
- ' <head>',
297
- ' <meta charset="utf-8" />',
298
- ' <meta name="viewport" content="width=device-width, initial-scale=1" />',
299
- ' <title>Home</title>',
300
- ' </head>',
301
- ' <body>',
302
- ' <h1>Home</h1>',
303
- ' <p>Update app/routes.ts and app/actions/controller to keep building your app.</p>',
304
- ' </body>',
305
- ' </html>',
306
- ' `',
307
- ' return createHtmlResponse(page)',
308
- ' },',
309
- ' },',
310
- `})`,
311
- '',
312
- ].join('\n');
313
- }
314
- if (extension === '.jsx') {
315
- return [
316
- `import { renderToStream } from 'remix/ui/server'`,
317
- `import { createHtmlResponse } from 'remix/response/html'`,
318
- '',
319
- 'export default {',
320
- ' actions: {',
321
- ' home() {',
322
- ' let page = <HomePage />',
323
- ' return createHtmlResponse(renderToStream(page))',
324
- ' },',
325
- ' },',
326
- '}',
327
- '',
328
- 'function HomePage() {',
329
- ' return () => (',
330
- ' <html lang="en">',
331
- ' <head>',
332
- ' <meta charSet="utf-8" />',
333
- ' <meta name="viewport" content="width=device-width, initial-scale=1" />',
334
- ' <title>Home</title>',
335
- ' </head>',
336
- ' <body>',
337
- ' <h1>Home</h1>',
338
- ' <p>Update app/routes.ts and app/actions/controller to keep building your app.</p>',
339
- ' </body>',
340
- ' </html>',
341
- ' )',
342
- '}',
343
- '',
344
- ].join('\n');
345
- }
346
- return [
347
- `import { createController } from 'remix/router'`,
348
- `import { renderToStream } from 'remix/ui/server'`,
349
- `import { createHtmlResponse } from 'remix/response/html'`,
350
- '',
351
- `import { routes } from '../routes.ts'`,
352
- '',
353
- `export default createController(routes, {`,
354
- ' actions: {',
355
- ' home() {',
356
- ' let page = <HomePage />',
357
- ' return createHtmlResponse(renderToStream(page))',
358
- ' },',
359
- ' },',
360
- `})`,
361
- '',
362
- 'function HomePage() {',
363
- ' return () => (',
364
- ' <html lang="en">',
365
- ' <head>',
366
- ' <meta charSet="utf-8" />',
367
- ' <meta name="viewport" content="width=device-width, initial-scale=1" />',
368
- ' <title>Home</title>',
369
- ' </head>',
370
- ' <body>',
371
- ' <h1>Home</h1>',
372
- ' <p>Update app/routes.ts and app/actions/controller to keep building your app.</p>',
373
- ' </body>',
374
- ' </html>',
375
- ' )',
376
- '}',
377
- '',
378
- ].join('\n');
379
- }
@@ -0,0 +1,9 @@
1
+ export interface RunRemixDoctorOptions {
2
+ cwd: string;
3
+ fix: boolean;
4
+ json: boolean;
5
+ remixVersion?: string;
6
+ strict: boolean;
7
+ }
8
+ export declare function runRemixDoctor(options: RunRemixDoctorOptions): Promise<number>;
9
+ //# sourceMappingURL=run.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"run.d.ts","sourceRoot":"","sources":["../../../src/lib/doctor/run.ts"],"names":[],"mappings":"AAsCA,MAAM,WAAW,qBAAqB;IACpC,GAAG,EAAE,MAAM,CAAA;IACX,GAAG,EAAE,OAAO,CAAA;IACZ,IAAI,EAAE,OAAO,CAAA;IACb,YAAY,CAAC,EAAE,MAAM,CAAA;IACrB,MAAM,EAAE,OAAO,CAAA;CAChB;AAED,wBAAsB,cAAc,CAAC,OAAO,EAAE,qBAAqB,GAAG,OAAO,CAAC,MAAM,CAAC,CAoCpF"}