@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,4 @@
1
+ import type { DoctorCheckResult } from '../types.js';
2
+ export declare function checkAppArchitecture(projectPath: string): Promise<DoctorCheckResult>;
3
+ export declare function scanFileAppArchitecture(projectPath: string, filePath: string): Promise<DoctorCheckResult>;
4
+ //# sourceMappingURL=app-architecture.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"app-architecture.d.ts","sourceRoot":"","sources":["../../src/checks/app-architecture.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,aAAa,CAAC;AAIrD,wBAAsB,oBAAoB,CAAC,WAAW,EAAE,MAAM,GAAG,OAAO,CAAC,iBAAiB,CAAC,CA4C1F;AAED,wBAAsB,uBAAuB,CAC3C,WAAW,EAAE,MAAM,EACnB,QAAQ,EAAE,MAAM,GACf,OAAO,CAAC,iBAAiB,CAAC,CAc5B"}
@@ -0,0 +1,75 @@
1
+ import path from 'node:path';
2
+ import { findSsrFindings } from './ssr-safety.js';
3
+ import { findFiles, pathExists, readOptionalText, relative, SOURCE_EXTENSIONS } from '../utils.js';
4
+ export async function checkAppArchitecture(projectPath) {
5
+ const appDirs = [path.join(projectPath, 'app'), path.join(projectPath, 'src', 'app')];
6
+ const existingAppDirs = [];
7
+ for (const appDir of appDirs) {
8
+ if (await pathExists(appDir)) {
9
+ existingAppDirs.push(appDir);
10
+ }
11
+ }
12
+ if (existingAppDirs.length === 0) {
13
+ return {
14
+ name: 'app architecture',
15
+ status: 'skip',
16
+ message: 'No Expo Router app directory found.',
17
+ };
18
+ }
19
+ const findings = [];
20
+ for (const appDir of existingAppDirs) {
21
+ const routeFiles = await findFiles(appDir, (filePath) => {
22
+ const extension = path.extname(filePath);
23
+ return SOURCE_EXTENSIONS.has(extension) && !filePath.endsWith('+api.ts');
24
+ });
25
+ for (const filePath of routeFiles) {
26
+ findings.push(...(await scanRouteFileArchitecture(projectPath, filePath)));
27
+ }
28
+ }
29
+ if (findings.length > 0) {
30
+ return {
31
+ name: 'app architecture',
32
+ status: 'warn',
33
+ message: 'Expo Router route files have architecture warnings.',
34
+ details: { findings: findings.slice(0, 50), truncated: findings.length > 50 },
35
+ };
36
+ }
37
+ return {
38
+ name: 'app architecture',
39
+ status: 'pass',
40
+ message: 'Route files passed the first-pass architecture scan.',
41
+ };
42
+ }
43
+ export async function scanFileAppArchitecture(projectPath, filePath) {
44
+ const findings = await scanRouteFileArchitecture(projectPath, filePath);
45
+ return findings.length > 0
46
+ ? {
47
+ name: 'app architecture',
48
+ status: 'warn',
49
+ message: 'File has architecture warnings.',
50
+ details: { findings },
51
+ }
52
+ : {
53
+ name: 'app architecture',
54
+ status: 'pass',
55
+ message: 'File passed the architecture scan.',
56
+ };
57
+ }
58
+ async function scanRouteFileArchitecture(projectPath, filePath) {
59
+ const contents = (await readOptionalText(filePath)) ?? '';
60
+ const findings = [];
61
+ const lineCount = contents.split(/\r?\n/).length;
62
+ const shortPath = relative(projectPath, filePath);
63
+ if (lineCount > 300) {
64
+ findings.push(`${shortPath}: ${lineCount} lines; consider moving logic into features/services.`);
65
+ }
66
+ if (/\b(from|require\()\s*['"][^'"]*(supabase|drizzle|postgres)/.test(contents)) {
67
+ findings.push(`${shortPath}: imports data-layer code directly from a route component.`);
68
+ }
69
+ findings.push(...findSsrFindings(projectPath, filePath, contents));
70
+ if (/\b(useMutation|useQuery|z\.object|schema\.parse)\b/.test(contents) && lineCount > 180) {
71
+ findings.push(`${shortPath}: mixes heavy data/form logic with a large route file.`);
72
+ }
73
+ return findings;
74
+ }
75
+ //# sourceMappingURL=app-architecture.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"app-architecture.js","sourceRoot":"","sources":["../../src/checks/app-architecture.ts"],"names":[],"mappings":"AAAA,OAAO,IAAI,MAAM,WAAW,CAAC;AAG7B,OAAO,EAAE,eAAe,EAAE,MAAM,iBAAiB,CAAC;AAClD,OAAO,EAAE,SAAS,EAAE,UAAU,EAAE,gBAAgB,EAAE,QAAQ,EAAE,iBAAiB,EAAE,MAAM,aAAa,CAAC;AAEnG,MAAM,CAAC,KAAK,UAAU,oBAAoB,CAAC,WAAmB;IAC5D,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,eAAe,GAAa,EAAE,CAAC;IAErC,KAAK,MAAM,MAAM,IAAI,OAAO,EAAE,CAAC;QAC7B,IAAI,MAAM,UAAU,CAAC,MAAM,CAAC,EAAE,CAAC;YAC7B,eAAe,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC;QAC/B,CAAC;IACH,CAAC;IAED,IAAI,eAAe,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;QACjC,OAAO;YACL,IAAI,EAAE,kBAAkB;YACxB,MAAM,EAAE,MAAM;YACd,OAAO,EAAE,qCAAqC;SAC/C,CAAC;IACJ,CAAC;IAED,MAAM,QAAQ,GAAa,EAAE,CAAC;IAC9B,KAAK,MAAM,MAAM,IAAI,eAAe,EAAE,CAAC;QACrC,MAAM,UAAU,GAAG,MAAM,SAAS,CAAC,MAAM,EAAE,CAAC,QAAQ,EAAE,EAAE;YACtD,MAAM,SAAS,GAAG,IAAI,CAAC,OAAO,CAAC,QAAQ,CAAC,CAAC;YACzC,OAAO,iBAAiB,CAAC,GAAG,CAAC,SAAS,CAAC,IAAI,CAAC,QAAQ,CAAC,QAAQ,CAAC,SAAS,CAAC,CAAC;QAC3E,CAAC,CAAC,CAAC;QAEH,KAAK,MAAM,QAAQ,IAAI,UAAU,EAAE,CAAC;YAClC,QAAQ,CAAC,IAAI,CAAC,GAAG,CAAC,MAAM,yBAAyB,CAAC,WAAW,EAAE,QAAQ,CAAC,CAAC,CAAC,CAAC;QAC7E,CAAC;IACH,CAAC;IAED,IAAI,QAAQ,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;QACxB,OAAO;YACL,IAAI,EAAE,kBAAkB;YACxB,MAAM,EAAE,MAAM;YACd,OAAO,EAAE,qDAAqD;YAC9D,OAAO,EAAE,EAAE,QAAQ,EAAE,QAAQ,CAAC,KAAK,CAAC,CAAC,EAAE,EAAE,CAAC,EAAE,SAAS,EAAE,QAAQ,CAAC,MAAM,GAAG,EAAE,EAAE;SAC9E,CAAC;IACJ,CAAC;IAED,OAAO;QACL,IAAI,EAAE,kBAAkB;QACxB,MAAM,EAAE,MAAM;QACd,OAAO,EAAE,sDAAsD;KAChE,CAAC;AACJ,CAAC;AAED,MAAM,CAAC,KAAK,UAAU,uBAAuB,CAC3C,WAAmB,EACnB,QAAgB;IAEhB,MAAM,QAAQ,GAAG,MAAM,yBAAyB,CAAC,WAAW,EAAE,QAAQ,CAAC,CAAC;IACxE,OAAO,QAAQ,CAAC,MAAM,GAAG,CAAC;QACxB,CAAC,CAAC;YACE,IAAI,EAAE,kBAAkB;YACxB,MAAM,EAAE,MAAM;YACd,OAAO,EAAE,iCAAiC;YAC1C,OAAO,EAAE,EAAE,QAAQ,EAAE;SACtB;QACH,CAAC,CAAC;YACE,IAAI,EAAE,kBAAkB;YACxB,MAAM,EAAE,MAAM;YACd,OAAO,EAAE,oCAAoC;SAC9C,CAAC;AACR,CAAC;AAED,KAAK,UAAU,yBAAyB,CAAC,WAAmB,EAAE,QAAgB;IAC5E,MAAM,QAAQ,GAAG,CAAC,MAAM,gBAAgB,CAAC,QAAQ,CAAC,CAAC,IAAI,EAAE,CAAC;IAC1D,MAAM,QAAQ,GAAa,EAAE,CAAC;IAC9B,MAAM,SAAS,GAAG,QAAQ,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC,MAAM,CAAC;IACjD,MAAM,SAAS,GAAG,QAAQ,CAAC,WAAW,EAAE,QAAQ,CAAC,CAAC;IAElD,IAAI,SAAS,GAAG,GAAG,EAAE,CAAC;QACpB,QAAQ,CAAC,IAAI,CAAC,GAAG,SAAS,KAAK,SAAS,uDAAuD,CAAC,CAAC;IACnG,CAAC;IAED,IAAI,4DAA4D,CAAC,IAAI,CAAC,QAAQ,CAAC,EAAE,CAAC;QAChF,QAAQ,CAAC,IAAI,CAAC,GAAG,SAAS,4DAA4D,CAAC,CAAC;IAC1F,CAAC;IAED,QAAQ,CAAC,IAAI,CAAC,GAAG,eAAe,CAAC,WAAW,EAAE,QAAQ,EAAE,QAAQ,CAAC,CAAC,CAAC;IAEnE,IAAI,oDAAoD,CAAC,IAAI,CAAC,QAAQ,CAAC,IAAI,SAAS,GAAG,GAAG,EAAE,CAAC;QAC3F,QAAQ,CAAC,IAAI,CAAC,GAAG,SAAS,wDAAwD,CAAC,CAAC;IACtF,CAAC;IAED,OAAO,QAAQ,CAAC;AAClB,CAAC"}
@@ -0,0 +1,4 @@
1
+ import type { DoctorCheckResult } from '../types.js';
2
+ export declare function checkEnvHygiene(projectPath: string): Promise<DoctorCheckResult>;
3
+ export declare function scanFileEnvHygiene(projectPath: string, filePath: string): Promise<DoctorCheckResult>;
4
+ //# sourceMappingURL=env-hygiene.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"env-hygiene.d.ts","sourceRoot":"","sources":["../../src/checks/env-hygiene.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,aAAa,CAAC;AAMrD,wBAAsB,eAAe,CAAC,WAAW,EAAE,MAAM,GAAG,OAAO,CAAC,iBAAiB,CAAC,CAwBrF;AAED,wBAAsB,kBAAkB,CACtC,WAAW,EAAE,MAAM,EACnB,QAAQ,EAAE,MAAM,GACf,OAAO,CAAC,iBAAiB,CAAC,CAc5B"}
@@ -0,0 +1,54 @@
1
+ import path from 'node:path';
2
+ import { findFiles, readOptionalText, relative, SOURCE_EXTENSIONS } from '../utils.js';
3
+ const PUBLIC_SECRET_PATTERN = /\bEXPO_PUBLIC_[A-Z0-9_]*(SECRET|SERVICE_ROLE|PRIVATE|PASSWORD|TOKEN|STRIPE_SECRET)[A-Z0-9_]*\b/g;
4
+ export async function checkEnvHygiene(projectPath) {
5
+ const envFiles = await findFiles(projectPath, (filePath) => {
6
+ const basename = path.basename(filePath);
7
+ return basename === '.env' || basename.startsWith('.env.');
8
+ });
9
+ const sourceFiles = await findFiles(projectPath, (filePath) => SOURCE_EXTENSIONS.has(path.extname(filePath)));
10
+ const findings = await findPublicSecretNames(projectPath, [...envFiles, ...sourceFiles]);
11
+ if (findings.length > 0) {
12
+ return {
13
+ name: 'env hygiene',
14
+ status: 'error',
15
+ message: 'Secret-looking EXPO_PUBLIC variables were found.',
16
+ details: { findings: findings.slice(0, 25), truncated: findings.length > 25 },
17
+ };
18
+ }
19
+ return {
20
+ name: 'env hygiene',
21
+ status: 'pass',
22
+ message: 'No secret-looking EXPO_PUBLIC variables found.',
23
+ };
24
+ }
25
+ export async function scanFileEnvHygiene(projectPath, filePath) {
26
+ const findings = await findPublicSecretNames(projectPath, [filePath]);
27
+ return findings.length > 0
28
+ ? {
29
+ name: 'env hygiene',
30
+ status: 'error',
31
+ message: 'Secret-looking EXPO_PUBLIC variables were found.',
32
+ details: { findings },
33
+ }
34
+ : {
35
+ name: 'env hygiene',
36
+ status: 'pass',
37
+ message: 'No secret-looking EXPO_PUBLIC variables found.',
38
+ };
39
+ }
40
+ async function findPublicSecretNames(projectPath, filePaths) {
41
+ const findings = [];
42
+ for (const filePath of filePaths) {
43
+ const contents = await readOptionalText(filePath);
44
+ if (!contents) {
45
+ continue;
46
+ }
47
+ const matches = [...new Set([...contents.matchAll(PUBLIC_SECRET_PATTERN)].map((m) => m[0]))];
48
+ for (const match of matches) {
49
+ findings.push(`${relative(projectPath, filePath)}: ${match}`);
50
+ }
51
+ }
52
+ return findings;
53
+ }
54
+ //# sourceMappingURL=env-hygiene.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"env-hygiene.js","sourceRoot":"","sources":["../../src/checks/env-hygiene.ts"],"names":[],"mappings":"AAAA,OAAO,IAAI,MAAM,WAAW,CAAC;AAG7B,OAAO,EAAE,SAAS,EAAE,gBAAgB,EAAE,QAAQ,EAAE,iBAAiB,EAAE,MAAM,aAAa,CAAC;AAEvF,MAAM,qBAAqB,GACzB,iGAAiG,CAAC;AAEpG,MAAM,CAAC,KAAK,UAAU,eAAe,CAAC,WAAmB;IACvD,MAAM,QAAQ,GAAG,MAAM,SAAS,CAAC,WAAW,EAAE,CAAC,QAAQ,EAAE,EAAE;QACzD,MAAM,QAAQ,GAAG,IAAI,CAAC,QAAQ,CAAC,QAAQ,CAAC,CAAC;QACzC,OAAO,QAAQ,KAAK,MAAM,IAAI,QAAQ,CAAC,UAAU,CAAC,OAAO,CAAC,CAAC;IAC7D,CAAC,CAAC,CAAC;IACH,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;IAEF,MAAM,QAAQ,GAAG,MAAM,qBAAqB,CAAC,WAAW,EAAE,CAAC,GAAG,QAAQ,EAAE,GAAG,WAAW,CAAC,CAAC,CAAC;IACzF,IAAI,QAAQ,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;QACxB,OAAO;YACL,IAAI,EAAE,aAAa;YACnB,MAAM,EAAE,OAAO;YACf,OAAO,EAAE,kDAAkD;YAC3D,OAAO,EAAE,EAAE,QAAQ,EAAE,QAAQ,CAAC,KAAK,CAAC,CAAC,EAAE,EAAE,CAAC,EAAE,SAAS,EAAE,QAAQ,CAAC,MAAM,GAAG,EAAE,EAAE;SAC9E,CAAC;IACJ,CAAC;IAED,OAAO;QACL,IAAI,EAAE,aAAa;QACnB,MAAM,EAAE,MAAM;QACd,OAAO,EAAE,gDAAgD;KAC1D,CAAC;AACJ,CAAC;AAED,MAAM,CAAC,KAAK,UAAU,kBAAkB,CACtC,WAAmB,EACnB,QAAgB;IAEhB,MAAM,QAAQ,GAAG,MAAM,qBAAqB,CAAC,WAAW,EAAE,CAAC,QAAQ,CAAC,CAAC,CAAC;IACtE,OAAO,QAAQ,CAAC,MAAM,GAAG,CAAC;QACxB,CAAC,CAAC;YACE,IAAI,EAAE,aAAa;YACnB,MAAM,EAAE,OAAO;YACf,OAAO,EAAE,kDAAkD;YAC3D,OAAO,EAAE,EAAE,QAAQ,EAAE;SACtB;QACH,CAAC,CAAC;YACE,IAAI,EAAE,aAAa;YACnB,MAAM,EAAE,MAAM;YACd,OAAO,EAAE,gDAAgD;SAC1D,CAAC;AACR,CAAC;AAED,KAAK,UAAU,qBAAqB,CAAC,WAAmB,EAAE,SAAmB;IAC3E,MAAM,QAAQ,GAAa,EAAE,CAAC;IAC9B,KAAK,MAAM,QAAQ,IAAI,SAAS,EAAE,CAAC;QACjC,MAAM,QAAQ,GAAG,MAAM,gBAAgB,CAAC,QAAQ,CAAC,CAAC;QAClD,IAAI,CAAC,QAAQ,EAAE,CAAC;YACd,SAAS;QACX,CAAC;QAED,MAAM,OAAO,GAAG,CAAC,GAAG,IAAI,GAAG,CAAC,CAAC,GAAG,QAAQ,CAAC,QAAQ,CAAC,qBAAqB,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC;QAC7F,KAAK,MAAM,KAAK,IAAI,OAAO,EAAE,CAAC;YAC5B,QAAQ,CAAC,IAAI,CAAC,GAAG,QAAQ,CAAC,WAAW,EAAE,QAAQ,CAAC,KAAK,KAAK,EAAE,CAAC,CAAC;QAChE,CAAC;IACH,CAAC;IACD,OAAO,QAAQ,CAAC;AAClB,CAAC"}
@@ -0,0 +1,10 @@
1
+ import type { DoctorCheckResult, DoctorMode, PackageJson } from '../types.js';
2
+ export declare function runEslintCheck(args: {
3
+ packageJson: PackageJson;
4
+ projectPath: string;
5
+ fix: boolean;
6
+ timeoutMs: number;
7
+ }): Promise<DoctorCheckResult | null>;
8
+ export declare function findScript(packageJson: PackageJson, candidates: string[]): string | null;
9
+ export declare function shouldRunScript(_mode: DoctorMode): boolean;
10
+ //# sourceMappingURL=eslint.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"eslint.d.ts","sourceRoot":"","sources":["../../src/checks/eslint.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,iBAAiB,EAAE,UAAU,EAAE,WAAW,EAAE,MAAM,aAAa,CAAC;AAQ9E,wBAAsB,cAAc,CAAC,IAAI,EAAE;IACzC,WAAW,EAAE,WAAW,CAAC;IACzB,WAAW,EAAE,MAAM,CAAC;IACpB,GAAG,EAAE,OAAO,CAAC;IACb,SAAS,EAAE,MAAM,CAAC;CACnB,GAAG,OAAO,CAAC,iBAAiB,GAAG,IAAI,CAAC,CAkBpC;AAED,wBAAgB,UAAU,CAAC,WAAW,EAAE,WAAW,EAAE,UAAU,EAAE,MAAM,EAAE,GAAG,MAAM,GAAG,IAAI,CAGxF;AAED,wBAAgB,eAAe,CAAC,KAAK,EAAE,UAAU,GAAG,OAAO,CAE1D"}
@@ -0,0 +1,23 @@
1
+ import { buildRunScriptCommand, commandResultToCheck, detectPackageManager, runShellCommand, } from '../utils.js';
2
+ export async function runEslintCheck(args) {
3
+ const scriptName = findScript(args.packageJson, ['lint']);
4
+ if (!scriptName) {
5
+ return {
6
+ name: 'lint',
7
+ status: 'warn',
8
+ message: 'No package script found for lint.',
9
+ details: { candidates: ['lint'] },
10
+ };
11
+ }
12
+ const packageManager = await detectPackageManager(args.projectPath, args.packageJson);
13
+ const command = buildRunScriptCommand(packageManager, scriptName, args.fix ? ['--', '--fix'] : []);
14
+ return commandResultToCheck('lint', command, await runShellCommand(command, args.projectPath, args.timeoutMs));
15
+ }
16
+ export function findScript(packageJson, candidates) {
17
+ const scripts = packageJson.scripts ?? {};
18
+ return candidates.find((candidate) => candidate in scripts) ?? null;
19
+ }
20
+ export function shouldRunScript(_mode) {
21
+ return true;
22
+ }
23
+ //# sourceMappingURL=eslint.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"eslint.js","sourceRoot":"","sources":["../../src/checks/eslint.ts"],"names":[],"mappings":"AACA,OAAO,EACL,qBAAqB,EACrB,oBAAoB,EACpB,oBAAoB,EACpB,eAAe,GAChB,MAAM,aAAa,CAAC;AAErB,MAAM,CAAC,KAAK,UAAU,cAAc,CAAC,IAKpC;IACC,MAAM,UAAU,GAAG,UAAU,CAAC,IAAI,CAAC,WAAW,EAAE,CAAC,MAAM,CAAC,CAAC,CAAC;IAC1D,IAAI,CAAC,UAAU,EAAE,CAAC;QAChB,OAAO;YACL,IAAI,EAAE,MAAM;YACZ,MAAM,EAAE,MAAM;YACd,OAAO,EAAE,mCAAmC;YAC5C,OAAO,EAAE,EAAE,UAAU,EAAE,CAAC,MAAM,CAAC,EAAE;SAClC,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,EAAE,IAAI,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,IAAI,EAAE,OAAO,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC;IACnG,OAAO,oBAAoB,CACzB,MAAM,EACN,OAAO,EACP,MAAM,eAAe,CAAC,OAAO,EAAE,IAAI,CAAC,WAAW,EAAE,IAAI,CAAC,SAAS,CAAC,CACjE,CAAC;AACJ,CAAC;AAED,MAAM,UAAU,UAAU,CAAC,WAAwB,EAAE,UAAoB;IACvE,MAAM,OAAO,GAAG,WAAW,CAAC,OAAO,IAAI,EAAE,CAAC;IAC1C,OAAO,UAAU,CAAC,IAAI,CAAC,CAAC,SAAS,EAAE,EAAE,CAAC,SAAS,IAAI,OAAO,CAAC,IAAI,IAAI,CAAC;AACtE,CAAC;AAED,MAAM,UAAU,eAAe,CAAC,KAAiB;IAC/C,OAAO,IAAI,CAAC;AACd,CAAC"}
@@ -0,0 +1,3 @@
1
+ import type { DoctorCheckResult, PackageJson } from '../types.js';
2
+ export declare function checkExpoConfiguration(packageJson: PackageJson, projectPath: string): Promise<DoctorCheckResult>;
3
+ //# sourceMappingURL=expo-config.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"expo-config.d.ts","sourceRoot":"","sources":["../../src/checks/expo-config.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,iBAAiB,EAAE,WAAW,EAAE,MAAM,aAAa,CAAC;AAGlE,wBAAsB,sBAAsB,CAC1C,WAAW,EAAE,WAAW,EACxB,WAAW,EAAE,MAAM,GAClB,OAAO,CAAC,iBAAiB,CAAC,CAsC5B"}
@@ -0,0 +1,51 @@
1
+ import path from 'node:path';
2
+ import { findFiles, isRecord, readNestedString, readOptionalText } from '../utils.js';
3
+ export async function checkExpoConfiguration(packageJson, projectPath) {
4
+ const deps = {
5
+ ...packageJson.dependencies,
6
+ ...packageJson.devDependencies,
7
+ };
8
+ const hasExpoRouter = 'expo-router' in deps;
9
+ const warnings = [];
10
+ if (hasExpoRouter && packageJson.main !== 'expo-router/entry') {
11
+ warnings.push('expo-router is installed but package.json main is not expo-router/entry.');
12
+ }
13
+ const apiRouteFiles = await findFiles(projectPath, (filePath) => filePath.endsWith('+api.ts'));
14
+ if (apiRouteFiles.length > 0) {
15
+ const appJson = await readAppJson(projectPath);
16
+ const webOutput = readNestedString(appJson, ['expo', 'web', 'output']);
17
+ if (webOutput !== 'server') {
18
+ warnings.push('Expo API routes found, but app.json does not set expo.web.output to "server".');
19
+ }
20
+ }
21
+ if (warnings.length > 0) {
22
+ return {
23
+ name: 'expo configuration',
24
+ status: 'warn',
25
+ message: 'Expo configuration has production-readiness warnings.',
26
+ details: { warnings },
27
+ };
28
+ }
29
+ return {
30
+ name: 'expo configuration',
31
+ status: hasExpoRouter || apiRouteFiles.length > 0 ? 'pass' : 'skip',
32
+ message: hasExpoRouter || apiRouteFiles.length > 0
33
+ ? 'Expo Router configuration looks consistent.'
34
+ : 'No Expo Router signals detected.',
35
+ };
36
+ }
37
+ async function readAppJson(projectPath) {
38
+ const appJsonPath = path.join(projectPath, 'app.json');
39
+ const raw = await readOptionalText(appJsonPath);
40
+ if (!raw) {
41
+ return null;
42
+ }
43
+ try {
44
+ const parsed = JSON.parse(raw);
45
+ return isRecord(parsed) ? parsed : null;
46
+ }
47
+ catch {
48
+ return null;
49
+ }
50
+ }
51
+ //# sourceMappingURL=expo-config.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"expo-config.js","sourceRoot":"","sources":["../../src/checks/expo-config.ts"],"names":[],"mappings":"AAAA,OAAO,IAAI,MAAM,WAAW,CAAC;AAG7B,OAAO,EAAE,SAAS,EAAE,QAAQ,EAAE,gBAAgB,EAAE,gBAAgB,EAAE,MAAM,aAAa,CAAC;AAEtF,MAAM,CAAC,KAAK,UAAU,sBAAsB,CAC1C,WAAwB,EACxB,WAAmB;IAEnB,MAAM,IAAI,GAAG;QACX,GAAG,WAAW,CAAC,YAAY;QAC3B,GAAG,WAAW,CAAC,eAAe;KAC/B,CAAC;IACF,MAAM,aAAa,GAAG,aAAa,IAAI,IAAI,CAAC;IAC5C,MAAM,QAAQ,GAAa,EAAE,CAAC;IAE9B,IAAI,aAAa,IAAI,WAAW,CAAC,IAAI,KAAK,mBAAmB,EAAE,CAAC;QAC9D,QAAQ,CAAC,IAAI,CAAC,0EAA0E,CAAC,CAAC;IAC5F,CAAC;IAED,MAAM,aAAa,GAAG,MAAM,SAAS,CAAC,WAAW,EAAE,CAAC,QAAQ,EAAE,EAAE,CAAC,QAAQ,CAAC,QAAQ,CAAC,SAAS,CAAC,CAAC,CAAC;IAC/F,IAAI,aAAa,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;QAC7B,MAAM,OAAO,GAAG,MAAM,WAAW,CAAC,WAAW,CAAC,CAAC;QAC/C,MAAM,SAAS,GAAG,gBAAgB,CAAC,OAAO,EAAE,CAAC,MAAM,EAAE,KAAK,EAAE,QAAQ,CAAC,CAAC,CAAC;QACvE,IAAI,SAAS,KAAK,QAAQ,EAAE,CAAC;YAC3B,QAAQ,CAAC,IAAI,CAAC,+EAA+E,CAAC,CAAC;QACjG,CAAC;IACH,CAAC;IAED,IAAI,QAAQ,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;QACxB,OAAO;YACL,IAAI,EAAE,oBAAoB;YAC1B,MAAM,EAAE,MAAM;YACd,OAAO,EAAE,uDAAuD;YAChE,OAAO,EAAE,EAAE,QAAQ,EAAE;SACtB,CAAC;IACJ,CAAC;IAED,OAAO;QACL,IAAI,EAAE,oBAAoB;QAC1B,MAAM,EAAE,aAAa,IAAI,aAAa,CAAC,MAAM,GAAG,CAAC,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,MAAM;QACnE,OAAO,EACL,aAAa,IAAI,aAAa,CAAC,MAAM,GAAG,CAAC;YACvC,CAAC,CAAC,6CAA6C;YAC/C,CAAC,CAAC,kCAAkC;KACzC,CAAC;AACJ,CAAC;AAED,KAAK,UAAU,WAAW,CAAC,WAAmB;IAC5C,MAAM,WAAW,GAAG,IAAI,CAAC,IAAI,CAAC,WAAW,EAAE,UAAU,CAAC,CAAC;IACvD,MAAM,GAAG,GAAG,MAAM,gBAAgB,CAAC,WAAW,CAAC,CAAC;IAChD,IAAI,CAAC,GAAG,EAAE,CAAC;QACT,OAAO,IAAI,CAAC;IACd,CAAC;IAED,IAAI,CAAC;QACH,MAAM,MAAM,GAAG,IAAI,CAAC,KAAK,CAAC,GAAG,CAAY,CAAC;QAC1C,OAAO,QAAQ,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,IAAI,CAAC;IAC1C,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,IAAI,CAAC;IACd,CAAC;AACH,CAAC"}
@@ -0,0 +1,8 @@
1
+ import type { DoctorCheckResult, DoctorMode, PackageJson } from '../types.js';
2
+ export declare function runExpoDoctorCheck(args: {
3
+ packageJson: PackageJson;
4
+ projectPath: string;
5
+ mode: DoctorMode;
6
+ timeoutMs: number;
7
+ }): Promise<DoctorCheckResult | null>;
8
+ //# sourceMappingURL=expo-doctor.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"expo-doctor.d.ts","sourceRoot":"","sources":["../../src/checks/expo-doctor.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,iBAAiB,EAAE,UAAU,EAAE,WAAW,EAAE,MAAM,aAAa,CAAC;AAS9E,wBAAsB,kBAAkB,CAAC,IAAI,EAAE;IAC7C,WAAW,EAAE,WAAW,CAAC;IACzB,WAAW,EAAE,MAAM,CAAC;IACpB,IAAI,EAAE,UAAU,CAAC;IACjB,SAAS,EAAE,MAAM,CAAC;CACnB,GAAG,OAAO,CAAC,iBAAiB,GAAG,IAAI,CAAC,CA0BpC"}
@@ -0,0 +1,22 @@
1
+ import { buildRunScriptCommand, commandResultToCheck, detectPackageManager, runShellCommand, } from '../utils.js';
2
+ import { findScript } from './eslint.js';
3
+ export async function runExpoDoctorCheck(args) {
4
+ const hasExpo = Boolean(args.packageJson.dependencies?.expo ?? args.packageJson.devDependencies?.expo);
5
+ if (!hasExpo || args.mode === 'fast') {
6
+ return null;
7
+ }
8
+ const candidates = ['expo-doctor', 'doctor'];
9
+ const scriptName = findScript(args.packageJson, candidates);
10
+ if (!scriptName) {
11
+ return {
12
+ name: 'expo doctor',
13
+ status: 'warn',
14
+ message: 'No package script found for expo doctor.',
15
+ details: { candidates },
16
+ };
17
+ }
18
+ const packageManager = await detectPackageManager(args.projectPath, args.packageJson);
19
+ const command = buildRunScriptCommand(packageManager, scriptName);
20
+ return commandResultToCheck('expo doctor', command, await runShellCommand(command, args.projectPath, args.timeoutMs));
21
+ }
22
+ //# sourceMappingURL=expo-doctor.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"expo-doctor.js","sourceRoot":"","sources":["../../src/checks/expo-doctor.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,IAKxC;IACC,MAAM,OAAO,GAAG,OAAO,CACrB,IAAI,CAAC,WAAW,CAAC,YAAY,EAAE,IAAI,IAAI,IAAI,CAAC,WAAW,CAAC,eAAe,EAAE,IAAI,CAC9E,CAAC;IACF,IAAI,CAAC,OAAO,IAAI,IAAI,CAAC,IAAI,KAAK,MAAM,EAAE,CAAC;QACrC,OAAO,IAAI,CAAC;IACd,CAAC;IAED,MAAM,UAAU,GAAG,CAAC,aAAa,EAAE,QAAQ,CAAC,CAAC;IAC7C,MAAM,UAAU,GAAG,UAAU,CAAC,IAAI,CAAC,WAAW,EAAE,UAAU,CAAC,CAAC;IAC5D,IAAI,CAAC,UAAU,EAAE,CAAC;QAChB,OAAO;YACL,IAAI,EAAE,aAAa;YACnB,MAAM,EAAE,MAAM;YACd,OAAO,EAAE,0CAA0C;YACnD,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,aAAa,EACb,OAAO,EACP,MAAM,eAAe,CAAC,OAAO,EAAE,IAAI,CAAC,WAAW,EAAE,IAAI,CAAC,SAAS,CAAC,CACjE,CAAC;AACJ,CAAC"}
@@ -0,0 +1,3 @@
1
+ import type { DoctorCheckResult } from '../types.js';
2
+ export declare function checkGitignoreEnv(projectPath: string): Promise<DoctorCheckResult>;
3
+ //# sourceMappingURL=gitignore-env.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"gitignore-env.d.ts","sourceRoot":"","sources":["../../src/checks/gitignore-env.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,aAAa,CAAC;AAGrD,wBAAsB,iBAAiB,CAAC,WAAW,EAAE,MAAM,GAAG,OAAO,CAAC,iBAAiB,CAAC,CA6BvF"}
@@ -0,0 +1,30 @@
1
+ import path from 'node:path';
2
+ import { pathExists, readOptionalText } from '../utils.js';
3
+ export async function checkGitignoreEnv(projectPath) {
4
+ const gitignorePath = path.join(projectPath, '.gitignore');
5
+ const envPath = path.join(projectPath, '.env');
6
+ if (!(await pathExists(envPath))) {
7
+ return {
8
+ name: 'gitignore env safety',
9
+ status: 'pass',
10
+ message: 'No root .env file found.',
11
+ };
12
+ }
13
+ const gitignore = (await readOptionalText(gitignorePath)) ?? '';
14
+ const ignoresEnv = gitignore
15
+ .split(/\r?\n/)
16
+ .map((line) => line.trim())
17
+ .some((line) => line === '.env' || line === '.env.*' || line === '*.env');
18
+ return ignoresEnv
19
+ ? {
20
+ name: 'gitignore env safety',
21
+ status: 'pass',
22
+ message: '.env is ignored by git.',
23
+ }
24
+ : {
25
+ name: 'gitignore env safety',
26
+ status: 'error',
27
+ message: 'Root .env exists but .gitignore does not ignore it.',
28
+ };
29
+ }
30
+ //# sourceMappingURL=gitignore-env.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"gitignore-env.js","sourceRoot":"","sources":["../../src/checks/gitignore-env.ts"],"names":[],"mappings":"AAAA,OAAO,IAAI,MAAM,WAAW,CAAC;AAG7B,OAAO,EAAE,UAAU,EAAE,gBAAgB,EAAE,MAAM,aAAa,CAAC;AAE3D,MAAM,CAAC,KAAK,UAAU,iBAAiB,CAAC,WAAmB;IACzD,MAAM,aAAa,GAAG,IAAI,CAAC,IAAI,CAAC,WAAW,EAAE,YAAY,CAAC,CAAC;IAC3D,MAAM,OAAO,GAAG,IAAI,CAAC,IAAI,CAAC,WAAW,EAAE,MAAM,CAAC,CAAC;IAE/C,IAAI,CAAC,CAAC,MAAM,UAAU,CAAC,OAAO,CAAC,CAAC,EAAE,CAAC;QACjC,OAAO;YACL,IAAI,EAAE,sBAAsB;YAC5B,MAAM,EAAE,MAAM;YACd,OAAO,EAAE,0BAA0B;SACpC,CAAC;IACJ,CAAC;IAED,MAAM,SAAS,GAAG,CAAC,MAAM,gBAAgB,CAAC,aAAa,CAAC,CAAC,IAAI,EAAE,CAAC;IAChE,MAAM,UAAU,GAAG,SAAS;SACzB,KAAK,CAAC,OAAO,CAAC;SACd,GAAG,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,IAAI,CAAC,IAAI,EAAE,CAAC;SAC1B,IAAI,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,IAAI,KAAK,MAAM,IAAI,IAAI,KAAK,QAAQ,IAAI,IAAI,KAAK,OAAO,CAAC,CAAC;IAE5E,OAAO,UAAU;QACf,CAAC,CAAC;YACE,IAAI,EAAE,sBAAsB;YAC5B,MAAM,EAAE,MAAM;YACd,OAAO,EAAE,yBAAyB;SACnC;QACH,CAAC,CAAC;YACE,IAAI,EAAE,sBAAsB;YAC5B,MAAM,EAAE,OAAO;YACf,OAAO,EAAE,qDAAqD;SAC/D,CAAC;AACR,CAAC"}
@@ -0,0 +1,15 @@
1
+ export { checkAppArchitecture, scanFileAppArchitecture } from './app-architecture.js';
2
+ export { checkEnvHygiene, scanFileEnvHygiene } from './env-hygiene.js';
3
+ export { runEslintCheck } from './eslint.js';
4
+ export { checkExpoConfiguration } from './expo-config.js';
5
+ export { runExpoDoctorCheck } from './expo-doctor.js';
6
+ export { checkGitignoreEnv } from './gitignore-env.js';
7
+ export { checkPackageScripts } from './package-scripts.js';
8
+ export { checkProjectDocs } from './project-docs.js';
9
+ export { checkTodoForContextMarkers } from './todo-for-context.js';
10
+ export { checkSeoMetadata } from './seo-metadata.js';
11
+ export { runScriptChecks } from './script-checks.js';
12
+ export { scanFileSsrSafety } from './ssr-safety.js';
13
+ export { checkStylingDependencies } from './styling-stack.js';
14
+ export { runTypeScriptCheck } from './typescript.js';
15
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/checks/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,oBAAoB,EAAE,uBAAuB,EAAE,MAAM,uBAAuB,CAAC;AACtF,OAAO,EAAE,eAAe,EAAE,kBAAkB,EAAE,MAAM,kBAAkB,CAAC;AACvE,OAAO,EAAE,cAAc,EAAE,MAAM,aAAa,CAAC;AAC7C,OAAO,EAAE,sBAAsB,EAAE,MAAM,kBAAkB,CAAC;AAC1D,OAAO,EAAE,kBAAkB,EAAE,MAAM,kBAAkB,CAAC;AACtD,OAAO,EAAE,iBAAiB,EAAE,MAAM,oBAAoB,CAAC;AACvD,OAAO,EAAE,mBAAmB,EAAE,MAAM,sBAAsB,CAAC;AAC3D,OAAO,EAAE,gBAAgB,EAAE,MAAM,mBAAmB,CAAC;AACrD,OAAO,EAAE,0BAA0B,EAAE,MAAM,uBAAuB,CAAC;AACnE,OAAO,EAAE,gBAAgB,EAAE,MAAM,mBAAmB,CAAC;AACrD,OAAO,EAAE,eAAe,EAAE,MAAM,oBAAoB,CAAC;AACrD,OAAO,EAAE,iBAAiB,EAAE,MAAM,iBAAiB,CAAC;AACpD,OAAO,EAAE,wBAAwB,EAAE,MAAM,oBAAoB,CAAC;AAC9D,OAAO,EAAE,kBAAkB,EAAE,MAAM,iBAAiB,CAAC"}
@@ -0,0 +1,15 @@
1
+ export { checkAppArchitecture, scanFileAppArchitecture } from './app-architecture.js';
2
+ export { checkEnvHygiene, scanFileEnvHygiene } from './env-hygiene.js';
3
+ export { runEslintCheck } from './eslint.js';
4
+ export { checkExpoConfiguration } from './expo-config.js';
5
+ export { runExpoDoctorCheck } from './expo-doctor.js';
6
+ export { checkGitignoreEnv } from './gitignore-env.js';
7
+ export { checkPackageScripts } from './package-scripts.js';
8
+ export { checkProjectDocs } from './project-docs.js';
9
+ export { checkTodoForContextMarkers } from './todo-for-context.js';
10
+ export { checkSeoMetadata } from './seo-metadata.js';
11
+ export { runScriptChecks } from './script-checks.js';
12
+ export { scanFileSsrSafety } from './ssr-safety.js';
13
+ export { checkStylingDependencies } from './styling-stack.js';
14
+ export { runTypeScriptCheck } from './typescript.js';
15
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/checks/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,oBAAoB,EAAE,uBAAuB,EAAE,MAAM,uBAAuB,CAAC;AACtF,OAAO,EAAE,eAAe,EAAE,kBAAkB,EAAE,MAAM,kBAAkB,CAAC;AACvE,OAAO,EAAE,cAAc,EAAE,MAAM,aAAa,CAAC;AAC7C,OAAO,EAAE,sBAAsB,EAAE,MAAM,kBAAkB,CAAC;AAC1D,OAAO,EAAE,kBAAkB,EAAE,MAAM,kBAAkB,CAAC;AACtD,OAAO,EAAE,iBAAiB,EAAE,MAAM,oBAAoB,CAAC;AACvD,OAAO,EAAE,mBAAmB,EAAE,MAAM,sBAAsB,CAAC;AAC3D,OAAO,EAAE,gBAAgB,EAAE,MAAM,mBAAmB,CAAC;AACrD,OAAO,EAAE,0BAA0B,EAAE,MAAM,uBAAuB,CAAC;AACnE,OAAO,EAAE,gBAAgB,EAAE,MAAM,mBAAmB,CAAC;AACrD,OAAO,EAAE,eAAe,EAAE,MAAM,oBAAoB,CAAC;AACrD,OAAO,EAAE,iBAAiB,EAAE,MAAM,iBAAiB,CAAC;AACpD,OAAO,EAAE,wBAAwB,EAAE,MAAM,oBAAoB,CAAC;AAC9D,OAAO,EAAE,kBAAkB,EAAE,MAAM,iBAAiB,CAAC"}
@@ -0,0 +1,3 @@
1
+ import type { DoctorCheckResult, PackageJson } from '../types.js';
2
+ export declare function checkPackageScripts(packageJson: PackageJson, projectPath: string): DoctorCheckResult;
3
+ //# sourceMappingURL=package-scripts.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"package-scripts.d.ts","sourceRoot":"","sources":["../../src/checks/package-scripts.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,iBAAiB,EAAE,WAAW,EAAE,MAAM,aAAa,CAAC;AAGlE,wBAAgB,mBAAmB,CACjC,WAAW,EAAE,WAAW,EACxB,WAAW,EAAE,MAAM,GAClB,iBAAiB,CAuCnB"}
@@ -0,0 +1,56 @@
1
+ import path from 'node:path';
2
+ import { pathExistsSyncLike } from '../utils.js';
3
+ export function checkPackageScripts(packageJson, projectPath) {
4
+ const scripts = packageJson.scripts ?? {};
5
+ const expectedScriptGroups = {
6
+ lint: ['lint'],
7
+ typecheck: ['typecheck', 'type-check', 'check'],
8
+ test: ['test', 'test:ci'],
9
+ doctor: ['doctor', 'mds:doctor', 'ci:verify'],
10
+ build: ['build:web:deploy', 'build:web', 'build', 'build:prod', 'build:preview', 'build:dev'],
11
+ };
12
+ const missing = Object.entries(expectedScriptGroups)
13
+ .filter(([, candidates]) => !candidates.some((script) => script in scripts))
14
+ .map(([label]) => label);
15
+ const missingScriptTargets = findMissingNodeScriptTargets(scripts, projectPath);
16
+ if (missingScriptTargets.length > 0) {
17
+ return {
18
+ name: 'package scripts',
19
+ status: 'error',
20
+ message: 'One or more package scripts reference files that do not exist.',
21
+ details: { missing, missingScriptTargets },
22
+ };
23
+ }
24
+ if (missing.length > 0) {
25
+ return {
26
+ name: 'package scripts',
27
+ status: 'warn',
28
+ message: 'Some recommended scripts are missing.',
29
+ details: { missing },
30
+ };
31
+ }
32
+ return {
33
+ name: 'package scripts',
34
+ status: 'pass',
35
+ message: 'Recommended package scripts are present.',
36
+ };
37
+ }
38
+ function findMissingNodeScriptTargets(scripts, projectPath) {
39
+ const missingTargets = [];
40
+ const nodeFilePattern = /\bnode\s+(?!-e\b)(["']?)([^\s"'&|;]+)\1/g;
41
+ for (const [scriptName, command] of Object.entries(scripts)) {
42
+ for (const match of command.matchAll(nodeFilePattern)) {
43
+ const target = match[2];
44
+ if (!target || target.startsWith('-') || target.includes('$')) {
45
+ continue;
46
+ }
47
+ const normalizedTarget = target.replace(/\\/g, path.sep);
48
+ const targetPath = path.resolve(projectPath, normalizedTarget);
49
+ if (!pathExistsSyncLike(targetPath)) {
50
+ missingTargets.push(`${scriptName}: ${target}`);
51
+ }
52
+ }
53
+ }
54
+ return missingTargets;
55
+ }
56
+ //# sourceMappingURL=package-scripts.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"package-scripts.js","sourceRoot":"","sources":["../../src/checks/package-scripts.ts"],"names":[],"mappings":"AAAA,OAAO,IAAI,MAAM,WAAW,CAAC;AAG7B,OAAO,EAAE,kBAAkB,EAAE,MAAM,aAAa,CAAC;AAEjD,MAAM,UAAU,mBAAmB,CACjC,WAAwB,EACxB,WAAmB;IAEnB,MAAM,OAAO,GAAG,WAAW,CAAC,OAAO,IAAI,EAAE,CAAC;IAC1C,MAAM,oBAAoB,GAAG;QAC3B,IAAI,EAAE,CAAC,MAAM,CAAC;QACd,SAAS,EAAE,CAAC,WAAW,EAAE,YAAY,EAAE,OAAO,CAAC;QAC/C,IAAI,EAAE,CAAC,MAAM,EAAE,SAAS,CAAC;QACzB,MAAM,EAAE,CAAC,QAAQ,EAAE,YAAY,EAAE,WAAW,CAAC;QAC7C,KAAK,EAAE,CAAC,kBAAkB,EAAE,WAAW,EAAE,OAAO,EAAE,YAAY,EAAE,eAAe,EAAE,WAAW,CAAC;KAC9F,CAAC;IAEF,MAAM,OAAO,GAAG,MAAM,CAAC,OAAO,CAAC,oBAAoB,CAAC;SACjD,MAAM,CAAC,CAAC,CAAC,EAAE,UAAU,CAAC,EAAE,EAAE,CAAC,CAAC,UAAU,CAAC,IAAI,CAAC,CAAC,MAAM,EAAE,EAAE,CAAC,MAAM,IAAI,OAAO,CAAC,CAAC;SAC3E,GAAG,CAAC,CAAC,CAAC,KAAK,CAAC,EAAE,EAAE,CAAC,KAAK,CAAC,CAAC;IAE3B,MAAM,oBAAoB,GAAG,4BAA4B,CAAC,OAAO,EAAE,WAAW,CAAC,CAAC;IAEhF,IAAI,oBAAoB,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;QACpC,OAAO;YACL,IAAI,EAAE,iBAAiB;YACvB,MAAM,EAAE,OAAO;YACf,OAAO,EAAE,gEAAgE;YACzE,OAAO,EAAE,EAAE,OAAO,EAAE,oBAAoB,EAAE;SAC3C,CAAC;IACJ,CAAC;IAED,IAAI,OAAO,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;QACvB,OAAO;YACL,IAAI,EAAE,iBAAiB;YACvB,MAAM,EAAE,MAAM;YACd,OAAO,EAAE,uCAAuC;YAChD,OAAO,EAAE,EAAE,OAAO,EAAE;SACrB,CAAC;IACJ,CAAC;IAED,OAAO;QACL,IAAI,EAAE,iBAAiB;QACvB,MAAM,EAAE,MAAM;QACd,OAAO,EAAE,0CAA0C;KACpD,CAAC;AACJ,CAAC;AAED,SAAS,4BAA4B,CACnC,OAA+B,EAC/B,WAAmB;IAEnB,MAAM,cAAc,GAAa,EAAE,CAAC;IACpC,MAAM,eAAe,GAAG,0CAA0C,CAAC;IAEnE,KAAK,MAAM,CAAC,UAAU,EAAE,OAAO,CAAC,IAAI,MAAM,CAAC,OAAO,CAAC,OAAO,CAAC,EAAE,CAAC;QAC5D,KAAK,MAAM,KAAK,IAAI,OAAO,CAAC,QAAQ,CAAC,eAAe,CAAC,EAAE,CAAC;YACtD,MAAM,MAAM,GAAG,KAAK,CAAC,CAAC,CAAC,CAAC;YACxB,IAAI,CAAC,MAAM,IAAI,MAAM,CAAC,UAAU,CAAC,GAAG,CAAC,IAAI,MAAM,CAAC,QAAQ,CAAC,GAAG,CAAC,EAAE,CAAC;gBAC9D,SAAS;YACX,CAAC;YAED,MAAM,gBAAgB,GAAG,MAAM,CAAC,OAAO,CAAC,KAAK,EAAE,IAAI,CAAC,GAAG,CAAC,CAAC;YACzD,MAAM,UAAU,GAAG,IAAI,CAAC,OAAO,CAAC,WAAW,EAAE,gBAAgB,CAAC,CAAC;YAC/D,IAAI,CAAC,kBAAkB,CAAC,UAAU,CAAC,EAAE,CAAC;gBACpC,cAAc,CAAC,IAAI,CAAC,GAAG,UAAU,KAAK,MAAM,EAAE,CAAC,CAAC;YAClD,CAAC;QACH,CAAC;IACH,CAAC;IAED,OAAO,cAAc,CAAC;AACxB,CAAC"}
@@ -0,0 +1,3 @@
1
+ import type { DoctorCheckResult } from '../types.js';
2
+ export declare function checkProjectDocs(projectPath: string): Promise<DoctorCheckResult>;
3
+ //# sourceMappingURL=project-docs.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"project-docs.d.ts","sourceRoot":"","sources":["../../src/checks/project-docs.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,aAAa,CAAC;AAGrD,wBAAsB,gBAAgB,CAAC,WAAW,EAAE,MAAM,GAAG,OAAO,CAAC,iBAAiB,CAAC,CA6CtF"}
@@ -0,0 +1,45 @@
1
+ import { readFile } from 'node:fs/promises';
2
+ import path from 'node:path';
3
+ import { firstExistingPath } from '../utils.js';
4
+ export async function checkProjectDocs(projectPath) {
5
+ const projectDir = path.join(projectPath, 'project');
6
+ const docs = [
7
+ { label: 'info.md', paths: [path.join(projectDir, 'info.md')], required: true },
8
+ {
9
+ label: 'todo.md',
10
+ paths: [path.join(projectDir, 'todo.md'), path.join(projectDir, 'TODO.md')],
11
+ required: true,
12
+ },
13
+ { label: 'guidelines.md', paths: [path.join(projectDir, 'guidelines.md')], required: true },
14
+ { label: 'style.md', paths: [path.join(projectDir, 'style.md')], required: false },
15
+ ];
16
+ const missing = [];
17
+ const empty = [];
18
+ for (const doc of docs) {
19
+ const filePath = await firstExistingPath(doc.paths);
20
+ if (!filePath) {
21
+ if (doc.required) {
22
+ missing.push(doc.label);
23
+ }
24
+ continue;
25
+ }
26
+ const contents = await readFile(filePath, 'utf8');
27
+ if (contents.trim().length === 0) {
28
+ empty.push(doc.label);
29
+ }
30
+ }
31
+ if (missing.length > 0 || empty.length > 0) {
32
+ return {
33
+ name: 'project docs',
34
+ status: 'warn',
35
+ message: 'Project memory files need attention.',
36
+ details: { missing, empty },
37
+ };
38
+ }
39
+ return {
40
+ name: 'project docs',
41
+ status: 'pass',
42
+ message: 'project/info.md, project/todo.md, and project/guidelines.md are present and non-empty.',
43
+ };
44
+ }
45
+ //# sourceMappingURL=project-docs.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"project-docs.js","sourceRoot":"","sources":["../../src/checks/project-docs.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,QAAQ,EAAE,MAAM,kBAAkB,CAAC;AAC5C,OAAO,IAAI,MAAM,WAAW,CAAC;AAG7B,OAAO,EAAE,iBAAiB,EAAE,MAAM,aAAa,CAAC;AAEhD,MAAM,CAAC,KAAK,UAAU,gBAAgB,CAAC,WAAmB;IACxD,MAAM,UAAU,GAAG,IAAI,CAAC,IAAI,CAAC,WAAW,EAAE,SAAS,CAAC,CAAC;IACrD,MAAM,IAAI,GAAG;QACX,EAAE,KAAK,EAAE,SAAS,EAAE,KAAK,EAAE,CAAC,IAAI,CAAC,IAAI,CAAC,UAAU,EAAE,SAAS,CAAC,CAAC,EAAE,QAAQ,EAAE,IAAI,EAAE;QAC/E;YACE,KAAK,EAAE,SAAS;YAChB,KAAK,EAAE,CAAC,IAAI,CAAC,IAAI,CAAC,UAAU,EAAE,SAAS,CAAC,EAAE,IAAI,CAAC,IAAI,CAAC,UAAU,EAAE,SAAS,CAAC,CAAC;YAC3E,QAAQ,EAAE,IAAI;SACf;QACD,EAAE,KAAK,EAAE,eAAe,EAAE,KAAK,EAAE,CAAC,IAAI,CAAC,IAAI,CAAC,UAAU,EAAE,eAAe,CAAC,CAAC,EAAE,QAAQ,EAAE,IAAI,EAAE;QAC3F,EAAE,KAAK,EAAE,UAAU,EAAE,KAAK,EAAE,CAAC,IAAI,CAAC,IAAI,CAAC,UAAU,EAAE,UAAU,CAAC,CAAC,EAAE,QAAQ,EAAE,KAAK,EAAE;KACnF,CAAC;IAEF,MAAM,OAAO,GAAa,EAAE,CAAC;IAC7B,MAAM,KAAK,GAAa,EAAE,CAAC;IAE3B,KAAK,MAAM,GAAG,IAAI,IAAI,EAAE,CAAC;QACvB,MAAM,QAAQ,GAAG,MAAM,iBAAiB,CAAC,GAAG,CAAC,KAAK,CAAC,CAAC;QACpD,IAAI,CAAC,QAAQ,EAAE,CAAC;YACd,IAAI,GAAG,CAAC,QAAQ,EAAE,CAAC;gBACjB,OAAO,CAAC,IAAI,CAAC,GAAG,CAAC,KAAK,CAAC,CAAC;YAC1B,CAAC;YACD,SAAS;QACX,CAAC;QAED,MAAM,QAAQ,GAAG,MAAM,QAAQ,CAAC,QAAQ,EAAE,MAAM,CAAC,CAAC;QAClD,IAAI,QAAQ,CAAC,IAAI,EAAE,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;YACjC,KAAK,CAAC,IAAI,CAAC,GAAG,CAAC,KAAK,CAAC,CAAC;QACxB,CAAC;IACH,CAAC;IAED,IAAI,OAAO,CAAC,MAAM,GAAG,CAAC,IAAI,KAAK,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;QAC3C,OAAO;YACL,IAAI,EAAE,cAAc;YACpB,MAAM,EAAE,MAAM;YACd,OAAO,EAAE,sCAAsC;YAC/C,OAAO,EAAE,EAAE,OAAO,EAAE,KAAK,EAAE;SAC5B,CAAC;IACJ,CAAC;IAED,OAAO;QACL,IAAI,EAAE,cAAc;QACpB,MAAM,EAAE,MAAM;QACd,OAAO,EAAE,wFAAwF;KAClG,CAAC;AACJ,CAAC"}
@@ -0,0 +1,9 @@
1
+ import type { DoctorCheckResult, DoctorMode, PackageJson } from '../types.js';
2
+ export declare function runScriptChecks(args: {
3
+ packageJson: PackageJson;
4
+ projectPath: string;
5
+ mode: DoctorMode;
6
+ fix: boolean;
7
+ timeoutMs: number;
8
+ }): Promise<DoctorCheckResult[]>;
9
+ //# sourceMappingURL=script-checks.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"script-checks.d.ts","sourceRoot":"","sources":["../../src/checks/script-checks.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,iBAAiB,EAAE,UAAU,EAAE,WAAW,EAAE,MAAM,aAAa,CAAC;AAW9E,wBAAsB,eAAe,CAAC,IAAI,EAAE;IAC1C,WAAW,EAAE,WAAW,CAAC;IACzB,WAAW,EAAE,MAAM,CAAC;IACpB,IAAI,EAAE,UAAU,CAAC;IACjB,GAAG,EAAE,OAAO,CAAC;IACb,SAAS,EAAE,MAAM,CAAC;CACnB,GAAG,OAAO,CAAC,iBAAiB,EAAE,CAAC,CAe/B"}
@@ -0,0 +1,59 @@
1
+ import { buildRunScriptCommand, commandResultToCheck, detectPackageManager, runShellCommand, } from '../utils.js';
2
+ import { runEslintCheck, findScript } from './eslint.js';
3
+ import { runExpoDoctorCheck } from './expo-doctor.js';
4
+ import { runTypeScriptCheck } from './typescript.js';
5
+ export async function runScriptChecks(args) {
6
+ const checks = [];
7
+ for (const check of [
8
+ await runEslintCheck(args),
9
+ await runTypeScriptCheck(args),
10
+ await runTestsCheck(args),
11
+ await runExpoDoctorCheck(args),
12
+ await runProductionBuildCheck(args),
13
+ ]) {
14
+ if (check) {
15
+ checks.push(check);
16
+ }
17
+ }
18
+ return checks;
19
+ }
20
+ async function runTestsCheck(args) {
21
+ const candidates = ['test', 'test:ci'];
22
+ const scriptName = findScript(args.packageJson, candidates);
23
+ if (!scriptName) {
24
+ return args.mode === 'fast'
25
+ ? null
26
+ : {
27
+ name: 'tests',
28
+ status: 'warn',
29
+ message: 'No package script found for tests.',
30
+ details: { candidates },
31
+ };
32
+ }
33
+ const packageManager = await detectPackageManager(args.projectPath, args.packageJson);
34
+ const command = buildRunScriptCommand(packageManager, scriptName);
35
+ return commandResultToCheck('tests', command, await runShellCommand(command, args.projectPath, args.timeoutMs));
36
+ }
37
+ async function runProductionBuildCheck(args) {
38
+ const candidates = args.mode === 'full'
39
+ ? ['build:web:deploy', 'build:web', 'build:prod', 'build:preview', 'build:dev', 'build']
40
+ : args.mode === 'ci'
41
+ ? ['build:web:deploy', 'build:web', 'build:prod', 'build']
42
+ : [];
43
+ if (candidates.length === 0) {
44
+ return null;
45
+ }
46
+ const scriptName = findScript(args.packageJson, candidates);
47
+ if (!scriptName) {
48
+ return {
49
+ name: 'production build',
50
+ status: 'warn',
51
+ message: 'No package script found for production build.',
52
+ details: { candidates },
53
+ };
54
+ }
55
+ const packageManager = await detectPackageManager(args.projectPath, args.packageJson);
56
+ const command = buildRunScriptCommand(packageManager, scriptName);
57
+ return commandResultToCheck('production build', command, await runShellCommand(command, args.projectPath, args.timeoutMs));
58
+ }
59
+ //# sourceMappingURL=script-checks.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"script-checks.js","sourceRoot":"","sources":["../../src/checks/script-checks.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,3 @@
1
+ import type { DoctorCheckResult } from '../types.js';
2
+ export declare function checkSeoMetadata(projectPath: string): Promise<DoctorCheckResult>;
3
+ //# sourceMappingURL=seo-metadata.d.ts.map