@@ -223,53 +223,69 @@ async function build({
223223 return ;
224224 }
225225
226- validatePackageJson ( pkg , config ?. commonjs ?? true ) ;
227-
228- // remove <project>/dist
229- await fse . remove ( distPath ) ;
230-
231- // Copy type definitions
232- await fse . ensureDir ( join ( distPath , "typings" ) ) ;
233-
234226 const declarations = await globby ( "**/*.d.ts" , {
235227 cwd : getBuildPath ( "esm" ) ,
236228 absolute : false ,
237229 ignore : filesToExcludeFromDist ,
238230 } ) ;
239231
240- await Promise . all (
241- declarations . map ( ( filePath ) =>
242- limit ( ( ) =>
243- fse . copy (
244- join ( getBuildPath ( "esm" ) , filePath ) ,
245- join ( distPath , "typings" , filePath )
246- )
247- )
248- )
249- ) ;
250-
251- // Move ESM to dist/esm
252- await fse . ensureDir ( join ( distPath , "esm" ) ) ;
253-
254232 const esmFiles = await globby ( "**/*.js" , {
255233 cwd : getBuildPath ( "esm" ) ,
256234 absolute : false ,
257235 ignore : filesToExcludeFromDist ,
258236 } ) ;
259237
238+ // Check whether al esm files are empty, if not - probably a types only build
239+ let emptyEsmFiles = true ;
240+ for ( const file of esmFiles ) {
241+ const src = await fse . readFile ( join ( getBuildPath ( "esm" ) , file ) ) ;
242+ if ( src . toString ( ) . trim ( ) !== "export {};" ) {
243+ emptyEsmFiles = false ;
244+ break ;
245+ }
246+ }
247+
248+ // Empty ESM files with existing declarations is a types-only package
249+ const typesOnly = emptyEsmFiles && declarations . length > 0 ;
250+
251+ validatePackageJson ( pkg , {
252+ typesOnly,
253+ includesCommonJS : config ?. commonjs ?? true ,
254+ } ) ;
255+
256+ // remove <project>/dist
257+ await fse . remove ( distPath ) ;
258+
259+ // Copy type definitions
260+ await fse . ensureDir ( join ( distPath , "typings" ) ) ;
260261 await Promise . all (
261- esmFiles . map ( ( filePath ) =>
262+ declarations . map ( ( filePath ) =>
262263 limit ( ( ) =>
263264 fse . copy (
264265 join ( getBuildPath ( "esm" ) , filePath ) ,
265- join ( distPath , "esm " , filePath )
266+ join ( distPath , "typings " , filePath )
266267 )
267268 )
268269 )
269270 ) ;
270271
271- if ( config ?. commonjs === undefined ) {
272- // Transpile ESM to CJS and move CJS to dist/cjs
272+ // If ESM files are not empty, copy them to dist/esm
273+ if ( ! emptyEsmFiles ) {
274+ await fse . ensureDir ( join ( distPath , "esm" ) ) ;
275+ await Promise . all (
276+ esmFiles . map ( ( filePath ) =>
277+ limit ( ( ) =>
278+ fse . copy (
279+ join ( getBuildPath ( "esm" ) , filePath ) ,
280+ join ( distPath , "esm" , filePath )
281+ )
282+ )
283+ )
284+ ) ;
285+ }
286+
287+ if ( ! emptyEsmFiles && config ?. commonjs === undefined ) {
288+ // Transpile ESM to CJS and move CJS to dist/cjs only if there's something to transpile
273289 await fse . ensureDir ( join ( distPath , "cjs" ) ) ;
274290
275291 const cjsFiles = await globby ( "**/*.js" , {
@@ -323,8 +339,9 @@ async function build({
323339 // move the package.json to dist
324340 await fse . writeFile (
325341 join ( distPath , "package.json" ) ,
326- JSON . stringify ( rewritePackageJson ( pkg ) , null , 2 )
342+ JSON . stringify ( rewritePackageJson ( pkg , typesOnly ) , null , 2 )
327343 ) ;
344+
328345 // move README.md and LICENSE and other specified files
329346 await copyToDist (
330347 cwd ,
@@ -350,7 +367,7 @@ async function build({
350367 reporter . success ( `Built ${ pkg . name } ` ) ;
351368}
352369
353- function rewritePackageJson ( pkg : Record < string , any > ) {
370+ function rewritePackageJson ( pkg : Record < string , any > , typesOnly : boolean ) {
354371 const newPkg : Record < string , any > = { } ;
355372 const fields = [
356373 "name" ,
@@ -382,19 +399,26 @@ function rewritePackageJson(pkg: Record<string, any>) {
382399
383400 const distDirStr = `${ DIST_DIR } /` ;
384401
385- newPkg . main = newPkg . main . replace ( distDirStr , "" ) ;
386- newPkg . module = newPkg . module . replace ( distDirStr , "" ) ;
402+ if ( typesOnly ) {
403+ newPkg . main = "" ;
404+ delete newPkg . module ;
405+ delete newPkg . type ;
406+ } else {
407+ newPkg . main = newPkg . main . replace ( distDirStr , "" ) ;
408+ newPkg . module = newPkg . module . replace ( distDirStr , "" ) ;
409+ }
387410 newPkg . typings = newPkg . typings . replace ( distDirStr , "" ) ;
388411 newPkg . typescript = {
389412 definition : newPkg . typescript . definition . replace ( distDirStr , "" ) ,
390413 } ;
391414
392- if ( ! pkg . exports ) {
393- newPkg . exports = presetFields . exports ;
415+ if ( ! typesOnly ) {
416+ if ( ! pkg . exports ) {
417+ newPkg . exports = presetFields . exports ;
418+ }
419+ newPkg . exports = rewriteExports ( pkg . exports , DIST_DIR ) ;
394420 }
395421
396- newPkg . exports = rewriteExports ( pkg . exports , DIST_DIR ) ;
397-
398422 if ( pkg . bin ) {
399423 newPkg . bin = { } ;
400424
@@ -406,7 +430,13 @@ function rewritePackageJson(pkg: Record<string, any>) {
406430 return newPkg ;
407431}
408432
409- export function validatePackageJson ( pkg : any , includesCommonJS : boolean ) {
433+ export function validatePackageJson (
434+ pkg : any ,
435+ opts : {
436+ typesOnly : boolean ;
437+ includesCommonJS : boolean ;
438+ }
439+ ) {
410440 function expect ( key : string , expected : unknown ) {
411441 const received = get ( pkg , key ) ;
412442
@@ -418,13 +448,23 @@ export function validatePackageJson(pkg: any, includesCommonJS: boolean) {
418448 ) ;
419449 }
420450
451+ // Type only packages have simpler rules (following the style of https://github.com/DefinitelyTyped/DefinitelyTyped packages)
452+ if ( opts . typesOnly ) {
453+ expect ( "main" , "" ) ;
454+ expect ( "module" , undefined ) ;
455+ expect ( "typings" , presetFields . typings ) ;
456+ expect ( "typescript.definition" , presetFields . typescript . definition ) ;
457+ expect ( "exports" , undefined ) ;
458+ return ;
459+ }
460+
421461 // If the package has NO binary we need to check the exports map.
422462 // a package should either
423463 // 1. have a bin property
424464 // 2. have a exports property
425465 // 3. have an exports and bin property
426466 if ( Object . keys ( pkg . bin ?? { } ) . length > 0 ) {
427- if ( includesCommonJS === true ) {
467+ if ( opts . includesCommonJS === true ) {
428468 expect ( "main" , presetFields . main ) ;
429469 expect ( "module" , presetFields . module ) ;
430470 expect ( "typings" , presetFields . typings ) ;
@@ -442,7 +482,7 @@ export function validatePackageJson(pkg: any, includesCommonJS: boolean) {
442482 pkg . typings !== undefined ||
443483 pkg . typescript !== undefined
444484 ) {
445- if ( includesCommonJS === true ) {
485+ if ( opts . includesCommonJS === true ) {
446486 // if there is no bin property, we NEED to check the exports.
447487 expect ( "main" , presetFields . main ) ;
448488 expect ( "module" , presetFields . module ) ;
0 commit comments