@mr.dj2u/doctor 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 (89) hide show
  1. package/dist/checks/app-architecture.d.ts +4 -0
  2. package/dist/checks/app-architecture.d.ts.map +1 -0
  3. package/dist/checks/app-architecture.js +75 -0
  4. package/dist/checks/app-architecture.js.map +1 -0
  5. package/dist/checks/env-hygiene.d.ts +4 -0
  6. package/dist/checks/env-hygiene.d.ts.map +1 -0
  7. package/dist/checks/env-hygiene.js +54 -0
  8. package/dist/checks/env-hygiene.js.map +1 -0
  9. package/dist/checks/eslint.d.ts +10 -0
  10. package/dist/checks/eslint.d.ts.map +1 -0
  11. package/dist/checks/eslint.js +23 -0
  12. package/dist/checks/eslint.js.map +1 -0
  13. package/dist/checks/expo-config.d.ts +3 -0
  14. package/dist/checks/expo-config.d.ts.map +1 -0
  15. package/dist/checks/expo-config.js +51 -0
  16. package/dist/checks/expo-config.js.map +1 -0
  17. package/dist/checks/expo-doctor.d.ts +8 -0
  18. package/dist/checks/expo-doctor.d.ts.map +1 -0
  19. package/dist/checks/expo-doctor.js +22 -0
  20. package/dist/checks/expo-doctor.js.map +1 -0
  21. package/dist/checks/gitignore-env.d.ts +3 -0
  22. package/dist/checks/gitignore-env.d.ts.map +1 -0
  23. package/dist/checks/gitignore-env.js +30 -0
  24. package/dist/checks/gitignore-env.js.map +1 -0
  25. package/dist/checks/index.d.ts +15 -0
  26. package/dist/checks/index.d.ts.map +1 -0
  27. package/dist/checks/index.js +15 -0
  28. package/dist/checks/index.js.map +1 -0
  29. package/dist/checks/package-scripts.d.ts +3 -0
  30. package/dist/checks/package-scripts.d.ts.map +1 -0
  31. package/dist/checks/package-scripts.js +56 -0
  32. package/dist/checks/package-scripts.js.map +1 -0
  33. package/dist/checks/project-docs.d.ts +3 -0
  34. package/dist/checks/project-docs.d.ts.map +1 -0
  35. package/dist/checks/project-docs.js +45 -0
  36. package/dist/checks/project-docs.js.map +1 -0
  37. package/dist/checks/script-checks.d.ts +9 -0
  38. package/dist/checks/script-checks.d.ts.map +1 -0
  39. package/dist/checks/script-checks.js +59 -0
  40. package/dist/checks/script-checks.js.map +1 -0
  41. package/dist/checks/seo-metadata.d.ts +3 -0
  42. package/dist/checks/seo-metadata.d.ts.map +1 -0
  43. package/dist/checks/seo-metadata.js +41 -0
  44. package/dist/checks/seo-metadata.js.map +1 -0
  45. package/dist/checks/ssr-safety.d.ts +4 -0
  46. package/dist/checks/ssr-safety.d.ts.map +1 -0
  47. package/dist/checks/ssr-safety.js +33 -0
  48. package/dist/checks/ssr-safety.js.map +1 -0
  49. package/dist/checks/styling-stack.d.ts +3 -0
  50. package/dist/checks/styling-stack.d.ts.map +1 -0
  51. package/dist/checks/styling-stack.js +35 -0
  52. package/dist/checks/styling-stack.js.map +1 -0
  53. package/dist/checks/todo-for-context.d.ts +3 -0
  54. package/dist/checks/todo-for-context.d.ts.map +1 -0
  55. package/dist/checks/todo-for-context.js +56 -0
  56. package/dist/checks/todo-for-context.js.map +1 -0
  57. package/dist/checks/typescript.d.ts +7 -0
  58. package/dist/checks/typescript.d.ts.map +1 -0
  59. package/dist/checks/typescript.js +18 -0
  60. package/dist/checks/typescript.js.map +1 -0
  61. package/dist/cli-main.d.ts +15 -0
  62. package/dist/cli-main.d.ts.map +1 -0
  63. package/dist/cli-main.js +83 -0
  64. package/dist/cli-main.js.map +1 -0
  65. package/dist/cli.d.ts +3 -0
  66. package/dist/cli.d.ts.map +1 -0
  67. package/dist/cli.js +13 -0
  68. package/dist/cli.js.map +1 -0
  69. package/dist/index.d.ts +7 -0
  70. package/dist/index.d.ts.map +1 -0
  71. package/dist/index.js +52 -0
  72. package/dist/index.js.map +1 -0
  73. package/dist/reporter.d.ts +5 -0
  74. package/dist/reporter.d.ts.map +1 -0
  75. package/dist/reporter.js +50 -0
  76. package/dist/reporter.js.map +1 -0
  77. package/dist/scan-file.d.ts +3 -0
  78. package/dist/scan-file.d.ts.map +1 -0
  79. package/dist/scan-file.js +35 -0
  80. package/dist/scan-file.js.map +1 -0
  81. package/dist/types.d.ts +46 -0
  82. package/dist/types.d.ts.map +1 -0
  83. package/dist/types.js +2 -0
  84. package/dist/types.js.map +1 -0
  85. package/dist/utils.d.ts +25 -0
  86. package/dist/utils.d.ts.map +1 -0
  87. package/dist/utils.js +211 -0
  88. package/dist/utils.js.map +1 -0
  89. package/package.json +45 -0
