55/** @typedef {import("./index.js").MinimizedResult } MinimizedResult */
66/** @typedef {import("./index.js").CustomOptions } CustomOptions */
77/** @typedef {import("./index.js").RawSourceMap } RawSourceMap */
8+ /** @typedef {import("@swc/core").JsMinifyOptions & { extractComments?: false | true | "some" | "all" | { regex: string } } } SwcMinifyOptionsWithExtractComments */
9+ /** @typedef {import("@swc/core").Output & { extractedComments?: string[] } } SwcMinifyOutput */
810
911/**
1012 * @template T
@@ -77,10 +79,9 @@ async function terserMinify(
7779 minimizerOptions ,
7880 extractComments ,
7981) {
80- // eslint-disable-next-line jsdoc/no-restricted-syntax
8182 /**
8283 * @param {unknown } value value
83- * @returns {value is object } true when value is object or function
84+ * @returns {boolean } true when value is object or function
8485 */
8586 const isObject = ( value ) => {
8687 const type = typeof value ;
@@ -333,10 +334,9 @@ async function uglifyJsMinify(
333334 minimizerOptions ,
334335 extractComments ,
335336) {
336- // eslint-disable-next-line jsdoc/no-restricted-syntax
337337 /**
338338 * @param {unknown } value value
339- * @returns {value is object } true when value is object or function
339+ * @returns {boolean } true when value is object or function
340340 */
341341 const isObject = ( value ) => {
342342 const type = typeof value ;
@@ -555,12 +555,145 @@ uglifyJsMinify.supportsWorkerThreads = () => true;
555555 * @param {Input } input input
556556 * @param {RawSourceMap= } sourceMap source map
557557 * @param {CustomOptions= } minimizerOptions options
558+ * @param {ExtractCommentsOptions= } extractComments extract comments option
558559 * @returns {Promise<MinimizedResult> } minimized result
559560 */
560- async function swcMinify ( input , sourceMap , minimizerOptions ) {
561+ async function swcMinify ( input , sourceMap , minimizerOptions , extractComments ) {
562+ /**
563+ * @param {unknown } value value
564+ * @returns {boolean } true when value is object or function
565+ */
566+ const isObject = ( value ) => {
567+ const type = typeof value ;
568+
569+ // eslint-disable-next-line no-eq-null, eqeqeq
570+ return value != null && ( type === "object" || type === "function" ) ;
571+ } ;
572+
573+ /**
574+ * @param {unknown } extractCommentsOptions extract comments option
575+ * @returns {Error } error for unsupported extract comments option
576+ */
577+ const createExtractCommentsError = ( extractCommentsOptions ) =>
578+ new Error (
579+ `The 'extractComments' option for 'swcMinify' only supports booleans, "some", "all", string patterns, RegExp values without flags, or object conditions that resolve to those forms. Received: ${ extractCommentsOptions instanceof RegExp ? extractCommentsOptions . toString ( ) : typeof extractCommentsOptions } .` ,
580+ ) ;
581+
582+ /**
583+ * @param {unknown } extractCommentsOptions extract comments option
584+ * @returns {{ extractComments: false | true | "some" | "all" | { regex: string }, useDefaultPreserveComments: boolean } } normalized swc extract comments options
585+ */
586+ const normalizeExtractComments = ( extractCommentsOptions ) => {
587+ if ( typeof extractCommentsOptions === "boolean" ) {
588+ return {
589+ extractComments : extractCommentsOptions ,
590+ useDefaultPreserveComments : ! extractCommentsOptions ,
591+ } ;
592+ }
593+
594+ if ( typeof extractCommentsOptions === "string" ) {
595+ return {
596+ extractComments :
597+ extractCommentsOptions === "some" || extractCommentsOptions === "all"
598+ ? extractCommentsOptions
599+ : { regex : extractCommentsOptions } ,
600+ useDefaultPreserveComments : false ,
601+ } ;
602+ }
603+
604+ if ( extractCommentsOptions instanceof RegExp ) {
605+ if ( extractCommentsOptions . flags ) {
606+ throw createExtractCommentsError ( extractCommentsOptions ) ;
607+ }
608+
609+ return {
610+ extractComments : { regex : extractCommentsOptions . source } ,
611+ useDefaultPreserveComments : false ,
612+ } ;
613+ }
614+
615+ if ( typeof extractCommentsOptions === "function" ) {
616+ throw createExtractCommentsError ( extractCommentsOptions ) ;
617+ }
618+
619+ if ( extractCommentsOptions && isObject ( extractCommentsOptions ) ) {
620+ const { condition = "some" } = /** @type {{ condition?: unknown } } */ (
621+ extractCommentsOptions
622+ ) ;
623+
624+ if ( typeof condition === "boolean" ) {
625+ return {
626+ extractComments : condition ? "some" : false ,
627+ useDefaultPreserveComments : false ,
628+ } ;
629+ }
630+
631+ if ( typeof condition === "string" ) {
632+ return {
633+ extractComments :
634+ condition === "some" || condition === "all"
635+ ? condition
636+ : { regex : condition } ,
637+ useDefaultPreserveComments : false ,
638+ } ;
639+ }
640+
641+ if ( condition instanceof RegExp ) {
642+ if ( condition . flags ) {
643+ throw createExtractCommentsError ( condition ) ;
644+ }
645+
646+ return {
647+ extractComments : { regex : condition . source } ,
648+ useDefaultPreserveComments : false ,
649+ } ;
650+ }
651+
652+ throw createExtractCommentsError ( condition ) ;
653+ }
654+
655+ return {
656+ extractComments : false ,
657+ useDefaultPreserveComments : false ,
658+ } ;
659+ } ;
660+
661+ /**
662+ * @param {typeof import("@swc/core") } swc swc module
663+ * @returns {boolean } true when the installed swc build supports comment extraction
664+ */
665+ const hasExtractCommentsSupport = ( swc ) => {
666+ try {
667+ if ( typeof swc . minifySync !== "function" ) {
668+ return false ;
669+ }
670+
671+ const result = /** @type {SwcMinifyOutput } */ (
672+ swc . minifySync (
673+ "/*! swc */var foo = 1;" ,
674+ /** @type {SwcMinifyOptionsWithExtractComments } */ ( {
675+ compress : false ,
676+ mangle : false ,
677+ format : {
678+ comments : false ,
679+ } ,
680+ extractComments : true ,
681+ } ) ,
682+ )
683+ ) ;
684+
685+ return (
686+ Array . isArray ( result . extractedComments ) &&
687+ result . extractedComments [ 0 ] === "/*! swc */"
688+ ) ;
689+ } catch ( _error ) {
690+ return false ;
691+ }
692+ } ;
693+
561694 /**
562695 * @param {PredefinedOptions<import("@swc/core").JsMinifyOptions> & import("@swc/core").JsMinifyOptions= } swcOptions swc options
563- * @returns {import("@swc/core").JsMinifyOptions & { sourceMap: undefined | boolean } & { compress: import("@swc/core").TerserCompressOptions } } built swc options
696+ * @returns {SwcMinifyOptionsWithExtractComments & { sourceMap: undefined | boolean } & { compress: import("@swc/core").TerserCompressOptions } } built swc options
564697 */
565698 const buildSwcOptions = ( swcOptions = { } ) =>
566699 // Need deep copy objects to avoid https://github.com/terser/terser/issues/366
@@ -579,6 +712,7 @@ async function swcMinify(input, sourceMap, minimizerOptions) {
579712 : typeof swcOptions . mangle === "boolean"
580713 ? swcOptions . mangle
581714 : { ...swcOptions . mangle } ,
715+ format : { ...swcOptions . format } ,
582716 // ecma: swcOptions.ecma,
583717 // keep_classnames: swcOptions.keep_classnames,
584718 // keep_fnames: swcOptions.keep_fnames,
@@ -599,12 +733,48 @@ async function swcMinify(input, sourceMap, minimizerOptions) {
599733
600734 // Copy `swc` options
601735 const swcOptions = buildSwcOptions ( minimizerOptions ) ;
736+ const normalizedExtractComments = normalizeExtractComments ( extractComments ) ;
737+
738+ if ( ! swcOptions . format ) {
739+ swcOptions . format = { } ;
740+ }
602741
603742 // Let `swc` generate a SourceMap
604743 if ( sourceMap ) {
605744 swcOptions . sourceMap = true ;
606745 }
607746
747+ if (
748+ normalizedExtractComments . useDefaultPreserveComments &&
749+ typeof swcOptions . format . comments === "undefined"
750+ ) {
751+ swcOptions . format . comments = "some" ;
752+ }
753+
754+ if ( normalizedExtractComments . extractComments !== false ) {
755+ if ( ! hasExtractCommentsSupport ( swc ) ) {
756+ let swcVersion ;
757+
758+ try {
759+ ( { version : swcVersion } = require ( "@swc/core/package.json" ) ) ;
760+ } catch ( _error ) {
761+ // Ignore
762+ }
763+
764+ return {
765+ errors : [
766+ new Error (
767+ `The 'extractComments' option for 'swcMinify' requires an @swc/core build with comment extraction support${ swcVersion ? ` (found ${ swcVersion } )` : "" } . Please upgrade @swc/core.` ,
768+ ) ,
769+ ] ,
770+ } ;
771+ }
772+
773+ /** @type {SwcMinifyOptionsWithExtractComments } */ (
774+ swcOptions
775+ ) . extractComments = normalizedExtractComments . extractComments ;
776+ }
777+
608778 if ( swcOptions . compress ) {
609779 // More optimizations
610780 if ( typeof swcOptions . compress . ecma === "undefined" ) {
@@ -621,7 +791,9 @@ async function swcMinify(input, sourceMap, minimizerOptions) {
621791 }
622792
623793 const [ [ filename , code ] ] = Object . entries ( input ) ;
624- const result = await swc . minify ( code , swcOptions ) ;
794+ const result = /** @type {SwcMinifyOutput } */ (
795+ await swc . minify ( code , swcOptions )
796+ ) ;
625797
626798 let map ;
627799
@@ -637,6 +809,7 @@ async function swcMinify(input, sourceMap, minimizerOptions) {
637809 return {
638810 code : result . code ,
639811 map,
812+ extractedComments : result . extractedComments || [ ] ,
640813 } ;
641814}
642815
0 commit comments