@@ -59,8 +59,55 @@ function fillTemplate(text: string, vars: ScaffoldVars): string {
5959 . replaceAll ( '__YEAR__' , String ( new Date ( ) . getFullYear ( ) ) )
6060}
6161
62+ function safariResourceCopyBuildPhase ( appName : string ) : string {
63+ return `/* Begin PBXShellScriptBuildPhase section */
64+ \t\t0D0000000000000000000023 /* Copy Web Extension Resources */ = {
65+ \t\t\tisa = PBXShellScriptBuildPhase;
66+ \t\t\talwaysOutOfDate = 1;
67+ \t\t\tbuildActionMask = 2147483647;
68+ \t\t\tfiles = (
69+ \t\t\t);
70+ \t\t\tinputFileListPaths = (
71+ \t\t\t\t"$(SRCROOT)/${ appName } Extension/Resources.inputs.xcfilelist",
72+ \t\t\t);
73+ \t\t\tname = "Copy Web Extension Resources";
74+ \t\t\toutputFileListPaths = (
75+ \t\t\t\t"$(SRCROOT)/${ appName } Extension/Resources.outputs.xcfilelist",
76+ \t\t\t);
77+ \t\t\trunOnlyForDeploymentPostprocessing = 0;
78+ \t\t\tshellPath = /bin/sh;
79+ \t\t\tshellScript = "set -e\\nresources_root=\\"\${SRCROOT}/${ appName } Extension/Resources/\\"\\nwhile IFS= read -r source_file; do\\n relative_path=\\"\${source_file#$resources_root}\\"\\n output_file=\\"\${TARGET_BUILD_DIR}/\${UNLOCALIZED_RESOURCES_FOLDER_PATH}/\${relative_path}\\"\\n /bin/mkdir -p \\"$(/usr/bin/dirname \\\"$output_file\\\")\\"\\n /bin/cp \\"$source_file\\" \\"$output_file\\"\\ndone < \\"$SCRIPT_INPUT_FILE_LIST_0\\"\\n";
80+ \t\t};
81+ /* End PBXShellScriptBuildPhase section */
82+
83+ `
84+ }
85+
86+ /** Upgrade a Stacks-generated project that copied Resources as a nested folder. */
87+ export function migrateSafariResourceBuildPhase ( project : string , appName : string ) : string {
88+ if ( project . includes ( 'SCRIPT_INPUT_FILE_LIST_0' ) )
89+ return project
90+ if ( project . includes ( '0D0000000000000000000023 /* Copy Web Extension Resources */' ) ) {
91+ return project . replace (
92+ / \/ \* B e g i n P B X S h e l l S c r i p t B u i l d P h a s e s e c t i o n \* \/ [ \s \S ] * ?\/ \* E n d P B X S h e l l S c r i p t B u i l d P h a s e s e c t i o n \* \/ \n / ,
93+ safariResourceCopyBuildPhase ( appName ) ,
94+ )
95+ }
96+ if ( ! project . includes ( '0C0000000000000000000023 /* Resources in Resources */' ) )
97+ return project
98+
99+ return project
100+ . replace ( / ^ \s * 0 C 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 2 3 \/ \* R e s o u r c e s i n R e s o u r c e s \* \/ .* \n / m, '' )
101+ . replace ( / ^ \s * 0 C 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 2 3 \/ \* R e s o u r c e s i n R e s o u r c e s \* \/ , \n / mg, '' )
102+ . replace (
103+ '\t\t\t\t0D0000000000000000000022 /* Resources */,\n' ,
104+ '\t\t\t\t0D0000000000000000000022 /* Resources */,\n\t\t\t\t0D0000000000000000000023 /* Copy Web Extension Resources */,\n' ,
105+ )
106+ . replace ( '/* Begin PBXSourcesBuildPhase section */' , `${ safariResourceCopyBuildPhase ( appName ) } /* Begin PBXSourcesBuildPhase section */` )
107+ }
108+
62109function * walk ( dir : string , prefix = '' ) : Generator < string > {
63- for ( const entry of readdirSync ( dir ) ) {
110+ for ( const entry of readdirSync ( dir ) . sort ( ) ) {
64111 const rel = prefix ? `${ prefix } /${ entry } ` : entry
65112 if ( statSync ( join ( dir , entry ) ) . isDirectory ( ) ) yield * walk ( join ( dir , entry ) , rel )
66113 else yield rel
@@ -202,7 +249,16 @@ export async function syncSafariResources(config: ExtensionConfig, options: Safa
202249 throw new Error ( `[browser-extension] ${ outdir } /manifest.json is missing. Run extension:build --target safari first.` )
203250
204251 const appName = safariAppName ( config )
205- const resources = join ( safariProjectDir ( cwd , options . dir ) , `${ appName } Extension` , 'Resources' )
252+ const projectDir = safariProjectDir ( cwd , options . dir )
253+ const projectPath = join ( projectDir , `${ appName } .xcodeproj` , 'project.pbxproj' )
254+ if ( existsSync ( projectPath ) ) {
255+ const project = await Bun . file ( projectPath ) . text ( )
256+ const migrated = migrateSafariResourceBuildPhase ( project , appName )
257+ if ( migrated !== project )
258+ await Bun . write ( projectPath , migrated )
259+ }
260+
261+ const resources = join ( projectDir , `${ appName } Extension` , 'Resources' )
206262 const exclude = new Set ( config . safariExclude ?? [ ] )
207263
208264 if ( existsSync ( resources ) ) {
@@ -214,15 +270,22 @@ export async function syncSafariResources(config: ExtensionConfig, options: Safa
214270 await mkdir ( resources , { recursive : true } )
215271
216272 let files = 0
273+ const synced : string [ ] = [ ]
217274 for ( const rel of walk ( outdir ) ) {
218- if ( exclude . has ( rel ) )
275+ if ( exclude . has ( rel ) || rel . split ( '/' ) . includes ( '.DS_Store' ) )
219276 continue
220277 const dest = join ( resources , rel )
221278 await mkdir ( dirname ( dest ) , { recursive : true } )
222279 cpSync ( join ( outdir , rel ) , dest )
280+ synced . push ( rel )
223281 files += 1
224282 }
225283
284+ const inputs = synced . map ( rel => join ( resources , rel ) ) . join ( '\n' )
285+ const outputs = synced . map ( rel => `$(TARGET_BUILD_DIR)/$(UNLOCALIZED_RESOURCES_FOLDER_PATH)/${ rel } ` ) . join ( '\n' )
286+ await Bun . write ( join ( projectDir , `${ appName } Extension` , 'Resources.inputs.xcfilelist' ) , `${ inputs } \n` )
287+ await Bun . write ( join ( projectDir , `${ appName } Extension` , 'Resources.outputs.xcfilelist' ) , `${ outputs } \n` )
288+
226289 return { resources, files }
227290}
228291
0 commit comments