@@ -0,0 +1 @@
1
+ {"version":3,"file":"seo-metadata.d.ts","sourceRoot":"","sources":["../../src/checks/seo-metadata.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,aAAa,CAAC;AAGrD,wBAAsB,gBAAgB,CAAC,WAAW,EAAE,MAAM,GAAG,OAAO,CAAC,iBAAiB,CAAC,CA8CtF"}
@@ -0,0 +1,41 @@
1
+ import path from 'node:path';
2
+ import { findFiles, pathExists, readOptionalText, SOURCE_EXTENSIONS } from '../utils.js';
3
+ export async function checkSeoMetadata(projectPath) {
4
+ const appDirs = [path.join(projectPath, 'app'), path.join(projectPath, 'src', 'app')];
5
+ const hasAppDir = await Promise.all(appDirs.map((appDir) => pathExists(appDir)));
6
+ if (!hasAppDir.some(Boolean)) {
7
+ return {
8
+ name: 'seo metadata',
9
+ status: 'skip',
10
+ message: 'No Expo Router app directory found.',
11
+ };
12
+ }
13
+ const sourceFiles = await findFiles(projectPath, (filePath) => SOURCE_EXTENSIONS.has(path.extname(filePath)));
14
+ const searchableText = (await Promise.all(sourceFiles.slice(0, 500).map((filePath) => readOptionalText(filePath))))
15
+ .filter((value) => Boolean(value))
16
+ .join('\n');
17
+ const signals = {
18
+ title: /\b(title|metadata|Head)\b/i.test(searchableText),
19
+ description: /\b(description|og:description|twitter:description)\b/i.test(searchableText),
20
+ canonical: /\b(canonical|og:url)\b/i.test(searchableText),
21
+ sitemap: await pathExists(path.join(projectPath, 'public', 'sitemap.xml')),
22
+ robots: await pathExists(path.join(projectPath, 'public', 'robots.txt')),
23
+ };
24
+ const missing = Object.entries(signals)
25
+ .filter(([, present]) => !present)
26
+ .map(([key]) => key);
27
+ if (missing.length > 0) {
28
+ return {
29
+ name: 'seo metadata',
30
+ status: 'warn',
31
+ message: 'Web metadata strategy has gaps.',
32
+ details: { missing },
33
+ };
34
+ }
35
+ return {
36
+ name: 'seo metadata',
37
+ status: 'pass',
38
+ message: 'Basic web metadata signals are present.',
39
+ };
40
+ }
41
+ //# sourceMappingURL=seo-metadata.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"seo-metadata.js","sourceRoot":"","sources":["../../src/checks/seo-metadata.ts"],"names":[],"mappings":"AAAA,OAAO,IAAI,MAAM,WAAW,CAAC;AAG7B,OAAO,EAAE,SAAS,EAAE,UAAU,EAAE,gBAAgB,EAAE,iBAAiB,EAAE,MAAM,aAAa,CAAC;AAEzF,MAAM,CAAC,KAAK,UAAU,gBAAgB,CAAC,WAAmB;IACxD,MAAM,OAAO,GAAG,CAAC,IAAI,CAAC,IAAI,CAAC,WAAW,EAAE,KAAK,CAAC,EAAE,IAAI,CAAC,IAAI,CAAC,WAAW,EAAE,KAAK,EAAE,KAAK,CAAC,CAAC,CAAC;IACtF,MAAM,SAAS,GAAG,MAAM,OAAO,CAAC,GAAG,CAAC,OAAO,CAAC,GAAG,CAAC,CAAC,MAAM,EAAE,EAAE,CAAC,UAAU,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC;IACjF,IAAI,CAAC,SAAS,CAAC,IAAI,CAAC,OAAO,CAAC,EAAE,CAAC;QAC7B,OAAO;YACL,IAAI,EAAE,cAAc;YACpB,MAAM,EAAE,MAAM;YACd,OAAO,EAAE,qCAAqC;SAC/C,CAAC;IACJ,CAAC;IAED,MAAM,WAAW,GAAG,MAAM,SAAS,CAAC,WAAW,EAAE,CAAC,QAAQ,EAAE,EAAE,CAC5D,iBAAiB,CAAC,GAAG,CAAC,IAAI,CAAC,OAAO,CAAC,QAAQ,CAAC,CAAC,CAC9C,CAAC;IACF,MAAM,cAAc,GAAG,CACrB,MAAM,OAAO,CAAC,GAAG,CAAC,WAAW,CAAC,KAAK,CAAC,CAAC,EAAE,GAAG,CAAC,CAAC,GAAG,CAAC,CAAC,QAAQ,EAAE,EAAE,CAAC,gBAAgB,CAAC,QAAQ,CAAC,CAAC,CAAC,CAC3F;SACE,MAAM,CAAC,CAAC,KAAK,EAAmB,EAAE,CAAC,OAAO,CAAC,KAAK,CAAC,CAAC;SAClD,IAAI,CAAC,IAAI,CAAC,CAAC;IAEd,MAAM,OAAO,GAAG;QACd,KAAK,EAAE,4BAA4B,CAAC,IAAI,CAAC,cAAc,CAAC;QACxD,WAAW,EAAE,uDAAuD,CAAC,IAAI,CAAC,cAAc,CAAC;QACzF,SAAS,EAAE,yBAAyB,CAAC,IAAI,CAAC,cAAc,CAAC;QACzD,OAAO,EAAE,MAAM,UAAU,CAAC,IAAI,CAAC,IAAI,CAAC,WAAW,EAAE,QAAQ,EAAE,aAAa,CAAC,CAAC;QAC1E,MAAM,EAAE,MAAM,UAAU,CAAC,IAAI,CAAC,IAAI,CAAC,WAAW,EAAE,QAAQ,EAAE,YAAY,CAAC,CAAC;KACzE,CAAC;IAEF,MAAM,OAAO,GAAG,MAAM,CAAC,OAAO,CAAC,OAAO,CAAC;SACpC,MAAM,CAAC,CAAC,CAAC,EAAE,OAAO,CAAC,EAAE,EAAE,CAAC,CAAC,OAAO,CAAC;SACjC,GAAG,CAAC,CAAC,CAAC,GAAG,CAAC,EAAE,EAAE,CAAC,GAAG,CAAC,CAAC;IAEvB,IAAI,OAAO,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;QACvB,OAAO;YACL,IAAI,EAAE,cAAc;YACpB,MAAM,EAAE,MAAM;YACd,OAAO,EAAE,iCAAiC;YAC1C,OAAO,EAAE,EAAE,OAAO,EAAE;SACrB,CAAC;IACJ,CAAC;IAED,OAAO;QACL,IAAI,EAAE,cAAc;QACpB,MAAM,EAAE,MAAM;QACd,OAAO,EAAE,yCAAyC;KACnD,CAAC;AACJ,CAAC"}
@@ -0,0 +1,4 @@
1
+ import type { DoctorCheckResult } from '../types.js';
2
+ export declare function scanFileSsrSafety(projectPath: string, filePath: string): Promise<DoctorCheckResult>;
3
+ export declare function findSsrFindings(projectPath: string, filePath: string, contents: string): string[];
4
+ //# sourceMappingURL=ssr-safety.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"ssr-safety.d.ts","sourceRoot":"","sources":["../../src/checks/ssr-safety.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,aAAa,CAAC;AAMrD,wBAAsB,iBAAiB,CACrC,WAAW,EAAE,MAAM,EACnB,QAAQ,EAAE,MAAM,GACf,OAAO,CAAC,iBAAiB,CAAC,CAgB5B;AAED,wBAAgB,eAAe,CAAC,WAAW,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,GAAG,MAAM,EAAE,CAcjG"}
@@ -0,0 +1,33 @@
1
+ import { readOptionalText, relative } from '../utils.js';
2
+ const BROWSER_GLOBAL_PATTERN = /\b(window|document|localStorage|sessionStorage|navigator)\b/g;
3
+ const WINDOW_GUARD_PATTERN = /typeof\s+(window|document|navigator)\s*!==?\s*['"]undefined['"]/;
4
+ export async function scanFileSsrSafety(projectPath, filePath) {
5
+ const contents = (await readOptionalText(filePath)) ?? '';
6
+ const findings = findSsrFindings(projectPath, filePath, contents);
7
+ return findings.length > 0
8
+ ? {
9
+ name: 'ssr safety',
10
+ status: 'warn',
11
+ message: 'Browser globals may need SSR/client-only guards.',
12
+ details: { findings },
13
+ }
14
+ : {
15
+ name: 'ssr safety',
16
+ status: 'pass',
17
+ message: 'No obvious SSR-unsafe browser globals found.',
18
+ };
19
+ }
20
+ export function findSsrFindings(projectPath, filePath, contents) {
21
+ if (!BROWSER_GLOBAL_PATTERN.test(contents)) {
22
+ BROWSER_GLOBAL_PATTERN.lastIndex = 0;
23
+ return [];
24
+ }
25
+ BROWSER_GLOBAL_PATTERN.lastIndex = 0;
26
+ if (WINDOW_GUARD_PATTERN.test(contents)) {
27
+ return [];
28
+ }
29
+ BROWSER_GLOBAL_PATTERN.lastIndex = 0;
30
+ const matches = [...new Set([...contents.matchAll(BROWSER_GLOBAL_PATTERN)].map((m) => m[0]))];
31
+ return [`${relative(projectPath, filePath)}: uses ${matches.join(', ')}; verify SSR/client guards.`];
32
+ }
33
+ //# sourceMappingURL=ssr-safety.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"ssr-safety.js","sourceRoot":"","sources":["../../src/checks/ssr-safety.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,gBAAgB,EAAE,QAAQ,EAAE,MAAM,aAAa,CAAC;AAEzD,MAAM,sBAAsB,GAAG,8DAA8D,CAAC;AAC9F,MAAM,oBAAoB,GAAG,iEAAiE,CAAC;AAE/F,MAAM,CAAC,KAAK,UAAU,iBAAiB,CACrC,WAAmB,EACnB,QAAgB;IAEhB,MAAM,QAAQ,GAAG,CAAC,MAAM,gBAAgB,CAAC,QAAQ,CAAC,CAAC,IAAI,EAAE,CAAC;IAC1D,MAAM,QAAQ,GAAG,eAAe,CAAC,WAAW,EAAE,QAAQ,EAAE,QAAQ,CAAC,CAAC;IAElE,OAAO,QAAQ,CAAC,MAAM,GAAG,CAAC;QACxB,CAAC,CAAC;YACE,IAAI,EAAE,YAAY;YAClB,MAAM,EAAE,MAAM;YACd,OAAO,EAAE,kDAAkD;YAC3D,OAAO,EAAE,EAAE,QAAQ,EAAE;SACtB;QACH,CAAC,CAAC;YACE,IAAI,EAAE,YAAY;YAClB,MAAM,EAAE,MAAM;YACd,OAAO,EAAE,8CAA8C;SACxD,CAAC;AACR,CAAC;AAED,MAAM,UAAU,eAAe,CAAC,WAAmB,EAAE,QAAgB,EAAE,QAAgB;IACrF,IAAI,CAAC,sBAAsB,CAAC,IAAI,CAAC,QAAQ,CAAC,EAAE,CAAC;QAC3C,sBAAsB,CAAC,SAAS,GAAG,CAAC,CAAC;QACrC,OAAO,EAAE,CAAC;IACZ,CAAC;IACD,sBAAsB,CAAC,SAAS,GAAG,CAAC,CAAC;IAErC,IAAI,oBAAoB,CAAC,IAAI,CAAC,QAAQ,CAAC,EAAE,CAAC;QACxC,OAAO,EAAE,CAAC;IACZ,CAAC;IAED,sBAAsB,CAAC,SAAS,GAAG,CAAC,CAAC;IACrC,MAAM,OAAO,GAAG,CAAC,GAAG,IAAI,GAAG,CAAC,CAAC,GAAG,QAAQ,CAAC,QAAQ,CAAC,sBAAsB,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC;IAC9F,OAAO,CAAC,GAAG,QAAQ,CAAC,WAAW,EAAE,QAAQ,CAAC,UAAU,OAAO,CAAC,IAAI,CAAC,IAAI,CAAC,6BAA6B,CAAC,CAAC;AACvG,CAAC"}
@@ -0,0 +1,3 @@
1
+ import type { DoctorCheckResult, PackageJson } from '../types.js';
2
+ export declare function checkStylingDependencies(packageJson: PackageJson): DoctorCheckResult;
3
+ //# sourceMappingURL=styling-stack.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"styling-stack.d.ts","sourceRoot":"","sources":["../../src/checks/styling-stack.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,iBAAiB,EAAE,WAAW,EAAE,MAAM,aAAa,CAAC;AAElE,wBAAgB,wBAAwB,CAAC,WAAW,EAAE,WAAW,GAAG,iBAAiB,CAqCpF"}
@@ -0,0 +1,35 @@
1
+ export function checkStylingDependencies(packageJson) {
2
+ const deps = {
3
+ ...packageJson.dependencies,
4
+ ...packageJson.devDependencies,
5
+ };
6
+ const hasUniwind = 'uniwind' in deps;
7
+ const hasNativeWind = 'nativewind' in deps;
8
+ if (hasUniwind && hasNativeWind) {
9
+ return {
10
+ name: 'styling stack',
11
+ status: 'warn',
12
+ message: 'Both Uniwind and NativeWind are installed; prefer one styling adapter.',
13
+ };
14
+ }
15
+ if (hasNativeWind && !hasUniwind) {
16
+ return {
17
+ name: 'styling stack',
18
+ status: 'warn',
19
+ message: 'NativeWind detected. MDS suite defaults new projects to Uniwind.',
20
+ };
21
+ }
22
+ if (hasUniwind) {
23
+ return {
24
+ name: 'styling stack',
25
+ status: 'pass',
26
+ message: 'Uniwind detected.',
27
+ };
28
+ }
29
+ return {
30
+ name: 'styling stack',
31
+ status: 'skip',
32
+ message: 'No Uniwind or NativeWind dependency detected.',
33
+ };
34
+ }
35
+ //# sourceMappingURL=styling-stack.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"styling-stack.js","sourceRoot":"","sources":["../../src/checks/styling-stack.ts"],"names":[],"mappings":"AAEA,MAAM,UAAU,wBAAwB,CAAC,WAAwB;IAC/D,MAAM,IAAI,GAAG;QACX,GAAG,WAAW,CAAC,YAAY;QAC3B,GAAG,WAAW,CAAC,eAAe;KAC/B,CAAC;IACF,MAAM,UAAU,GAAG,SAAS,IAAI,IAAI,CAAC;IACrC,MAAM,aAAa,GAAG,YAAY,IAAI,IAAI,CAAC;IAE3C,IAAI,UAAU,IAAI,aAAa,EAAE,CAAC;QAChC,OAAO;YACL,IAAI,EAAE,eAAe;YACrB,MAAM,EAAE,MAAM;YACd,OAAO,EAAE,wEAAwE;SAClF,CAAC;IACJ,CAAC;IAED,IAAI,aAAa,IAAI,CAAC,UAAU,EAAE,CAAC;QACjC,OAAO;YACL,IAAI,EAAE,eAAe;YACrB,MAAM,EAAE,MAAM;YACd,OAAO,EAAE,kEAAkE;SAC5E,CAAC;IACJ,CAAC;IAED,IAAI,UAAU,EAAE,CAAC;QACf,OAAO;YACL,IAAI,EAAE,eAAe;YACrB,MAAM,EAAE,MAAM;YACd,OAAO,EAAE,mBAAmB;SAC7B,CAAC;IACJ,CAAC;IAED,OAAO;QACL,IAAI,EAAE,eAAe;QACrB,MAAM,EAAE,MAAM;QACd,OAAO,EAAE,+CAA+C;KACzD,CAAC;AACJ,CAAC"}
@@ -0,0 +1,3 @@
1
+ import type { DoctorCheckResult } from '../types.js';
2
+ export declare function checkTodoForContextMarkers(projectPath: string): Promise<DoctorCheckResult>;
3
+ //# sourceMappingURL=todo-for-context.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"todo-for-context.d.ts","sourceRoot":"","sources":["../../src/checks/todo-for-context.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,aAAa,CAAC;AAWrD,wBAAsB,0BAA0B,CAAC,WAAW,EAAE,MAAM,GAAG,OAAO,CAAC,iBAAiB,CAAC,CAoDhG"}
@@ -0,0 +1,56 @@
1
+ import { readFile } from 'node:fs/promises';
2
+ import path from 'node:path';
3
+ import { firstExistingPath } from '../utils.js';
4
+ const MARKER = '# TodoForContext(optional):';
5
+ export async function checkTodoForContextMarkers(projectPath) {
6
+ const projectDir = path.join(projectPath, 'project');
7
+ const candidates = [
8
+ { label: 'project/info.md', paths: [path.join(projectDir, 'info.md')] },
9
+ { label: 'project/style.md', paths: [path.join(projectDir, 'style.md')] },
10
+ { label: 'project/guidelines.md', paths: [path.join(projectDir, 'guidelines.md')] },
11
+ {
12
+ label: 'project/todo.md',
13
+ paths: [path.join(projectDir, 'todo.md'), path.join(projectDir, 'TODO.md')],
14
+ },
15
+ { label: 'project/intake-agent.md', paths: [path.join(projectDir, 'intake-agent.md')] },
16
+ { label: 'project/release-flow.md', paths: [path.join(projectDir, 'release-flow.md')] },
17
+ ];
18
+ const hits = [];
19
+ for (const candidate of candidates) {
20
+ const filePath = await firstExistingPath(candidate.paths);
21
+ if (!filePath) {
22
+ continue;
23
+ }
24
+ const contents = await readFile(filePath, 'utf8');
25
+ const lines = contents.split(/\r?\n/);
26
+ for (let index = 0; index < lines.length; index += 1) {
27
+ const line = lines[index] ?? '';
28
+ if (isUnresolvedMarkerLine(line)) {
29
+ hits.push({
30
+ file: candidate.label,
31
+ line: index + 1,
32
+ text: line.trim(),
33
+ });
34
+ }
35
+ }
36
+ }
37
+ if (hits.length === 0) {
38
+ return {
39
+ name: 'todo-for-context markers',
40
+ status: 'pass',
41
+ message: 'No unresolved # TodoForContext(optional): markers in project memory.',
42
+ };
43
+ }
44
+ return {
45
+ name: 'todo-for-context markers',
46
+ status: 'error',
47
+ message: `${hits.length} unresolved # TodoForContext(optional): ${hits.length === 1 ? 'marker' : 'markers'} ` +
48
+ 'block agentic onboarding, project intake, and phase work. Fill the section or delete the marker line.',
49
+ details: { hits },
50
+ };
51
+ }
52
+ function isUnresolvedMarkerLine(line) {
53
+ const trimmed = line.trim();
54
+ return trimmed.startsWith(MARKER) || trimmed.startsWith(`- ${MARKER}`) || trimmed.startsWith(`* ${MARKER}`);
55
+ }
56
+ //# sourceMappingURL=todo-for-context.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"todo-for-context.js","sourceRoot":"","sources":["../../src/checks/todo-for-context.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,7 @@
1
+ import type { DoctorCheckResult, PackageJson } from '../types.js';
2
+ export declare function runTypeScriptCheck(args: {
3
+ packageJson: PackageJson;
4
+ projectPath: string;
5
+ timeoutMs: number;
6
+ }): Promise<DoctorCheckResult | null>;
7
+ //# sourceMappingURL=typescript.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"typescript.d.ts","sourceRoot":"","sources":["../../src/checks/typescript.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,iBAAiB,EAAE,WAAW,EAAE,MAAM,aAAa,CAAC;AASlE,wBAAsB,kBAAkB,CAAC,IAAI,EAAE;IAC7C,WAAW,EAAE,WAAW,CAAC;IACzB,WAAW,EAAE,MAAM,CAAC;IACpB,SAAS,EAAE,MAAM,CAAC;CACnB,GAAG,OAAO,CAAC,iBAAiB,GAAG,IAAI,CAAC,CAmBpC"}
@@ -0,0 +1,18 @@
1
+ import { buildRunScriptCommand, commandResultToCheck, detectPackageManager, runShellCommand, } from '../utils.js';
2
+ import { findScript } from './eslint.js';
3
+ export async function runTypeScriptCheck(args) {
4
+ const candidates = ['typecheck', 'type-check', 'check'];
5
+ const scriptName = findScript(args.packageJson, candidates);
6
+ if (!scriptName) {
7
+ return {
8
+ name: 'typecheck',
9
+ status: 'warn',
10
+ message: 'No package script found for typecheck.',
11
+ details: { candidates },
12
+ };
13
+ }
14
+ const packageManager = await detectPackageManager(args.projectPath, args.packageJson);
15
+ const command = buildRunScriptCommand(packageManager, scriptName);
16
+ return commandResultToCheck('typecheck', command, await runShellCommand(command, args.projectPath, args.timeoutMs));
17
+ }
18
+ //# sourceMappingURL=typescript.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"typescript.js","sourceRoot":"","sources":["../../src/checks/typescript.ts"],"names":[],"mappings":"AACA,OAAO,EACL,qBAAqB,EACrB,oBAAoB,EACpB,oBAAoB,EACpB,eAAe,GAChB,MAAM,aAAa,CAAC;AACrB,OAAO,EAAE,UAAU,EAAE,MAAM,aAAa,CAAC;AAEzC,MAAM,CAAC,KAAK,UAAU,kBAAkB,CAAC,IAIxC;IACC,MAAM,UAAU,GAAG,CAAC,WAAW,EAAE,YAAY,EAAE,OAAO,CAAC,CAAC;IACxD,MAAM,UAAU,GAAG,UAAU,CAAC,IAAI,CAAC,WAAW,EAAE,UAAU,CAAC,CAAC;IAC5D,IAAI,CAAC,UAAU,EAAE,CAAC;QAChB,OAAO;YACL,IAAI,EAAE,WAAW;YACjB,MAAM,EAAE,MAAM;YACd,OAAO,EAAE,wCAAwC;YACjD,OAAO,EAAE,EAAE,UAAU,EAAE;SACxB,CAAC;IACJ,CAAC;IAED,MAAM,cAAc,GAAG,MAAM,oBAAoB,CAAC,IAAI,CAAC,WAAW,EAAE,IAAI,CAAC,WAAW,CAAC,CAAC;IACtF,MAAM,OAAO,GAAG,qBAAqB,CAAC,cAAc,EAAE,UAAU,CAAC,CAAC;IAClE,OAAO,oBAAoB,CACzB,WAAW,EACX,OAAO,EACP,MAAM,eAAe,CAAC,OAAO,EAAE,IAAI,CAAC,WAAW,EAAE,IAAI,CAAC,SAAS,CAAC,CACjE,CAAC;AACJ,CAAC"}
@@ -0,0 +1,15 @@
1
+ import type { DoctorMode } from './types.js';
2
+ export interface DoctorCliRunResult {
3
+ output: string;
4
+ exitCode: number;
5
+ }
6
+ export interface DoctorCliArgs {
7
+ projectPath: string;
8
+ mode: DoctorMode;
9
+ json: boolean;
10
+ runScripts: boolean;
11
+ timeoutMs: number;
12
+ }
13
+ export declare function parseDoctorCliArgs(argv: string[]): DoctorCliArgs;
14
+ export declare function runDoctorCli(argv: string[]): Promise<DoctorCliRunResult>;
15
+ //# sourceMappingURL=cli-main.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"cli-main.d.ts","sourceRoot":"","sources":["../src/cli-main.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,YAAY,CAAC;AAE7C,MAAM,WAAW,kBAAkB;IACjC,MAAM,EAAE,MAAM,CAAC;IACf,QAAQ,EAAE,MAAM,CAAC;CAClB;AAED,MAAM,WAAW,aAAa;IAC5B,WAAW,EAAE,MAAM,CAAC;IACpB,IAAI,EAAE,UAAU,CAAC;IACjB,IAAI,EAAE,OAAO,CAAC;IACd,UAAU,EAAE,OAAO,CAAC;IACpB,SAAS,EAAE,MAAM,CAAC;CACnB;AAED,wBAAgB,kBAAkB,CAAC,IAAI,EAAE,MAAM,EAAE,GAAG,aAAa,CA0EhE;AAED,wBAAsB,YAAY,CAAC,IAAI,EAAE,MAAM,EAAE,GAAG,OAAO,CAAC,kBAAkB,CAAC,CAY9E"}
@@ -0,0 +1,83 @@
1
+ import { formatHumanReport, runDoctor } from './index.js';
2
+ export function parseDoctorCliArgs(argv) {
3
+ let projectPath = '.';
4
+ let mode = null;
5
+ let json = false;
6
+ let runScripts = true;
7
+ let timeoutMs = 120000;
8
+ const positionals = [];
9
+ for (let index = 0; index < argv.length; index += 1) {
10
+ const arg = argv[index] ?? '';
11
+ if (!arg) {
12
+ continue;
13
+ }
14
+ if (!arg.startsWith('-')) {
15
+ positionals.push(arg);
16
+ continue;
17
+ }
18
+ if (arg === '--ci') {
19
+ mode = 'ci';
20
+ continue;
21
+ }
22
+ if (arg === '--fast') {
23
+ mode = 'fast';
24
+ continue;
25
+ }
26
+ if (arg === '--full') {
27
+ mode = 'full';
28
+ continue;
29
+ }
30
+ if (arg === '--json') {
31
+ json = true;
32
+ continue;
33
+ }
34
+ if (arg === '--scripts') {
35
+ runScripts = true;
36
+ continue;
37
+ }
38
+ if (arg === '--no-scripts') {
39
+ runScripts = false;
40
+ continue;
41
+ }
42
+ if (arg === '--timeout-ms') {
43
+ const value = argv[index + 1];
44
+ if (!value || value.startsWith('-')) {
45
+ throw new Error('--timeout-ms requires a numeric value.');
46
+ }
47
+ const parsed = Number(value);
48
+ if (!Number.isFinite(parsed) || parsed <= 0) {
49
+ throw new Error(`--timeout-ms must be a positive number; got ${value}.`);
50
+ }
51
+ timeoutMs = Math.floor(parsed);
52
+ index += 1;
53
+ continue;
54
+ }
55
+ throw new Error(`Unknown argument: ${arg}`);
56
+ }
57
+ if (positionals.length > 1) {
58
+ throw new Error(`Unexpected extra arguments: ${positionals.slice(1).join(' ')}`);
59
+ }
60
+ if (positionals.length === 1) {
61
+ projectPath = positionals[0] ?? '.';
62
+ }
63
+ return {
64
+ projectPath,
65
+ mode: mode ?? 'ci',
66
+ json,
67
+ runScripts,
68
+ timeoutMs,
69
+ };
70
+ }
71
+ export async function runDoctorCli(argv) {
72
+ const args = parseDoctorCliArgs(argv);
73
+ const report = await runDoctor(args.projectPath, {
74
+ mode: args.mode,
75
+ runScripts: args.runScripts,
76
+ timeoutMs: args.timeoutMs,
77
+ });
78
+ return {
79
+ output: args.json ? JSON.stringify(report, null, 2) : formatHumanReport(report),
80
+ exitCode: report.summary.errors > 0 ? 1 : 0,
81
+ };
82
+ }
83
+ //# sourceMappingURL=cli-main.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"cli-main.js","sourceRoot":"","sources":["../src/cli-main.ts"],"names":[],"mappings":"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"}
package/dist/cli.d.ts ADDED
@@ -0,0 +1,3 @@
1
+ #!/usr/bin/env node
2
+ export {};
3
+ //# sourceMappingURL=cli.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"cli.d.ts","sourceRoot":"","sources":["../src/cli.ts"],"names":[],"mappings":""}
package/dist/cli.js ADDED
@@ -0,0 +1,13 @@
1
+ #!/usr/bin/env node
2
+ import { runDoctorCli } from './cli-main.js';
3
+ async function main() {
4
+ const result = await runDoctorCli(process.argv.slice(2));
5
+ process.stdout.write(result.output + '\n');
6
+ process.exitCode = result.exitCode;
7
+ }
8
+ main().catch((error) => {
9
+ const message = error instanceof Error ? error.message : String(error);
10
+ process.stderr.write(`${message}\n`);
11
+ process.exit(1);
12
+ });
13
+ //# sourceMappingURL=cli.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"cli.js","sourceRoot":"","sources":["../src/cli.ts"],"names":[],"mappings":";AAEA,OAAO,EAAE,YAAY,EAAE,MAAM,eAAe,CAAC;AAE7C,KAAK,UAAU,IAAI;IACjB,MAAM,MAAM,GAAG,MAAM,YAAY,CAAC,OAAO,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,CAAC;IACzD,OAAO,CAAC,MAAM,CAAC,KAAK,CAAC,MAAM,CAAC,MAAM,GAAG,IAAI,CAAC,CAAC;IAC3C,OAAO,CAAC,QAAQ,GAAG,MAAM,CAAC,QAAQ,CAAC;AACrC,CAAC;AAED,IAAI,EAAE,CAAC,KAAK,CAAC,CAAC,KAAc,EAAE,EAAE;IAC9B,MAAM,OAAO,GAAG,KAAK,YAAY,KAAK,CAAC,CAAC,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC;IACvE,OAAO,CAAC,MAAM,CAAC,KAAK,CAAC,GAAG,OAAO,IAAI,CAAC,CAAC;IACrC,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;AAClB,CAAC,CAAC,CAAC"}
@@ -0,0 +1,7 @@
1
+ import type { DoctorOptions, DoctorReport } from './types.js';
2
+ export type { DoctorCheckResult, DoctorCheckStatus, DoctorMode, DoctorOptions, DoctorReport, PackageJson, ScanFileOptions, } from './types.js';
3
+ export { createReport, formatHumanReport, formatJsonReport } from './reporter.js';
4
+ export { scanFile } from './scan-file.js';
5
+ export declare function runDoctor(projectPath: string, options?: DoctorOptions): Promise<DoctorReport>;
6
+ export declare function fixDoctor(projectPath: string, options?: Omit<DoctorOptions, 'fix'>): Promise<DoctorReport>;
7
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAeA,OAAO,KAAK,EAAE,aAAa,EAAE,YAAY,EAAE,MAAM,YAAY,CAAC;AAG9D,YAAY,EACV,iBAAiB,EACjB,iBAAiB,EACjB,UAAU,EACV,aAAa,EACb,YAAY,EACZ,WAAW,EACX,eAAe,GAChB,MAAM,YAAY,CAAC;AAEpB,OAAO,EAAE,YAAY,EAAE,iBAAiB,EAAE,gBAAgB,EAAE,MAAM,eAAe,CAAC;AAClF,OAAO,EAAE,QAAQ,EAAE,MAAM,gBAAgB,CAAC;AAE1C,wBAAsB,SAAS,CAC7B,WAAW,EAAE,MAAM,EACnB,OAAO,GAAE,aAAkB,GAC1B,OAAO,CAAC,YAAY,CAAC,CAkDvB;AAED,wBAAsB,SAAS,CAC7B,WAAW,EAAE,MAAM,EACnB,OAAO,GAAE,IAAI,CAAC,aAAa,EAAE,KAAK,CAAM,GACvC,OAAO,CAAC,YAAY,CAAC,CAEvB"}
package/dist/index.js ADDED
@@ -0,0 +1,52 @@
1
+ import path from 'node:path';
2
+ import { checkAppArchitecture, checkEnvHygiene, checkExpoConfiguration, checkGitignoreEnv, checkPackageScripts, checkProjectDocs, checkSeoMetadata, checkStylingDependencies, checkTodoForContextMarkers, runScriptChecks, } from './checks/index.js';
3
+ import { createReport } from './reporter.js';
4
+ import { pathExists, readPackageJson } from './utils.js';
5
+ export { createReport, formatHumanReport, formatJsonReport } from './reporter.js';
6
+ export { scanFile } from './scan-file.js';
7
+ export async function runDoctor(projectPath, options = {}) {
8
+ const resolvedProjectPath = path.resolve(projectPath);
9
+ const mode = options.mode ?? 'fast';
10
+ const checks = [];
11
+ if (!(await pathExists(resolvedProjectPath))) {
12
+ checks.push({
13
+ name: 'project path',
14
+ status: 'error',
15
+ message: `Project path does not exist: ${resolvedProjectPath}`,
16
+ });
17
+ return createReport(resolvedProjectPath, mode, checks);
18
+ }
19
+ const packageJsonPath = path.join(resolvedProjectPath, 'package.json');
20
+ const packageJson = await readPackageJson(packageJsonPath);
21
+ checks.push(await checkProjectDocs(resolvedProjectPath));
22
+ checks.push(await checkTodoForContextMarkers(resolvedProjectPath));
23
+ checks.push(await checkGitignoreEnv(resolvedProjectPath));
24
+ if (!packageJson) {
25
+ checks.push({
26
+ name: 'package.json',
27
+ status: 'warn',
28
+ message: 'No package.json found; package scripts and dependency checks were skipped.',
29
+ });
30
+ return createReport(resolvedProjectPath, mode, checks);
31
+ }
32
+ checks.push(checkPackageScripts(packageJson, resolvedProjectPath));
33
+ checks.push(checkStylingDependencies(packageJson));
34
+ checks.push(await checkExpoConfiguration(packageJson, resolvedProjectPath));
35
+ checks.push(await checkEnvHygiene(resolvedProjectPath));
36
+ checks.push(await checkAppArchitecture(resolvedProjectPath));
37
+ checks.push(await checkSeoMetadata(resolvedProjectPath));
38
+ if (options.runScripts !== false) {
39
+ checks.push(...(await runScriptChecks({
40
+ packageJson,
41
+ projectPath: resolvedProjectPath,
42
+ mode,
43
+ fix: options.fix ?? false,
44
+ timeoutMs: options.timeoutMs ?? 120000,
45
+ })));
46
+ }
47
+ return createReport(resolvedProjectPath, mode, checks);
48
+ }
49
+ export async function fixDoctor(projectPath, options = {}) {
50
+ return runDoctor(projectPath, { ...options, fix: true });
51
+ }
52
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,5 @@
1
+ import type { DoctorCheckResult, DoctorMode, DoctorReport } from './types.js';
2
+ export declare function createReport(projectPath: string, mode: DoctorMode, checks: DoctorCheckResult[]): DoctorReport;
3
+ export declare function formatJsonReport(report: DoctorReport): string;
4
+ export declare function formatHumanReport(report: DoctorReport): string;
5
+ //# sourceMappingURL=reporter.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"reporter.d.ts","sourceRoot":"","sources":["../src/reporter.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,iBAAiB,EAAE,UAAU,EAAE,YAAY,EAAE,MAAM,YAAY,CAAC;AAM9E,wBAAgB,YAAY,CAC1B,WAAW,EAAE,MAAM,EACnB,IAAI,EAAE,UAAU,EAChB,MAAM,EAAE,iBAAiB,EAAE,GAC1B,YAAY,CAkBd;AAED,wBAAgB,gBAAgB,CAAC,MAAM,EAAE,YAAY,GAAG,MAAM,CAE7D;AAED,wBAAgB,iBAAiB,CAAC,MAAM,EAAE,YAAY,GAAG,MAAM,CAqB9D"}
@@ -0,0 +1,50 @@
1
+ function computeScore(summary) {
2
+ return Math.max(0, 100 - summary.errors * 25 - summary.warnings * 5);
3
+ }
4
+ export function createReport(projectPath, mode, checks) {
5
+ const summary = {
6
+ errors: checks.filter((check) => check.status === 'error').length,
7
+ warnings: checks.filter((check) => check.status === 'warn').length,
8
+ passed: checks.filter((check) => check.status === 'pass').length,
9
+ skipped: checks.filter((check) => check.status === 'skip').length,
10
+ };
11
+ return {
12
+ projectPath,
13
+ timestamp: new Date().toISOString(),
14
+ mode,
15
+ checks,
16
+ summary: {
17
+ score: computeScore(summary),
18
+ ...summary,
19
+ },
20
+ };
21
+ }
22
+ export function formatJsonReport(report) {
23
+ return JSON.stringify(report, null, 2);
24
+ }
25
+ export function formatHumanReport(report) {
26
+ const lines = [`mds doctor (${report.mode})`, report.projectPath, ''];
27
+ for (const check of report.checks) {
28
+ lines.push(`${label(check)} ${check.name}: ${check.message}`);
29
+ if (check.status !== 'pass' && check.details) {
30
+ const detailText = JSON.stringify(check.details, null, 2);
31
+ for (const line of detailText.split('\n')) {
32
+ lines.push(` ${line}`);
33
+ }
34
+ }
35
+ }
36
+ lines.push('');
37
+ lines.push(`score ${report.summary.score}/100 | ` +
38
+ `${report.summary.errors} errors | ${report.summary.warnings} warnings | ` +
39
+ `${report.summary.passed} passed | ${report.summary.skipped} skipped`);
40
+ return lines.join('\n');
41
+ }
42
+ function label(check) {
43
+ return {
44
+ pass: 'PASS',
45
+ warn: 'WARN',
46
+ error: 'FAIL',
47
+ skip: 'SKIP',
48
+ }[check.status];
49
+ }
50
+ //# sourceMappingURL=reporter.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"reporter.js","sourceRoot":"","sources":["../src/reporter.ts"],"names":[],"mappings":"AAEA,SAAS,YAAY,CAAC,OAA6C;IACjE,OAAO,IAAI,CAAC,GAAG,CAAC,CAAC,EAAE,GAAG,GAAG,OAAO,CAAC,MAAM,GAAG,EAAE,GAAG,OAAO,CAAC,QAAQ,GAAG,CAAC,CAAC,CAAC;AACvE,CAAC;AAED,MAAM,UAAU,YAAY,CAC1B,WAAmB,EACnB,IAAgB,EAChB,MAA2B;IAE3B,MAAM,OAAO,GAAG;QACd,MAAM,EAAE,MAAM,CAAC,MAAM,CAAC,CAAC,KAAK,EAAE,EAAE,CAAC,KAAK,CAAC,MAAM,KAAK,OAAO,CAAC,CAAC,MAAM;QACjE,QAAQ,EAAE,MAAM,CAAC,MAAM,CAAC,CAAC,KAAK,EAAE,EAAE,CAAC,KAAK,CAAC,MAAM,KAAK,MAAM,CAAC,CAAC,MAAM;QAClE,MAAM,EAAE,MAAM,CAAC,MAAM,CAAC,CAAC,KAAK,EAAE,EAAE,CAAC,KAAK,CAAC,MAAM,KAAK,MAAM,CAAC,CAAC,MAAM;QAChE,OAAO,EAAE,MAAM,CAAC,MAAM,CAAC,CAAC,KAAK,EAAE,EAAE,CAAC,KAAK,CAAC,MAAM,KAAK,MAAM,CAAC,CAAC,MAAM;KAClE,CAAC;IAEF,OAAO;QACL,WAAW;QACX,SAAS,EAAE,IAAI,IAAI,EAAE,CAAC,WAAW,EAAE;QACnC,IAAI;QACJ,MAAM;QACN,OAAO,EAAE;YACP,KAAK,EAAE,YAAY,CAAC,OAAO,CAAC;YAC5B,GAAG,OAAO;SACX;KACF,CAAC;AACJ,CAAC;AAED,MAAM,UAAU,gBAAgB,CAAC,MAAoB;IACnD,OAAO,IAAI,CAAC,SAAS,CAAC,MAAM,EAAE,IAAI,EAAE,CAAC,CAAC,CAAC;AACzC,CAAC;AAED,MAAM,UAAU,iBAAiB,CAAC,MAAoB;IACpD,MAAM,KAAK,GAAG,CAAC,eAAe,MAAM,CAAC,IAAI,GAAG,EAAE,MAAM,CAAC,WAAW,EAAE,EAAE,CAAC,CAAC;IAEtE,KAAK,MAAM,KAAK,IAAI,MAAM,CAAC,MAAM,EAAE,CAAC;QAClC,KAAK,CAAC,IAAI,CAAC,GAAG,KAAK,CAAC,KAAK,CAAC,IAAI,KAAK,CAAC,IAAI,KAAK,KAAK,CAAC,OAAO,EAAE,CAAC,CAAC;QAC9D,IAAI,KAAK,CAAC,MAAM,KAAK,MAAM,IAAI,KAAK,CAAC,OAAO,EAAE,CAAC;YAC7C,MAAM,UAAU,GAAG,IAAI,CAAC,SAAS,CAAC,KAAK,CAAC,OAAO,EAAE,IAAI,EAAE,CAAC,CAAC,CAAC;YAC1D,KAAK,MAAM,IAAI,IAAI,UAAU,CAAC,KAAK,CAAC,IAAI,CAAC,EAAE,CAAC;gBAC1C,KAAK,CAAC,IAAI,CAAC,KAAK,IAAI,EAAE,CAAC,CAAC;YAC1B,CAAC;QACH,CAAC;IACH,CAAC;IAED,KAAK,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC;IACf,KAAK,CAAC,IAAI,CACR,SAAS,MAAM,CAAC,OAAO,CAAC,KAAK,SAAS;QACtC,GAAG,MAAM,CAAC,OAAO,CAAC,MAAM,aAAa,MAAM,CAAC,OAAO,CAAC,QAAQ,cAAc;QACxE,GAAG,MAAM,CAAC,OAAO,CAAC,MAAM,aAAa,MAAM,CAAC,OAAO,CAAC,OAAO,UAAU,CACxE,CAAC;IAEF,OAAO,KAAK,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;AAC1B,CAAC;AAED,SAAS,KAAK,CAAC,KAAwB;IACrC,OAAO;QACL,IAAI,EAAE,MAAM;QACZ,IAAI,EAAE,MAAM;QACZ,KAAK,EAAE,MAAM;QACb,IAAI,EAAE,MAAM;KACb,CAAC,KAAK,CAAC,MAAM,CAAC,CAAC;AAClB,CAAC"}
@@ -0,0 +1,3 @@
1
+ import type { DoctorReport, ScanFileOptions } from './types.js';
2
+ export declare function scanFile(filePath: string, options?: ScanFileOptions): Promise<DoctorReport>;
3
+ //# sourceMappingURL=scan-file.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"scan-file.d.ts","sourceRoot":"","sources":["../src/scan-file.ts"],"names":[],"mappings":"AAKA,OAAO,KAAK,EAAE,YAAY,EAAE,eAAe,EAAE,MAAM,YAAY,CAAC;AAGhE,wBAAsB,QAAQ,CAC5B,QAAQ,EAAE,MAAM,EAChB,OAAO,GAAE,eAAoB,GAC5B,OAAO,CAAC,YAAY,CAAC,CAqBvB"}
@@ -0,0 +1,35 @@
1
+ import { existsSync } from 'node:fs';
2
+ import path from 'node:path';
3
+ import { scanFileAppArchitecture, scanFileEnvHygiene, scanFileSsrSafety } from './checks/index.js';
4
+ import { createReport } from './reporter.js';
5
+ import { pathExists } from './utils.js';
6
+ export async function scanFile(filePath, options = {}) {
7
+ const resolvedFilePath = path.resolve(filePath);
8
+ const projectPath = path.resolve(options.projectPath ?? findProjectRoot(resolvedFilePath));
9
+ if (!(await pathExists(resolvedFilePath))) {
10
+ return createReport(projectPath, 'fast', [
11
+ {
12
+ name: 'file path',
13
+ status: 'error',
14
+ message: `File path does not exist: ${resolvedFilePath}`,
15
+ },
16
+ ]);
17
+ }
18
+ const checks = await Promise.all([
19
+ scanFileEnvHygiene(projectPath, resolvedFilePath),
20
+ scanFileSsrSafety(projectPath, resolvedFilePath),
21
+ scanFileAppArchitecture(projectPath, resolvedFilePath),
22
+ ]);
23
+ return createReport(projectPath, 'fast', checks);
24
+ }
25
+ function findProjectRoot(filePath) {
26
+ let current = path.dirname(filePath);
27
+ while (current !== path.dirname(current)) {
28
+ if (existsSync(path.join(current, 'package.json'))) {
29
+ return current;
30
+ }
31
+ current = path.dirname(current);
32
+ }
33
+ return path.dirname(filePath);
34
+ }
35
+ //# sourceMappingURL=scan-file.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"scan-file.js","sourceRoot":"","sources":["../src/scan-file.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,UAAU,EAAE,MAAM,SAAS,CAAC;AACrC,OAAO,IAAI,MAAM,WAAW,CAAC;AAE7B,OAAO,EAAE,uBAAuB,EAAE,kBAAkB,EAAE,iBAAiB,EAAE,MAAM,mBAAmB,CAAC;AACnG,OAAO,EAAE,YAAY,EAAE,MAAM,eAAe,CAAC;AAE7C,OAAO,EAAE,UAAU,EAAE,MAAM,YAAY,CAAC;AAExC,MAAM,CAAC,KAAK,UAAU,QAAQ,CAC5B,QAAgB,EAChB,UAA2B,EAAE;IAE7B,MAAM,gBAAgB,GAAG,IAAI,CAAC,OAAO,CAAC,QAAQ,CAAC,CAAC;IAChD,MAAM,WAAW,GAAG,IAAI,CAAC,OAAO,CAAC,OAAO,CAAC,WAAW,IAAI,eAAe,CAAC,gBAAgB,CAAC,CAAC,CAAC;IAE3F,IAAI,CAAC,CAAC,MAAM,UAAU,CAAC,gBAAgB,CAAC,CAAC,EAAE,CAAC;QAC1C,OAAO,YAAY,CAAC,WAAW,EAAE,MAAM,EAAE;YACvC;gBACE,IAAI,EAAE,WAAW;gBACjB,MAAM,EAAE,OAAO;gBACf,OAAO,EAAE,6BAA6B,gBAAgB,EAAE;aACzD;SACF,CAAC,CAAC;IACL,CAAC;IAED,MAAM,MAAM,GAAG,MAAM,OAAO,CAAC,GAAG,CAAC;QAC/B,kBAAkB,CAAC,WAAW,EAAE,gBAAgB,CAAC;QACjD,iBAAiB,CAAC,WAAW,EAAE,gBAAgB,CAAC;QAChD,uBAAuB,CAAC,WAAW,EAAE,gBAAgB,CAAC;KACvD,CAAC,CAAC;IAEH,OAAO,YAAY,CAAC,WAAW,EAAE,MAAM,EAAE,MAAM,CAAC,CAAC;AACnD,CAAC;AAED,SAAS,eAAe,CAAC,QAAgB;IACvC,IAAI,OAAO,GAAG,IAAI,CAAC,OAAO,CAAC,QAAQ,CAAC,CAAC;IACrC,OAAO,OAAO,KAAK,IAAI,CAAC,OAAO,CAAC,OAAO,CAAC,EAAE,CAAC;QACzC,IAAI,UAAU,CAAC,IAAI,CAAC,IAAI,CAAC,OAAO,EAAE,cAAc,CAAC,CAAC,EAAE,CAAC;YACnD,OAAO,OAAO,CAAC;QACjB,CAAC;QACD,OAAO,GAAG,IAAI,CAAC,OAAO,CAAC,OAAO,CAAC,CAAC;IAClC,CAAC;IACD,OAAO,IAAI,CAAC,OAAO,CAAC,QAAQ,CAAC,CAAC;AAChC,CAAC"}