@@ -244,13 +244,16 @@ module.exports = function (api) {
244244 name : "process.env.IS_PUBLISH" ,
245245 value : bool ( process . env . IS_PUBLISH ) ,
246246 } ,
247+ "flag-IS_PUBLISH" ,
247248 ] ,
248249
249- process . env . STRIP_BABEL_8_FLAG && [
250+ [
250251 pluginToggleBooleanFlag ,
251252 {
252253 name : "process.env.BABEL_8_BREAKING" ,
253- value : bool ( process . env . BABEL_8_BREAKING ) ,
254+ value : process . env . STRIP_BABEL_8_FLAG
255+ ? bool ( process . env . BABEL_8_BREAKING )
256+ : null ,
254257 } ,
255258 "flag-BABEL_8_BREAKING" ,
256259 ] ,
@@ -572,39 +575,48 @@ function pluginPolyfillsOldNode({ template, types: t }) {
572575}
573576
574577/**
578+ * If `value` is null, it will leave the conditions in code. It will remove
579+ * them in attributes by replacing them with <if true> || <if false>
580+ *
575581 * @param {import("@babel/core") } pluginAPI
576582 * @returns {import("@babel/core").PluginObject }
577583 */
578- function pluginToggleBooleanFlag ( { types : t } , { name, value } ) {
579- if ( typeof value !== "boolean" ) throw new Error ( `.value must be a boolean` ) ;
584+ function pluginToggleBooleanFlag ( { types : t , template } , { name, value } ) {
585+ if ( typeof value !== "boolean" && value !== null ) {
586+ throw new Error ( `.value must be a boolean, or null` ) ;
587+ }
580588
581- function evaluate ( test ) {
589+ function parseExpression ( source ) {
590+ // eslint-disable-next-line regexp/no-useless-assertions
591+ return template . expression ( source , { placeholderPattern : / (? ! .) ./ } ) ( { } ) ;
592+ }
593+
594+ /**
595+ * @param {import("@babel/core").types.Expression } test
596+ */
597+ function evaluate ( test , value ) {
582598 const res = {
583599 replace : replacement => ( { replacement, value : null , unrelated : false } ) ,
584600 value : value => ( { replacement : null , value, unrelated : false } ) ,
585- unrelated : ( ) => ( {
586- replacement : test . node ,
587- value : null ,
588- unrelated : true ,
589- } ) ,
601+ unrelated : ( ) => ( { replacement : test , value : null , unrelated : true } ) ,
590602 } ;
591603
592- if ( test . isIdentifier ( { name } ) || test . matchesPattern ( name ) ) {
604+ if ( t . isIdentifier ( test , { name } ) || t . matchesPattern ( test , name ) ) {
593605 return res . value ( value ) ;
594606 }
595607
596- if ( test . isUnaryExpression ( { operator : "!" } ) ) {
597- const arg = evaluate ( test . get ( " argument" ) ) ;
608+ if ( t . isUnaryExpression ( test , { operator : "!" } ) ) {
609+ const arg = evaluate ( test . argument , value ) ;
598610 return arg . unrelated
599611 ? res . unrelated ( )
600612 : arg . replacement
601613 ? res . replacement ( t . unaryExpression ( "!" , arg . replacement ) )
602614 : res . value ( ! arg . value ) ;
603615 }
604616
605- if ( test . isLogicalExpression ( { operator : "||" } ) ) {
606- const left = evaluate ( test . get ( " left" ) ) ;
607- const right = evaluate ( test . get ( " right" ) ) ;
617+ if ( t . isLogicalExpression ( test , { operator : "||" } ) ) {
618+ const left = evaluate ( test . left , value ) ;
619+ const right = evaluate ( test . right , value ) ;
608620
609621 if ( left . value === true || right . value === true ) return res . value ( true ) ;
610622 if ( left . value === false && right . value === false ) {
@@ -618,9 +630,9 @@ function pluginToggleBooleanFlag({ types: t }, { name, value }) {
618630 ) ;
619631 }
620632
621- if ( test . isLogicalExpression ( { operator : "&&" } ) ) {
622- const left = evaluate ( test . get ( " left" ) ) ;
623- const right = evaluate ( test . get ( " right" ) ) ;
633+ if ( t . isLogicalExpression ( test , { operator : "&&" } ) ) {
634+ const left = evaluate ( test . left , value ) ;
635+ const right = evaluate ( test . right , value ) ;
624636
625637 if ( left . value === true && right . value === true ) return res . value ( true ) ;
626638 if ( left . value === false || right . value === false ) {
@@ -640,6 +652,8 @@ function pluginToggleBooleanFlag({ types: t }, { name, value }) {
640652 return {
641653 visitor : {
642654 "IfStatement|ConditionalExpression" ( path ) {
655+ if ( value === null ) return ;
656+
643657 let test = path . get ( "test" ) ;
644658
645659 // yarn-plugin-conditions injects bool(process.env.BABEL_8_BREAKING)
@@ -652,7 +666,7 @@ function pluginToggleBooleanFlag({ types: t }, { name, value }) {
652666 test = test . get ( "arguments" ) [ 0 ] ;
653667 }
654668
655- const res = evaluate ( test ) ;
669+ const res = evaluate ( test . node , value ) ;
656670
657671 if ( res . unrelated ) return ;
658672 if ( res . replacement ) {
@@ -666,7 +680,9 @@ function pluginToggleBooleanFlag({ types: t }, { name, value }) {
666680 }
667681 } ,
668682 LogicalExpression ( path ) {
669- const res = evaluate ( path ) ;
683+ if ( value === null ) return ;
684+
685+ const res = evaluate ( path . node , value ) ;
670686 if ( res . unrelated ) return ;
671687 if ( res . replacement ) {
672688 path . replaceWith ( res . replacement ) ;
@@ -675,15 +691,90 @@ function pluginToggleBooleanFlag({ types: t }, { name, value }) {
675691 }
676692 } ,
677693 MemberExpression ( path ) {
694+ if ( value === null ) return ;
695+
678696 if ( path . matchesPattern ( name ) ) {
679697 throw path . buildCodeFrameError ( "This check could not be stripped." ) ;
680698 }
681699 } ,
682700 ReferencedIdentifier ( path ) {
701+ if ( value === null ) return ;
702+
683703 if ( path . node . name === name ) {
684704 throw path . buildCodeFrameError ( "This check could not be stripped." ) ;
685705 }
686706 } ,
707+ ImportDeclaration ( path ) {
708+ if ( ! path . node . attributes ) return ;
709+
710+ /** @type {null | import("@babel/core").NodePath<import("@babel/core").types.ImportAttribute> } */
711+ const ifAttribute = path
712+ . get ( "attributes" )
713+ . find ( attr => attr . node . key . name === "if" ) ;
714+ if ( ifAttribute == null ) {
715+ return ;
716+ }
717+
718+ const condition = parseExpression ( ifAttribute . node . value . value ) ;
719+
720+ let res ;
721+ res_block: if ( value !== null ) {
722+ res = evaluate ( condition , value ) ;
723+ } else {
724+ const ifTrue = evaluate ( condition , true ) ;
725+ if ( ifTrue . unrelated || ifTrue . value === true ) {
726+ res = ifTrue ;
727+ break res_block;
728+ }
729+ const ifFalse = evaluate ( condition , false ) ;
730+ if ( ifFalse . unrelated ) throw new Error ( "Cannot be unrelated" ) ;
731+ if ( ifFalse . value === true ) {
732+ res = ifFalse ;
733+ break res_block;
734+ }
735+
736+ if ( ifTrue . value === false ) {
737+ res = ifFalse ;
738+ break res_block;
739+ }
740+ if ( ifTrue . value === false ) {
741+ res = ifTrue ;
742+ break res_block;
743+ }
744+
745+ if ( ! ifTrue . replacement && ! ifFalse . replacement ) {
746+ throw new Error ( "Expected two replacements" ) ;
747+ }
748+
749+ res = {
750+ replacement : t . binaryExpression (
751+ "||" ,
752+ ifTrue . replacement ,
753+ ifFalse . replacement
754+ ) ,
755+ value : null ,
756+ unrelated : false ,
757+ } ;
758+ }
759+
760+ if ( res . unrelated ) return ;
761+ if ( res . replacement ) {
762+ ifAttribute
763+ . get ( "value" )
764+ . replaceWith (
765+ t . stringLiteral (
766+ ifAttribute . toString . call ( { node : res . replacement } )
767+ )
768+ ) ;
769+ } else if ( res . value === true ) {
770+ ifAttribute . remove ( ) ;
771+ if ( path . node . attributes . length === 0 ) {
772+ path . node . attributes = null ;
773+ }
774+ } else if ( res . value === false ) {
775+ path . remove ( ) ;
776+ }
777+ } ,
687778 } ,
688779 } ;
689780}
@@ -916,6 +1007,12 @@ function pluginImportMetaUrl({ types: t, template }) {
9161007
9171008 // Let's just remove this declaration to unshadow the "global" cjs require.
9181009 path . remove ( ) ;
1010+ path . scope . crawl ( ) ;
1011+
1012+ const createRequireBinding = path . scope . getBinding ( "createRequire" ) ;
1013+ if ( ! createRequireBinding . referenced ) {
1014+ createRequireBinding . path . remove ( ) ;
1015+ }
9191016 } ,
9201017
9211018 // import.meta.url
0 commit comments