@@ -97,6 +97,9 @@ export class IOSProjectService
9797{
9898 private static IOS_PROJECT_NAME_PLACEHOLDER = "__PROJECT_NAME__" ;
9999 private static IOS_PLATFORM_NAME = "ios" ;
100+ // CLI-managed folder under the platform root where we write generated
101+ // artifacts (e.g. plugin modulemaps) so we never write into node_modules
102+ private static GENERATED_PLUGINS_DIR_NAME = ".plugins" ;
100103
101104 constructor (
102105 $fs : IFileSystem ,
@@ -530,7 +533,10 @@ export class IOSProjectService
530533 singlePlatformFramework ,
531534 path . extname ( singlePlatformFramework ) ,
532535 ) ;
533- let frameworkBinaryPath = path . join ( singlePlatformFramework , frameworkName )
536+ let frameworkBinaryPath = path . join (
537+ singlePlatformFramework ,
538+ frameworkName ,
539+ ) ;
534540 if ( library . BinaryPath ) {
535541 frameworkBinaryPath = path . join (
536542 frameworkPath ,
@@ -548,7 +554,9 @@ export class IOSProjectService
548554 frameworkPath ,
549555 path . extname ( frameworkPath ) ,
550556 ) ;
551- return await isDynamicFrameworkBundle ( path . join ( frameworkPath , frameworkName ) ) ;
557+ return await isDynamicFrameworkBundle (
558+ path . join ( frameworkPath , frameworkName ) ,
559+ ) ;
552560 }
553561 }
554562
@@ -658,7 +666,29 @@ export class IOSProjectService
658666 ) ;
659667 project . addToHeaderSearchPaths ( { relativePath : relativeHeaderSearchPath } ) ;
660668
661- this . generateModulemap ( headersSubpath , libraryName ) ;
669+ // Write the generated modulemap into a CLI-managed folder under the
670+ // platform root (never into node_modules). The modulemap references the
671+ // plugin's headers in-place via relative paths, so nothing is copied.
672+ const modulemapDir = path . join (
673+ this . getPlatformData ( projectData ) . projectRoot ,
674+ IOSProjectService . GENERATED_PLUGINS_DIR_NAME ,
675+ libraryName ,
676+ ) ;
677+ const hasModulemap = this . generateModulemap (
678+ headersSubpath ,
679+ libraryName ,
680+ modulemapDir ,
681+ ) ;
682+ if ( hasModulemap ) {
683+ // Put the modulemap dir on the header search path so clang discovers
684+ // the module there instead of inside node_modules.
685+ project . addToHeaderSearchPaths ( {
686+ relativePath : this . getLibSubpathRelativeToProjectPath (
687+ modulemapDir ,
688+ projectData ,
689+ ) ,
690+ } ) ;
691+ }
662692 this . savePbxProj ( project , projectData ) ;
663693 }
664694
@@ -1682,6 +1712,19 @@ export class IOSProjectService
16821712 project . removeFromHeaderSearchPaths ( {
16831713 relativePath : relativeHeaderSearchPath ,
16841714 } ) ;
1715+
1716+ // Remove the generated modulemap dir search path (see addStaticLibrary)
1717+ const modulemapDir = path . join (
1718+ this . getPlatformData ( projectData ) . projectRoot ,
1719+ IOSProjectService . GENERATED_PLUGINS_DIR_NAME ,
1720+ path . basename ( staticLibPath , ".a" ) ,
1721+ ) ;
1722+ project . removeFromHeaderSearchPaths ( {
1723+ relativePath : this . getLibSubpathRelativeToProjectPath (
1724+ modulemapDir ,
1725+ projectData ,
1726+ ) ,
1727+ } ) ;
16851728 } ,
16861729 ) ;
16871730
@@ -1691,29 +1734,52 @@ export class IOSProjectService
16911734 private generateModulemap (
16921735 headersFolderPath : string ,
16931736 libraryName : string ,
1694- ) : void {
1737+ modulemapDir : string ,
1738+ ) : boolean {
1739+ const modulemapPath = path . join ( modulemapDir , "module.modulemap" ) ;
1740+
1741+ // A plugin may ship a `.a` without an `include/{lib}` headers folder. In
1742+ // that case there's nothing to expose as a module - clean up any stale
1743+ // modulemap and bail out instead of letting readDirectory throw.
1744+ if ( ! this . $fs . exists ( headersFolderPath ) ) {
1745+ if ( this . $fs . exists ( modulemapPath ) ) {
1746+ this . $fs . deleteFile ( modulemapPath ) ;
1747+ }
1748+ return false ;
1749+ }
1750+
16951751 const headersFilter = ( fileName : string , containingFolderPath : string ) =>
16961752 path . extname ( fileName ) === ".h" &&
16971753 this . $fs . getFsStats ( path . join ( containingFolderPath , fileName ) ) . isFile ( ) ;
16981754 const headersFolderContents = this . $fs . readDirectory ( headersFolderPath ) ;
1699- let headers = _ ( headersFolderContents )
1700- . filter ( ( item ) => headersFilter ( item , headersFolderPath ) )
1701- . value ( ) ;
1755+ const headerFiles = headersFolderContents . filter ( ( item ) =>
1756+ headersFilter ( item , headersFolderPath ) ,
1757+ ) ;
17021758
1703- if ( ! headers . length ) {
1704- this . $fs . deleteFile ( path . join ( headersFolderPath , "module.modulemap" ) ) ;
1705- return ;
1759+ if ( ! headerFiles . length ) {
1760+ if ( this . $fs . exists ( modulemapPath ) ) {
1761+ this . $fs . deleteFile ( modulemapPath ) ;
1762+ }
1763+ return false ;
17061764 }
17071765
1708- headers = _ . map ( headers , ( value ) => `header "${ value } "` ) ;
1766+ // Reference the plugin's headers (still in node_modules) relative to the
1767+ // generated modulemap's location, so we don't copy headers or write into
1768+ // node_modules.
1769+ const headers = _ . map ( headerFiles , ( value ) => {
1770+ const relativeHeaderPath = path . relative (
1771+ modulemapDir ,
1772+ path . join ( headersFolderPath , value ) ,
1773+ ) ;
1774+ return `header "${ relativeHeaderPath } "` ;
1775+ } ) ;
17091776
17101777 const modulemap = `module ${ libraryName } { explicit module ${ libraryName } { ${ headers . join (
17111778 " " ,
17121779 ) } } }`;
1713- this . $fs . writeFile (
1714- path . join ( headersFolderPath , "module.modulemap" ) ,
1715- modulemap ,
1716- ) ;
1780+ this . $fs . ensureDirectoryExists ( modulemapDir ) ;
1781+ this . $fs . writeFile ( modulemapPath , modulemap ) ;
1782+ return true ;
17171783 }
17181784
17191785 private async mergeProjectXcconfigFiles (
0 commit comments