Skip to content

Commit

Permalink
Merge pull request #3294 from reduxjs/feature/ci-types-wrong
Browse files Browse the repository at this point in the history
  • Loading branch information
markerikson authored Mar 25, 2023
2 parents 369885e + 5962fb8 commit 007bcc3
Show file tree
Hide file tree
Showing 7 changed files with 2,281 additions and 2 deletions.
13 changes: 11 additions & 2 deletions .github/workflows/tests.yml
Original file line number Diff line number Diff line change
Expand Up @@ -149,7 +149,16 @@ jobs:
fail-fast: false
matrix:
node: ['16.x']
example: ['cra4', 'cra5', 'next', 'vite', 'node-standard', 'node-esm']
example:
[
'cra4',
'cra5',
'next',
'vite',
'node-standard',
'node-esm',
'are-the-types-wrong',
]
defaults:
run:
working-directory: ./examples/publish-ci/${{ matrix.example }}
Expand Down Expand Up @@ -186,5 +195,5 @@ jobs:
- name: Build example
run: yarn build

- name: Run Playwright test
- name: Run test step
run: yarn test
35 changes: 35 additions & 0 deletions examples/publish-ci/are-the-types-wrong/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
# See https://help.github.com/articles/ignoring-files/ for more about ignoring files.

# dependencies
/node_modules
/.pnp
.pnp.js

# testing
/coverage

# production
/build

# misc
.DS_Store
.env.local
.env.development.local
.env.test.local
.env.production.local

npm-debug.log*
yarn-debug.log*
yarn-error.log*

