@@ -9,7 +9,18 @@ import { platform } from 'os';
9
9
import type { Input } from '../types' ;
10
10
11
11
type Options = Input & {
12
- options ?: { project ?: string ; tsc ?: string } ;
12
+ options ?: {
13
+ esm ?: boolean ;
14
+ project ?: string ;
15
+ tsc ?: string ;
16
+ } ;
17
+ } ;
18
+
19
+ type Field = {
20
+ name : string ;
21
+ value : string | undefined ;
22
+ output : string | undefined ;
23
+ error : boolean ;
13
24
} ;
14
25
15
26
export default async function build ( {
@@ -156,6 +167,13 @@ export default async function build({
156
167
// Ignore
157
168
}
158
169
170
+ const outputs = options ?. esm
171
+ ? {
172
+ commonjs : path . join ( output , 'commonjs' ) ,
173
+ module : path . join ( output , 'module' ) ,
174
+ }
175
+ : { commonjs : output } ;
176
+
159
177
const result = spawn . sync (
160
178
tsc ,
161
179
[
@@ -168,7 +186,7 @@ export default async function build({
168
186
'--project' ,
169
187
project ,
170
188
'--outDir' ,
171
- output ,
189
+ outputs . commonjs ,
172
190
] ,
173
191
{
174
192
stdio : 'inherit' ,
@@ -179,6 +197,18 @@ export default async function build({
179
197
if ( result . status === 0 ) {
180
198
await del ( [ tsbuildinfo ] ) ;
181
199
200
+ if ( outputs ?. module ) {
201
+ // When ESM compatible output is enabled, we need to generate 2 builds for commonjs and esm
202
+ // In this case we copy the already generated types, and add `package.json` with `type` field
203
+ await fs . copy ( outputs . commonjs , outputs . module ) ;
204
+ await fs . writeJSON ( path . join ( outputs . commonjs , 'package.json' ) , {
205
+ type : 'commonjs' ,
206
+ } ) ;
207
+ await fs . writeJSON ( path . join ( outputs . module , 'package.json' ) , {
208
+ type : 'module' ,
209
+ } ) ;
210
+ }
211
+
182
212
report . success (
183
213
`Wrote definition files to ${ kleur . blue ( path . relative ( root , output ) ) } `
184
214
) ;
@@ -187,16 +217,52 @@ export default async function build({
187
217
await fs . readFile ( path . join ( root , 'package.json' ) , 'utf-8' )
188
218
) ;
189
219
190
- const getGeneratedTypesPath = async ( ) => {
220
+ const fields : Field [ ] = [
221
+ ...( pkg . exports ?. [ '.' ] ?. types
222
+ ? [
223
+ {
224
+ name : "exports['.'].types" ,
225
+ value : pkg . exports ?. [ '.' ] ?. types ,
226
+ output : outputs . commonjs ,
227
+ error : options ?. esm === true ,
228
+ } ,
229
+ ]
230
+ : [
231
+ {
232
+ name : 'types' ,
233
+ value : pkg . types ,
234
+ output : outputs . commonjs ,
235
+ error : options ?. esm === true ,
236
+ } ,
237
+ ] ) ,
238
+ {
239
+ name : "exports['.'].import.types" ,
240
+ value : pkg . exports ?. [ '.' ] ?. import ?. types ,
241
+ output : outputs . module ,
242
+ error : ! options ?. esm ,
243
+ } ,
244
+ {
245
+ name : "exports['.'].require.types" ,
246
+ value : pkg . exports ?. [ '.' ] ?. require ?. types ,
247
+ output : outputs . commonjs ,
248
+ error : ! options ?. esm ,
249
+ } ,
250
+ ] ;
251
+
252
+ const getGeneratedTypesPath = async ( field : Field ) => {
253
+ if ( ! field . output || field . error ) {
254
+ return null ;
255
+ }
256
+
191
257
if ( pkg . source ) {
192
258
const indexDTsName =
193
259
path . basename ( pkg . source ) . replace ( / \. ( j s x ? | t s x ? ) $ / , '' ) + '.d.ts' ;
194
260
195
261
const potentialPaths = [
196
- path . join ( output , path . dirname ( pkg . source ) , indexDTsName ) ,
262
+ path . join ( field . output , path . dirname ( pkg . source ) , indexDTsName ) ,
197
263
path . join (
198
- output ,
199
- path . dirname ( path . relative ( source , path . join ( root , pkg . source ) ) ) ,
264
+ field . output ,
265
+ path . relative ( source , path . join ( root , path . dirname ( pkg . source ) ) ) ,
200
266
indexDTsName
201
267
) ,
202
268
] ;
@@ -211,37 +277,63 @@ export default async function build({
211
277
return null ;
212
278
} ;
213
279
214
- const fields = [
215
- { name : 'types' , value : pkg . types } ,
216
- { name : "exports['.'].types" , value : pkg . exports ?. [ '.' ] ?. types } ,
217
- ] ;
218
-
219
- if ( fields . some ( ( field ) => field . value ) ) {
280
+ const invalidFieldNames = (
220
281
await Promise . all (
221
- fields . map ( async ( { name, value } ) => {
222
- if ( ! value ) {
223
- return ;
282
+ fields . map ( async ( field ) => {
283
+ if ( field . error ) {
284
+ if ( field . value ) {
285
+ report . error (
286
+ `The ${ kleur . blue ( field . name ) } field in ${ kleur . blue (
287
+ `package.json`
288
+ ) } should not be set when the ${ kleur . blue (
289
+ 'esm'
290
+ ) } option is ${ options ?. esm ? 'enabled' : 'disabled' } .`
291
+ ) ;
292
+
293
+ return field . name ;
294
+ }
295
+
296
+ return null ;
224
297
}
225
298
226
- const typesPath = path . join ( root , value ) ;
299
+ if (
300
+ field . name . startsWith ( 'exports' ) &&
301
+ field . value &&
302
+ ! / ^ \. \/ / . test ( field . value )
303
+ ) {
304
+ report . warn (
305
+ `The ${ kleur . blue ( field . name ) } field in ${ kleur . blue (
306
+ `package.json`
307
+ ) } should be a relative path starting with ${ kleur . blue (
308
+ './'
309
+ ) } . Found: ${ kleur . blue ( field . value ) } `
310
+ ) ;
227
311
228
- if ( ! ( await fs . pathExists ( typesPath ) ) ) {
229
- const generatedTypesPath = await getGeneratedTypesPath ( ) ;
312
+ return field . name ;
313
+ }
230
314
231
- if ( ! generatedTypesPath ) {
232
- report . warn (
233
- `Failed to detect the entry point for the generated types. Make sure you have a valid ${ kleur . blue (
234
- 'source'
235
- ) } field in your ${ kleur . blue ( 'package.json' ) } .`
236
- ) ;
237
- }
315
+ const generatedTypesPath = await getGeneratedTypesPath ( field ) ;
316
+ const isValid =
317
+ field . value && generatedTypesPath
318
+ ? path . join ( root , field . value ) ===
319
+ path . join ( root , generatedTypesPath )
320
+ : false ;
321
+
322
+ if ( ! isValid ) {
323
+ const type =
324
+ field . value &&
325
+ ( await fs . pathExists ( path . join ( root , field . value ) ) )
326
+ ? 'invalid'
327
+ : 'non-existent' ;
238
328
239
329
report . error (
240
- `The ${ kleur . blue ( name ) } field in ${ kleur . blue (
241
- 'package.json'
242
- ) } points to a non-existent file: ${ kleur . blue (
243
- value
244
- ) } .\nVerify the path points to the correct file under ${ kleur . blue (
330
+ `The ${ kleur . blue ( field . name ) } field ${
331
+ field . value
332
+ ? `in ${ kleur . blue (
333
+ 'package.json'
334
+ ) } points to a ${ type } file: ${ kleur . blue ( field . value ) } `
335
+ : `is missing in ${ kleur . blue ( 'package.json' ) } `
336
+ } .\nVerify the path points to the correct file under ${ kleur . blue (
245
337
path . relative ( root , output )
246
338
) } ${
247
339
generatedTypesPath
@@ -250,21 +342,17 @@ export default async function build({
250
342
} `
251
343
) ;
252
344
253
- throw new Error ( `Found incorrect path in ' ${ name } ' field.` ) ;
345
+ return field . name ;
254
346
}
347
+
348
+ return null ;
255
349
} )
256
- ) ;
257
- } else {
258
- const generatedTypesPath = await getGeneratedTypesPath ( ) ;
350
+ )
351
+ ) . filter ( ( name ) : name is string => name != null ) ;
259
352
260
- report . warn (
261
- `No ${ kleur . blue (
262
- fields . map ( ( field ) => field . name ) . join ( ' or ' )
263
- ) } field found in ${ kleur . blue ( 'package.json' ) } .\nConsider ${
264
- generatedTypesPath
265
- ? `pointing it to ${ kleur . blue ( generatedTypesPath ) } `
266
- : 'adding it'
267
- } so that consumers of your package can use the types.`
353
+ if ( invalidFieldNames . length ) {
354
+ throw new Error (
355
+ `Found errors for fields: ${ invalidFieldNames . join ( ', ' ) } .`
268
356
) ;
269
357
}
270
358
} else {
0 commit comments