@@ -8,11 +8,82 @@ import { blue, dim } from 'ansis'
8
8
import Debug from 'debug'
9
9
import { fsRemove } from '../utils/fs'
10
10
import { logger } from '../utils/logger'
11
- import type { ResolvedOptions } from '../options'
11
+ import type { AttwOptions , ResolvedOptions } from '../options'
12
+ import type { Problem } from '@arethetypeswrong/core'
12
13
13
14
const debug = Debug ( 'tsdown:attw' )
14
15
const exec = promisify ( child_process . exec )
15
16
17
+ /**
18
+ * ATTW profiles.
19
+ * Defines the resolution modes to ignore for each profile.
20
+ *
21
+ * @see https://github.com/arethetypeswrong/arethetypeswrong.github.io/blob/main/packages/cli/README.md#profiles
22
+ */
23
+ const profiles : Record < Required < AttwOptions > [ 'profile' ] , string [ ] > = {
24
+ strict : [ ] ,
25
+ node16 : [ 'node10' ] ,
26
+ esmOnly : [ 'node10' , 'node16-cjs' ] ,
27
+ }
28
+
29
+ /**
30
+ * Format an ATTW problem for display
31
+ */
32
+ function formatProblem ( problem : Problem ) : string {
33
+ const resolutionKind =
34
+ 'resolutionKind' in problem ? ` (${ problem . resolutionKind } )` : ''
35
+ const entrypoint = 'entrypoint' in problem ? ` at ${ problem . entrypoint } ` : ''
36
+
37
+ switch ( problem . kind ) {
38
+ case 'NoResolution' :
39
+ return ` ❌ No resolution${ resolutionKind } ${ entrypoint } `
40
+
41
+ case 'UntypedResolution' :
42
+ return ` ⚠️ Untyped resolution${ resolutionKind } ${ entrypoint } `
43
+
44
+ case 'FalseESM' :
45
+ return ` 🔄 False ESM: Types indicate ESM (${ problem . typesModuleKind } ) but implementation is CJS (${ problem . implementationModuleKind } )\n Types: ${ problem . typesFileName } | Implementation: ${ problem . implementationFileName } `
46
+
47
+ case 'FalseCJS' :
48
+ return ` 🔄 False CJS: Types indicate CJS (${ problem . typesModuleKind } ) but implementation is ESM (${ problem . implementationModuleKind } )\n Types: ${ problem . typesFileName } | Implementation: ${ problem . implementationFileName } `
49
+
50
+ case 'CJSResolvesToESM' :
51
+ return ` ⚡ CJS resolves to ESM${ resolutionKind } ${ entrypoint } `
52
+
53
+ case 'NamedExports' : {
54
+ const missingExports =
55
+ problem . missing ?. length > 0
56
+ ? ` Missing: ${ problem . missing . join ( ', ' ) } `
57
+ : ''
58
+ const allMissing = problem . isMissingAllNamed
59
+ ? ' (all named exports missing)'
60
+ : ''
61
+ return ` 📤 Named exports problem${ allMissing } ${ missingExports } \n Types: ${ problem . typesFileName } | Implementation: ${ problem . implementationFileName } `
62
+ }
63
+
64
+ case 'FallbackCondition' :
65
+ return ` 🎯 Fallback condition used${ resolutionKind } ${ entrypoint } `
66
+
67
+ case 'FalseExportDefault' :
68
+ return ` 🎭 False export default\n Types: ${ problem . typesFileName } | Implementation: ${ problem . implementationFileName } `
69
+
70
+ case 'MissingExportEquals' :
71
+ return ` 📝 Missing export equals\n Types: ${ problem . typesFileName } | Implementation: ${ problem . implementationFileName } `
72
+
73
+ case 'InternalResolutionError' :
74
+ return ` 💥 Internal resolution error in ${ problem . fileName } (${ problem . resolutionOption } )\n Module: ${ problem . moduleSpecifier } | Mode: ${ problem . resolutionMode } `
75
+
76
+ case 'UnexpectedModuleSyntax' :
77
+ return ` 📋 Unexpected module syntax in ${ problem . fileName } \n Expected: ${ problem . moduleKind } | Found: ${ problem . syntax === 99 ? 'ESM' : 'CJS' } `
78
+
79
+ case 'CJSOnlyExportsDefault' :
80
+ return ` 🏷️ CJS only exports default in ${ problem . fileName } `
81
+
82
+ default :
83
+ return ` ❓ Unknown problem: ${ JSON . stringify ( problem ) } `
84
+ }
85
+ }
86
+
16
87
export async function attw ( options : ResolvedOptions ) : Promise < void > {
17
88
if ( ! options . attw ) return
18
89
if ( ! options . pkg ) {
@@ -38,7 +109,7 @@ export async function attw(options: ResolvedOptions): Promise<void> {
38
109
try {
39
110
const { stdout : tarballInfo } = await exec (
40
111
`npm pack --json ----pack-destination ${ tempDir } ` ,
41
- { encoding : 'utf-8' } ,
112
+ { encoding : 'utf-8' , cwd : options . cwd } ,
42
113
)
43
114
const parsed = JSON . parse ( tarballInfo )
44
115
if ( ! Array . isArray ( parsed ) || ! parsed [ 0 ] ?. filename ) {
@@ -48,14 +119,29 @@ export async function attw(options: ResolvedOptions): Promise<void> {
48
119
const tarball = await readFile ( tarballPath )
49
120
50
121
const pkg = attwCore . createPackageFromTarballData ( tarball )
51
- const checkResult = await attwCore . checkPackage (
52
- pkg ,
53
- options . attw === true ? { } : options . attw ,
54
- )
122
+ const attwOptions = options . attw === true ? { } : options . attw
123
+ const checkResult = await attwCore . checkPackage ( pkg , attwOptions )
124
+ const profile = attwOptions . profile ?? 'strict'
125
+ const level = attwOptions . level ?? 'warn'
55
126
56
127
if ( checkResult . types !== false && checkResult . problems ) {
57
- for ( const problem of checkResult . problems ) {
58
- logger . warn ( 'Are the types wrong problem:' , problem )
128
+ const problems = checkResult . problems . filter ( ( problem ) => {
129
+ // Only apply profile filter to problems that have resolutionKind
130
+ if ( 'resolutionKind' in problem ) {
131
+ return ! profiles [ profile ] ?. includes ( problem . resolutionKind )
132
+ }
133
+ // Include all other problem types
134
+ return true
135
+ } )
136
+ if ( problems . length ) {
137
+ const problemList = problems . map ( formatProblem ) . join ( '\n' )
138
+ const problemMessage = `Are the types wrong problems found:\n${ problemList } `
139
+
140
+ if ( level === 'error' ) {
141
+ throw new Error ( problemMessage )
142
+ }
143
+
144
+ logger . warn ( problemMessage )
59
145
}
60
146
} else {
61
147
logger . success (
0 commit comments