Skip to content

Commit d455236

Browse files
cipolleschimeta-codesync[bot]
authored andcommitted
Add function to set build from source to true (#53674)
Summary: Pull Request resolved: #53674 ## Context When configuring an app to build with SwiftPM from source, there is a sequence of operations we need to run in order to prepare the project correctly. ## Changed Add a function updates the PAckage.swift file to set BUILD_FROM_SOURCE to true ## Changelog: [Internal] - Reviewed By: cortinico Differential Revision: D81778460 fbshipit-source-id: b06ebc546aa8e87acdb15706a0559e9046173784
1 parent 87d6043 commit d455236

2 files changed

Lines changed: 274 additions & 0 deletions

File tree

packages/react-native/scripts/swiftpm/__tests__/prepare-app-utils-test.js

Lines changed: 232 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@ const {
1515
findXcodeProjectDirectory,
1616
runIosPrebuild,
1717
runPodDeintegrate,
18+
setBuildFromSource,
1819
} = require('../prepare-app-utils');
1920

2021
// Mock child_process module
@@ -189,6 +190,237 @@ describe('findXcodeProjectDirectory', () => {
189190
});
190191
});
191192

193+
describe('setBuildFromSource', () => {
194+
let mockFs;
195+
let mockPath;
196+
let mockConsoleLog;
197+
let mockConsoleWarn;
198+
199+
beforeEach(() => {
200+
// Setup mocks
201+
mockFs = require('fs');
202+
mockPath = require('path');
203+
mockConsoleLog = console.log;
204+
mockConsoleWarn = console.warn;
205+
206+
// Clear and reset all mocks completely
207+
jest.clearAllMocks();
208+
jest.resetAllMocks();
209+
210+
// Set up fresh mock implementations
211+
mockFs.existsSync = jest.fn();
212+
mockFs.readFileSync = jest.fn();
213+
mockFs.writeFileSync = jest.fn();
214+
215+
// Mock path.join to return realistic paths
216+
mockPath.join.mockImplementation((...args) => args.join('/'));
217+
});
218+
219+
it('should update BUILD_FROM_SOURCE from false to true successfully', async () => {
220+
// Setup
221+
const reactNativePath = '/path/to/react-native';
222+
const mockPackageSwiftContent = `
223+
// Package.swift
224+
import PackageDescription
225+
226+
let BUILD_FROM_SOURCE = false
227+
228+
let package = Package(
229+
name: "ReactNative",
230+
platforms: [.iOS(.v13)],
231+
// rest of package
232+
)
233+
`;
234+
235+
const expectedUpdatedContent = `
236+
// Package.swift
237+
import PackageDescription
238+
239+
let BUILD_FROM_SOURCE = true
240+
241+
let package = Package(
242+
name: "ReactNative",
243+
platforms: [.iOS(.v13)],
244+
// rest of package
245+
)
246+
`;
247+
248+
mockFs.existsSync.mockReturnValue(true);
249+
mockFs.readFileSync.mockReturnValue(mockPackageSwiftContent);
250+
mockFs.writeFileSync.mockImplementation(() => {});
251+
252+
// Execute
253+
await setBuildFromSource(reactNativePath);
254+
255+
// Assert
256+
expect(mockFs.existsSync).toHaveBeenCalledWith(
257+
'/path/to/react-native/Package.swift',
258+
);
259+
expect(mockFs.readFileSync).toHaveBeenCalledWith(
260+
'/path/to/react-native/Package.swift',
261+
'utf8',
262+
);
263+
expect(mockFs.writeFileSync).toHaveBeenCalledWith(
264+
'/path/to/react-native/Package.swift',
265+
expectedUpdatedContent,
266+
'utf8',
267+
);
268+
expect(mockConsoleLog).toHaveBeenCalledWith(
269+
'Updating BUILD_FROM_SOURCE in: /path/to/react-native/Package.swift',
270+
);
271+
expect(mockConsoleLog).toHaveBeenCalledWith(
272+
'✓ BUILD_FROM_SOURCE set to true in Package.swift',
273+
);
274+
expect(mockConsoleWarn).not.toHaveBeenCalled();
275+
});
276+
277+
it('should handle when BUILD_FROM_SOURCE is already true', async () => {
278+
// Setup
279+
const reactNativePath = '/path/to/react-native';
280+
const mockPackageSwiftContent = `
281+
// Package.swift
282+
import PackageDescription
283+
284+
let BUILD_FROM_SOURCE = true
285+
286+
let package = Package(
287+
name: "ReactNative",
288+
platforms: [.iOS(.v13)],
289+
// rest of package
290+
)
291+
`;
292+
293+
mockFs.existsSync.mockReturnValue(true);
294+
mockFs.readFileSync.mockReturnValue(mockPackageSwiftContent);
295+
mockFs.writeFileSync.mockImplementation(() => {});
296+
297+
// Execute
298+
await setBuildFromSource(reactNativePath);
299+
300+
// Assert
301+
expect(mockFs.existsSync).toHaveBeenCalledWith(
302+
'/path/to/react-native/Package.swift',
303+
);
304+
expect(mockFs.readFileSync).toHaveBeenCalledWith(
305+
'/path/to/react-native/Package.swift',
306+
'utf8',
307+
);
308+
expect(mockFs.writeFileSync).not.toHaveBeenCalled(); // Should not write when already true
309+
expect(mockConsoleLog).toHaveBeenCalledWith(
310+
'Updating BUILD_FROM_SOURCE in: /path/to/react-native/Package.swift',
311+
);
312+
expect(mockConsoleLog).toHaveBeenCalledWith(
313+
'✓ BUILD_FROM_SOURCE is already set to true in Package.swift',
314+
);
315+
expect(mockConsoleWarn).not.toHaveBeenCalled();
316+
});
317+
318+
it('should warn when BUILD_FROM_SOURCE declaration is not found', async () => {
319+
// Setup
320+
const reactNativePath = '/path/to/react-native';
321+
const mockPackageSwiftContent = `
322+
// Package.swift
323+
import PackageDescription
324+
325+
let package = Package(
326+
name: "ReactNative",
327+
platforms: [.iOS(.v13)],
328+
// rest of package without BUILD_FROM_SOURCE
329+
)
330+
`;
331+
332+
mockFs.existsSync.mockReturnValue(true);
333+
mockFs.readFileSync.mockReturnValue(mockPackageSwiftContent);
334+
mockFs.writeFileSync.mockImplementation(() => {});
335+
336+
// Execute
337+
await setBuildFromSource(reactNativePath);
338+
339+
// Assert
340+
expect(mockFs.existsSync).toHaveBeenCalledWith(
341+
'/path/to/react-native/Package.swift',
342+
);
343+
expect(mockFs.readFileSync).toHaveBeenCalledWith(
344+
'/path/to/react-native/Package.swift',
345+
'utf8',
346+
);
347+
expect(mockFs.writeFileSync).not.toHaveBeenCalled(); // Should not write when declaration not found
348+
expect(mockConsoleLog).toHaveBeenCalledWith(
349+
'Updating BUILD_FROM_SOURCE in: /path/to/react-native/Package.swift',
350+
);
351+
expect(mockConsoleWarn).toHaveBeenCalledWith(
352+
'⚠️ BUILD_FROM_SOURCE declaration not found in Package.swift',
353+
);
354+
});
355+
356+
it('should throw error when Package.swift does not exist', async () => {
357+
// Setup
358+
const reactNativePath = '/path/to/react-native';
359+
360+
mockFs.existsSync.mockReturnValue(false);
361+
362+
// Execute & Assert
363+
await expect(setBuildFromSource(reactNativePath)).rejects.toThrow(
364+
'Package.swift not found at: /path/to/react-native/Package.swift',
365+
);
366+
367+
expect(mockFs.existsSync).toHaveBeenCalledWith(
368+
'/path/to/react-native/Package.swift',
369+
);
370+
expect(mockFs.readFileSync).not.toHaveBeenCalled();
371+
expect(mockFs.writeFileSync).not.toHaveBeenCalled();
372+
expect(mockConsoleLog).not.toHaveBeenCalled();
373+
});
374+
375+
it('should handle multiple BUILD_FROM_SOURCE occurrences', async () => {
376+
// Setup
377+
const reactNativePath = '/path/to/react-native';
378+
const mockPackageSwiftContent = `
379+
// Package.swift
380+
import PackageDescription
381+
382+
let BUILD_FROM_SOURCE = false
383+
// Some comment about BUILD_FROM_SOURCE = false
384+
let anotherVar = "BUILD_FROM_SOURCE = false in string"
385+
386+
let package = Package(
387+
name: "ReactNative",
388+
platforms: [.iOS(.v13)],
389+
// Another BUILD_FROM_SOURCE = false comment
390+
)
391+
`;
392+
393+
const expectedUpdatedContent = `
394+
// Package.swift
395+
import PackageDescription
396+
397+
let BUILD_FROM_SOURCE = true
398+
// Some comment about BUILD_FROM_SOURCE = false
399+
let anotherVar = "BUILD_FROM_SOURCE = false in string"
400+
401+
let package = Package(
402+
name: "ReactNative",
403+
platforms: [.iOS(.v13)],
404+
// Another BUILD_FROM_SOURCE = false comment
405+
)
406+
`;
407+
408+
mockFs.existsSync.mockReturnValue(true);
409+
mockFs.readFileSync.mockReturnValue(mockPackageSwiftContent);
410+
mockFs.writeFileSync.mockImplementation(() => {});
411+
412+
// Execute
413+
await setBuildFromSource(reactNativePath);
414+
415+
// Assert - should replace only the declaration, not comments or strings
416+
expect(mockFs.writeFileSync).toHaveBeenCalledWith(
417+
'/path/to/react-native/Package.swift',
418+
expectedUpdatedContent,
419+
'utf8',
420+
);
421+
});
422+
});
423+
192424
describe('runIosPrebuild', () => {
193425
let mockExecSync;
194426
let mockConsoleLog;

packages/react-native/scripts/swiftpm/prepare-app-utils.js

Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -165,9 +165,51 @@ async function configureAppForSwift(
165165
}
166166
}
167167

168+
/**
169+
* Set BUILD_FROM_SOURCE to true in Package.swift
170+
*/
171+
async function setBuildFromSource(
172+
reactNativePath /*: string */,
173+
) /*: Promise<void> */ {
174+
const packageSwiftPath = path.join(reactNativePath, 'Package.swift');
175+
176+
if (!fs.existsSync(packageSwiftPath)) {
177+
throw new Error(`Package.swift not found at: ${packageSwiftPath}`);
178+
}
179+
180+
try {
181+
console.log(`Updating BUILD_FROM_SOURCE in: ${packageSwiftPath}`);
182+
183+
const content = fs.readFileSync(packageSwiftPath, 'utf8');
184+
185+
// Check if BUILD_FROM_SOURCE = false exists and replace it with true
186+
if (content.includes('let BUILD_FROM_SOURCE = false')) {
187+
const updatedContent = content.replace(
188+
/let BUILD_FROM_SOURCE = false/g,
189+
'let BUILD_FROM_SOURCE = true',
190+
);
191+
fs.writeFileSync(packageSwiftPath, updatedContent, 'utf8');
192+
console.log('✓ BUILD_FROM_SOURCE set to true in Package.swift');
193+
} else if (content.includes('let BUILD_FROM_SOURCE = true')) {
194+
console.log(
195+
'✓ BUILD_FROM_SOURCE is already set to true in Package.swift',
196+
);
197+
} else {
198+
console.warn(
199+
'⚠️ BUILD_FROM_SOURCE declaration not found in Package.swift',
200+
);
201+
}
202+
} catch (error) {
203+
throw new Error(
204+
`Failed to update BUILD_FROM_SOURCE in Package.swift: ${error.message}`,
205+
);
206+
}
207+
}
208+
168209
module.exports = {
169210
findXcodeProjectDirectory,
170211
runPodDeintegrate,
171212
runIosPrebuild,
172213
configureAppForSwift,
214+
setBuildFromSource,
173215
};

0 commit comments

Comments
 (0)