typesversions
.cache
.yarnrc
.yarn/*
!.yarn/patches
!.yarn/releases
!.yarn/plugins
!.yarn/sdks
!.yarn/versions
.pnp.*
*.tgz
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
diff --git a/package.json b/package.json
index 60791b6ccd3575279eddef2ac795802bd5044abd..41be518d2f21a659ca71095850cb18264a814f8d 100644
--- a/package.json
+++ b/package.json
@@ -25,6 +25,7 @@
"prepublishOnly": "npm run tsc && npm run test"
},
"type": "module",
+ "types": "./dist/index.d.ts",
"exports": {
".": {
"development": "./src/index.ts",
268 changes: 268 additions & 0 deletions examples/publish-ci/are-the-types-wrong/main.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,268 @@
import path from 'path'
import fs from 'fs'

import { fileURLToPath } from 'node:url'
import type {
Analysis,
ProblemSummary,
Problem,
ResolutionKind,
ProblemKind,
} from '@arethetypeswrong/core'
import {
checkTgz,
summarizeProblems,
getProblems,
} from '@arethetypeswrong/core'
import React from 'react'
import { render, Text, Box, Static } from 'ink'

const allResolutionKinds: ResolutionKind[] = [
'node10',
'node16-cjs',
'node16-esm',
'bundler',
]

const problemEmoji: Record<ProblemKind, string> = {
Wildcard: '❓',
NoResolution: '💀',
UntypedResolution: '❌',
FalseCJS: '🎭',
FalseESM: '👺',
CJSResolvesToESM: '⚠️',
FallbackCondition: '🐛',
CJSOnlyExportsDefault: '🤨',
FalseExportDefault: '❗️',
UnexpectedESMSyntax: '🚭',
UnexpectedCJSSyntax: '🚱',
}

const problemShortDescriptions: Record<ProblemKind, string> = {
Wildcard: `${problemEmoji.Wildcard} Unable to check`,
NoResolution: `${problemEmoji.NoResolution} Failed to resolve`,
UntypedResolution: `${problemEmoji.UntypedResolution} No types`,
FalseCJS: `${problemEmoji.FalseCJS} Masquerading as CJS`,
FalseESM: `${problemEmoji.FalseESM} Masquerading as ESM`,
CJSResolvesToESM: `${problemEmoji.CJSResolvesToESM} ESM (dynamic import only)`,
FallbackCondition: `${problemEmoji.FallbackCondition} Used fallback condition`,
CJSOnlyExportsDefault: `${problemEmoji.CJSOnlyExportsDefault} CJS default export`,
FalseExportDefault: `${problemEmoji.FalseExportDefault} Incorrect default export`,
UnexpectedESMSyntax: `${problemEmoji.UnexpectedESMSyntax} Unexpected ESM syntax`,
UnexpectedCJSSyntax: `${problemEmoji.UnexpectedCJSSyntax} Unexpected CJS syntax`,
}

const resolutionKinds: Record<ResolutionKind, string> = {
node10: 'node10',
'node16-cjs': 'node16 (from CJS)',
'node16-esm': 'node16 (from ESM)',
bundler: 'bundler',
}

const moduleKinds = {
1: '(CJS)',
99: '(ESM)',
'': '',
}

const __filename = fileURLToPath(import.meta.url)
const __dirname = path.dirname(__filename)

interface Checks {
analysis: Analysis
problemSummaries?: ProblemSummary[]
problems?: Problem[]
}

const rtkPackagePath = path.join(__dirname, './package.tgz')

const rtkPackageTgzBytes = fs.readFileSync(rtkPackagePath)

function Header({ text, width }: { text: string; width: number | string }) {
return (
<Box borderStyle="single" width={width}>
<Text color="blue">{text}</Text>
</Box>
)
}

function Traces({
analysis,
subpaths,
}: {
analysis: Analysis
subpaths: string[]
}) {
if (!('entrypointResolutions' in analysis)) {
return null
}

return (
<Box flexDirection="column" width="100%">
{subpaths.map((subpath) => {
const resolutionDetails = Object.entries(
analysis.entrypointResolutions[subpath]
)
return (
<Box width="100%" key={'traces-' + subpath} flexDirection="column">
<Text color="blue" bold>
Traces for Subpath: {subpath}
</Text>
{resolutionDetails.map(([resolutionKind, details]) => {
return (
<Box
width="100%"
key={`resolutionDetails-${resolutionKind}-${subpath}`}
flexDirection="column"
>
<Text bold>{resolutionKind} Traces:</Text>
<Box flexDirection="column">
{details.trace.map((traceLine, i) => {
return (
<Text
key={`resolutionDetails-traces-${subpath}-${resolutionKind}-${i}`}
>
{traceLine}
</Text>
)
})}
</Box>
</Box>
)
})}
</Box>
)
})}
</Box>
)
}

function ChecksTable(props: { checks?: Checks }) {
if (!props.checks || !props.checks.analysis.containsTypes) {
return null
}

const { analysis, problems, problemSummaries } = props.checks
const subpaths = Object.keys(analysis.entrypointResolutions).filter(
(key) => !key.includes('package.json')
)
const entrypoints = subpaths.map((s) =>
s === '.'
? analysis.packageName
: `${analysis.packageName}/${s.substring(2)}`
)

const numColumns = entrypoints.length + 1

const columnWidth = `${100 / numColumns}%`

return (
<Box flexDirection="column" width="100%">
<Box>
<Header key={'empty'} text={''} width={columnWidth} />
{entrypoints.map((text) => {
return <Header key={text} text={text} width={columnWidth} />
})}
</Box>
{allResolutionKinds.map((resolutionKind) => {
return (
<Box key={resolutionKind} width="100%">
<Box borderStyle="single" width={columnWidth}>
<Text>{resolutionKinds[resolutionKind]}</Text>
</Box>
{subpaths.map((subpath) => {
const problemsForCell = problems?.filter(
(problem) =>
problem.entrypoint === subpath &&
problem.resolutionKind === resolutionKind
)
const resolution =
analysis.entrypointResolutions[subpath][resolutionKind]
.resolution

let content: React.ReactNode

if (problemsForCell?.length) {
content = (
<Box flexDirection="column">
{problemsForCell.map((problem) => {
return (
<Box key={problem.kind}>
<Text>{problemShortDescriptions[problem.kind]}</Text>
</Box>
)
})}
</Box>
)
} else if (resolution?.isJson) {
content = <Text>✅ (JSON)</Text>
} else {
content = (
<Text>
{'✅ ' +
moduleKinds[resolution?.moduleKind?.detectedKind || '']}
</Text>
)
}
return (
<Box key={subpath} width={columnWidth} borderStyle="single">
{content}
</Box>
)
})}
</Box>
)
})}
{problemSummaries?.map((summary) => {
return (
<Box width="100%" key={summary.kind} flexDirection="column">
<Text color="red" bold>
{summary.kind}: {summary.title}
</Text>
{summary.messages.map((message) => {
return (
<Text key={message.messageText}>{message.messageText}</Text>
)
})}
</Box>
)
})}
<Traces analysis={analysis} subpaths={subpaths} />
</Box>
)
}

;(async function main() {
const analysis = await checkTgz(rtkPackageTgzBytes)

const checks: Checks = {
analysis,
problems: undefined,
problemSummaries: undefined,
}
if ('entrypointResolutions' in analysis) {
const problems = analysis.containsTypes ? getProblems(analysis) : undefined
checks.problems = problems

if (problems) {
const problemSummaries = analysis.containsTypes
? summarizeProblems(problems, analysis)
: undefined
checks.problemSummaries = problemSummaries
}
}

// Ink will duplicate all of its output if it is longer than the terminal height.
// Known bug with the underlying rendering: https://github.com/vadimdemedes/ink/issues/48
// Solution is to mark the entire content as "static" so it won't get updated, but flushed.
render(
<Static items={[checks]}>
{(checks: Checks, index: number) => {
return <ChecksTable key={`checks-${index}`} checks={checks} />
}}
</Static>
)

const exitCode = checks.problems?.length ?? 0
process.exit(exitCode)
})()
25 changes: 25 additions & 0 deletions examples/publish-ci/are-the-types-wrong/package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
{
"name": "are-the-types-wrong",
"packageManager": "[email protected]",
"type": "module",
"scripts": {
"build": "echo Nothing to build",
"test": "yarn tsx main.tsx"
},
"dependencies": {
"@reduxjs/toolkit": "^1.9.3",
"@tanstack/react-table": "^8.7.9",
"ink": "^4.0.0",
"object-hash": "^3.0.0",
"react": "^18.2.0"
},
"devDependencies": {
"@arethetypeswrong/core": "^0.0.4",
"@types/react": "^18.0.28",
"shelljs": "^0.8.5",
"tsx": "^3.12.5"
},
"resolutions": {
"@arethetypeswrong/core@^0.0.4": "patch:@arethetypeswrong/core@npm%3A0.0.4#./.yarn/patches/@arethetypeswrong-core-npm-0.0.4-edb717a66b.patch"
}
}
10 changes: 10 additions & 0 deletions examples/publish-ci/are-the-types-wrong/tsconfig.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
{
"compilerOptions": {
"target": "ESNext",
"module": "esnext",
"moduleResolution": "node",
"esModuleInterop": true,

"jsx": "react",
}
}
Loading

0 comments on commit 007bcc3

Please sign in to comment.