|
| 1 | +import path from 'path' |
| 2 | +import fs from 'fs' |
| 3 | +import util from 'util' |
| 4 | + |
| 5 | +import { fileURLToPath } from 'node:url' |
| 6 | +import { |
| 7 | + checkTgz, |
| 8 | + summarizeProblems, |
| 9 | + getProblems, |
| 10 | + Analysis, |
| 11 | + ProblemSummary, |
| 12 | + Problem, |
| 13 | + ResolutionKind, |
| 14 | + ProblemKind, |
| 15 | +} from 'are-the-types-wrong-core' |
| 16 | +import React from 'react' |
| 17 | +import { render, Text, Box } from 'ink' |
| 18 | + |
| 19 | +const allResolutionKinds: ResolutionKind[] = [ |
| 20 | + 'node10', |
| 21 | + 'node16-cjs', |
| 22 | + 'node16-esm', |
| 23 | + 'bundler', |
| 24 | +] |
| 25 | + |
| 26 | +const problemEmoji: Record<ProblemKind, string> = { |
| 27 | + Wildcard: '❓', |
| 28 | + NoResolution: '💀', |
| 29 | + UntypedResolution: '❌', |
| 30 | + FalseCJS: '🎭', |
| 31 | + FalseESM: '👺', |
| 32 | + CJSResolvesToESM: '⚠️', |
| 33 | + FallbackCondition: '🐛', |
| 34 | + CJSOnlyExportsDefault: '🤨', |
| 35 | + FalseExportDefault: '❗️', |
| 36 | +} |
| 37 | + |
| 38 | +const problemShortDescriptions: Record<ProblemKind, string> = { |
| 39 | + Wildcard: `${problemEmoji.Wildcard} Unable to check`, |
| 40 | + NoResolution: `${problemEmoji.NoResolution} Failed to resolve`, |
| 41 | + UntypedResolution: `${problemEmoji.UntypedResolution} No types`, |
| 42 | + FalseCJS: `${problemEmoji.FalseCJS} Masquerading as CJS`, |
| 43 | + FalseESM: `${problemEmoji.FalseESM} Masquerading as ESM`, |
| 44 | + CJSResolvesToESM: `${problemEmoji.CJSResolvesToESM} ESM (dynamic import only)`, |
| 45 | + FallbackCondition: `${problemEmoji.FallbackCondition} Used fallback condition`, |
| 46 | + CJSOnlyExportsDefault: `${problemEmoji.CJSOnlyExportsDefault} CJS default export`, |
| 47 | + FalseExportDefault: `${problemEmoji.FalseExportDefault} Incorrect default export`, |
| 48 | +} |
| 49 | + |
| 50 | +const resolutionKinds: Record<ResolutionKind, string> = { |
| 51 | + node10: 'node10', |
| 52 | + 'node16-cjs': 'node16 (from CJS)', |
| 53 | + 'node16-esm': 'node16 (from ESM)', |
| 54 | + bundler: 'bundler', |
| 55 | +} |
| 56 | + |
| 57 | +const moduleKinds = { |
| 58 | + 1: '(CJS)', |
| 59 | + 99: '(ESM)', |
| 60 | + '': '', |
| 61 | +} |
| 62 | + |
| 63 | +const __filename = fileURLToPath(import.meta.url) |
| 64 | +const __dirname = path.dirname(__filename) |
| 65 | + |
| 66 | +interface Checks { |
| 67 | + analysis: Analysis |
| 68 | + problemSummaries?: ProblemSummary[] |
| 69 | + problems?: Problem[] |
| 70 | +} |
| 71 | + |
| 72 | +const rtkPackagePath = path.join(__dirname, './package.tgz') |
| 73 | + |
| 74 | +const rtkPackageTgzBytes = fs.readFileSync(rtkPackagePath) |
| 75 | + |
| 76 | +function Header({ text, width }: { text: string; width: number | string }) { |
| 77 | + return ( |
| 78 | + <Box borderStyle="single" width={width}> |
| 79 | + <Text color="blue">{text}</Text> |
| 80 | + </Box> |
| 81 | + ) |
| 82 | +} |
| 83 | + |
| 84 | +function ChecksTable(props: { checks?: Checks }) { |
| 85 | + if (!props.checks || !props.checks.analysis.containsTypes) { |
| 86 | + return null |
| 87 | + } |
| 88 | + |
| 89 | + const { analysis, problems, problemSummaries } = props.checks |
| 90 | + const subpaths = Object.keys(analysis.entrypointResolutions).filter( |
| 91 | + (key) => !key.includes('package.json') |
| 92 | + ) |
| 93 | + const entrypoints = subpaths.map((s) => |
| 94 | + s === '.' |
| 95 | + ? analysis.packageName |
| 96 | + : `${analysis.packageName}/${s.substring(2)}` |
| 97 | + ) |
| 98 | + |
| 99 | + const numColumns = entrypoints.length + 1 |
| 100 | + |
| 101 | + const columnWidth = `${100 / numColumns}%` |
| 102 | + |
| 103 | + return ( |
| 104 | + <Box flexDirection="column" width="100%"> |
| 105 | + <Box> |
| 106 | + <Header key={'empty'} text={''} width={columnWidth} /> |
| 107 | + {entrypoints.map((text) => { |
| 108 | + return <Header key={text} text={text} width={columnWidth} /> |
| 109 | + })} |
| 110 | + </Box> |
| 111 | + {allResolutionKinds.map((resolutionKind) => { |
| 112 | + return ( |
| 113 | + <Box key={resolutionKind} width="100%"> |
| 114 | + <Box borderStyle="single" width={columnWidth}> |
| 115 | + <Text>{resolutionKinds[resolutionKind]}</Text> |
| 116 | + </Box> |
| 117 | + {subpaths.map((subpath) => { |
| 118 | + const problemsForCell = problems?.filter( |
| 119 | + (problem) => |
| 120 | + problem.entrypoint === subpath && |
| 121 | + problem.resolutionKind === resolutionKind |
| 122 | + ) |
| 123 | + const resolution = |
| 124 | + analysis.entrypointResolutions[subpath][resolutionKind] |
| 125 | + .resolution |
| 126 | + |
| 127 | + let content: React.ReactNode |
| 128 | + |
| 129 | + if (problemsForCell?.length) { |
| 130 | + content = ( |
| 131 | + <Box flexDirection="column"> |
| 132 | + {problemsForCell.map((problem) => { |
| 133 | + return ( |
| 134 | + <Box key={problem.kind}> |
| 135 | + <Text>{problemShortDescriptions[problem.kind]}</Text> |
| 136 | + </Box> |
| 137 | + ) |
| 138 | + })} |
| 139 | + </Box> |
| 140 | + ) |
| 141 | + } else if (resolution?.isJson) { |
| 142 | + content = <Text>✅ (JSON)</Text> |
| 143 | + } else { |
| 144 | + content = ( |
| 145 | + <Text> |
| 146 | + {'✅ ' + moduleKinds[resolution?.moduleKind || '']} |
| 147 | + </Text> |
| 148 | + ) |
| 149 | + } |
| 150 | + return ( |
| 151 | + <Box key={subpath} width={columnWidth} borderStyle="single"> |
| 152 | + {content} |
| 153 | + </Box> |
| 154 | + ) |
| 155 | + })} |
| 156 | + </Box> |
| 157 | + ) |
| 158 | + })} |
| 159 | + {problemSummaries?.map((summary) => { |
| 160 | + return ( |
| 161 | + <Box width="100%" key={summary.kind} flexDirection="column"> |
| 162 | + <Text color="red" bold> |
| 163 | + {summary.kind}: {summary.title} |
| 164 | + </Text> |
| 165 | + {summary.messages.map((message) => { |
| 166 | + return ( |
| 167 | + <Text key={message.messageText}>{message.messageText}</Text> |
| 168 | + ) |
| 169 | + })} |
| 170 | + </Box> |
| 171 | + ) |
| 172 | + })} |
| 173 | + </Box> |
| 174 | + ) |
| 175 | +} |
| 176 | + |
| 177 | +;(async function main() { |
| 178 | + const analysis = await checkTgz(rtkPackageTgzBytes) |
| 179 | + if ('entrypointResolutions' in analysis) { |
| 180 | + const problems = analysis.containsTypes ? getProblems(analysis) : undefined |
| 181 | + |
| 182 | + // console.log( |
| 183 | + // 'Analysis: ', |
| 184 | + // util.inspect(analysis.entrypointResolutions, { depth: 3 }) |
| 185 | + // ) |
| 186 | + if (problems) { |
| 187 | + // for (let problem of problems) { |
| 188 | + // console.log('Problem: ', problem) |
| 189 | + // } |
| 190 | + const problemSummaries = analysis.containsTypes |
| 191 | + ? summarizeProblems(problems, analysis) |
| 192 | + : undefined |
| 193 | + // if (problemSummaries) { |
| 194 | + // for (let summary of problemSummaries) { |
| 195 | + // console.log('Summary: ', summary) |
| 196 | + // } |
| 197 | + // } |
| 198 | + const checks: Checks = { |
| 199 | + analysis, |
| 200 | + problems, |
| 201 | + problemSummaries, |
| 202 | + } |
| 203 | + |
| 204 | + render(<ChecksTable checks={checks} />) |
| 205 | + } |
| 206 | + } |
| 207 | +})() |
0 commit comments