mirror of
https://github.com/facebook/react-native.git
synced 2025-11-01 09:14:26 +00:00
Compare commits
1
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
5983edd21b |
+2
-2
@@ -75,7 +75,7 @@ module.system.haste.module_ref_prefix=m#
|
||||
|
||||
react.runtime=automatic
|
||||
|
||||
experimental.error_code_migration=new
|
||||
suppress_type=$FlowFixMe
|
||||
|
||||
ban_spread_key_props=true
|
||||
|
||||
@@ -100,4 +100,4 @@ untyped-import
|
||||
untyped-type-import
|
||||
|
||||
[version]
|
||||
^0.279.0
|
||||
^0.278.0
|
||||
|
||||
@@ -71,17 +71,17 @@ runs:
|
||||
mv build_"$SLICE" "$FINAL_PATH"
|
||||
|
||||
# check whether everything is there
|
||||
if [[ -d "$FINAL_PATH/lib/hermesvm.framework" ]]; then
|
||||
echo "Successfully built hermesvm.framework for $SLICE in $FLAVOR"
|
||||
if [[ -d "$FINAL_PATH/API/hermes/hermes.framework" ]]; then
|
||||
echo "Successfully built hermes.framework for $SLICE in $FLAVOR"
|
||||
else
|
||||
echo "Failed to built hermesvm.framework for $SLICE in $FLAVOR"
|
||||
echo "Failed to built hermes.framework for $SLICE in $FLAVOR"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
if [[ -d "$FINAL_PATH/lib/hermesvm.framework.dSYM" ]]; then
|
||||
echo "Successfully built hermesvm.framework.dSYM for $SLICE in $FLAVOR"
|
||||
if [[ -d "$FINAL_PATH/API/hermes/hermes.framework.dSYM" ]]; then
|
||||
echo "Successfully built hermes.framework.dSYM for $SLICE in $FLAVOR"
|
||||
else
|
||||
echo "Failed to built hermesvm.framework.dSYM for $SLICE in $FLAVOR"
|
||||
echo "Failed to built hermes.framework.dSYM for $SLICE in $FLAVOR"
|
||||
echo "Please try again"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
@@ -186,7 +186,7 @@ runs:
|
||||
|
||||
cd ./packages/react-native/sdks/hermes || exit 1
|
||||
|
||||
DSYM_FILE_PATH=lib/hermesvm.framework.dSYM
|
||||
DSYM_FILE_PATH=API/hermes/hermes.framework.dSYM
|
||||
cp -r build_macosx/$DSYM_FILE_PATH "$WORKING_DIR/macosx/"
|
||||
cp -r build_catalyst/$DSYM_FILE_PATH "$WORKING_DIR/catalyst/"
|
||||
cp -r build_iphoneos/$DSYM_FILE_PATH "$WORKING_DIR/iphoneos/"
|
||||
@@ -197,10 +197,10 @@ runs:
|
||||
cp -r build_xrsimulator/$DSYM_FILE_PATH "$WORKING_DIR/xrsimulator/"
|
||||
|
||||
DEST_DIR="/tmp/hermes/dSYM/$FLAVOR"
|
||||
tar -C "$WORKING_DIR" -czvf "hermesvm.framework.dSYM" .
|
||||
tar -C "$WORKING_DIR" -czvf "hermes.framework.dSYM" .
|
||||
|
||||
mkdir -p "$DEST_DIR"
|
||||
mv "hermesvm.framework.dSYM" "$DEST_DIR"
|
||||
mv "hermes.framework.dSYM" "$DEST_DIR"
|
||||
- name: Upload hermes dSYM artifacts
|
||||
uses: actions/upload-artifact@v4.3.4
|
||||
with:
|
||||
|
||||
@@ -99,8 +99,8 @@ runs:
|
||||
mkdir -p ./packages/react-native/ReactAndroid/external-artifacts/artifacts/
|
||||
cp ${{ inputs.hermes-ws-dir }}/hermes-runtime-darwin/hermes-ios-Debug.tar.gz ./packages/react-native/ReactAndroid/external-artifacts/artifacts/hermes-ios-debug.tar.gz
|
||||
cp ${{ inputs.hermes-ws-dir }}/hermes-runtime-darwin/hermes-ios-Release.tar.gz ./packages/react-native/ReactAndroid/external-artifacts/artifacts/hermes-ios-release.tar.gz
|
||||
cp ${{ inputs.hermes-ws-dir }}/dSYM/Debug/hermesvm.framework.dSYM ./packages/react-native/ReactAndroid/external-artifacts/artifacts/hermes-framework-dSYM-debug.tar.gz
|
||||
cp ${{ inputs.hermes-ws-dir }}/dSYM/Release/hermesvm.framework.dSYM ./packages/react-native/ReactAndroid/external-artifacts/artifacts/hermes-framework-dSYM-release.tar.gz
|
||||
cp ${{ inputs.hermes-ws-dir }}/dSYM/Debug/hermes.framework.dSYM ./packages/react-native/ReactAndroid/external-artifacts/artifacts/hermes-framework-dSYM-debug.tar.gz
|
||||
cp ${{ inputs.hermes-ws-dir }}/dSYM/Release/hermes.framework.dSYM ./packages/react-native/ReactAndroid/external-artifacts/artifacts/hermes-framework-dSYM-release.tar.gz
|
||||
- name: Download ReactNativeDependencies
|
||||
uses: actions/download-artifact@v4
|
||||
with:
|
||||
|
||||
@@ -0,0 +1,32 @@
|
||||
name: setup-xcode-build-cache
|
||||
description: Add caching to iOS jobs to speed up builds
|
||||
inputs:
|
||||
hermes-version:
|
||||
description: The version of hermes
|
||||
required: true
|
||||
flavor:
|
||||
description: The flavor that is going to be built
|
||||
default: Debug
|
||||
use-frameworks:
|
||||
description: Whether we are bulding with DynamicFrameworks or StaticLibraries
|
||||
default: StaticLibraries
|
||||
ruby-version:
|
||||
description: The ruby version we are going to use
|
||||
default: 2.6.10
|
||||
|
||||
runs:
|
||||
using: composite
|
||||
steps:
|
||||
- name: See commands.yml with_xcodebuild_cache
|
||||
shell: bash
|
||||
run: echo "See commands.yml with_xcodebuild_cache"
|
||||
- name: Cache podfile lock
|
||||
uses: actions/cache@v4
|
||||
with:
|
||||
path: packages/rn-tester/Podfile.lock
|
||||
key: v13-podfilelock-${{ github.job }}-NewArch-${{ inputs.flavor }}-${{ inputs.use-frameworks }}-${{ inputs.ruby-version }}-${{ hashfiles('packages/rn-tester/Podfile') }}-${{ inputs.hermes-version }}
|
||||
- name: Cache cocoapods
|
||||
uses: actions/cache@v4
|
||||
with:
|
||||
path: packages/rn-tester/Pods
|
||||
key: v15-cocoapods-${{ github.job }}-NewArch-${{ inputs.flavor }}-${{ inputs.use-frameworks }}-${{ inputs.ruby-version }}-${{ hashfiles('packages/rn-tester/Podfile.lock') }}-${{ hashfiles('packages/rn-tester/Podfile') }}-${{ inputs.hermes-version}}
|
||||
@@ -102,6 +102,13 @@ runs:
|
||||
- name: Print ReactCore folder
|
||||
shell: bash
|
||||
run: ls -lR /tmp/ReactCore
|
||||
- name: Setup xcode build cache
|
||||
uses: ./.github/actions/setup-xcode-build-cache
|
||||
with:
|
||||
hermes-version: ${{ inputs.hermes-version }}
|
||||
use-frameworks: ${{ inputs.use-frameworks }}
|
||||
flavor: ${{ inputs.flavor }}
|
||||
ruby-version: ${{ inputs.ruby-version }}
|
||||
- name: Install CocoaPods dependencies
|
||||
shell: bash
|
||||
run: |
|
||||
|
||||
@@ -0,0 +1,52 @@
|
||||
name: test-library-on-nightly
|
||||
description: Tests a library on a nightly
|
||||
inputs:
|
||||
library-npm-package:
|
||||
description: The library npm package to add
|
||||
required: true
|
||||
platform:
|
||||
description: whether we want to build for iOS or Android
|
||||
required: true
|
||||
runs:
|
||||
using: composite
|
||||
steps:
|
||||
- name: Create new app
|
||||
shell: bash
|
||||
run: |
|
||||
cd /tmp
|
||||
npx @react-native-community/cli init RNApp --skip-install --version nightly
|
||||
- name: Add library
|
||||
shell: bash
|
||||
run: |
|
||||
cd /tmp/RNApp
|
||||
yarn add ${{ inputs.library-npm-package }}
|
||||
|
||||
# iOS
|
||||
- name: Setup xcode
|
||||
if: ${{ inputs.platform == 'ios' }}
|
||||
uses: ./.github/actions/setup-xcode
|
||||
- name: Build iOS
|
||||
shell: bash
|
||||
if: ${{ inputs.platform == 'ios' }}
|
||||
run: |
|
||||
cd /tmp/RNApp/ios
|
||||
bundle install
|
||||
RCT_USE_RN_DEP=1 RCT_USE_PREBUILT_RNCORE=1 bundle exec pod install
|
||||
xcodebuild build \
|
||||
-workspace RNApp.xcworkspace \
|
||||
-scheme RNApp \
|
||||
-sdk iphonesimulator
|
||||
|
||||
# Android
|
||||
- name: Setup Java for Android
|
||||
if: ${{ inputs.platform == 'android' }}
|
||||
uses: actions/setup-java@v2
|
||||
with:
|
||||
java-version: '17'
|
||||
distribution: 'zulu'
|
||||
- name: Build Android
|
||||
shell: bash
|
||||
if: ${{ inputs.platform == 'android' }}
|
||||
run: |
|
||||
cd /tmp/RNApp/android
|
||||
./gradlew assembleDebug
|
||||
@@ -0,0 +1,897 @@
|
||||
/**
|
||||
* Copyright (c) Meta Platforms, Inc. and affiliates.
|
||||
*
|
||||
* This source code is licensed under the MIT license found in the
|
||||
* LICENSE file in the root directory of this source tree.
|
||||
*
|
||||
* @format
|
||||
*/
|
||||
|
||||
'use strict';
|
||||
|
||||
const {
|
||||
FirebaseClient,
|
||||
compareResults,
|
||||
getYesterdayDate,
|
||||
getTodayDate,
|
||||
} = require('../firebaseUtils');
|
||||
|
||||
describe('FirebaseClient', () => {
|
||||
const originalFetch = global.fetch;
|
||||
const originalEnv = process.env;
|
||||
|
||||
beforeEach(() => {
|
||||
global.fetch = jest.fn();
|
||||
process.env = {
|
||||
...originalEnv,
|
||||
FIREBASE_APP_EMAIL: 'test@example.com',
|
||||
FIREBASE_APP_PASS: 'testpassword',
|
||||
FIREBASE_APP_APIKEY: 'test-api-key',
|
||||
FIREBASE_APP_PROJECTNAME: 'test-project',
|
||||
};
|
||||
jest.spyOn(console, 'log').mockImplementation(() => {});
|
||||
jest.spyOn(console, 'error').mockImplementation(() => {});
|
||||
});
|
||||
|
||||
afterEach(() => {
|
||||
global.fetch = originalFetch;
|
||||
process.env = originalEnv;
|
||||
jest.restoreAllMocks();
|
||||
});
|
||||
|
||||
describe('constructor', () => {
|
||||
it('should initialize with environment variables', () => {
|
||||
const client = new FirebaseClient();
|
||||
expect(client.email).toBe('test@example.com');
|
||||
expect(client.password).toBe('testpassword');
|
||||
expect(client.apiKey).toBe('test-api-key');
|
||||
expect(client.projectId).toBe('test-project');
|
||||
expect(client.databaseUrl).toBe(
|
||||
'test-project-default-rtdb.firebaseio.com',
|
||||
);
|
||||
expect(client.idToken).toBeNull();
|
||||
});
|
||||
});
|
||||
|
||||
describe('authenticate', () => {
|
||||
it('should authenticate successfully', async () => {
|
||||
const mockResponse = {
|
||||
idToken: 'mock-id-token',
|
||||
refreshToken: 'mock-refresh-token',
|
||||
};
|
||||
|
||||
global.fetch.mockResolvedValueOnce({
|
||||
ok: true,
|
||||
text: jest.fn().mockResolvedValueOnce(JSON.stringify(mockResponse)),
|
||||
});
|
||||
|
||||
const client = new FirebaseClient();
|
||||
await client.authenticate();
|
||||
|
||||
expect(client.idToken).toBe('mock-id-token');
|
||||
expect(global.fetch).toHaveBeenCalledWith(
|
||||
'https://identitytoolkit.googleapis.com/v1/accounts:signInWithPassword?key=test-api-key',
|
||||
{
|
||||
method: 'POST',
|
||||
headers: {
|
||||
'Content-Type': 'application/json',
|
||||
},
|
||||
body: JSON.stringify({
|
||||
email: 'test@example.com',
|
||||
password: 'testpassword',
|
||||
returnSecureToken: true,
|
||||
}),
|
||||
},
|
||||
);
|
||||
});
|
||||
|
||||
it('should throw error when email is missing', async () => {
|
||||
delete process.env.FIREBASE_APP_EMAIL;
|
||||
const client = new FirebaseClient();
|
||||
|
||||
await expect(client.authenticate()).rejects.toThrow(
|
||||
'Firebase credentials not found in environment variables',
|
||||
);
|
||||
});
|
||||
|
||||
it('should throw error when password is missing', async () => {
|
||||
delete process.env.FIREBASE_APP_PASS;
|
||||
const client = new FirebaseClient();
|
||||
|
||||
await expect(client.authenticate()).rejects.toThrow(
|
||||
'Firebase credentials not found in environment variables',
|
||||
);
|
||||
});
|
||||
|
||||
it('should handle authentication failure', async () => {
|
||||
global.fetch.mockResolvedValueOnce({
|
||||
ok: false,
|
||||
status: 400,
|
||||
text: jest.fn().mockResolvedValueOnce(
|
||||
JSON.stringify({
|
||||
error: {message: 'Invalid credentials'},
|
||||
}),
|
||||
),
|
||||
});
|
||||
|
||||
const client = new FirebaseClient();
|
||||
|
||||
await expect(client.authenticate()).rejects.toThrow(
|
||||
'HTTP 400: Invalid credentials',
|
||||
);
|
||||
});
|
||||
});
|
||||
|
||||
describe('makeRequest', () => {
|
||||
it('should make successful GET request', async () => {
|
||||
const mockData = {test: 'data'};
|
||||
global.fetch.mockResolvedValueOnce({
|
||||
ok: true,
|
||||
text: jest.fn().mockResolvedValueOnce(JSON.stringify(mockData)),
|
||||
});
|
||||
|
||||
const client = new FirebaseClient();
|
||||
const result = await client.makeRequest('example.com', '/test', 'GET');
|
||||
|
||||
expect(result).toEqual(mockData);
|
||||
expect(global.fetch).toHaveBeenCalledWith('https://example.com/test', {
|
||||
method: 'GET',
|
||||
headers: {
|
||||
'Content-Type': 'application/json',
|
||||
},
|
||||
});
|
||||
});
|
||||
|
||||
it('should make successful POST request with data', async () => {
|
||||
const mockData = {success: true};
|
||||
const postData = {test: 'post data'};
|
||||
|
||||
global.fetch.mockResolvedValueOnce({
|
||||
ok: true,
|
||||
text: jest.fn().mockResolvedValueOnce(JSON.stringify(mockData)),
|
||||
});
|
||||
|
||||
const client = new FirebaseClient();
|
||||
const result = await client.makeRequest(
|
||||
'example.com',
|
||||
'/test',
|
||||
'POST',
|
||||
postData,
|
||||
);
|
||||
|
||||
expect(result).toEqual(mockData);
|
||||
expect(global.fetch).toHaveBeenCalledWith('https://example.com/test', {
|
||||
method: 'POST',
|
||||
headers: {
|
||||
'Content-Type': 'application/json',
|
||||
},
|
||||
body: JSON.stringify(postData),
|
||||
});
|
||||
});
|
||||
|
||||
it('should handle non-JSON response', async () => {
|
||||
const textResponse = 'plain text response';
|
||||
global.fetch.mockResolvedValueOnce({
|
||||
ok: true,
|
||||
text: jest.fn().mockResolvedValueOnce(textResponse),
|
||||
});
|
||||
|
||||
const client = new FirebaseClient();
|
||||
const result = await client.makeRequest('example.com', '/test', 'GET');
|
||||
|
||||
expect(result).toBe(textResponse);
|
||||
});
|
||||
|
||||
it('should handle HTTP error with JSON error message', async () => {
|
||||
global.fetch.mockResolvedValueOnce({
|
||||
ok: false,
|
||||
status: 404,
|
||||
text: jest.fn().mockResolvedValueOnce(
|
||||
JSON.stringify({
|
||||
error: {message: 'Not found'},
|
||||
}),
|
||||
),
|
||||
});
|
||||
|
||||
const client = new FirebaseClient();
|
||||
|
||||
await expect(
|
||||
client.makeRequest('example.com', '/test', 'GET'),
|
||||
).rejects.toThrow('HTTP 404: Not found');
|
||||
});
|
||||
|
||||
it('should handle HTTP error with plain text error message', async () => {
|
||||
global.fetch.mockResolvedValueOnce({
|
||||
ok: false,
|
||||
status: 500,
|
||||
text: jest.fn().mockResolvedValueOnce('Internal Server Error'),
|
||||
});
|
||||
|
||||
const client = new FirebaseClient();
|
||||
|
||||
await expect(
|
||||
client.makeRequest('example.com', '/test', 'GET'),
|
||||
).rejects.toThrow('HTTP 500: Internal Server Error');
|
||||
});
|
||||
});
|
||||
|
||||
describe('makeDatabaseRequest', () => {
|
||||
it('should make database request with existing token', async () => {
|
||||
const mockData = {test: 'data'};
|
||||
global.fetch.mockResolvedValueOnce({
|
||||
ok: true,
|
||||
text: jest.fn().mockResolvedValueOnce(JSON.stringify(mockData)),
|
||||
});
|
||||
|
||||
const client = new FirebaseClient();
|
||||
client.idToken = 'existing-token';
|
||||
|
||||
const result = await client.makeDatabaseRequest('2023-12-01', 'GET');
|
||||
|
||||
expect(result).toEqual(mockData);
|
||||
expect(global.fetch).toHaveBeenCalledWith(
|
||||
'https://test-project-default-rtdb.firebaseio.com/nightly-results/2023-12-01.json?auth=existing-token',
|
||||
{
|
||||
method: 'GET',
|
||||
headers: {
|
||||
'Content-Type': 'application/json',
|
||||
},
|
||||
},
|
||||
);
|
||||
});
|
||||
|
||||
it('should authenticate before making request if no token exists', async () => {
|
||||
const authResponse = {idToken: 'new-token'};
|
||||
const dataResponse = {test: 'data'};
|
||||
|
||||
global.fetch
|
||||
.mockResolvedValueOnce({
|
||||
ok: true,
|
||||
text: jest.fn().mockResolvedValueOnce(JSON.stringify(authResponse)),
|
||||
})
|
||||
.mockResolvedValueOnce({
|
||||
ok: true,
|
||||
text: jest.fn().mockResolvedValueOnce(JSON.stringify(dataResponse)),
|
||||
});
|
||||
|
||||
const client = new FirebaseClient();
|
||||
const result = await client.makeDatabaseRequest('2023-12-01', 'GET');
|
||||
|
||||
expect(result).toEqual(dataResponse);
|
||||
expect(global.fetch).toHaveBeenCalledTimes(2);
|
||||
expect(client.idToken).toBe('new-token');
|
||||
});
|
||||
|
||||
it('should make PUT request with data', async () => {
|
||||
global.fetch.mockResolvedValueOnce({
|
||||
ok: true,
|
||||
text: jest.fn().mockResolvedValueOnce('null'),
|
||||
});
|
||||
|
||||
const client = new FirebaseClient();
|
||||
client.idToken = 'existing-token';
|
||||
const testData = [{library: 'test', status: 'success'}];
|
||||
|
||||
await client.makeDatabaseRequest('2023-12-01', 'PUT', testData);
|
||||
|
||||
expect(global.fetch).toHaveBeenCalledWith(
|
||||
'https://test-project-default-rtdb.firebaseio.com/nightly-results/2023-12-01.json?auth=existing-token',
|
||||
{
|
||||
method: 'PUT',
|
||||
headers: {
|
||||
'Content-Type': 'application/json',
|
||||
},
|
||||
body: JSON.stringify(testData),
|
||||
},
|
||||
);
|
||||
});
|
||||
});
|
||||
|
||||
describe('storeResults', () => {
|
||||
it('should store results successfully', async () => {
|
||||
global.fetch.mockResolvedValueOnce({
|
||||
ok: true,
|
||||
text: jest.fn().mockResolvedValueOnce('null'),
|
||||
});
|
||||
|
||||
const client = new FirebaseClient();
|
||||
client.idToken = 'existing-token';
|
||||
const results = [{library: 'test', status: 'success'}];
|
||||
|
||||
await client.storeResults('2023-12-01', results);
|
||||
|
||||
expect(global.fetch).toHaveBeenCalledWith(
|
||||
'https://test-project-default-rtdb.firebaseio.com/nightly-results/2023-12-01.json?auth=existing-token',
|
||||
{
|
||||
method: 'PUT',
|
||||
headers: {
|
||||
'Content-Type': 'application/json',
|
||||
},
|
||||
body: JSON.stringify(results),
|
||||
},
|
||||
);
|
||||
expect(console.log).toHaveBeenCalledWith(
|
||||
'Successfully stored results for 2023-12-01',
|
||||
);
|
||||
});
|
||||
});
|
||||
|
||||
describe('getResults', () => {
|
||||
it('should retrieve results successfully', async () => {
|
||||
const mockResults = [{library: 'test', status: 'success'}];
|
||||
global.fetch.mockResolvedValueOnce({
|
||||
ok: true,
|
||||
text: jest.fn().mockResolvedValueOnce(JSON.stringify(mockResults)),
|
||||
});
|
||||
|
||||
const client = new FirebaseClient();
|
||||
client.idToken = 'existing-token';
|
||||
|
||||
const results = await client.getResults('2023-12-01');
|
||||
|
||||
expect(results).toEqual(mockResults);
|
||||
});
|
||||
|
||||
it('should return null for 404 errors', async () => {
|
||||
global.fetch.mockResolvedValueOnce({
|
||||
ok: false,
|
||||
status: 404,
|
||||
text: jest.fn().mockResolvedValueOnce('Not Found'),
|
||||
});
|
||||
|
||||
const client = new FirebaseClient();
|
||||
client.idToken = 'existing-token';
|
||||
|
||||
const results = await client.getResults('2023-12-01');
|
||||
|
||||
expect(results).toBeNull();
|
||||
});
|
||||
|
||||
it('should throw error for non-404 HTTP errors', async () => {
|
||||
global.fetch.mockResolvedValueOnce({
|
||||
ok: false,
|
||||
status: 500,
|
||||
text: jest.fn().mockResolvedValueOnce('Internal Server Error'),
|
||||
});
|
||||
|
||||
const client = new FirebaseClient();
|
||||
client.idToken = 'existing-token';
|
||||
|
||||
await expect(client.getResults('2023-12-01')).rejects.toThrow(
|
||||
'HTTP 500: Internal Server Error',
|
||||
);
|
||||
});
|
||||
});
|
||||
|
||||
describe('getLatestResults', () => {
|
||||
it('should authenticate before making requests if no token exists', async () => {
|
||||
const authResponse = {idToken: 'new-token'};
|
||||
const mockResults = [{library: 'test', status: 'success'}];
|
||||
|
||||
global.fetch
|
||||
.mockResolvedValueOnce({
|
||||
ok: true,
|
||||
text: jest.fn().mockResolvedValueOnce(JSON.stringify(authResponse)),
|
||||
})
|
||||
.mockResolvedValueOnce({
|
||||
ok: true,
|
||||
text: jest.fn().mockResolvedValueOnce(JSON.stringify(mockResults)),
|
||||
});
|
||||
|
||||
const client = new FirebaseClient();
|
||||
const result = await client.getLatestResults('2023-12-15', 1);
|
||||
|
||||
expect(result).toEqual({
|
||||
results: mockResults,
|
||||
date: '2023-12-14',
|
||||
});
|
||||
expect(client.idToken).toBe('new-token');
|
||||
expect(console.log).toHaveBeenCalledWith(
|
||||
'Checking for results on 2023-12-14 (1 days back)...',
|
||||
);
|
||||
expect(console.log).toHaveBeenCalledWith(
|
||||
'Found results from 2023-12-14 (1 days back)',
|
||||
);
|
||||
});
|
||||
|
||||
it('should find results from the previous day', async () => {
|
||||
const mockResults = [{library: 'test', status: 'success'}];
|
||||
global.fetch.mockResolvedValueOnce({
|
||||
ok: true,
|
||||
text: jest.fn().mockResolvedValueOnce(JSON.stringify(mockResults)),
|
||||
});
|
||||
|
||||
const client = new FirebaseClient();
|
||||
client.idToken = 'existing-token';
|
||||
|
||||
const result = await client.getLatestResults('2023-12-15', 7);
|
||||
|
||||
expect(result).toEqual({
|
||||
results: mockResults,
|
||||
date: '2023-12-14',
|
||||
});
|
||||
expect(console.log).toHaveBeenCalledWith(
|
||||
'Checking for results on 2023-12-14 (1 days back)...',
|
||||
);
|
||||
expect(console.log).toHaveBeenCalledWith(
|
||||
'Found results from 2023-12-14 (1 days back)',
|
||||
);
|
||||
});
|
||||
|
||||
it('should find results from several days back', async () => {
|
||||
const mockResults = [{library: 'test', status: 'success'}];
|
||||
|
||||
// Mock 404 responses for first 2 days, then success on 3rd day
|
||||
global.fetch
|
||||
.mockResolvedValueOnce({
|
||||
ok: false,
|
||||
status: 404,
|
||||
text: jest.fn().mockResolvedValueOnce('Not Found'),
|
||||
})
|
||||
.mockResolvedValueOnce({
|
||||
ok: false,
|
||||
status: 404,
|
||||
text: jest.fn().mockResolvedValueOnce('Not Found'),
|
||||
})
|
||||
.mockResolvedValueOnce({
|
||||
ok: true,
|
||||
text: jest.fn().mockResolvedValueOnce(JSON.stringify(mockResults)),
|
||||
});
|
||||
|
||||
const client = new FirebaseClient();
|
||||
client.idToken = 'existing-token';
|
||||
|
||||
const result = await client.getLatestResults('2023-12-15', 7);
|
||||
|
||||
expect(result).toEqual({
|
||||
results: mockResults,
|
||||
date: '2023-12-12',
|
||||
});
|
||||
expect(console.log).toHaveBeenCalledWith(
|
||||
'Checking for results on 2023-12-14 (1 days back)...',
|
||||
);
|
||||
expect(console.log).toHaveBeenCalledWith(
|
||||
'Checking for results on 2023-12-13 (2 days back)...',
|
||||
);
|
||||
expect(console.log).toHaveBeenCalledWith(
|
||||
'Checking for results on 2023-12-12 (3 days back)...',
|
||||
);
|
||||
expect(console.log).toHaveBeenCalledWith(
|
||||
'Found results from 2023-12-12 (3 days back)',
|
||||
);
|
||||
});
|
||||
|
||||
it('should skip empty results and continue searching', async () => {
|
||||
const mockResults = [{library: 'test', status: 'success'}];
|
||||
|
||||
// Mock empty array for first day, then valid results on second day
|
||||
global.fetch
|
||||
.mockResolvedValueOnce({
|
||||
ok: true,
|
||||
text: jest.fn().mockResolvedValueOnce(JSON.stringify([])),
|
||||
})
|
||||
.mockResolvedValueOnce({
|
||||
ok: true,
|
||||
text: jest.fn().mockResolvedValueOnce(JSON.stringify(mockResults)),
|
||||
});
|
||||
|
||||
const client = new FirebaseClient();
|
||||
client.idToken = 'existing-token';
|
||||
|
||||
const result = await client.getLatestResults('2023-12-15', 7);
|
||||
|
||||
expect(result).toEqual({
|
||||
results: mockResults,
|
||||
date: '2023-12-13',
|
||||
});
|
||||
expect(console.log).toHaveBeenCalledWith(
|
||||
'Checking for results on 2023-12-14 (1 days back)...',
|
||||
);
|
||||
expect(console.log).toHaveBeenCalledWith(
|
||||
'Checking for results on 2023-12-13 (2 days back)...',
|
||||
);
|
||||
expect(console.log).toHaveBeenCalledWith(
|
||||
'Found results from 2023-12-13 (2 days back)',
|
||||
);
|
||||
});
|
||||
|
||||
it('should return null when no results found within maxDaysBack', async () => {
|
||||
// Mock 404 responses for all days
|
||||
global.fetch.mockResolvedValue({
|
||||
ok: false,
|
||||
status: 404,
|
||||
text: jest.fn().mockResolvedValue('Not Found'),
|
||||
});
|
||||
|
||||
const client = new FirebaseClient();
|
||||
client.idToken = 'existing-token';
|
||||
|
||||
const result = await client.getLatestResults('2023-12-15', 3);
|
||||
|
||||
expect(result).toEqual({
|
||||
results: null,
|
||||
date: null,
|
||||
});
|
||||
expect(console.log).toHaveBeenCalledWith(
|
||||
'No previous results found within the last 3 days',
|
||||
);
|
||||
expect(global.fetch).toHaveBeenCalledTimes(3);
|
||||
});
|
||||
|
||||
it('should use default maxDaysBack of 7 when not specified', async () => {
|
||||
// Mock 404 responses for all days
|
||||
global.fetch.mockResolvedValue({
|
||||
ok: false,
|
||||
status: 404,
|
||||
text: jest.fn().mockResolvedValue('Not Found'),
|
||||
});
|
||||
|
||||
const client = new FirebaseClient();
|
||||
client.idToken = 'existing-token';
|
||||
|
||||
const result = await client.getLatestResults('2023-12-15');
|
||||
|
||||
expect(result).toEqual({
|
||||
results: null,
|
||||
date: null,
|
||||
});
|
||||
expect(console.log).toHaveBeenCalledWith(
|
||||
'No previous results found within the last 7 days',
|
||||
);
|
||||
expect(global.fetch).toHaveBeenCalledTimes(7);
|
||||
});
|
||||
|
||||
it('should handle non-404 errors and continue searching', async () => {
|
||||
const mockResults = [{library: 'test', status: 'success'}];
|
||||
|
||||
// Mock 500 error for first day, then success on second day
|
||||
global.fetch
|
||||
.mockResolvedValueOnce({
|
||||
ok: false,
|
||||
status: 500,
|
||||
text: jest.fn().mockResolvedValueOnce('Internal Server Error'),
|
||||
})
|
||||
.mockResolvedValueOnce({
|
||||
ok: true,
|
||||
text: jest.fn().mockResolvedValueOnce(JSON.stringify(mockResults)),
|
||||
});
|
||||
|
||||
const client = new FirebaseClient();
|
||||
client.idToken = 'existing-token';
|
||||
|
||||
const result = await client.getLatestResults('2023-12-15', 7);
|
||||
|
||||
expect(result).toEqual({
|
||||
results: mockResults,
|
||||
date: '2023-12-13',
|
||||
});
|
||||
expect(console.log).toHaveBeenCalledWith(
|
||||
'No results found for 2023-12-14: HTTP 500: Internal Server Error',
|
||||
);
|
||||
expect(console.log).toHaveBeenCalledWith(
|
||||
'Found results from 2023-12-13 (2 days back)',
|
||||
);
|
||||
});
|
||||
|
||||
it('should handle date boundaries correctly', async () => {
|
||||
const mockResults = [{library: 'test', status: 'success'}];
|
||||
global.fetch.mockResolvedValueOnce({
|
||||
ok: true,
|
||||
text: jest.fn().mockResolvedValueOnce(JSON.stringify(mockResults)),
|
||||
});
|
||||
|
||||
const client = new FirebaseClient();
|
||||
client.idToken = 'existing-token';
|
||||
|
||||
// Test month boundary
|
||||
const result = await client.getLatestResults('2023-12-01', 1);
|
||||
|
||||
expect(result).toEqual({
|
||||
results: mockResults,
|
||||
date: '2023-11-30',
|
||||
});
|
||||
expect(console.log).toHaveBeenCalledWith(
|
||||
'Checking for results on 2023-11-30 (1 days back)...',
|
||||
);
|
||||
});
|
||||
|
||||
it('should handle year boundary correctly', async () => {
|
||||
const mockResults = [{library: 'test', status: 'success'}];
|
||||
global.fetch.mockResolvedValueOnce({
|
||||
ok: true,
|
||||
text: jest.fn().mockResolvedValueOnce(JSON.stringify(mockResults)),
|
||||
});
|
||||
|
||||
const client = new FirebaseClient();
|
||||
client.idToken = 'existing-token';
|
||||
|
||||
// Test year boundary
|
||||
const result = await client.getLatestResults('2024-01-01', 1);
|
||||
|
||||
expect(result).toEqual({
|
||||
results: mockResults,
|
||||
date: '2023-12-31',
|
||||
});
|
||||
expect(console.log).toHaveBeenCalledWith(
|
||||
'Checking for results on 2023-12-31 (1 days back)...',
|
||||
);
|
||||
});
|
||||
|
||||
it('should handle null results and continue searching', async () => {
|
||||
const mockResults = [{library: 'test', status: 'success'}];
|
||||
|
||||
// Mock null for first day, then valid results on second day
|
||||
global.fetch
|
||||
.mockResolvedValueOnce({
|
||||
ok: true,
|
||||
text: jest.fn().mockResolvedValueOnce('null'),
|
||||
})
|
||||
.mockResolvedValueOnce({
|
||||
ok: true,
|
||||
text: jest.fn().mockResolvedValueOnce(JSON.stringify(mockResults)),
|
||||
});
|
||||
|
||||
const client = new FirebaseClient();
|
||||
client.idToken = 'existing-token';
|
||||
|
||||
const result = await client.getLatestResults('2023-12-15', 7);
|
||||
|
||||
expect(result).toEqual({
|
||||
results: mockResults,
|
||||
date: '2023-12-13',
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('compareResults', () => {
|
||||
it('should handle null previous results', () => {
|
||||
const currentResults = [
|
||||
{library: 'lib1', platform: 'iOS', status: 'failed'},
|
||||
{library: 'lib2', platform: 'Android', status: 'success'},
|
||||
];
|
||||
|
||||
const result = compareResults(currentResults, null);
|
||||
|
||||
expect(result).toEqual({
|
||||
broken: [],
|
||||
recovered: [],
|
||||
newFailures: [{library: 'lib1', platform: 'iOS', status: 'failed'}],
|
||||
});
|
||||
});
|
||||
|
||||
it('should handle undefined previous results', () => {
|
||||
const currentResults = [
|
||||
{library: 'lib1', platform: 'iOS', status: 'failed'},
|
||||
];
|
||||
|
||||
const result = compareResults(currentResults, undefined);
|
||||
|
||||
expect(result).toEqual({
|
||||
broken: [],
|
||||
recovered: [],
|
||||
newFailures: [{library: 'lib1', platform: 'iOS', status: 'failed'}],
|
||||
});
|
||||
});
|
||||
|
||||
it('should identify broken tests', () => {
|
||||
const currentResults = [
|
||||
{library: 'lib1', platform: 'iOS', status: 'failed'},
|
||||
{library: 'lib2', platform: 'Android', status: 'success'},
|
||||
];
|
||||
|
||||
const previousResults = [
|
||||
{library: 'lib1', platform: 'iOS', status: 'success'},
|
||||
{library: 'lib2', platform: 'Android', status: 'success'},
|
||||
];
|
||||
|
||||
const result = compareResults(currentResults, previousResults);
|
||||
|
||||
expect(result.broken).toEqual([
|
||||
{
|
||||
library: 'lib1',
|
||||
platform: 'iOS',
|
||||
previousStatus: 'success',
|
||||
currentStatus: 'failed',
|
||||
},
|
||||
]);
|
||||
expect(result.recovered).toEqual([]);
|
||||
});
|
||||
|
||||
it('should identify recovered tests', () => {
|
||||
const currentResults = [
|
||||
{library: 'lib1', platform: 'iOS', status: 'success'},
|
||||
{library: 'lib2', platform: 'Android', status: 'success'},
|
||||
];
|
||||
|
||||
const previousResults = [
|
||||
{library: 'lib1', platform: 'iOS', status: 'failed'},
|
||||
{library: 'lib2', platform: 'Android', status: 'success'},
|
||||
];
|
||||
|
||||
const result = compareResults(currentResults, previousResults);
|
||||
|
||||
expect(result.broken).toEqual([]);
|
||||
expect(result.recovered).toEqual([
|
||||
{
|
||||
library: 'lib1',
|
||||
platform: 'iOS',
|
||||
previousStatus: 'failed',
|
||||
currentStatus: 'success',
|
||||
},
|
||||
]);
|
||||
});
|
||||
|
||||
it('should identify both broken and recovered tests', () => {
|
||||
const currentResults = [
|
||||
{library: 'lib1', platform: 'iOS', status: 'failed'},
|
||||
{library: 'lib2', platform: 'Android', status: 'success'},
|
||||
{library: 'lib3', platform: 'iOS', status: 'success'},
|
||||
];
|
||||
|
||||
const previousResults = [
|
||||
{library: 'lib1', platform: 'iOS', status: 'success'},
|
||||
{library: 'lib2', platform: 'Android', status: 'failed'},
|
||||
{library: 'lib3', platform: 'iOS', status: 'success'},
|
||||
];
|
||||
|
||||
const result = compareResults(currentResults, previousResults);
|
||||
|
||||
expect(result.broken).toEqual([
|
||||
{
|
||||
library: 'lib1',
|
||||
platform: 'iOS',
|
||||
previousStatus: 'success',
|
||||
currentStatus: 'failed',
|
||||
},
|
||||
]);
|
||||
expect(result.recovered).toEqual([
|
||||
{
|
||||
library: 'lib2',
|
||||
platform: 'Android',
|
||||
previousStatus: 'failed',
|
||||
currentStatus: 'success',
|
||||
},
|
||||
]);
|
||||
});
|
||||
|
||||
it('should handle tests that are not in previous results', () => {
|
||||
const currentResults = [
|
||||
{library: 'lib1', platform: 'iOS', status: 'failed'},
|
||||
{library: 'lib2', platform: 'Android', status: 'success'},
|
||||
];
|
||||
|
||||
const previousResults = [
|
||||
{library: 'lib1', platform: 'iOS', status: 'success'},
|
||||
];
|
||||
|
||||
const result = compareResults(currentResults, previousResults);
|
||||
|
||||
expect(result.broken).toEqual([
|
||||
{
|
||||
library: 'lib1',
|
||||
platform: 'iOS',
|
||||
previousStatus: 'success',
|
||||
currentStatus: 'failed',
|
||||
},
|
||||
]);
|
||||
expect(result.recovered).toEqual([]);
|
||||
});
|
||||
|
||||
it('should handle empty current results', () => {
|
||||
const currentResults = [];
|
||||
const previousResults = [
|
||||
{library: 'lib1', platform: 'iOS', status: 'success'},
|
||||
];
|
||||
|
||||
const result = compareResults(currentResults, previousResults);
|
||||
|
||||
expect(result.broken).toEqual([]);
|
||||
expect(result.recovered).toEqual([]);
|
||||
});
|
||||
|
||||
it('should handle empty previous results', () => {
|
||||
const currentResults = [
|
||||
{library: 'lib1', platform: 'iOS', status: 'failed'},
|
||||
];
|
||||
const previousResults = [];
|
||||
|
||||
const result = compareResults(currentResults, previousResults);
|
||||
|
||||
expect(result.broken).toEqual([]);
|
||||
expect(result.recovered).toEqual([]);
|
||||
// When previousResults is an empty array (not null/undefined),
|
||||
// the function doesn't return newFailures property
|
||||
expect(result.newFailures).toBeUndefined();
|
||||
});
|
||||
|
||||
it('should handle different status values', () => {
|
||||
const currentResults = [
|
||||
{library: 'lib1', platform: 'iOS', status: 'timeout'},
|
||||
{library: 'lib2', platform: 'Android', status: 'success'},
|
||||
];
|
||||
|
||||
const previousResults = [
|
||||
{library: 'lib1', platform: 'iOS', status: 'success'},
|
||||
{library: 'lib2', platform: 'Android', status: 'error'},
|
||||
];
|
||||
|
||||
const result = compareResults(currentResults, previousResults);
|
||||
|
||||
expect(result.broken).toEqual([
|
||||
{
|
||||
library: 'lib1',
|
||||
platform: 'iOS',
|
||||
previousStatus: 'success',
|
||||
currentStatus: 'timeout',
|
||||
},
|
||||
]);
|
||||
expect(result.recovered).toEqual([
|
||||
{
|
||||
library: 'lib2',
|
||||
platform: 'Android',
|
||||
previousStatus: 'error',
|
||||
currentStatus: 'success',
|
||||
},
|
||||
]);
|
||||
});
|
||||
});
|
||||
|
||||
describe('getYesterdayDate', () => {
|
||||
it("should return yesterday's date in YYYY-MM-DD format", () => {
|
||||
const mockDate = new Date('2023-12-15T10:30:00Z');
|
||||
jest.spyOn(global, 'Date').mockImplementation(() => mockDate);
|
||||
|
||||
const result = getYesterdayDate();
|
||||
|
||||
expect(result).toBe('2023-12-14');
|
||||
|
||||
global.Date.mockRestore();
|
||||
});
|
||||
|
||||
it('should handle month boundary correctly', () => {
|
||||
const mockDate = new Date('2023-12-01T10:30:00Z');
|
||||
jest.spyOn(global, 'Date').mockImplementation(() => mockDate);
|
||||
|
||||
const result = getYesterdayDate();
|
||||
|
||||
expect(result).toBe('2023-11-30');
|
||||
|
||||
global.Date.mockRestore();
|
||||
});
|
||||
|
||||
it('should handle year boundary correctly', () => {
|
||||
const mockDate = new Date('2024-01-01T10:30:00Z');
|
||||
jest.spyOn(global, 'Date').mockImplementation(() => mockDate);
|
||||
|
||||
const result = getYesterdayDate();
|
||||
|
||||
expect(result).toBe('2023-12-31');
|
||||
|
||||
global.Date.mockRestore();
|
||||
});
|
||||
});
|
||||
|
||||
describe('getTodayDate', () => {
|
||||
it("should return today's date in YYYY-MM-DD format", () => {
|
||||
const mockDate = new Date('2023-12-15T10:30:00Z');
|
||||
jest.spyOn(global, 'Date').mockImplementation(() => mockDate);
|
||||
|
||||
const result = getTodayDate();
|
||||
|
||||
expect(result).toBe('2023-12-15');
|
||||
|
||||
global.Date.mockRestore();
|
||||
});
|
||||
|
||||
it('should handle different times of day correctly', () => {
|
||||
const mockDate = new Date('2023-12-15T23:59:59Z');
|
||||
jest.spyOn(global, 'Date').mockImplementation(() => mockDate);
|
||||
|
||||
const result = getTodayDate();
|
||||
|
||||
expect(result).toBe('2023-12-15');
|
||||
|
||||
global.Date.mockRestore();
|
||||
});
|
||||
});
|
||||
@@ -0,0 +1,189 @@
|
||||
/**
|
||||
* Copyright (c) Meta Platforms, Inc. and affiliates.
|
||||
*
|
||||
* This source code is licensed under the MIT license found in the
|
||||
* LICENSE file in the root directory of this source tree.
|
||||
*
|
||||
* @format
|
||||
*/
|
||||
|
||||
'use strict';
|
||||
|
||||
const {
|
||||
prepareFailurePayload,
|
||||
sendMessageToDiscord,
|
||||
} = require('../notifyDiscord');
|
||||
|
||||
describe('prepareFailurePayload', () => {
|
||||
it('should handle undefined failures', () => {
|
||||
const message = prepareFailurePayload(undefined);
|
||||
expect(message).toEqual({
|
||||
content:
|
||||
'⚠️ **React Native Nightly Integration Failures** ⚠️\n\nNo failures to report.',
|
||||
});
|
||||
});
|
||||
|
||||
it('should handle empty failures array', () => {
|
||||
const message = prepareFailurePayload([]);
|
||||
expect(message).toEqual({
|
||||
content:
|
||||
'⚠️ **React Native Nightly Integration Failures** ⚠️\n\nNo failures to report.',
|
||||
});
|
||||
});
|
||||
|
||||
it('should format a single failure correctly', () => {
|
||||
const failures = [
|
||||
{
|
||||
library: 'react-native-reanimated',
|
||||
platform: 'iOS',
|
||||
},
|
||||
];
|
||||
|
||||
const message = prepareFailurePayload(failures);
|
||||
expect(message).toEqual({
|
||||
content:
|
||||
'⚠️ **React Native Nightly Integration Failures** ⚠️\n\nThe integration of libraries with React Native nightly failed for the following libraries:\n\n❌ [iOS] react-native-reanimated',
|
||||
});
|
||||
});
|
||||
|
||||
it('should sort multiple failures by platform and library name', () => {
|
||||
const failures = [
|
||||
{
|
||||
library: 'react-native-reanimated',
|
||||
platform: 'iOS',
|
||||
},
|
||||
{
|
||||
library: 'react-native-gesture-handler',
|
||||
platform: 'Android',
|
||||
},
|
||||
{
|
||||
library: 'react-native-screens',
|
||||
platform: 'iOS',
|
||||
},
|
||||
{
|
||||
library: 'react-native-svg',
|
||||
platform: 'Android',
|
||||
},
|
||||
];
|
||||
|
||||
const message = prepareFailurePayload(failures);
|
||||
|
||||
// The failures should be sorted: first Android (alphabetically), then iOS
|
||||
// Within each platform, libraries should be sorted alphabetically
|
||||
expect(message).toEqual({
|
||||
content:
|
||||
'⚠️ **React Native Nightly Integration Failures** ⚠️\n\nThe integration of libraries with React Native nightly failed for the following libraries:\n\n❌ [Android] react-native-gesture-handler\n❌ [Android] react-native-svg\n❌ [iOS] react-native-reanimated\n❌ [iOS] react-native-screens',
|
||||
});
|
||||
});
|
||||
|
||||
it('should handle failures with missing properties', () => {
|
||||
const failures = [
|
||||
{
|
||||
// Missing library
|
||||
platform: 'iOS',
|
||||
},
|
||||
{
|
||||
library: 'react-native-gesture-handler',
|
||||
// Missing platform
|
||||
},
|
||||
{
|
||||
// Both missing
|
||||
},
|
||||
];
|
||||
|
||||
const message = prepareFailurePayload(failures);
|
||||
|
||||
expect(message).toEqual({
|
||||
content:
|
||||
'⚠️ **React Native Nightly Integration Failures** ⚠️\n\nThe integration of libraries with React Native nightly failed for the following libraries:\n\n❌ [iOS] Unknown\n❌ [Unknown] react-native-gesture-handler\n❌ [Unknown] Unknown',
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('sendMessageToDiscord', () => {
|
||||
// Store the original fetch function
|
||||
const originalFetch = global.fetch;
|
||||
|
||||
// Setup and teardown for each test
|
||||
beforeEach(() => {
|
||||
// Mock the global fetch function
|
||||
global.fetch = jest.fn();
|
||||
// Silence console logs during tests
|
||||
jest.spyOn(console, 'log').mockImplementation(() => {});
|
||||
jest.spyOn(console, 'error').mockImplementation(() => {});
|
||||
});
|
||||
|
||||
afterEach(() => {
|
||||
// Restore the original fetch function
|
||||
global.fetch = originalFetch;
|
||||
// Restore console functions
|
||||
jest.restoreAllMocks();
|
||||
});
|
||||
|
||||
it('should throw an error if webhook URL is missing', async () => {
|
||||
await expect(sendMessageToDiscord(null, {})).rejects.toThrow(
|
||||
'Discord webhook URL is missing',
|
||||
);
|
||||
});
|
||||
|
||||
it('should send a message successfully', async () => {
|
||||
// Mock a successful response
|
||||
global.fetch.mockResolvedValueOnce({
|
||||
ok: true,
|
||||
status: 200,
|
||||
});
|
||||
|
||||
const webhook = 'https://discord.com/api/webhooks/123/abc';
|
||||
const message = {content: 'Test message'};
|
||||
|
||||
await expect(sendMessageToDiscord(webhook, message)).resolves.not.toThrow();
|
||||
|
||||
// Verify fetch was called with the right arguments
|
||||
expect(global.fetch).toHaveBeenCalledWith(webhook, {
|
||||
method: 'POST',
|
||||
headers: {
|
||||
'Content-Type': 'application/json',
|
||||
},
|
||||
body: JSON.stringify(message),
|
||||
});
|
||||
|
||||
// Verify console.log was called
|
||||
expect(console.log).toHaveBeenCalledWith(
|
||||
'Successfully sent message to Discord',
|
||||
);
|
||||
});
|
||||
|
||||
it('should throw an error if the response is not ok', async () => {
|
||||
// Mock a failed response
|
||||
global.fetch.mockResolvedValueOnce({
|
||||
ok: false,
|
||||
status: 400,
|
||||
text: jest.fn().mockResolvedValueOnce('Bad Request'),
|
||||
});
|
||||
|
||||
const webhook = 'https://discord.com/api/webhooks/123/abc';
|
||||
const message = {content: 'Test message'};
|
||||
|
||||
await expect(sendMessageToDiscord(webhook, message)).rejects.toThrow(
|
||||
'HTTP status code: 400',
|
||||
);
|
||||
|
||||
// Verify console.error was called
|
||||
expect(console.error).toHaveBeenCalledWith(
|
||||
'Failed to send message to Discord: 400 Bad Request',
|
||||
);
|
||||
});
|
||||
|
||||
it('should throw an error if fetch fails', async () => {
|
||||
// Mock a network error
|
||||
const networkError = new Error('Network error');
|
||||
global.fetch.mockRejectedValueOnce(networkError);
|
||||
|
||||
const webhook = 'https://discord.com/api/webhooks/123/abc';
|
||||
const message = {content: 'Test message'};
|
||||
|
||||
await expect(sendMessageToDiscord(webhook, message)).rejects.toThrow(
|
||||
'Network error',
|
||||
);
|
||||
});
|
||||
});
|
||||
@@ -0,0 +1,174 @@
|
||||
/**
|
||||
* Copyright (c) Meta Platforms, Inc. and affiliates.
|
||||
*
|
||||
* This source code is licensed under the MIT license found in the
|
||||
* LICENSE file in the root directory of this source tree.
|
||||
*
|
||||
* @format
|
||||
*/
|
||||
|
||||
const fs = require('fs');
|
||||
const path = require('path');
|
||||
const {
|
||||
prepareFailurePayload,
|
||||
prepareComparisonPayload,
|
||||
sendMessageToDiscord,
|
||||
} = require('./notifyDiscord');
|
||||
const {
|
||||
FirebaseClient,
|
||||
compareResults,
|
||||
getYesterdayDate,
|
||||
getTodayDate,
|
||||
} = require('./firebaseUtils');
|
||||
|
||||
function readOutcomes() {
|
||||
const baseDir = '/tmp';
|
||||
let outcomes = [];
|
||||
fs.readdirSync(baseDir).forEach(file => {
|
||||
const fullPath = path.join(baseDir, file);
|
||||
if (fullPath.endsWith('outcome') && fs.statSync(fullPath).isDirectory) {
|
||||
fs.readdirSync(fullPath).forEach(subFile => {
|
||||
const subFullPath = path.join(fullPath, subFile);
|
||||
if (subFullPath.endsWith('outcome')) {
|
||||
const [library, status] = String(fs.readFileSync(subFullPath, 'utf8'))
|
||||
.trim()
|
||||
.split(':');
|
||||
const platform = subFile.includes('android') ? 'Android' : 'iOS';
|
||||
console.log(
|
||||
`[${platform}] ${library} completed with status ${status}`,
|
||||
);
|
||||
outcomes.push({
|
||||
library: library.trim(),
|
||||
platform,
|
||||
status: status.trim(),
|
||||
});
|
||||
}
|
||||
});
|
||||
} else if (fullPath.endsWith('outcome')) {
|
||||
const [library, status] = String(fs.readFileSync(fullPath, 'utf8'))
|
||||
.trim()
|
||||
.split(':');
|
||||
const platform = file.includes('android') ? 'Android' : 'iOS';
|
||||
console.log(`[${platform}] ${library} completed with status ${status}`);
|
||||
outcomes.push({
|
||||
library: library.trim(),
|
||||
platform,
|
||||
status: status.trim(),
|
||||
});
|
||||
}
|
||||
});
|
||||
return outcomes;
|
||||
}
|
||||
|
||||
function printFailures(outcomes) {
|
||||
console.log('Printing failures...');
|
||||
let failedLibraries = [];
|
||||
outcomes.forEach(entry => {
|
||||
if (entry.status !== 'success') {
|
||||
console.log(
|
||||
`❌ [${entry.platform}] ${entry.library} failed with status ${entry.status}`,
|
||||
);
|
||||
failedLibraries.push({
|
||||
library: entry.library,
|
||||
platform: entry.platform,
|
||||
});
|
||||
}
|
||||
});
|
||||
return failedLibraries;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sends a message to Discord with the list of failures.
|
||||
* @param {string} webHook - The Discord webhook URL
|
||||
* @param {Array<Object>} failures - List of failures to report
|
||||
* @returns {Promise<void>} - A promise that resolves when the message is sent
|
||||
*/
|
||||
async function notifyDiscord(webHook, failures) {
|
||||
if (!webHook) {
|
||||
console.error('Discord webhook URL is missing');
|
||||
return;
|
||||
}
|
||||
|
||||
if (!failures || failures.length === 0) {
|
||||
console.log('No failures to report to Discord');
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
// Use the prepareFailurePayload function to format the message
|
||||
const message = prepareFailurePayload(failures);
|
||||
|
||||
// Use the sendMessageToDiscord function to send the message
|
||||
await sendMessageToDiscord(webHook, message);
|
||||
} catch (error) {
|
||||
console.error('Error in notifyDiscord function:', error);
|
||||
throw error;
|
||||
}
|
||||
}
|
||||
|
||||
async function collectResults(discordWebHook) {
|
||||
const outcomes = readOutcomes();
|
||||
const failures = printFailures(outcomes);
|
||||
|
||||
// Send failure notification if there are current failures
|
||||
if (failures.length > 0) {
|
||||
if (discordWebHook) {
|
||||
console.log('Sending current failures to Discord...');
|
||||
await notifyDiscord(discordWebHook, failures);
|
||||
} else {
|
||||
console.log('Discord webhook not set');
|
||||
}
|
||||
}
|
||||
|
||||
// Initialize Firebase client
|
||||
const firebaseClient = new FirebaseClient();
|
||||
const today = getTodayDate();
|
||||
|
||||
try {
|
||||
// Store today's results in Firebase
|
||||
console.log(`Storing results for ${today} in Firebase...`);
|
||||
await firebaseClient.storeResults(today, outcomes);
|
||||
|
||||
// Get the most recent previous results for comparison
|
||||
console.log(`Looking for most recent previous results before ${today}...`);
|
||||
const {results: previousResults, date: previousDate} =
|
||||
await firebaseClient.getLatestResults(today);
|
||||
|
||||
let broken = [];
|
||||
let recovered = [];
|
||||
|
||||
if (previousResults) {
|
||||
console.log(`Comparing with results from ${previousDate}`);
|
||||
// Compare results and identify broken/recovered jobs
|
||||
const comparison = compareResults(outcomes, previousResults);
|
||||
broken = comparison.broken;
|
||||
recovered = comparison.recovered;
|
||||
|
||||
console.log(
|
||||
`Found ${broken.length} newly broken jobs and ${recovered.length} recovered jobs compared to ${previousDate}`,
|
||||
);
|
||||
} else {
|
||||
console.log(
|
||||
'No previous results found for comparison - this might be the first run or no recent data available',
|
||||
);
|
||||
}
|
||||
|
||||
// Send comparison message to Discord if there are changes
|
||||
if (discordWebHook && (broken.length > 0 || recovered.length > 0)) {
|
||||
console.log('Sending comparison results to Discord...');
|
||||
const comparisonMessage = prepareComparisonPayload(broken, recovered);
|
||||
await sendMessageToDiscord(discordWebHook, comparisonMessage);
|
||||
}
|
||||
|
||||
console.log('✅ All tests passed!');
|
||||
} catch (error) {
|
||||
console.error('Error in collectResults:', error);
|
||||
// If Firebase fails but there are no test failures, don't fail the workflow
|
||||
console.log('⚠️ Firebase operations failed, but all tests passed');
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = {
|
||||
collectResults,
|
||||
notifyDiscord,
|
||||
};
|
||||
@@ -0,0 +1,261 @@
|
||||
/**
|
||||
* Copyright (c) Meta Platforms, Inc. and affiliates.
|
||||
*
|
||||
* This source code is licensed under the MIT license found in the
|
||||
* LICENSE file in the root directory of this source tree.
|
||||
*
|
||||
* @format
|
||||
*/
|
||||
|
||||
// We connect to firebase using a plain HTTP request because we don't want to
|
||||
// add yet another devDependency to the react-native monorepo.
|
||||
class FirebaseClient {
|
||||
constructor() {
|
||||
this.email = process.env.FIREBASE_APP_EMAIL;
|
||||
this.password = process.env.FIREBASE_APP_PASS;
|
||||
this.apiKey = process.env.FIREBASE_APP_APIKEY;
|
||||
this.projectId = process.env.FIREBASE_APP_PROJECTNAME;
|
||||
this.databaseUrl = `${this.projectId}-default-rtdb.firebaseio.com`;
|
||||
this.idToken = null;
|
||||
}
|
||||
|
||||
async authenticate() {
|
||||
if (!this.email || !this.password) {
|
||||
throw new Error(
|
||||
'Firebase credentials not found in environment variables',
|
||||
);
|
||||
}
|
||||
|
||||
const authData = {
|
||||
email: this.email,
|
||||
password: this.password,
|
||||
returnSecureToken: true,
|
||||
};
|
||||
|
||||
const response = await this.makeRequest(
|
||||
'identitytoolkit.googleapis.com',
|
||||
`/v1/accounts:signInWithPassword?key=${this.apiKey}`,
|
||||
'POST',
|
||||
authData,
|
||||
);
|
||||
|
||||
this.idToken = response.idToken;
|
||||
return;
|
||||
}
|
||||
|
||||
/**
|
||||
* Make a database request for a specific date
|
||||
* @param {string} date - Date in YYYY-MM-DD format
|
||||
* @param {string} method - HTTP method
|
||||
* @param {*} data - Data to send (optional)
|
||||
* @returns {Promise<*>} - Response data
|
||||
*/
|
||||
async makeDatabaseRequest(date, method, data = null) {
|
||||
if (!this.idToken) {
|
||||
await this.authenticate();
|
||||
}
|
||||
|
||||
const path = `/nightly-results/${date}.json?auth=${this.idToken}`;
|
||||
return this.makeRequest(this.databaseUrl, path, method, data);
|
||||
}
|
||||
|
||||
/**
|
||||
* Store test results for a specific date
|
||||
* @param {string} date - Date in YYYY-MM-DD format
|
||||
* @param {Array<Object>} results - Array of test results
|
||||
* @returns {Promise<void>}
|
||||
*/
|
||||
async storeResults(date, results) {
|
||||
await this.makeDatabaseRequest(date, 'PUT', results);
|
||||
console.log(`Successfully stored results for ${date}`);
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieve test results for a specific date
|
||||
* @param {string} date - Date in YYYY-MM-DD format
|
||||
* @returns {Promise<Array<Object>|null>} - Array of test results or null if not found
|
||||
*/
|
||||
async getResults(date) {
|
||||
try {
|
||||
return await this.makeDatabaseRequest(date, 'GET');
|
||||
} catch (error) {
|
||||
if (error.message.includes('404')) {
|
||||
return null; // No results found for this specific date.
|
||||
}
|
||||
throw error;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Find the most recent available job results before the given date
|
||||
* @param {string} currentDate - Current date in YYYY-MM-DD format
|
||||
* @param {number} maxDaysBack - Maximum number of days to look back (default: 7)
|
||||
* @returns {Promise<{results: Array<Object>|null, date: string|null}>} - Most recent results and their date
|
||||
*/
|
||||
async getLatestResults(currentDate, maxDaysBack = 7) {
|
||||
if (!this.idToken) {
|
||||
await this.authenticate();
|
||||
}
|
||||
|
||||
const currentDateObj = new Date(currentDate);
|
||||
|
||||
for (let daysBack = 1; daysBack <= maxDaysBack; daysBack++) {
|
||||
const checkDate = new Date(currentDateObj);
|
||||
checkDate.setDate(checkDate.getDate() - daysBack);
|
||||
const checkDateStr = checkDate.toISOString().split('T')[0];
|
||||
|
||||
console.log(
|
||||
`Checking for results on ${checkDateStr} (${daysBack} days back)...`,
|
||||
);
|
||||
|
||||
try {
|
||||
const results = await this.getResults(checkDateStr);
|
||||
if (results && results.length > 0) {
|
||||
console.log(
|
||||
`Found results from ${checkDateStr} (${daysBack} days back)`,
|
||||
);
|
||||
return {results, date: checkDateStr};
|
||||
}
|
||||
} catch (error) {
|
||||
console.log(`No results found for ${checkDateStr}: ${error.message}`);
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
console.log(
|
||||
`No previous results found within the last ${maxDaysBack} days`,
|
||||
);
|
||||
return {results: null, date: null};
|
||||
}
|
||||
|
||||
async makeRequest(hostname, path, method, data = null) {
|
||||
const url = `https://${hostname}${path}`;
|
||||
const options = {
|
||||
method,
|
||||
headers: {
|
||||
'Content-Type': 'application/json',
|
||||
},
|
||||
};
|
||||
|
||||
if (data) {
|
||||
options.body = JSON.stringify(data);
|
||||
}
|
||||
|
||||
const response = await fetch(url, options);
|
||||
const responseText = await response.text();
|
||||
|
||||
if (!response.ok) {
|
||||
let errorMessage;
|
||||
try {
|
||||
const parsedError = JSON.parse(responseText);
|
||||
errorMessage = parsedError.error?.message || responseText;
|
||||
} catch {
|
||||
errorMessage = responseText;
|
||||
}
|
||||
throw new Error(`HTTP ${response.status}: ${errorMessage}`);
|
||||
}
|
||||
|
||||
try {
|
||||
return JSON.parse(responseText);
|
||||
} catch {
|
||||
return responseText;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Compare current results with previous day's results
|
||||
* @param {Array<Object>} currentResults - Today's test results
|
||||
* @param {Array<Object>} previousResults - Yesterday's test results
|
||||
* @returns {Object} - Object containing broken and recovered tests
|
||||
*/
|
||||
function compareResults(currentResults, previousResults) {
|
||||
if (!previousResults) {
|
||||
return {
|
||||
broken: [],
|
||||
recovered: [],
|
||||
newFailures: currentResults.filter(result => result.status !== 'success'),
|
||||
};
|
||||
}
|
||||
|
||||
// Create maps for easier lookup
|
||||
const currentMap = new Map();
|
||||
const previousMap = new Map();
|
||||
|
||||
currentResults.forEach(result => {
|
||||
const key = `${result.library}-${result.platform}`;
|
||||
currentMap.set(key, result);
|
||||
});
|
||||
|
||||
previousResults.forEach(result => {
|
||||
const key = `${result.library}-${result.platform}`;
|
||||
previousMap.set(key, result);
|
||||
});
|
||||
|
||||
const broken = [];
|
||||
const recovered = [];
|
||||
|
||||
// Check for broken tests (was success, now failed)
|
||||
for (const [key, currentResult] of currentMap) {
|
||||
const previousResult = previousMap.get(key);
|
||||
if (previousResult) {
|
||||
if (
|
||||
previousResult.status === 'success' &&
|
||||
currentResult.status !== 'success'
|
||||
) {
|
||||
broken.push({
|
||||
library: currentResult.library,
|
||||
platform: currentResult.platform,
|
||||
previousStatus: previousResult.status,
|
||||
currentStatus: currentResult.status,
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Check for recovered tests (was failed, now success)
|
||||
for (const [key, currentResult] of currentMap) {
|
||||
const previousResult = previousMap.get(key);
|
||||
if (previousResult) {
|
||||
if (
|
||||
previousResult.status !== 'success' &&
|
||||
currentResult.status === 'success'
|
||||
) {
|
||||
recovered.push({
|
||||
library: currentResult.library,
|
||||
platform: currentResult.platform,
|
||||
previousStatus: previousResult.status,
|
||||
currentStatus: currentResult.status,
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return {broken, recovered};
|
||||
}
|
||||
|
||||
/**
|
||||
* Get yesterday's date in YYYY-MM-DD format
|
||||
* @returns {string} - Yesterday's date
|
||||
*/
|
||||
function getYesterdayDate() {
|
||||
const yesterday = new Date();
|
||||
yesterday.setDate(yesterday.getDate() - 1);
|
||||
return yesterday.toISOString().split('T')[0];
|
||||
}
|
||||
|
||||
/**
|
||||
* Get today's date in YYYY-MM-DD format
|
||||
* @returns {string} - Today's date
|
||||
*/
|
||||
function getTodayDate() {
|
||||
const today = new Date();
|
||||
return today.toISOString().split('T')[0];
|
||||
}
|
||||
|
||||
module.exports = {
|
||||
FirebaseClient,
|
||||
compareResults,
|
||||
getYesterdayDate,
|
||||
getTodayDate,
|
||||
};
|
||||
@@ -87,11 +87,11 @@ async function launchAppOnSimulator(appId, udid, isDebug) {
|
||||
|
||||
function startVideoRecording(jsengine, currentAttempt) {
|
||||
console.log(
|
||||
`Start video record using pid: video_record_${currentAttempt}.pid`,
|
||||
`Start video record using pid: video_record_${jsengine}_${currentAttempt}.pid`,
|
||||
);
|
||||
|
||||
const recordingArgs =
|
||||
`simctl io booted recordVideo video_record_${currentAttempt}.mov`.split(
|
||||
`simctl io booted recordVideo video_record_${jsengine}_${currentAttempt}.mov`.split(
|
||||
' ',
|
||||
);
|
||||
const recordingProcess = childProcess.spawn('xcrun', recordingArgs, {
|
||||
|
||||
@@ -0,0 +1,136 @@
|
||||
/**
|
||||
* Copyright (c) Meta Platforms, Inc. and affiliates.
|
||||
*
|
||||
* This source code is licensed under the MIT license found in the
|
||||
* LICENSE file in the root directory of this source tree.
|
||||
*
|
||||
* @format
|
||||
*/
|
||||
|
||||
/**
|
||||
* Sends a message to Discord using the webhook URL.
|
||||
* @param {string} webHook - The Discord webhook URL
|
||||
* @param {Object} message - The message to send
|
||||
* @returns {Promise<void>} - A promise that resolves when the message is sent
|
||||
*/
|
||||
async function sendMessageToDiscord(webHook, message) {
|
||||
if (!webHook) {
|
||||
throw new Error('Discord webhook URL is missing');
|
||||
}
|
||||
|
||||
// Send the request using fetch
|
||||
const response = await fetch(webHook, {
|
||||
method: 'POST',
|
||||
headers: {
|
||||
'Content-Type': 'application/json',
|
||||
},
|
||||
body: JSON.stringify(message),
|
||||
});
|
||||
|
||||
// Handle the response
|
||||
if (response.ok) {
|
||||
console.log('Successfully sent message to Discord');
|
||||
return;
|
||||
} else {
|
||||
const errorText = await response.text();
|
||||
console.error(
|
||||
`Failed to send message to Discord: ${response.status} ${errorText}`,
|
||||
);
|
||||
throw new Error(`HTTP status code: ${response.status}`);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Sorts jobs by platform first, then by library name.
|
||||
* @param {Array<Object>} jobs - Array of jobs with platform and library properties
|
||||
* @returns {Array<Object>} - Sorted array of jobs
|
||||
*/
|
||||
function sortResultsByPlatformAndLibrary(jobs) {
|
||||
return [...jobs].sort((a, b) => {
|
||||
// First sort by platform
|
||||
const platformA = a.platform || 'Unknown';
|
||||
const platformB = b.platform || 'Unknown';
|
||||
|
||||
if (platformA !== platformB) {
|
||||
return platformA.localeCompare(platformB);
|
||||
}
|
||||
|
||||
// Then sort by library name
|
||||
const libraryA = a.library || 'Unknown';
|
||||
const libraryB = b.library || 'Unknown';
|
||||
return libraryA.localeCompare(libraryB);
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Prepares a formatted Discord message payload from a list of failures.
|
||||
* @param {Array<Object>} failures - List of failures to format
|
||||
* @returns {Object} - The formatted Discord message payload
|
||||
*/
|
||||
function prepareFailurePayload(failures) {
|
||||
if (!failures || failures.length === 0) {
|
||||
return {
|
||||
content:
|
||||
'⚠️ **React Native Nightly Integration Failures** ⚠️\n\nNo failures to report.',
|
||||
};
|
||||
}
|
||||
|
||||
// Sort failures by platform and then by library name
|
||||
const sortedFailures = sortResultsByPlatformAndLibrary(failures);
|
||||
|
||||
// Format the failures into a message
|
||||
const formattedFailures = sortedFailures
|
||||
.map(failure => {
|
||||
const library = failure.library || 'Unknown';
|
||||
const platform = failure.platform || 'Unknown';
|
||||
return `❌ [${platform}] ${library}`;
|
||||
})
|
||||
.join('\n');
|
||||
|
||||
return {
|
||||
content: `⚠️ **React Native Nightly Integration Failures** ⚠️\n\nThe integration of libraries with React Native nightly failed for the following libraries:\n\n${formattedFailures}`,
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Prepares a formatted Discord message payload for broken and recovered nightly jobs.
|
||||
* @param {Array<Object>} broken - List of newly broken jobs
|
||||
* @param {Array<Object>} recovered - List of recovered jobs
|
||||
* @returns {Object} - The formatted Discord message payload
|
||||
*/
|
||||
function prepareComparisonPayload(broken, recovered) {
|
||||
let content = '📊 **React Native Nightly Integration Status Update** 📊\n\n';
|
||||
|
||||
if (broken.length === 0 && recovered.length === 0) {
|
||||
content +=
|
||||
'No changes from yesterday - all nightly jobs maintained their previous status.';
|
||||
} else {
|
||||
if (broken.length > 0) {
|
||||
content += '🔴 **Newly Broken Jobs:**\n';
|
||||
const sortedBroken = sortResultsByPlatformAndLibrary(broken);
|
||||
|
||||
sortedBroken.forEach(job => {
|
||||
content += `❌ [${job.platform}] ${job.library} (was ${job.previousStatus}, now ${job.currentStatus})\n`;
|
||||
});
|
||||
content += '\n';
|
||||
}
|
||||
|
||||
if (recovered.length > 0) {
|
||||
content += '🟢 **Recovered Jobs:**\n';
|
||||
const sortedRecovered = sortResultsByPlatformAndLibrary(recovered);
|
||||
|
||||
sortedRecovered.forEach(job => {
|
||||
content += `✅ [${job.platform}] ${job.library} (was ${job.previousStatus}, now ${job.currentStatus})\n`;
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
return {content};
|
||||
}
|
||||
|
||||
// Export the functions using CommonJS syntax
|
||||
module.exports = {
|
||||
prepareFailurePayload,
|
||||
prepareComparisonPayload,
|
||||
sendMessageToDiscord,
|
||||
};
|
||||
@@ -0,0 +1,38 @@
|
||||
# This jobs runs every day 2 hours after the nightly job and its purpose is to report
|
||||
# a failure in case the nightly failed to be published. We are going to hook this to an internal automation.
|
||||
name: Check Nightlies
|
||||
|
||||
on:
|
||||
workflow_dispatch:
|
||||
# nightly build @ 4:15 AM UTC
|
||||
schedule:
|
||||
- cron: '15 4 * * *'
|
||||
|
||||
jobs:
|
||||
check-nightly:
|
||||
runs-on: ubuntu-latest
|
||||
if: github.repository == 'facebook/react-native'
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v4
|
||||
- name: Check nightly
|
||||
run: |
|
||||
TODAY=$(date "+%Y%m%d")
|
||||
echo "Checking nightly for $TODAY"
|
||||
NIGHTLY="$(npm view react-native | grep $TODAY)"
|
||||
if [[ -z $NIGHTLY ]]; then
|
||||
echo 'Nightly job failed.'
|
||||
exit 1
|
||||
else
|
||||
echo 'Nightly Worked, All Good!'
|
||||
fi
|
||||
|
||||
test-libraries:
|
||||
uses: ./.github/workflows/test-libraries-on-nightlies.yml
|
||||
needs: check-nightly
|
||||
secrets:
|
||||
discord_webhook_url: ${{ secrets.NIGHTLY_DISCORD_WEBHOOK }}
|
||||
firebase_app_email: ${{ secrets.FIREBASE_APP_EMAIL }}
|
||||
firebase_app_pass: ${{ secrets.FIREBASE_APP_PASS }}
|
||||
firebase_app_projectname: ${{ secrets.FIREBASE_APP_PROJECTNAME }}
|
||||
firebase_app_apikey: ${{ secrets.FIREBASE_APP_APIKEY }}
|
||||
@@ -179,9 +179,8 @@ jobs:
|
||||
- name: Compress and Rename dSYM
|
||||
if: steps.restore-xcframework.outputs.cache-hit != 'true'
|
||||
run: |
|
||||
cd packages/react-native/third-party/Symbols/
|
||||
tar -cz -f ../ReactNativeDependencies${{ matrix.flavor }}.framework.dSYM.tar.gz .
|
||||
mv ../ReactNativeDependencies${{ matrix.flavor }}.framework.dSYM.tar.gz ./ReactNativeDependencies${{ matrix.flavor }}.framework.dSYM.tar.gz
|
||||
tar -cz -f packages/react-native/third-party/Symbols/ReactNativeDependencies${{ matrix.flavor }}.framework.dSYM.tar.gz \
|
||||
packages/react-native/third-party/Symbols/ReactNativeDependencies.framework.dSYM
|
||||
- name: Upload XCFramework Artifact
|
||||
uses: actions/upload-artifact@v4
|
||||
with:
|
||||
|
||||
@@ -246,11 +246,6 @@ jobs:
|
||||
- name: Print ReactCore folder
|
||||
shell: bash
|
||||
run: ls -lR /tmp/ReactCore
|
||||
- name: Configure git
|
||||
shell: bash
|
||||
run: |
|
||||
git config --global user.email "react-native-bot@meta.com"
|
||||
git config --global user.name "React Native Bot"
|
||||
- name: Prepare artifacts
|
||||
run: |
|
||||
REACT_NATIVE_PKG=$(find /tmp/react-native-tmp -type f -name "*.tgz")
|
||||
|
||||
@@ -0,0 +1,113 @@
|
||||
name: Test Libraries on Nightlies
|
||||
|
||||
on:
|
||||
workflow_call:
|
||||
secrets:
|
||||
discord_webhook_url:
|
||||
required: true
|
||||
firebase_app_email:
|
||||
required: true
|
||||
firebase_app_pass:
|
||||
required: true
|
||||
firebase_app_apikey:
|
||||
required: true
|
||||
firebase_app_projectname:
|
||||
required: true
|
||||
|
||||
|
||||
# We use the matrix.library entry to specify the dependency we want to use
|
||||
# The key is used directly as the <pkg> in the `yarn add <pkg>` command.
|
||||
jobs:
|
||||
runner-setup:
|
||||
runs-on: ubuntu-latest
|
||||
outputs:
|
||||
runners: '{"ios":"macos-14-large", "android": "ubuntu-latest"}'
|
||||
steps:
|
||||
- run: echo no-op
|
||||
|
||||
test-library-on-nightly:
|
||||
name: "[${{ matrix.platform }}] ${{ matrix.library }}"
|
||||
needs: runner-setup
|
||||
runs-on: ${{ fromJSON(needs.runner-setup.outputs.runners)[matrix.platform] }}
|
||||
continue-on-error: true
|
||||
strategy:
|
||||
matrix:
|
||||
library: [
|
||||
"react-native-async-storage",
|
||||
"react-native-blob-util",
|
||||
"@react-native-clipboard/clipboard",
|
||||
"@react-native-community/datetimepicker",
|
||||
"react-native-gesture-handler",
|
||||
"react-native-image-picker",
|
||||
"react-native-linear-gradient",
|
||||
"@react-native-masked-view/masked-view",
|
||||
# "react-native-maps", React Native Maps with the New Arch support has a complex cocoapods setup for iOS. It needs a dedicated workflow.
|
||||
"@react-native-community/netinfo",
|
||||
"react-native-reanimated@nightly react-native-worklets@nightly", #reanimated requires worklet to be explicitly installed as a separate package
|
||||
"react-native-svg",
|
||||
"react-native-video",
|
||||
"react-native-webview",
|
||||
"react-native-mmkv",
|
||||
"react-native-screens",
|
||||
"react-native-pager-view",
|
||||
"@react-native-community/slider",
|
||||
# additional OSS libs used internally
|
||||
"scandit-react-native-datacapture-barcode scandit-react-native-datacapture-core",
|
||||
"react-native-contacts",
|
||||
"react-native-device-info",
|
||||
"react-native-email-link",
|
||||
"@dr.pogodin/react-native-fs",
|
||||
"react-native-permissions",
|
||||
"react-native-vector-icons",
|
||||
"react-native-masked-view",
|
||||
"@react-native-community/image-editor",
|
||||
]
|
||||
platform: [ios, android]
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v4
|
||||
- name: Set up Node.js
|
||||
uses: ./.github/actions/setup-node
|
||||
- name: Test ${{ matrix.library }}
|
||||
id: run-test
|
||||
uses: ./.github/actions/test-library-on-nightly
|
||||
with:
|
||||
library-npm-package: ${{ matrix.library }}
|
||||
platform: ${{ matrix.platform}}
|
||||
- name: Save outcome
|
||||
id: save-outcome
|
||||
if: always()
|
||||
run: |
|
||||
LIB_FOLDER=$(echo "${{matrix.library}}" | tr ' ' '_' | tr '/' '_')
|
||||
echo "${{matrix.library}}: ${{steps.run-test.outcome}}" > "/tmp/$LIB_FOLDER-${{ matrix.platform }}-outcome"
|
||||
echo "lib_folder=$LIB_FOLDER" >> $GITHUB_OUTPUT
|
||||
- name: Upload Artifact
|
||||
if: always()
|
||||
uses: actions/upload-artifact@v4
|
||||
with:
|
||||
name: ${{ steps.save-outcome.outputs.lib_folder }}-${{ matrix.platform }}-outcome
|
||||
path: /tmp/${{ steps.save-outcome.outputs.lib_folder }}-${{ matrix.platform }}-outcome
|
||||
|
||||
|
||||
collect-results:
|
||||
runs-on: ubuntu-latest
|
||||
needs: [test-library-on-nightly]
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v4
|
||||
- name: Restore outcomes
|
||||
uses: actions/download-artifact@v4
|
||||
with:
|
||||
pattern: '*-outcome'
|
||||
path: /tmp
|
||||
- name: Collect failures
|
||||
uses: actions/github-script@v6
|
||||
env:
|
||||
FIREBASE_APP_EMAIL: ${{ secrets.firebase_app_email }}
|
||||
FIREBASE_APP_PASS: ${{ secrets.firebase_app_pass }}
|
||||
FIREBASE_APP_APIKEY: ${{ secrets.firebase_app_apikey }}
|
||||
FIREBASE_APP_PROJECTNAME: ${{ secrets.firebase_app_projectname }}
|
||||
with:
|
||||
script: |
|
||||
const {collectResults} = require('./.github/workflow-scripts/collectNightlyOutcomes.js');
|
||||
await collectResults('${{secrets.discord_webhook_url}}');
|
||||
@@ -1,27 +1,5 @@
|
||||
# Changelog (pre 0.80)
|
||||
|
||||
## v0.79.6
|
||||
|
||||
### Added
|
||||
|
||||
#### Android specific
|
||||
|
||||
- **RNGP** Add support for `exclusiveEnterpriseRepository` ([df5ac988ce](https://github.com/facebook/react-native/commit/df5ac988cec936c430d41b0fcc15181dc06e46a1) by [@cortinico](https://github.com/cortinico))
|
||||
|
||||
#### iOS specific
|
||||
|
||||
- **Cocoapods:** Add the ENTERPRISE_REPOSITORY env var to let user consume artifacts from their personal maven mirror ([a74d930c93](https://github.com/facebook/react-native/commit/a74d930c93ffae8c02142e8cc016a4c390a5f784) by [@cipolleschi](https://github.com/cipolleschi))
|
||||
|
||||
### Fixed
|
||||
|
||||
- **Codegen:** Add missing Babel dependencies ([bf2c3af93b](https://github.com/facebook/react-native/commit/bf2c3af93b146943cb35866fa9badcd188e63f5b) by [@tido64](https://github.com/tido64))
|
||||
|
||||
#### Android specific
|
||||
|
||||
- **Legacy Arch:** Fix Legacy arch crashing or freezing upon reload ([db600b2e9e](https://github.com/facebook/react-native/commit/db600b2e9e87863cad6dd5ce262dc1f793bcaeb0) by [@robhogan](https://github.com/robhogan))
|
||||
- **Modal:** Fix Modal first frame being rendered on top-left corner ([5a315f8d6b](https://github.com/facebook/react-native/commit/5a315f8d6b0ea54442c7ef94b7346b0c73fd0b4c) by [@cortinico](https://github.com/cortinico))
|
||||
- **TurboModule:** Fix emitting event from turbo module crashes on 32bit android ([43bc43e5e8](https://github.com/facebook/react-native/commit/43bc43e5e85519d2924c4fc80765e66d0c48b1a9) by [@vladimirivanoviliev](https://github.com/vladimirivanoviliev))
|
||||
|
||||
## v0.79.5
|
||||
|
||||
### Fixed
|
||||
|
||||
+73
-24
@@ -1,6 +1,71 @@
|
||||
# Changelog
|
||||
|
||||
## v0.81.0
|
||||
## v0.81.0-rc.5
|
||||
|
||||
### Fixed
|
||||
|
||||
#### Android specific
|
||||
- **Runtime:** Fixed `ReactHostImpl.nativeModules` always returning an empty list ([2f46a49](https://github.com/facebook/react-native/commit/2f46a49b8d8a11d5cf4342eee83c469b545c6779) by [@lukmccall](https://github.com/lukmccall))
|
||||
|
||||
## v0.81.0-rc.4 - Burned
|
||||
|
||||
## v0.81.0-rc.3
|
||||
|
||||
### Changed
|
||||
|
||||
- **Metro:** Metro to ^0.83.1 ([e247be793c](https://github.com/facebook/react-native/commit/e247be793c70a374955d798d8cbbc6eba58080ec) by [@motiz88](https://github.com/motiz88))
|
||||
|
||||
### Fixed
|
||||
|
||||
#### Android specific
|
||||
|
||||
- **rngp:** Fix a race condition with codegen libraries missing sources ([9013a9e666](https://github.com/facebook/react-native/commit/9013a9e66629677c47e1b69703f9fc8f4cbc1c2c) by [@cortinico](https://github.com/cortinico))
|
||||
- **API:** Make accessors inside HeadlessJsTaskService open again ([7ef57163cb](https://github.com/facebook/react-native/commit/7ef57163cb016317e43e563da7ea181989f6abca) by [@cortinico](https://github.com/cortinico))
|
||||
|
||||
## v0.81.0-rc.2
|
||||
|
||||
### Changed
|
||||
|
||||
- **API:** `NewAppScreen` no longer internally handles device safe area, use optional `safeAreaInsets` prop (aligned in 0.81 template) ([732bd12dc2](https://github.com/facebook/react-native/commit/732bd12dc21460641ef01b23f2eb722f26b060d5) by [@huntie](https://github.com/huntie))
|
||||
- **Babel:** Added support to `react-native/babel-preset` for a `hermesParserOptions` option, that expects an object that enables overriding `hermes-parser` options. ([0508eddfe6](https://github.com/facebook/react-native/commit/0508eddfe60df60cb3bfa4074ae199bd0e492d5f) by [@yungsters](https://github.com/yungsters))
|
||||
|
||||
### Fixed
|
||||
|
||||
#### iOS specific
|
||||
|
||||
- **Podspec:** Fixed issue with RNDeps release/debug switch failing ([4ee2b60a1e](https://github.com/facebook/react-native/commit/4ee2b60a1eacca744d58a7ad336ca9d3714289f6) by [@chrfalch](https://github.com/chrfalch))
|
||||
- **Podspec:** Fixed missing script for resolving prebuilt xcframework when switching between release/debug ([2e55241a90](https://github.com/facebook/react-native/commit/2e55241a901b4cd95917de68ce9078928820a208) by [@chrfalch](https://github.com/chrfalch))
|
||||
|
||||
## v0.81.0-rc.1
|
||||
|
||||
### Added
|
||||
|
||||
#### iOS specific
|
||||
|
||||
- **CocoaPods** Add the `ENTERPRISE_REPOSITORY` env variable to cocoapods infra ([23f3bf9239](https://github.com/facebook/react-native/commit/23f3bf9239a849590f1c72b25732d0090780128c) by [@cipolleschi](https://github.com/cipolleschi))
|
||||
- **Prebuild:** Add release/debug switch script for React-Core-prebuilt ([42d1a7934c](https://github.com/facebook/react-native/commit/42d1a7934cad4b2c92653e3fa7781c2af8f44df4) by [@chrfalch](https://github.com/chrfalch))
|
||||
- **Prebuild:** Added support for using USE_FRAMEWORKS with prebuilt React Native Core ([40e45f5366](https://github.com/facebook/react-native/commit/40e45f53661ce80c3a6fbbf07f52dc900afcad52) by [@chrfalch](https://github.com/chrfalch))
|
||||
|
||||
### Changed
|
||||
|
||||
- **Metro:** Bump Metro to 0.83.0 ([6b9f5d622f](https://github.com/facebook/react-native/commit/6b9f5d622ffbe79da8f4e7b7d8094504a480425e) by [@robhogan](https://github.com/robhogan))
|
||||
|
||||
#### Android specific
|
||||
|
||||
- **Gradle:** Gradle to 8.14.3 ([6892dde363](https://github.com/facebook/react-native/commit/6892dde36373bbef2d0afe535ae818b1a7164f08) by [@cortinico](https://github.com/cortinico))
|
||||
- **Gradle:** Expose `react_renderer_bridging` headers via prefab ([d1730ff960](https://github.com/facebook/react-native/commit/d1730ff960fcb9a01ee94b9e46e5a9fbb7d73f4a) by [@tomekzaw](https://github.com/tomekzaw))
|
||||
- **Legacy Arch:** Introduce more deprecation warnings for Legacy Arch classes ([625f69f284](https://github.com/facebook/react-native/commit/625f69f284ddfd9c6beecaa4052a871d092053ef) by [@cortinico](https://github.com/cortinico))
|
||||
|
||||
### Fixed
|
||||
|
||||
- **Yoga:** Fixed nodes with `display: contents` set being cloned with the wrong owner ([d4b36b0300](https://github.com/facebook/react-native/commit/d4b36b03003eb2de9eaf5b57bb639bae8cc12f20) by [@j-piasecki](https://github.com/j-piasecki))
|
||||
|
||||
#### iOS specific
|
||||
|
||||
- **Podspec:** Fixed premature return in header file generation from podspec globs ([f2b064c2d4](https://github.com/facebook/react-native/commit/f2b064c2d40c39017ac2a31bf3caf8acef23038c) by [@chrfalch](https://github.com/chrfalch))
|
||||
|
||||
|
||||
## v0.81.0-rc.0
|
||||
|
||||
### Breaking
|
||||
|
||||
@@ -61,7 +126,6 @@
|
||||
#### iOS specific
|
||||
|
||||
- **borderWidth:** Add support for different `borderWidth`s ([70962ef3ed](https://github.com/facebook/react-native/commit/70962ef3ed06a76a96cb2e72c374dc028628c829) by [@a-klotz-p8](https://github.com/a-klotz-p8))
|
||||
- **CocoaPods:** Add the `ENTERPRISE_REPOSITORY` env variable to cocoapods infra ([23f3bf9239](https://github.com/facebook/react-native/commit/23f3bf9239a849590f1c72b25732d0090780128c) by [@cipolleschi](https://github.com/cipolleschi))
|
||||
- **Modal:** Allow to interactively swipe down `Modal`s. ([28986a7599](https://github.com/facebook/react-native/commit/28986a7599952a77b8b8e433f72ca837afde310e) by [@okwasniewski](https://github.com/okwasniewski))
|
||||
- **Package.swift:** Added missing search path to `Package.swift` ([592b09781b](https://github.com/facebook/react-native/commit/592b09781bb94fe6dc00ba49c7a86649980fed5d) by [@chrfalch](https://github.com/chrfalch))
|
||||
- **Prebuild:** Add more logging around `computeNightlyTarballURL` in ios pre-build ([1a6887bd70](https://github.com/facebook/react-native/commit/1a6887bd70cdefb8fbc421467de841ece74d5c6b) by [@cortinico](https://github.com/cortinico))
|
||||
@@ -72,17 +136,13 @@
|
||||
- **Prebuild:** Added building `XCFframework` from the prebuild script ([55534f518a](https://github.com/facebook/react-native/commit/55534f518aab53bcdc3fe12d987ab7ef6e620c77) by [@chrfalch](https://github.com/chrfalch))
|
||||
- **Prebuild:** Added building swift package from the prebuild script ([3c01b1b6f0](https://github.com/facebook/react-native/commit/3c01b1b6f04d285c97bb182131135903b0c1cdd5) by [@chrfalch](https://github.com/chrfalch))
|
||||
- **Prebuild:** Added downloading of hermes artifacts when pre-building for iOS. ([41d2b5de0a](https://github.com/facebook/react-native/commit/41d2b5de0af21c72227ef030dcddf208e2eb221a) by [@chrfalch](https://github.com/chrfalch))
|
||||
- **Prebuild:** Add release/debug switch script for React-Core-prebuilt ([42d1a7934c](https://github.com/facebook/react-native/commit/42d1a7934cad4b2c92653e3fa7781c2af8f44df4) by [@chrfalch](https://github.com/chrfalch))
|
||||
- **Prebuild:** Added support for using USE_FRAMEWORKS with prebuilt React Native Core ([40e45f5366](https://github.com/facebook/react-native/commit/40e45f53661ce80c3a6fbbf07f52dc900afcad52) by [@chrfalch](https://github.com/chrfalch))
|
||||
- **runtime:** Added `HERMES_ENABLE_DEBUGGER` to debug configuration for the `reactRuntime` target. ([560ac23001](https://github.com/facebook/react-native/commit/560ac23001b02f19d4c6ca4ea493c17060cfaf5f) by [@chrfalch](https://github.com/chrfalch))
|
||||
|
||||
### Changed
|
||||
|
||||
- **API:** `NewAppScreen` no longer internally handles device safe area, use optional `safeAreaInsets` prop (aligned in 0.81 template) ([732bd12dc2](https://github.com/facebook/react-native/commit/732bd12dc21460641ef01b23f2eb722f26b060d5) by [@huntie](https://github.com/huntie))
|
||||
- **Animated:** Animated now always flattens `props.style`, which fixes an error that results from `props.style` objects in which `AnimatedNode` instances are shadowed (i.e. flattened to not exist in the resulting `props.style` object). ([da520848c9](https://github.com/facebook/react-native/commit/da520848c931f356d013623c412af11dce7ff114) by [@yungsters](https://github.com/yungsters))
|
||||
- **Animated:** Creates a feature flag that changes `Animated` to no longer produce invalid `props.style` if every `AnimatedNode` instance is shadowed via style flattening. ([5c8c5388fc](https://github.com/facebook/react-native/commit/5c8c5388fc53ef2430b0bb6bbbc628479819e23d) by [@yungsters](https://github.com/yungsters))
|
||||
- **Animated:** Enabled a feature flag that optimizes `Animated` to reduce memory usage. ([2a13d20085](https://github.com/facebook/react-native/commit/2a13d200850e4f161a29b252a0ccedc158e53937) by [@yungsters](https://github.com/yungsters))
|
||||
- **Babel:** Added support to `react-native/babel-preset` for a `hermesParserOptions` option, that expects an object that enables overriding `hermes-parser` options. ([0508eddfe6](https://github.com/facebook/react-native/commit/0508eddfe60df60cb3bfa4074ae199bd0e492d5f) by [@yungsters](https://github.com/yungsters))
|
||||
- **Error handling:** Errors will no longer have the "js engine" suffix. ([a293925280](https://github.com/facebook/react-native/commit/a2939252803d5cd4b68340da08820174c30a53e6) by [@yungsters](https://github.com/yungsters))
|
||||
- **Fibers:** Reduces memory usage, by improving memory management of parent alternate fibers. (Previously, a parent fiber might retain memory associated with shadow nodes from a previous commit.) ([0411c43b3a](https://github.com/facebook/react-native/commit/0411c43b3a239384c778baad22c7b4c501008449) by [@yungsters](https://github.com/yungsters))
|
||||
- **infoLog:** Removed `infoLog` from `react-native` package ([8a0cfec815](https://github.com/facebook/react-native/commit/8a0cfec81584e966c9e6ea0f5e438022e0129bcd) by [@coado](https://github.com/coado))
|
||||
@@ -90,26 +150,24 @@
|
||||
- **Jest:** Improved default mocking for Jest unit tests. ([1fd9508ecc](https://github.com/facebook/react-native/commit/1fd9508ecc499df89b086e0c46035f43f6f78ad9) by [@yungsters](https://github.com/yungsters))
|
||||
- **LegacyArchitecture:** Raise loglevel for assertion of `LegacyArchitecture` classes ([38a4b62211](https://github.com/facebook/react-native/commit/38a4b6221164d36eb4ac95c9f3bc7f7e7235e383) by [@mdvacca](https://github.com/mdvacca))
|
||||
- **LegacyArchitecture:** Raise logLevel of `LegacyArchitecture` classes when minimizing of legacy architecture is enabled ([0d1cde7f36](https://github.com/facebook/react-native/commit/0d1cde7f36e9de72c997fc812bba023694c2a369) by [@mdvacca](https://github.com/mdvacca))
|
||||
- **Metro:** Metro to ^0.83.1 ([e247be793c](https://github.com/facebook/react-native/commit/e247be793c70a374955d798d8cbbc6eba58080ec) by [@motiz88](https://github.com/motiz88))
|
||||
- **Metro:** Bump Metro to `^0.82.5` ([083644647e](https://github.com/facebook/react-native/commit/083644647eff502f484b3ba24f9d361d5df56546) by [@robhogan](https://github.com/robhogan))
|
||||
- **React DevTools:** Bumped React DevTools to `6.1.5` ([c302902b1d](https://github.com/facebook/react-native/commit/c302902b1db7e8f8ac5b61472c095dc0755d6d1c) by [@hoxyq](https://github.com/hoxyq))
|
||||
- **RuntimeExecutor:** `RuntimeExecutor`: Remove noexcept from sync ui thread utils ([7ef278af50](https://github.com/facebook/react-native/commit/7ef278af505deba6b8a47876c6824f9a7fefa427) by [@RSNara](https://github.com/RSNara))
|
||||
- **Typescript:** Bump `types/react` to `19.1` ([3ae9328571](https://github.com/facebook/react-native/commit/3ae932857174e9c39cd5d9c53922f849aa1401b1) by [@gabrieldonadel](https://github.com/gabrieldonadel))
|
||||
|
||||
#### Android specific
|
||||
|
||||
- **Android SDK:** Updated targetSdk to 36 in Android. ([477d8df312](https://github.com/facebook/react-native/commit/477d8df3126b325b8cc9b410f1eaeb56b727d4d9) by [@kikoso](https://github.com/kikoso))
|
||||
- **APIs:** Deprecate `DefaultNewArchitectureEntryPoint.load(Boolean, Boolean, Boolean)` ([efdf73983c](https://github.com/facebook/react-native/commit/efdf73983cef1f371511b6e1efa5e01835ebcabb) by [@cortinico](https://github.com/cortinico))
|
||||
- **APIs:** Make `com.facebook.react.views.common.ContextUtils` internal ([d1ef8f1fa3](https://github.com/facebook/react-native/commit/d1ef8f1fa36cbfc34d05c409abf693e4e1cac3de) by [@cortinico](https://github.com/cortinico))
|
||||
- **deps:** Bump `AGP` to `8.11.0` ([04858ecbab](https://github.com/facebook/react-native/commit/04858ecbab808ddca80e20e76f1359619bb5e865) by [@cortinico](https://github.com/cortinico))
|
||||
- **deps:** Bump `Gradle` to 8.14.3 ([6892dde363](https://github.com/facebook/react-native/commit/6892dde36373bbef2d0afe535ae818b1a7164f08) by [@cortinico](https://github.com/cortinico))
|
||||
- **Gradle:** Expose `react_renderer_bridging` headers via prefab ([d1730ff960](https://github.com/facebook/react-native/commit/d1730ff960fcb9a01ee94b9e46e5a9fbb7d73f4a) by [@tomekzaw](https://github.com/tomekzaw))
|
||||
- **deps:** Bump `Gradle` to `8.14.2` ([e20bb56f3b](https://github.com/facebook/react-native/commit/e20bb56f3b4db0d3e69154b95b952b1fe8e29959) by [@cortinico](https://github.com/cortinico))
|
||||
- **JS FPS:** Hide JS FPS on performance overlay as not accurate ([feec8d0148](https://github.com/facebook/react-native/commit/feec8d014877b2177f1c7dded7eb9664f53ee471) by [@cortinico](https://github.com/cortinico))
|
||||
- Updated targetSdk to 36 in Android. ([477d8df312](https://github.com/facebook/react-native/commit/477d8df3126b325b8cc9b410f1eaeb56b727d4d9) by [@kikoso](https://github.com/kikoso))
|
||||
- **Kotlin:** Convert `UIManagerModuleConstantsHelper` to Kotlin ([45fd7feb9f](https://github.com/facebook/react-native/commit/45fd7feb9f083e5c8afc916732aed9795d344e09) by [@cortinico](https://github.com/cortinico))
|
||||
- **Kotlin:** Migrate `ThemedReactContext` to Kotlin ([78c9671c24](https://github.com/facebook/react-native/commit/78c9671c241a86bedb17862e549842b7e36d77ea) by [@cortinico](https://github.com/cortinico))
|
||||
- **Kotlin:** Convert `ReactViewGroup` to Kotlin ([48395d346b](https://github.com/facebook/react-native/commit/48395d346bc89f63d38889e58508304df0088e4f) by [@cortinico](https://github.com/cortinico))
|
||||
- **Kotlin:** Migrate `com.facebook.react.LazyReactPackage` to Kotlin. ([b4ae5c1de1](https://github.com/facebook/react-native/commit/b4ae5c1de1003c343d43c3be1b59ee2b800b9258) by [@Xintre](https://github.com/Xintre))
|
||||
- **Kotlin:** Apply Collections Kotlin DSL helpers in `ReactAndroid` package ([b2ffd34a39](https://github.com/facebook/react-native/commit/b2ffd34a392de2bddba5ee13248796ccc2db6039) by [@l2hyunwoo](https://github.com/l2hyunwoo))
|
||||
- **Legacy Arch:** Introduce more deprecation warnings for Legacy Arch classes ([625f69f284](https://github.com/facebook/react-native/commit/625f69f284ddfd9c6beecaa4052a871d092053ef) by [@cortinico](https://github.com/cortinico))
|
||||
|
||||
#### iOS specific
|
||||
|
||||
@@ -165,21 +223,19 @@
|
||||
- **Typescript:** Add `ImageSource` type to TypeScript ([42ca46b95c](https://github.com/facebook/react-native/commit/42ca46b95cf9938de00b76dc61948a4ae7116e2b) by [@okwasniewski](https://github.com/okwasniewski))
|
||||
- **Typescript:** Devtools TS Types ([8f189fce03](https://github.com/facebook/react-native/commit/8f189fce03db367abdceca6ad57ae28b613fdd7d) by [@krystofwoldrich](https://github.com/krystofwoldrich))
|
||||
- **Yoga:** Fix possible invalid measurements with width or height is zero pixels ([5cc4d0a086](https://github.com/facebook/react-native/commit/5cc4d0a086d450e0f9d8ab6194013348f9de1f58) by [@NickGerleman](https://github.com/NickGerleman))
|
||||
- **Yoga:** Fixed nodes with `display: contents` set being cloned with the wrong owner ([d4b36b0300](https://github.com/facebook/react-native/commit/d4b36b03003eb2de9eaf5b57bb639bae8cc12f20) by [@j-piasecki](https://github.com/j-piasecki))
|
||||
|
||||
#### Android specific
|
||||
- **API:** Make accessors inside HeadlessJsTaskService open again ([7ef57163cb](https://github.com/facebook/react-native/commit/7ef57163cb016317e43e563da7ea181989f6abca) by [@cortinico](https://github.com/cortinico))
|
||||
|
||||
- **BaseViewManager:** Remove focus change listener when dropping/recycling view instances ([94cbf206d6](https://github.com/facebook/react-native/commit/94cbf206d607477257c65039d97565a79e94c7dd) by [@Abbondanzo](https://github.com/Abbondanzo))
|
||||
- **BoringLayout:** Include fallback line spacing in `BoringLayout` ([2fe6c1a947](https://github.com/facebook/react-native/commit/2fe6c1a94758223a5342fdfa90163971eb588e6a) by [@NickGerleman](https://github.com/NickGerleman))
|
||||
- **Bridgeless:** Adding `shouldForwardToReactInstance` check in `ReactDelegate` for Bridgeless ([0f7bf66bba](https://github.com/facebook/react-native/commit/0f7bf66bba8498c89384e96ad9219cdad0107b0c) by [@arushikesarwani94](https://github.com/arushikesarwani94))
|
||||
- **Codegen:** Fix combining schema in Codegen process to exclude platforms correctly ([6104ccdc6e](https://github.com/facebook/react-native/commit/6104ccdc6ef89c2d4da25e60dcc55d73038e023f) by [@arushikesarwani94](https://github.com/arushikesarwani94))
|
||||
- **Edge To Edge:** Fix `Dimensions` `window` values on Android < 15 when edge-to-edge is enabled ([85d10ed904](https://github.com/facebook/react-native/commit/85d10ed90401a13de1f74aeddd773736195da285) by [@zoontek](https://github.com/zoontek))
|
||||
- **FBReactNativeSpec:** Extract out `FBReactNativeSpec`'s core components including Unimplemented from auto-generated registry ([b417b0c2d5](https://github.com/facebook/react-native/commit/b417b0c2d56dc37f824c0e77e98d1014d21cd8f8) by [@arushikesarwani94](https://github.com/arushikesarwani94))
|
||||
- **Gradle:** Fix Gradle v8.0 builds by using .set() for Property ([777397667c](https://github.com/facebook/react-native/commit/777397667c2625aab3fc907b9ef4bd564963d8bb) by [@meghancampbel9](https://github.com/meghancampbel9))
|
||||
- **ImageFetcher:** Change `free` to `delete` to call destructor of `ImageFetcher` and release `contextContainer`. ([90da666691](https://github.com/facebook/react-native/commit/90da666691745ab9bf3930dc3347d8e51683099f) by [@WoLewicki](https://github.com/WoLewicki))
|
||||
- **Modal:** Fix `Modal` first frame being rendered on top-left corner ([b950fa2afb](https://github.com/facebook/react-native/commit/b950fa2afb20e2213ff6c733cb1c2465b90406ef) by [@cortinico](https://github.com/cortinico))
|
||||
- **onTextLayout:** Fix `onTextLayout` metrics not incorporating `ReactTextViewManagerCallback` ([a6a2884d63](https://github.com/facebook/react-native/commit/a6a2884d63717a42ac2bafd2054991ce8b32a2e9) by [@NickGerleman](https://github.com/NickGerleman))
|
||||
- **RNGP:** Fix a race condition with codegen libraries missing sources ([9013a9e666](https://github.com/facebook/react-native/commit/9013a9e66629677c47e1b69703f9fc8f4cbc1c2c) by [@cortinico](https://github.com/cortinico))
|
||||
- **Runtime:** Fixed `ReactHostImpl.nativeModules` always returning an empty list ([2f46a49](https://github.com/facebook/react-native/commit/2f46a49b8d8a11d5cf4342eee83c469b545c6779) by [@lukmccall](https://github.com/lukmccall))
|
||||
- **Text:** Fix more text rounding bugs ([1fe3ff86c3](https://github.com/facebook/react-native/commit/1fe3ff86c364fad023ad1e426f26608699314339) by [@NickGerleman](https://github.com/NickGerleman))
|
||||
- **Text:** Fix `TextLayoutManager` `MeasureMode` Regression ([99119a2104](https://github.com/facebook/react-native/commit/99119a210487af18983145fd374ff7ebc88931f3) by [@NickGerleman](https://github.com/NickGerleman))
|
||||
- **TextInput:** Fix bug where focus would jump to top text input upon clearing a separate text input. ([79c47987b7](https://github.com/facebook/react-native/commit/79c47987b74ab044574fc542fd4b13a9f11aa491) by [@joevilches](https://github.com/joevilches))
|
||||
@@ -187,13 +243,10 @@
|
||||
#### iOS specific
|
||||
|
||||
- **Gradient**: Gradient interpolation for transparent colors ([097d482446](https://github.com/facebook/react-native/commit/097d482446b7a03ca0f8c7e0254f4d770e05c79c) by [@intergalacticspacehighway](https://github.com/intergalacticspacehighway))
|
||||
- **Package.swift:** Add missing `React-RCTSettings` to `Package.swift` ([e40c1d265a](https://github.com/facebook/react-native/commit/e40c1d265a2045730dcf751eed4ebf32e099f0c7) by [@chrfalch](https://github.com/chrfalch))
|
||||
- **Package.swift:** Fixed defines in `Package.swift` ([e2f6ce4ddf](https://github.com/facebook/react-native/commit/e2f6ce4ddfbea5814fc5d8632df14daeae3636d1) by [@chrfalch](https://github.com/chrfalch))
|
||||
- **Podspec:** Fixed premature return in header file generation from podspec globs ([f2b064c2d4](https://github.com/facebook/react-native/commit/f2b064c2d40c39017ac2a31bf3caf8acef23038c) by [@chrfalch](https://github.com/chrfalch))
|
||||
- **Podspec:** Fixed issue with RNDeps release/debug switch failing ([4ee2b60a1e](https://github.com/facebook/react-native/commit/4ee2b60a1eacca744d58a7ad336ca9d3714289f6) by [@chrfalch](https://github.com/chrfalch))
|
||||
- **Podspec:** Fixed missing script for resolving prebuilt xcframework when switching between release/debug ([2e55241a90](https://github.com/facebook/react-native/commit/2e55241a901b4cd95917de68ce9078928820a208) by [@chrfalch](https://github.com/chrfalch))
|
||||
- **Prebuild:** Fixed wrong path in prebuild hermes check ([be11f2ee77](https://github.com/facebook/react-native/commit/be11f2ee77fd793efe0a1aa225897a1924163925) by [@chrfalch](https://github.com/chrfalch))
|
||||
- **Prebuild:** Fixed resolving build type when downloading hermes artifacts ([9371e20192](https://github.com/facebook/react-native/commit/9371e201927fd105e797bf06e43943dd21e04381) by [@chrfalch](https://github.com/chrfalch))
|
||||
- **Package.swift:** Add missing `React-RCTSettings` to `Package.swift` ([e40c1d265a](https://github.com/facebook/react-native/commit/e40c1d265a2045730dcf751eed4ebf32e099f0c7) by [@chrfalch](https://github.com/chrfalch))
|
||||
- **Package.swift:** Fixed defines in `Package.swift` ([e2f6ce4ddf](https://github.com/facebook/react-native/commit/e2f6ce4ddfbea5814fc5d8632df14daeae3636d1) by [@chrfalch](https://github.com/chrfalch))
|
||||
- **RCTImage:** Allow for consuming `RCTImage` in Swift codebase by enabling "Defines Module" option ([1d80586730](https://github.com/facebook/react-native/commit/1d8058673085580f402ec3a320fce810db7ad2ef) by [@kkafar](https://github.com/kkafar))
|
||||
- **RCTImageComponentView:** Fix `RCTImageComponentView` image loading after source props change with no layout invalidation ([cd5d74518b](https://github.com/facebook/react-native/commit/cd5d74518becb3355519373211d2f54ff7dbd208) by Nick Lefever)
|
||||
- **RCTScreenSize:** Make `RCTScreenSize` take horizontal orientation into account ([50ce8c77a7](https://github.com/facebook/react-native/commit/50ce8c77a74f2f2574030db04dc88c6092e68ba8) by [@okwasniewski](https://github.com/okwasniewski))
|
||||
@@ -554,10 +607,6 @@
|
||||
- **Text:** Selection range not respected when changing text or selection when selection is forced ([d32ea66e6a](https://github.com/facebook/react-native/commit/d32ea66e6a945dd84092532401b265b12d482668) by Olivier Bouillet)
|
||||
- **TextInput:** Fix TextInput `onContentSizeChange` event being dispatched only once on iOS on the new architecture ([5fd5188172](https://github.com/facebook/react-native/commit/5fd51881727b2d86f87abf04db032940ac0ec8c4) by [@j-piasecki](https://github.com/j-piasecki))
|
||||
|
||||
## v0.79.6
|
||||
|
||||
See [CHANGELOG-0.7x](./CHANGELOG-0.7x.md#v0796)
|
||||
|
||||
## v0.79.5
|
||||
|
||||
See [CHANGELOG-0.7x](./CHANGELOG-0.7x.md#v0795)
|
||||
|
||||
+12
-28
@@ -26,12 +26,10 @@ fun getListReactAndroidProperty(name: String) = reactAndroidProperties.getProper
|
||||
|
||||
apiValidation {
|
||||
ignoredPackages.addAll(
|
||||
getListReactAndroidProperty("binaryCompatibilityValidator.ignoredPackages")
|
||||
)
|
||||
getListReactAndroidProperty("binaryCompatibilityValidator.ignoredPackages"))
|
||||
ignoredClasses.addAll(getListReactAndroidProperty("binaryCompatibilityValidator.ignoredClasses"))
|
||||
nonPublicMarkers.addAll(
|
||||
getListReactAndroidProperty("binaryCompatibilityValidator.nonPublicMarkers")
|
||||
)
|
||||
getListReactAndroidProperty("binaryCompatibilityValidator.nonPublicMarkers"))
|
||||
validationDisabled =
|
||||
reactAndroidProperties
|
||||
.getProperty("binaryCompatibilityValidator.validationDisabled")
|
||||
@@ -39,9 +37,8 @@ apiValidation {
|
||||
}
|
||||
|
||||
version =
|
||||
if (
|
||||
project.hasProperty("isSnapshot") && (project.property("isSnapshot") as? String).toBoolean()
|
||||
) {
|
||||
if (project.hasProperty("isSnapshot") &&
|
||||
(project.property("isSnapshot") as? String).toBoolean()) {
|
||||
"${reactAndroidProperties.getProperty("VERSION_NAME")}-SNAPSHOT"
|
||||
} else {
|
||||
reactAndroidProperties.getProperty("VERSION_NAME")
|
||||
@@ -69,10 +66,8 @@ tasks.register("clean", Delete::class.java) {
|
||||
description = "Remove all the build files and intermediate build outputs"
|
||||
dependsOn(gradle.includedBuild("gradle-plugin").task(":clean"))
|
||||
subprojects.forEach {
|
||||
if (
|
||||
it.project.plugins.hasPlugin("com.android.library") ||
|
||||
it.project.plugins.hasPlugin("com.android.application")
|
||||
) {
|
||||
if (it.project.plugins.hasPlugin("com.android.library") ||
|
||||
it.project.plugins.hasPlugin("com.android.application")) {
|
||||
dependsOn(it.tasks.named("clean"))
|
||||
}
|
||||
}
|
||||
@@ -82,13 +77,10 @@ tasks.register("clean", Delete::class.java) {
|
||||
delete(rootProject.file("./packages/react-native/sdks/download/"))
|
||||
delete(rootProject.file("./packages/react-native/sdks/hermes/"))
|
||||
delete(
|
||||
rootProject.file("./packages/react-native/ReactAndroid/src/main/jni/prebuilt/lib/arm64-v8a/")
|
||||
)
|
||||
rootProject.file("./packages/react-native/ReactAndroid/src/main/jni/prebuilt/lib/arm64-v8a/"))
|
||||
delete(
|
||||
rootProject.file(
|
||||
"./packages/react-native/ReactAndroid/src/main/jni/prebuilt/lib/armeabi-v7a/"
|
||||
)
|
||||
)
|
||||
"./packages/react-native/ReactAndroid/src/main/jni/prebuilt/lib/armeabi-v7a/"))
|
||||
delete(rootProject.file("./packages/react-native/ReactAndroid/src/main/jni/prebuilt/lib/x86/"))
|
||||
delete(rootProject.file("./packages/react-native/ReactAndroid/src/main/jni/prebuilt/lib/x86_64/"))
|
||||
delete(rootProject.file("./packages/react-native-codegen/lib"))
|
||||
@@ -106,8 +98,7 @@ tasks.register("publishAllToMavenTempLocal") {
|
||||
dependsOn(":packages:react-native:ReactAndroid:publishAllPublicationsToMavenTempLocalRepository")
|
||||
// We don't publish the external-artifacts to Maven Local as ci is using it via workspace.
|
||||
dependsOn(
|
||||
":packages:react-native:ReactAndroid:hermes-engine:publishAllPublicationsToMavenTempLocalRepository"
|
||||
)
|
||||
":packages:react-native:ReactAndroid:hermes-engine:publishAllPublicationsToMavenTempLocalRepository")
|
||||
}
|
||||
|
||||
tasks.register("publishAndroidToSonatype") {
|
||||
@@ -129,8 +120,7 @@ if (project.findProperty("react.internal.useHermesNightly")?.toString()?.toBoole
|
||||
That's fine for local development, but you should not commit this change.
|
||||
********************************************************************************
|
||||
"""
|
||||
.trimIndent()
|
||||
)
|
||||
.trimIndent())
|
||||
allprojects {
|
||||
configurations.all {
|
||||
resolutionStrategy.dependencySubstitution {
|
||||
@@ -162,12 +152,10 @@ allprojects {
|
||||
"**/build/**",
|
||||
"**/hermes-engine/**",
|
||||
"**/internal/featureflags/**",
|
||||
"**/systeminfo/ReactNativeVersion.kt",
|
||||
)
|
||||
"**/systeminfo/ReactNativeVersion.kt")
|
||||
listOf(
|
||||
com.ncorti.ktfmt.gradle.tasks.KtfmtCheckTask::class,
|
||||
com.ncorti.ktfmt.gradle.tasks.KtfmtFormatTask::class,
|
||||
)
|
||||
com.ncorti.ktfmt.gradle.tasks.KtfmtFormatTask::class)
|
||||
.forEach { tasks.withType(it) { exclude(excludePatterns) } }
|
||||
|
||||
// Disable the problematic ktfmt script tasks due to symbolic link issues in subprojects
|
||||
@@ -177,7 +165,3 @@ allprojects {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// We intentionally disable the `ktfmtCheck` tasks as the formatting is primarly handled inside
|
||||
// fbsource
|
||||
allprojects { tasks.withType<com.ncorti.ktfmt.gradle.tasks.KtfmtCheckTask>() { enabled = false } }
|
||||
|
||||
@@ -91,19 +91,7 @@ declare module 'tinybench' {
|
||||
beforeEach?: (this: Task) => void | Promise<void>,
|
||||
};
|
||||
|
||||
// This is defined as an interface in tinybench but we define it as an object
|
||||
// to catch problems like `overriddenDuration` being misspelled.
|
||||
export type FnReturnedObject = {
|
||||
overriddenDuration?: number,
|
||||
};
|
||||
|
||||
// This type is defined as returning `unknown` instead of `void` in tinybench,
|
||||
// but we type it this way to avoid mistakes (we can make breaking changes
|
||||
// in our definition that they can't).
|
||||
export type Fn = () =>
|
||||
| Promise<void | FnReturnedObject>
|
||||
| void
|
||||
| FnReturnedObject;
|
||||
export type Fn = () => Promise<mixed> | mixed;
|
||||
|
||||
declare export class Bench extends EventTarget {
|
||||
concurrency: null | 'task' | 'bench';
|
||||
Vendored
+2
-2
@@ -21,7 +21,7 @@ declare type ws$PerMessageDeflateOptions = {
|
||||
maxPayload?: number,
|
||||
};
|
||||
|
||||
/* $FlowFixMe[incompatible-type] - Found with Flow v0.143.1 upgrade
|
||||
/* $FlowFixMe[incompatible-extend] - Found with Flow v0.143.1 upgrade
|
||||
* "on" definition failing with string is incompatible with string literal */
|
||||
declare class ws$WebSocketServer extends events$EventEmitter {
|
||||
/**
|
||||
@@ -141,7 +141,7 @@ declare type ws$UnexpectedResponseListener = (
|
||||
) => mixed;
|
||||
declare type ws$UpgradeListener = (response: http$IncomingMessage<>) => mixed;
|
||||
|
||||
/* $FlowFixMe[incompatible-type] - Found with Flow v0.143.1 upgrade
|
||||
/* $FlowFixMe[incompatible-extend] - Found with Flow v0.143.1 upgrade
|
||||
* "on" definition failing with string is incompatible with string literal */
|
||||
declare class ws$WebSocket extends events$EventEmitter {
|
||||
static Server: typeof ws$WebSocketServer;
|
||||
|
||||
Vendored
BIN
Binary file not shown.
+1
-1
@@ -1,6 +1,6 @@
|
||||
distributionBase=GRADLE_USER_HOME
|
||||
distributionPath=wrapper/dists
|
||||
distributionUrl=https\://services.gradle.org/distributions/gradle-9.0.0-bin.zip
|
||||
distributionUrl=https\://services.gradle.org/distributions/gradle-8.14.3-bin.zip
|
||||
networkTimeout=10000
|
||||
validateDistributionUrl=true
|
||||
zipStoreBase=GRADLE_USER_HOME
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
#!/bin/sh
|
||||
|
||||
#
|
||||
# Copyright © 2015 the original authors.
|
||||
# Copyright © 2015-2021 the original authors.
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||
# you may not use this file except in compliance with the License.
|
||||
|
||||
@@ -86,7 +86,7 @@ module.exports = {
|
||||
globalPrefix: '',
|
||||
hermesParser: true,
|
||||
hot: false,
|
||||
// $FlowFixMe[incompatible-type] TODO: Remove when `inlineRequires` has been removed from metro-babel-transformer in OSS
|
||||
// $FlowFixMe[incompatible-call] TODO: Remove when `inlineRequires` has been removed from metro-babel-transformer in OSS
|
||||
inlineRequires: true,
|
||||
minify: false,
|
||||
platform: '',
|
||||
@@ -111,7 +111,7 @@ module.exports = {
|
||||
|
||||
return generate(
|
||||
ast,
|
||||
// $FlowFixMe[incompatible-type] Error found when improving flow typing for libs
|
||||
// $FlowFixMe[prop-missing] Error found when improving flow typing for libs
|
||||
{
|
||||
code: true,
|
||||
comments: false,
|
||||
|
||||
+8
-8
@@ -63,7 +63,7 @@
|
||||
"@typescript-eslint/parser": "^8.36.0",
|
||||
"ansi-styles": "^4.2.1",
|
||||
"babel-plugin-minify-dead-code-elimination": "^0.5.2",
|
||||
"babel-plugin-syntax-hermes-parser": "0.32.0",
|
||||
"babel-plugin-syntax-hermes-parser": "0.30.0",
|
||||
"babel-plugin-transform-define": "^2.1.4",
|
||||
"babel-plugin-transform-flow-enums": "^0.0.2",
|
||||
"clang-format": "^1.8.0",
|
||||
@@ -75,17 +75,17 @@
|
||||
"eslint-plugin-babel": "^5.3.1",
|
||||
"eslint-plugin-eslint-comments": "^3.2.0",
|
||||
"eslint-plugin-ft-flow": "^2.0.1",
|
||||
"eslint-plugin-jest": "^29.0.1",
|
||||
"eslint-plugin-jest": "^27.9.0",
|
||||
"eslint-plugin-jsx-a11y": "^6.6.0",
|
||||
"eslint-plugin-react": "^7.30.1",
|
||||
"eslint-plugin-react-native": "^4.0.0",
|
||||
"eslint-plugin-redundant-undefined": "^0.4.0",
|
||||
"eslint-plugin-relay": "^1.8.3",
|
||||
"flow-api-translator": "0.32.0",
|
||||
"flow-bin": "^0.279.0",
|
||||
"flow-api-translator": "0.30.0",
|
||||
"flow-bin": "^0.278.0",
|
||||
"glob": "^7.1.1",
|
||||
"hermes-eslint": "0.32.0",
|
||||
"hermes-transform": "0.32.0",
|
||||
"hermes-eslint": "0.30.0",
|
||||
"hermes-transform": "0.30.0",
|
||||
"ini": "^5.0.0",
|
||||
"inquirer": "^7.1.0",
|
||||
"jest": "^29.7.0",
|
||||
@@ -102,7 +102,7 @@
|
||||
"node-fetch": "^2.2.0",
|
||||
"nullthrows": "^1.1.1",
|
||||
"prettier": "3.6.2",
|
||||
"prettier-plugin-hermes-parser": "0.32.0",
|
||||
"prettier-plugin-hermes-parser": "0.31.1",
|
||||
"react": "19.1.1",
|
||||
"react-test-renderer": "19.1.1",
|
||||
"rimraf": "^3.0.2",
|
||||
@@ -110,7 +110,7 @@
|
||||
"signedsource": "^1.0.0",
|
||||
"supports-color": "^7.1.0",
|
||||
"temp-dir": "^2.0.0",
|
||||
"tinybench": "^4.1.0",
|
||||
"tinybench": "^3.1.0",
|
||||
"typescript": "5.8.3",
|
||||
"ws": "^6.2.3"
|
||||
},
|
||||
|
||||
@@ -81,147 +81,10 @@ export {Commands};
|
||||
export default (codegenNativeComponent<ModuleProps>('Module'): NativeType);
|
||||
`;
|
||||
|
||||
const COMMANDS_WITH_COVERAGE_INVALID = `
|
||||
// @flow
|
||||
|
||||
const codegenNativeComponent = require('codegenNativeComponent');
|
||||
import type {NativeComponentType} from 'codegenNativeComponent';
|
||||
|
||||
import type {ViewProps} from 'ViewPropTypes';
|
||||
|
||||
type ModuleProps = $ReadOnly<{|
|
||||
...ViewProps,
|
||||
|}>;
|
||||
|
||||
type NativeType = NativeComponentType<ModuleProps>;
|
||||
|
||||
// Coverage instrumentation of invalid Commands export - should still fail
|
||||
export const Commands = (cov_1234567890().s[0]++, {
|
||||
hotspotUpdate: () => {},
|
||||
scrollTo: () => {},
|
||||
});
|
||||
|
||||
export default (codegenNativeComponent<ModuleProps>('Module'): NativeType);
|
||||
`;
|
||||
|
||||
const COMMANDS_WITH_COVERAGE_WRONG_FUNCTION = `
|
||||
// @flow
|
||||
|
||||
const codegenNativeComponent = require('codegenNativeComponent');
|
||||
import type {NativeComponentType} from 'codegenNativeComponent';
|
||||
|
||||
import type {ViewProps} from 'ViewPropTypes';
|
||||
|
||||
type ModuleProps = $ReadOnly<{|
|
||||
...ViewProps,
|
||||
|}>;
|
||||
|
||||
type NativeType = NativeComponentType<ModuleProps>;
|
||||
|
||||
// Coverage instrumentation of wrong function call - should fail
|
||||
export const Commands = (cov_abcdef123().s[0]++, someOtherFunction({
|
||||
supportedCommands: ['pause', 'play'],
|
||||
}));
|
||||
|
||||
export default (codegenNativeComponent<ModuleProps>('Module'): NativeType);
|
||||
`;
|
||||
|
||||
const COMMANDS_WITH_COMPLEX_COVERAGE_INVALID = `
|
||||
// @flow
|
||||
|
||||
const codegenNativeComponent = require('codegenNativeComponent');
|
||||
import type {NativeComponentType} from 'codegenNativeComponent';
|
||||
|
||||
import type {ViewProps} from 'ViewPropTypes';
|
||||
|
||||
type ModuleProps = $ReadOnly<{|
|
||||
...ViewProps,
|
||||
|}>;
|
||||
|
||||
type NativeType = NativeComponentType<ModuleProps>;
|
||||
|
||||
// Complex coverage instrumentation with invalid nested structure - should fail
|
||||
export const Commands = (
|
||||
cov_xyz789().f[1]++,
|
||||
cov_xyz789().s[2]++,
|
||||
{
|
||||
pause: (ref) => {},
|
||||
play: (ref) => {},
|
||||
}
|
||||
);
|
||||
|
||||
export default (codegenNativeComponent<ModuleProps>('Module'): NativeType);
|
||||
`;
|
||||
|
||||
const COMMANDS_WITH_COVERAGE_WRONG_NAME = `
|
||||
// @flow
|
||||
|
||||
const codegenNativeCommands = require('codegenNativeCommands');
|
||||
const codegenNativeComponent = require('codegenNativeComponent');
|
||||
import type {NativeComponentType} from 'codegenNativeComponent';
|
||||
|
||||
import type {ViewProps} from 'ViewPropTypes';
|
||||
|
||||
type ModuleProps = $ReadOnly<{|
|
||||
...ViewProps,
|
||||
|}>;
|
||||
|
||||
type NativeType = NativeComponentType<ModuleProps>;
|
||||
|
||||
interface NativeCommands {
|
||||
+pause: (viewRef: React.ElementRef<NativeType>) => void;
|
||||
+play: (viewRef: React.ElementRef<NativeType>) => void;
|
||||
}
|
||||
|
||||
// Coverage instrumentation with correct function but wrong export name - should fail
|
||||
export const WrongName = (cov_wrong123().s[0]++, codegenNativeCommands<NativeCommands>({
|
||||
supportedCommands: ['pause', 'play'],
|
||||
}));
|
||||
|
||||
export default (codegenNativeComponent<ModuleProps>('Module'): NativeType);
|
||||
`;
|
||||
|
||||
const COMMANDS_WITH_COVERAGE_TYPE_CAST_INVALID = `
|
||||
// @flow
|
||||
|
||||
const codegenNativeComponent = require('codegenNativeComponent');
|
||||
import type {NativeComponentType} from 'codegenNativeComponent';
|
||||
|
||||
import type {ViewProps} from 'ViewPropTypes';
|
||||
|
||||
type ModuleProps = $ReadOnly<{|
|
||||
...ViewProps,
|
||||
|}>;
|
||||
|
||||
type NativeType = NativeComponentType<ModuleProps>;
|
||||
|
||||
interface NativeCommands {
|
||||
+pause: (viewRef: React.ElementRef<NativeType>) => void;
|
||||
+play: (viewRef: React.ElementRef<NativeType>) => void;
|
||||
}
|
||||
|
||||
// Coverage instrumentation with type cast but wrong function - should fail
|
||||
export const Commands: NativeCommands = (cov_cast123().s[0]++, invalidFunction({
|
||||
supportedCommands: ['pause', 'play'],
|
||||
}));
|
||||
|
||||
export default (codegenNativeComponent<ModuleProps>('Module'): NativeType);
|
||||
`;
|
||||
|
||||
module.exports = {
|
||||
'CommandsExportedWithDifferentNameNativeComponent.js':
|
||||
COMMANDS_EXPORTED_WITH_DIFFERENT_NAME,
|
||||
'CommandsExportedWithShorthandNativeComponent.js':
|
||||
COMMANDS_EXPORTED_WITH_SHORTHAND,
|
||||
'OtherCommandsExportNativeComponent.js': OTHER_COMMANDS_EXPORT,
|
||||
'CommandsWithCoverageInvalidNativeComponent.js':
|
||||
COMMANDS_WITH_COVERAGE_INVALID,
|
||||
'CommandsWithCoverageWrongFunctionNativeComponent.js':
|
||||
COMMANDS_WITH_COVERAGE_WRONG_FUNCTION,
|
||||
'CommandsWithComplexCoverageInvalidNativeComponent.js':
|
||||
COMMANDS_WITH_COMPLEX_COVERAGE_INVALID,
|
||||
'CommandsWithCoverageWrongNameNativeComponent.js':
|
||||
COMMANDS_WITH_COVERAGE_WRONG_NAME,
|
||||
'CommandsWithCoverageTypeCastInvalidNativeComponent.js':
|
||||
COMMANDS_WITH_COVERAGE_TYPE_CAST_INVALID,
|
||||
};
|
||||
|
||||
@@ -59,92 +59,6 @@ export default codegenNativeComponent<ModuleProps>('Module', {
|
||||
});
|
||||
`;
|
||||
|
||||
// Coverage instrumentation test cases - should be recognized as valid
|
||||
const COMMANDS_WITH_SIMPLE_COVERAGE = `
|
||||
// @flow
|
||||
|
||||
const codegenNativeCommands = require('codegenNativeCommands');
|
||||
const codegenNativeComponent = require('codegenNativeComponent');
|
||||
|
||||
import type {ViewProps} from 'ViewPropTypes';
|
||||
import type {NativeComponentType} from 'codegenNativeComponent';
|
||||
|
||||
type ModuleProps = $ReadOnly<{|
|
||||
...ViewProps,
|
||||
|}>;
|
||||
|
||||
type NativeType = NativeComponentType<ModuleProps>;
|
||||
|
||||
interface NativeCommands {
|
||||
+pause: (viewRef: React.ElementRef<NativeType>) => void;
|
||||
+play: (viewRef: React.ElementRef<NativeType>) => void;
|
||||
}
|
||||
|
||||
export const Commands = (cov_1234567890.s[0]++, codegenNativeCommands<NativeCommands>({
|
||||
supportedCommands: ['pause', 'play'],
|
||||
}));
|
||||
|
||||
export default codegenNativeComponent<ModuleProps>('Module');
|
||||
`;
|
||||
|
||||
const COMMANDS_WITH_COMPLEX_COVERAGE = `
|
||||
// @flow
|
||||
|
||||
const codegenNativeCommands = require('codegenNativeCommands');
|
||||
const codegenNativeComponent = require('codegenNativeComponent');
|
||||
|
||||
import type {ViewProps} from 'ViewPropTypes';
|
||||
import type {NativeComponentType} from 'codegenNativeComponent';
|
||||
|
||||
type ModuleProps = $ReadOnly<{|
|
||||
...ViewProps,
|
||||
|}>;
|
||||
|
||||
type NativeType = NativeComponentType<ModuleProps>;
|
||||
|
||||
interface NativeCommands {
|
||||
+seek: (viewRef: React.ElementRef<NativeType>, position: number) => void;
|
||||
+stop: (viewRef: React.ElementRef<NativeType>) => void;
|
||||
}
|
||||
|
||||
export const Commands = (
|
||||
cov_abcdef123().f[2]++,
|
||||
cov_abcdef123().s[5]++,
|
||||
codegenNativeCommands<NativeCommands>({
|
||||
supportedCommands: ['seek', 'stop'],
|
||||
})
|
||||
);
|
||||
|
||||
export default codegenNativeComponent<ModuleProps>('Module');
|
||||
`;
|
||||
|
||||
const COMMANDS_WITH_TYPE_CAST_COVERAGE = `
|
||||
// @flow
|
||||
|
||||
const codegenNativeCommands = require('codegenNativeCommands');
|
||||
const codegenNativeComponent = require('codegenNativeComponent');
|
||||
|
||||
import type {ViewProps} from 'ViewPropTypes';
|
||||
import type {NativeComponentType} from 'codegenNativeComponent';
|
||||
|
||||
type ModuleProps = $ReadOnly<{|
|
||||
...ViewProps,
|
||||
|}>;
|
||||
|
||||
type NativeType = NativeComponentType<ModuleProps>;
|
||||
|
||||
interface NativeCommands {
|
||||
+mute: (viewRef: React.ElementRef<NativeType>) => void;
|
||||
+unmute: (viewRef: React.ElementRef<NativeType>) => void;
|
||||
}
|
||||
|
||||
export const Commands: NativeCommands = (cov_xyz789().s[1]++, codegenNativeCommands<NativeCommands>({
|
||||
supportedCommands: ['mute', 'unmute'],
|
||||
}));
|
||||
|
||||
export default codegenNativeComponent<ModuleProps>('Module');
|
||||
`;
|
||||
|
||||
const FULL_NATIVE_COMPONENT_WITH_TYPE_EXPORT = `
|
||||
// @flow
|
||||
|
||||
@@ -193,9 +107,4 @@ module.exports = {
|
||||
'NotANativeComponent.js': NOT_A_NATIVE_COMPONENT,
|
||||
'FullNativeComponent.js': FULL_NATIVE_COMPONENT,
|
||||
'FullTypedNativeComponent.js': FULL_NATIVE_COMPONENT_WITH_TYPE_EXPORT,
|
||||
'CommandsWithSimpleCoverageNativeComponent.js': COMMANDS_WITH_SIMPLE_COVERAGE,
|
||||
'CommandsWithComplexCoverageNativeComponent.js':
|
||||
COMMANDS_WITH_COMPLEX_COVERAGE,
|
||||
'CommandsWithTypeCastCoverageNativeComponent.js':
|
||||
COMMANDS_WITH_TYPE_CAST_COVERAGE,
|
||||
};
|
||||
|
||||
@@ -1,77 +1,5 @@
|
||||
// Jest Snapshot v1, https://goo.gl/fbAQLP
|
||||
|
||||
exports[`Babel plugin inline view configs can inline config for CommandsWithComplexCoverageNativeComponent.js 1`] = `
|
||||
"// @flow
|
||||
|
||||
const codegenNativeCommands = require('codegenNativeCommands');
|
||||
const codegenNativeComponent = require('codegenNativeComponent');
|
||||
import type { ViewProps } from 'ViewPropTypes';
|
||||
import type { NativeComponentType } from 'codegenNativeComponent';
|
||||
type ModuleProps = $ReadOnly<{|
|
||||
...ViewProps
|
||||
|}>;
|
||||
type NativeType = NativeComponentType<ModuleProps>;
|
||||
interface NativeCommands {
|
||||
+seek: (viewRef: React.ElementRef<NativeType>, position: number) => void,
|
||||
+stop: (viewRef: React.ElementRef<NativeType>) => void,
|
||||
}
|
||||
const NativeComponentRegistry = require('react-native/Libraries/NativeComponent/NativeComponentRegistry');
|
||||
let nativeComponentName = 'Module';
|
||||
export const __INTERNAL_VIEW_CONFIG = {
|
||||
uiViewClassName: \\"Module\\",
|
||||
validAttributes: {}
|
||||
};
|
||||
export default NativeComponentRegistry.get(nativeComponentName, () => __INTERNAL_VIEW_CONFIG);"
|
||||
`;
|
||||
|
||||
exports[`Babel plugin inline view configs can inline config for CommandsWithSimpleCoverageNativeComponent.js 1`] = `
|
||||
"// @flow
|
||||
|
||||
const codegenNativeCommands = require('codegenNativeCommands');
|
||||
const codegenNativeComponent = require('codegenNativeComponent');
|
||||
import type { ViewProps } from 'ViewPropTypes';
|
||||
import type { NativeComponentType } from 'codegenNativeComponent';
|
||||
type ModuleProps = $ReadOnly<{|
|
||||
...ViewProps
|
||||
|}>;
|
||||
type NativeType = NativeComponentType<ModuleProps>;
|
||||
interface NativeCommands {
|
||||
+pause: (viewRef: React.ElementRef<NativeType>) => void,
|
||||
+play: (viewRef: React.ElementRef<NativeType>) => void,
|
||||
}
|
||||
const NativeComponentRegistry = require('react-native/Libraries/NativeComponent/NativeComponentRegistry');
|
||||
let nativeComponentName = 'Module';
|
||||
export const __INTERNAL_VIEW_CONFIG = {
|
||||
uiViewClassName: \\"Module\\",
|
||||
validAttributes: {}
|
||||
};
|
||||
export default NativeComponentRegistry.get(nativeComponentName, () => __INTERNAL_VIEW_CONFIG);"
|
||||
`;
|
||||
|
||||
exports[`Babel plugin inline view configs can inline config for CommandsWithTypeCastCoverageNativeComponent.js 1`] = `
|
||||
"// @flow
|
||||
|
||||
const codegenNativeCommands = require('codegenNativeCommands');
|
||||
const codegenNativeComponent = require('codegenNativeComponent');
|
||||
import type { ViewProps } from 'ViewPropTypes';
|
||||
import type { NativeComponentType } from 'codegenNativeComponent';
|
||||
type ModuleProps = $ReadOnly<{|
|
||||
...ViewProps
|
||||
|}>;
|
||||
type NativeType = NativeComponentType<ModuleProps>;
|
||||
interface NativeCommands {
|
||||
+mute: (viewRef: React.ElementRef<NativeType>) => void,
|
||||
+unmute: (viewRef: React.ElementRef<NativeType>) => void,
|
||||
}
|
||||
const NativeComponentRegistry = require('react-native/Libraries/NativeComponent/NativeComponentRegistry');
|
||||
let nativeComponentName = 'Module';
|
||||
export const __INTERNAL_VIEW_CONFIG = {
|
||||
uiViewClassName: \\"Module\\",
|
||||
validAttributes: {}
|
||||
};
|
||||
export default NativeComponentRegistry.get(nativeComponentName, () => __INTERNAL_VIEW_CONFIG);"
|
||||
`;
|
||||
|
||||
exports[`Babel plugin inline view configs can inline config for FullNativeComponent.js 1`] = `
|
||||
"// @flow
|
||||
|
||||
@@ -225,61 +153,6 @@ exports[`Babel plugin inline view configs fails on inline config for CommandsExp
|
||||
24 |"
|
||||
`;
|
||||
|
||||
exports[`Babel plugin inline view configs fails on inline config for CommandsWithComplexCoverageInvalidNativeComponent.js 1`] = `
|
||||
"/CommandsWithComplexCoverageInvalidNativeComponent.js: 'Commands' is a reserved export and may only be used to export the result of codegenNativeCommands.
|
||||
14 |
|
||||
15 | // Complex coverage instrumentation with invalid nested structure - should fail
|
||||
> 16 | export const Commands = (
|
||||
| ^
|
||||
17 | cov_xyz789().f[1]++,
|
||||
18 | cov_xyz789().s[2]++,
|
||||
19 | {"
|
||||
`;
|
||||
|
||||
exports[`Babel plugin inline view configs fails on inline config for CommandsWithCoverageInvalidNativeComponent.js 1`] = `
|
||||
"/CommandsWithCoverageInvalidNativeComponent.js: 'Commands' is a reserved export and may only be used to export the result of codegenNativeCommands.
|
||||
14 |
|
||||
15 | // Coverage instrumentation of invalid Commands export - should still fail
|
||||
> 16 | export const Commands = (cov_1234567890().s[0]++, {
|
||||
| ^
|
||||
17 | hotspotUpdate: () => {},
|
||||
18 | scrollTo: () => {},
|
||||
19 | });"
|
||||
`;
|
||||
|
||||
exports[`Babel plugin inline view configs fails on inline config for CommandsWithCoverageTypeCastInvalidNativeComponent.js 1`] = `
|
||||
"/CommandsWithCoverageTypeCastInvalidNativeComponent.js: 'Commands' is a reserved export and may only be used to export the result of codegenNativeCommands.
|
||||
19 |
|
||||
20 | // Coverage instrumentation with type cast but wrong function - should fail
|
||||
> 21 | export const Commands: NativeCommands = (cov_cast123().s[0]++, invalidFunction({
|
||||
| ^
|
||||
22 | supportedCommands: ['pause', 'play'],
|
||||
23 | }));
|
||||
24 |"
|
||||
`;
|
||||
|
||||
exports[`Babel plugin inline view configs fails on inline config for CommandsWithCoverageWrongFunctionNativeComponent.js 1`] = `
|
||||
"/CommandsWithCoverageWrongFunctionNativeComponent.js: 'Commands' is a reserved export and may only be used to export the result of codegenNativeCommands.
|
||||
14 |
|
||||
15 | // Coverage instrumentation of wrong function call - should fail
|
||||
> 16 | export const Commands = (cov_abcdef123().s[0]++, someOtherFunction({
|
||||
| ^
|
||||
17 | supportedCommands: ['pause', 'play'],
|
||||
18 | }));
|
||||
19 |"
|
||||
`;
|
||||
|
||||
exports[`Babel plugin inline view configs fails on inline config for CommandsWithCoverageWrongNameNativeComponent.js 1`] = `
|
||||
"/CommandsWithCoverageWrongNameNativeComponent.js: Native commands must be exported with the name 'Commands'
|
||||
20 |
|
||||
21 | // Coverage instrumentation with correct function but wrong export name - should fail
|
||||
> 22 | export const WrongName = (cov_wrong123().s[0]++, codegenNativeCommands<NativeCommands>({
|
||||
| ^
|
||||
23 | supportedCommands: ['pause', 'play'],
|
||||
24 | }));
|
||||
25 |"
|
||||
`;
|
||||
|
||||
exports[`Babel plugin inline view configs fails on inline config for OtherCommandsExportNativeComponent.js 1`] = `
|
||||
"/OtherCommandsExportNativeComponent.js: 'Commands' is a reserved export and may only be used to export the result of codegenNativeCommands.
|
||||
17 | }
|
||||
|
||||
@@ -102,58 +102,6 @@ function isCodegenDeclaration(declaration) {
|
||||
return false;
|
||||
}
|
||||
|
||||
function isCodegenNativeCommandsDeclaration(declaration) {
|
||||
if (!declaration) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Handle direct calls: codegenNativeCommands()
|
||||
if (
|
||||
declaration.type === 'CallExpression' &&
|
||||
declaration.callee &&
|
||||
declaration.callee.type === 'Identifier' &&
|
||||
declaration.callee.name === 'codegenNativeCommands'
|
||||
) {
|
||||
return true;
|
||||
}
|
||||
|
||||
// Handle coverage instrumentation: (cov_xxx().s[0]++, codegenNativeCommands())
|
||||
if (declaration.type === 'SequenceExpression' && declaration.expressions) {
|
||||
// Get the last expression in the sequence (the actual function call)
|
||||
const lastExpression =
|
||||
declaration.expressions[declaration.expressions.length - 1];
|
||||
// Recursively check if the last expression is a valid codegenNativeCommands call
|
||||
return isCodegenNativeCommandsDeclaration(lastExpression);
|
||||
}
|
||||
|
||||
// Handle Flow type casts: (codegenNativeCommands(): NativeCommands)
|
||||
if (
|
||||
(declaration.type === 'TypeCastExpression' ||
|
||||
declaration.type === 'AsExpression') &&
|
||||
declaration.expression &&
|
||||
declaration.expression.type === 'CallExpression' &&
|
||||
declaration.expression.callee &&
|
||||
declaration.expression.callee.type === 'Identifier' &&
|
||||
declaration.expression.callee.name === 'codegenNativeCommands'
|
||||
) {
|
||||
return true;
|
||||
}
|
||||
|
||||
// Handle TypeScript assertions: codegenNativeCommands() as NativeCommands
|
||||
if (
|
||||
declaration.type === 'TSAsExpression' &&
|
||||
declaration.expression &&
|
||||
declaration.expression.type === 'CallExpression' &&
|
||||
declaration.expression.callee &&
|
||||
declaration.expression.callee.type === 'Identifier' &&
|
||||
declaration.expression.callee.name === 'codegenNativeCommands'
|
||||
) {
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
module.exports = function ({parse, types: t}) {
|
||||
return {
|
||||
pre(state) {
|
||||
@@ -177,12 +125,12 @@ module.exports = function ({parse, types: t}) {
|
||||
const firstDeclaration = path.node.declaration.declarations[0];
|
||||
|
||||
if (firstDeclaration.type === 'VariableDeclarator') {
|
||||
// Check if this is a valid codegenNativeCommands call, handling type annotations
|
||||
const isValidCommandsExport = isCodegenNativeCommandsDeclaration(
|
||||
firstDeclaration.init,
|
||||
);
|
||||
|
||||
if (isValidCommandsExport) {
|
||||
if (
|
||||
firstDeclaration.init &&
|
||||
firstDeclaration.init.type === 'CallExpression' &&
|
||||
firstDeclaration.init.callee.type === 'Identifier' &&
|
||||
firstDeclaration.init.callee.name === 'codegenNativeCommands'
|
||||
) {
|
||||
if (
|
||||
firstDeclaration.id.type === 'Identifier' &&
|
||||
firstDeclaration.id.name !== 'Commands'
|
||||
|
||||
@@ -40,9 +40,6 @@
|
||||
"peerDependenciesMeta": {
|
||||
"@react-native-community/cli": {
|
||||
"optional": true
|
||||
},
|
||||
"@react-native/metro-config": {
|
||||
"optional": true
|
||||
}
|
||||
},
|
||||
"engines": {
|
||||
|
||||
@@ -116,7 +116,7 @@ function copyAll(filesToCopy: CopiedFiles) {
|
||||
const src = queue.shift();
|
||||
// $FlowFixMe[incompatible-type]
|
||||
const dest = filesToCopy[src];
|
||||
// $FlowFixMe[incompatible-type]
|
||||
// $FlowFixMe[incompatible-call]
|
||||
copy(src, dest, copyNext);
|
||||
}
|
||||
};
|
||||
|
||||
@@ -31,9 +31,9 @@ type MiddlewareReturn = {
|
||||
...
|
||||
};
|
||||
|
||||
// $FlowFixMe[incompatible-type]
|
||||
// $FlowFixMe
|
||||
const unusedStubWSServer: ws$WebSocketServer = {};
|
||||
// $FlowFixMe[incompatible-type]
|
||||
// $FlowFixMe
|
||||
const unusedMiddlewareStub: Server = {};
|
||||
|
||||
const communityMiddlewareFallback = {
|
||||
|
||||
@@ -153,7 +153,7 @@ function buildDiffUrl(oldVersion: string, newVersion: string) {
|
||||
* Returns the most recent React Native version available to upgrade to.
|
||||
*/
|
||||
async function getLatestRnDiffPurgeVersion(): Promise<LatestVersions | void> {
|
||||
const options: RequestOptions = {
|
||||
const options = {
|
||||
// https://developer.github.com/v3/#user-agent-required
|
||||
headers: {'User-Agent': '@react-native/community-cli-plugin'} as Headers,
|
||||
};
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
@generated SignedSource<<b6a779e724ecce5e727ee674dc20f809>>
|
||||
Git revision: e87564a24cf233c60aaebee8c418ec85724f7214
|
||||
@generated SignedSource<<9252db36d4b1db907a38c08935ceeb38>>
|
||||
Git revision: 921566790e9e16d0ecace6e49b3cfaace205958c
|
||||
Built with --nohooks: false
|
||||
Is local checkout: false
|
||||
Remote URL: https://github.com/facebook/react-native-devtools-frontend
|
||||
|
||||
+1
-1
File diff suppressed because one or more lines are too long
@@ -1,33 +0,0 @@
|
||||
// Jest Snapshot v1, https://goo.gl/fbAQLP
|
||||
|
||||
exports[`prepareDebuggerShellFromDotSlashFile fails with the expected error message for a missing dotslash file 1`] = `
|
||||
Object {
|
||||
"code": "unexpected_error",
|
||||
"humanReadableMessage": "An unexpected error occured while installing the latest version of React Native DevTools. Using a fallback version instead.",
|
||||
"verboseInfo": Any<String>,
|
||||
}
|
||||
`;
|
||||
|
||||
exports[`prepareDebuggerShellFromDotSlashFile fails with the expected error message for missing platforms 1`] = `
|
||||
Object {
|
||||
"code": "platform_not_supported",
|
||||
"humanReadableMessage": "The latest version of React Native DevTools is not supported on this platform. Using a fallback version instead.",
|
||||
"verboseInfo": Any<String>,
|
||||
}
|
||||
`;
|
||||
|
||||
exports[`prepareDebuggerShellFromDotSlashFile scenarios requiring a local HTTP server fails with the expected error message for a corrupted tarball 1`] = `
|
||||
Object {
|
||||
"code": "possible_corruption",
|
||||
"humanReadableMessage": "Failed to verify the latest version of React Native DevTools. Using a fallback version instead. ",
|
||||
"verboseInfo": Any<String>,
|
||||
}
|
||||
`;
|
||||
|
||||
exports[`prepareDebuggerShellFromDotSlashFile scenarios requiring a local HTTP server fails with the expected error message for a network error 1`] = `
|
||||
Object {
|
||||
"code": "likely_offline",
|
||||
"humanReadableMessage": "Failed to download the latest version of React Native DevTools. Using a fallback version instead. Connect to the internet or check your network settings.",
|
||||
"verboseInfo": Any<String>,
|
||||
}
|
||||
`;
|
||||
@@ -1,59 +0,0 @@
|
||||
#!/usr/bin/env dotslash
|
||||
|
||||
{
|
||||
"name": "React Native DevTools",
|
||||
"platforms": {
|
||||
"linux-aarch64": {
|
||||
"size": 113510892,
|
||||
"hash": "sha256",
|
||||
"digest": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855",
|
||||
"providers": [
|
||||
{
|
||||
"type": "http",
|
||||
"url": "http://$HOST:$PORT/corrupted.tar.gz"
|
||||
}
|
||||
],
|
||||
"format": "tar.gz",
|
||||
"path": "React Native DevTools-linux-arm64/React Native DevTools"
|
||||
},
|
||||
"linux-x86_64": {
|
||||
"size": 113243910,
|
||||
"hash": "sha256",
|
||||
"digest": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855",
|
||||
"providers": [
|
||||
{
|
||||
"type": "http",
|
||||
"url": "http://$HOST:$PORT/corrupted.tar.gz"
|
||||
}
|
||||
],
|
||||
"format": "tar.gz",
|
||||
"path": "React Native DevTools-linux-x64/React Native DevTools"
|
||||
},
|
||||
"macos-aarch64": {
|
||||
"size": 108810433,
|
||||
"hash": "sha256",
|
||||
"digest": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855",
|
||||
"providers": [
|
||||
{
|
||||
"type": "http",
|
||||
"url": "http://$HOST:$PORT/corrupted.tar.gz"
|
||||
}
|
||||
],
|
||||
"format": "tar.gz",
|
||||
"path": "React Native DevTools.app/Contents/MacOS/React Native DevTools"
|
||||
},
|
||||
"macos-x86_64": {
|
||||
"size": 113769989,
|
||||
"hash": "sha256",
|
||||
"digest": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855",
|
||||
"providers": [
|
||||
{
|
||||
"type": "http",
|
||||
"url": "http://$HOST:$PORT/corrupted.tar.gz"
|
||||
}
|
||||
],
|
||||
"format": "tar.gz",
|
||||
"path": "React Native DevTools.app/Contents/MacOS/React Native DevTools"
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,59 +0,0 @@
|
||||
#!/usr/bin/env dotslash
|
||||
|
||||
{
|
||||
"name": "React Native DevTools",
|
||||
"platforms": {
|
||||
"linux-aarch64": {
|
||||
"size": 113510892,
|
||||
"hash": "sha256",
|
||||
"digest": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855",
|
||||
"providers": [
|
||||
{
|
||||
"type": "http",
|
||||
"url": "https://$HOST:$PORT/does-not-exist"
|
||||
}
|
||||
],
|
||||
"format": "tar.gz",
|
||||
"path": "React Native DevTools-linux-arm64/React Native DevTools"
|
||||
},
|
||||
"linux-x86_64": {
|
||||
"size": 113243910,
|
||||
"hash": "sha256",
|
||||
"digest": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855",
|
||||
"providers": [
|
||||
{
|
||||
"type": "http",
|
||||
"url": "https://$HOST:$PORT/does-not-exist"
|
||||
}
|
||||
],
|
||||
"format": "tar.gz",
|
||||
"path": "React Native DevTools-linux-x64/React Native DevTools"
|
||||
},
|
||||
"macos-aarch64": {
|
||||
"size": 108810433,
|
||||
"hash": "sha256",
|
||||
"digest": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855",
|
||||
"providers": [
|
||||
{
|
||||
"type": "http",
|
||||
"url": "https://$HOST:$PORT/does-not-exist"
|
||||
}
|
||||
],
|
||||
"format": "tar.gz",
|
||||
"path": "React Native DevTools.app/Contents/MacOS/React Native DevTools"
|
||||
},
|
||||
"macos-x86_64": {
|
||||
"size": 113769989,
|
||||
"hash": "sha256",
|
||||
"digest": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855",
|
||||
"providers": [
|
||||
{
|
||||
"type": "http",
|
||||
"url": "https://$HOST:$PORT/does-not-exist"
|
||||
}
|
||||
],
|
||||
"format": "tar.gz",
|
||||
"path": "React Native DevTools.app/Contents/MacOS/React Native DevTools"
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,6 +0,0 @@
|
||||
#!/usr/bin/env dotslash
|
||||
|
||||
{
|
||||
"name": "React Native DevTools",
|
||||
"platforms": {}
|
||||
}
|
||||
@@ -1,139 +0,0 @@
|
||||
/**
|
||||
* Copyright (c) Meta Platforms, Inc. and affiliates.
|
||||
*
|
||||
* This source code is licensed under the MIT license found in the
|
||||
* LICENSE file in the root directory of this source tree.
|
||||
*
|
||||
* @flow strict-local
|
||||
* @format
|
||||
*/
|
||||
|
||||
const {
|
||||
prepareDebuggerShellFromDotSlashFile,
|
||||
} = require('../src/node/private/LaunchUtils');
|
||||
const fs = require('fs').promises;
|
||||
const http = require('http');
|
||||
const os = require('os');
|
||||
const path = require('path');
|
||||
|
||||
// The implementation of prepareDebuggerShellFromDotSlashFile relies on
|
||||
// details of DotSlash that are not guaranteed to be stable (support for
|
||||
// `dotslash -- fetch <file>`, certain strings being printed to stderr).
|
||||
// This (admittedly elaborate) test suite ensures we'll fail loudly if we
|
||||
// try to upgrade DotSlash to a version that breaks our assumptions.
|
||||
describe('prepareDebuggerShellFromDotSlashFile', () => {
|
||||
test('fails with the expected error message for missing platforms', async () => {
|
||||
const result = await prepareDebuggerShellFromDotSlashFile(
|
||||
path.join(__dirname, 'dotslash-file-with-missing-platforms.jsonc'),
|
||||
);
|
||||
expect(result).toMatchSnapshot({
|
||||
verboseInfo: expect.any(String),
|
||||
});
|
||||
});
|
||||
|
||||
test('fails with the expected error message for a missing dotslash file', async () => {
|
||||
const result = await prepareDebuggerShellFromDotSlashFile(
|
||||
path.join(__dirname, 'dotslash-file-that-does-not-exist.jsonc'),
|
||||
);
|
||||
expect(result).toMatchSnapshot({
|
||||
verboseInfo: expect.any(String),
|
||||
});
|
||||
});
|
||||
|
||||
describe('scenarios requiring a local HTTP server', () => {
|
||||
let server, scratchDir;
|
||||
|
||||
beforeEach(async () => {
|
||||
scratchDir = await fs.mkdtemp(path.join(os.tmpdir(), 'dotslash-test-'));
|
||||
server = http.createServer((request, response) => {
|
||||
if (request.url === '/corrupted.tar.gz') {
|
||||
response.writeHead(200, {'Content-Type': 'application/gzip'});
|
||||
response.end(
|
||||
'Hello, world!\n' + 'This simulated a corrupted tarball.',
|
||||
);
|
||||
} else {
|
||||
response.writeHead(404);
|
||||
response.end();
|
||||
}
|
||||
});
|
||||
await new Promise((resolve, reject) => {
|
||||
server.on('error', reject);
|
||||
server.listen(0, 'localhost', () => {
|
||||
server.removeListener('error', reject);
|
||||
resolve();
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
afterEach(async () => {
|
||||
await fs.rm(scratchDir, {recursive: true, force: true});
|
||||
if (server.listening) {
|
||||
await new Promise((resolve, reject) => {
|
||||
server.close(error => {
|
||||
if (error) {
|
||||
reject(error);
|
||||
} else {
|
||||
resolve();
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
test('fails with the expected error message for a corrupted tarball', async () => {
|
||||
const dotslashFileContents = injectHostPort(
|
||||
await fs.readFile(
|
||||
path.join(
|
||||
__dirname,
|
||||
'dotslash-file-simulating-data-corruption.jsonc',
|
||||
),
|
||||
'utf8',
|
||||
),
|
||||
server.address(),
|
||||
);
|
||||
|
||||
await fs.writeFile(
|
||||
path.join(scratchDir, 'dotslash-file.jsonc'),
|
||||
dotslashFileContents,
|
||||
);
|
||||
const result = await prepareDebuggerShellFromDotSlashFile(
|
||||
path.join(scratchDir, 'dotslash-file.jsonc'),
|
||||
);
|
||||
expect(result).toMatchSnapshot({
|
||||
verboseInfo: expect.any(String),
|
||||
});
|
||||
});
|
||||
|
||||
test('fails with the expected error message for a network error', async () => {
|
||||
const dotslashFileContents = injectHostPort(
|
||||
await fs.readFile(
|
||||
path.join(__dirname, 'dotslash-file-simulating-network-error.jsonc'),
|
||||
'utf8',
|
||||
),
|
||||
server.address(),
|
||||
);
|
||||
|
||||
await fs.writeFile(
|
||||
path.join(scratchDir, 'dotslash-file.jsonc'),
|
||||
dotslashFileContents,
|
||||
);
|
||||
const result = await prepareDebuggerShellFromDotSlashFile(
|
||||
path.join(scratchDir, 'dotslash-file.jsonc'),
|
||||
);
|
||||
expect(result).toMatchSnapshot({
|
||||
verboseInfo: expect.any(String),
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
function injectHostPort(
|
||||
dotslashFileContents: string,
|
||||
address: net$Socket$address,
|
||||
) {
|
||||
const host =
|
||||
address.family === 'IPv6' ? `[${address.address}]` : address.address;
|
||||
return dotslashFileContents
|
||||
.replaceAll('$HOST', host)
|
||||
.replaceAll('$PORT', address.port.toString());
|
||||
}
|
||||
@@ -22,7 +22,7 @@ describe('Electron dependency', () => {
|
||||
// $FlowFixMe[untyped-import] - package.json is not typed
|
||||
const ourPackageJson = require('../package.json');
|
||||
|
||||
const declaredElectronVersion = ourPackageJson.devDependencies.electron;
|
||||
const declaredElectronVersion = ourPackageJson.dependencies.electron;
|
||||
expect(declaredElectronVersion).toBeTruthy();
|
||||
|
||||
// $FlowFixMe[untyped-import] - package.json is not typed
|
||||
|
||||
@@ -1,75 +1,62 @@
|
||||
#!/usr/bin/env dotslash
|
||||
|
||||
// @generated SignedSource<<134c50a92ea8bbff7e42738b3e38c148>>
|
||||
// @generated SignedSource<<686df5695b32a90cd465412d979a1a3f>>
|
||||
|
||||
|
||||
{
|
||||
"name": "React Native DevTools",
|
||||
"platforms": {
|
||||
"linux-aarch64": {
|
||||
"size": 116052435,
|
||||
"size": 113511487,
|
||||
"hash": "sha256",
|
||||
"digest": "957719cc5fc97f7a8841b3d2a7e1f8d625ab47b9031814ed11e0576b53552555",
|
||||
"digest": "c22f7d5e029357f05055ce7c56cc284a0d441d558e103a7b7ebae118c67d0b95",
|
||||
"providers": [
|
||||
{
|
||||
"type": "http",
|
||||
"url": "https://scontent.xx.fbcdn.net/mci_ab/uap/?ab_b=m&ab_page=react_native_devtools_binaries&ab_entry=AQOj5i7H4UTSZnrzYlWsTuSz6NHYS2de67x1ZurivOvqQdO7dsCf5R7C06hj7a2PydZR1HOmVEoGsuswoiGRg80pmS2AhhCds1lkchqG5puV5Bx2IY1rUosYTsZceD2AXgR7ptTD6NqgEGVG7O2KFpruI08RIQZUm60Kdn8LBeGfN3JddCIdsVQEsOk"
|
||||
"url": "https://scontent.xx.fbcdn.net/mci_ab/uap/?ab_b=m&ab_page=react_native_devtools_binaries&ab_entry=AQPRilWDJV8xyKEv9lqYn7Hf2kyZg6nqW8KctGZCXsU95lS_MTFyAmEULrB3J6hGCjqBY2Zl-uq_FjERAIgzbZFWX2eceacTbutBSOKEj6QTZzkVN_L1zPh2lGh24x29MHpkwUzw4E-kAXV43f-11QxqD7orI1QicyOnsmUqeRNKE_QjM9rNRsw6iE8"
|
||||
}
|
||||
],
|
||||
"format": "tar.gz",
|
||||
"path": "React Native DevTools-linux-arm64/React Native DevTools"
|
||||
},
|
||||
"linux-x86_64": {
|
||||
"size": 115924513,
|
||||
"size": 113244728,
|
||||
"hash": "sha256",
|
||||
"digest": "d8fe8e8df471e486697d5a42c7e77e7260ad3c44b10830f28529dca9f290535a",
|
||||
"digest": "d749aee18d6c969f033511ed631b439c578068cc20786974bc0ee707c6c2f177",
|
||||
"providers": [
|
||||
{
|
||||
"type": "http",
|
||||
"url": "https://scontent.xx.fbcdn.net/mci_ab/uap/?ab_b=m&ab_page=react_native_devtools_binaries&ab_entry=AQO_hyY-qUuuZXCew5qzp-tMfPL8fLLTUr5h4TLRbynsM3CmTlSILlmDcWP_xKAi5BJZSb6sOhATNKtRxn4pduIVkniIcL_6m1mGbt-1Qy8iKt0aUmANj5Nv8Wpryw6XL5OKYqtxHIsBg6YiZHW9JXYRCBk6oGCl1LGbhUZp2LGIFXAsOJy_DRcV"
|
||||
"url": "https://scontent.xx.fbcdn.net/mci_ab/uap/?ab_b=m&ab_page=react_native_devtools_binaries&ab_entry=AQP8NAAXjHJ-9NJThip1nnmVim4yiS1tdYAXmWl2Remiluq5f13nXA8YEadsmGRLWxXz2WJouHXSK47ea4a30fxewyKeTt0niFn3T-lr_91m3Ve5ZS-FOZ_9CRVCkv5zC-Z1FlXJGOnphfWetIU10Pw9tho3IzjdSNlXle0XTrkJ2AyKSl4cKPaX"
|
||||
}
|
||||
],
|
||||
"format": "tar.gz",
|
||||
"path": "React Native DevTools-linux-x64/React Native DevTools"
|
||||
},
|
||||
"macos-aarch64": {
|
||||
"size": 110899100,
|
||||
"size": 108805847,
|
||||
"hash": "sha256",
|
||||
"digest": "fe4dd8fa12bcb5a82e9843c6d80a9199d7b144487bde3d05e62cb4492d050f49",
|
||||
"digest": "dc4a6cfa3d2d8646db8793ef497e43ad72be54bb14ed96345d059880ff72ce1e",
|
||||
"providers": [
|
||||
{
|
||||
"type": "http",
|
||||
"url": "https://scontent.xx.fbcdn.net/mci_ab/uap/?ab_b=m&ab_page=react_native_devtools_binaries&ab_entry=AQOFvf5d4K1OypXm39lo851JiwM_hB2yfmdcFhZxgP6rN9jYNH0kg97ZF8HDc6DMYIBxVojrm7jlFgpk4Inc6AzXF6z5ypQfZwgM54F8wLaZqYY9Yk46nnQlQefjwGqrXKsniE_S9S2nqFQDTmQUbGGQIuM_bQjGievQsxXLrUadSGAcMu-MEB79a64Z"
|
||||
"url": "https://scontent.xx.fbcdn.net/mci_ab/uap/?ab_b=m&ab_page=react_native_devtools_binaries&ab_entry=AQPuZx9G4h30mUDM0hZ8y74LbzAi7-S6jnOqq8uyfDdIptLwIgpb8CKB8F2tduvaBFMGd7obwXd2NVy01r6XKVdgAK5-QTp8PYUPUE7VSCi6QAqobXcQ2uq_lY-jgyj8XkV4Ua4gA6KR609Bmh-beSTOrSqMymqVodaGqoGjB_9jVwfD99_PfFlex-Ta"
|
||||
}
|
||||
],
|
||||
"format": "tar.gz",
|
||||
"path": "React Native DevTools.app/Contents/MacOS/React Native DevTools"
|
||||
},
|
||||
"macos-x86_64": {
|
||||
"size": 117756587,
|
||||
"size": 113766610,
|
||||
"hash": "sha256",
|
||||
"digest": "3fe8a73843c3f0e1b72ffff0d069f9f1064f845be6fd44380d55aa1ab87220ac",
|
||||
"digest": "74178859ce6a1c26c32055e192b1b123822c76c827ea86a6dfdb5463b89b1ace",
|
||||
"providers": [
|
||||
{
|
||||
"type": "http",
|
||||
"url": "https://scontent.xx.fbcdn.net/mci_ab/uap/?ab_b=m&ab_page=react_native_devtools_binaries&ab_entry=AQNzRlsBElUT0wqSwTvxhvwPnuYpfI8Aka8Z4Wlmkmw_0pIwU0iEIh16wwH3OZTAMJ4_pM8YpyBl51FQXR5bai58MVtI_cI7aRxTfF63HKvk0iU0IxV06etgiaYiEmcyneLTb7UdYVdaZiwx7x_a0oaZU8FiuBlhg_YRzE6tM8oTD1hQf0T-a17USg"
|
||||
"url": "https://scontent.xx.fbcdn.net/mci_ab/uap/?ab_b=m&ab_page=react_native_devtools_binaries&ab_entry=AQN6DjGhr8_L7N_cwrh8pi3cfNcbn9dot_QQpgwe3Vv-780FDgi6JUeMvbo_wbFoT2rogxfHZ0-NbXbKdWYwhGUoGeQuikT57QnVMOYuPUTjlQUgYy0Ng9XPhq3iSEYwlMV1XsUJuPFRUg-hIHcgaaA55JoTLXZ1fLYydhHnmgRxGHc4pxaHvSpTtQ"
|
||||
}
|
||||
],
|
||||
"format": "tar.gz",
|
||||
"path": "React Native DevTools.app/Contents/MacOS/React Native DevTools"
|
||||
},
|
||||
"windows-x86_64": {
|
||||
"size": 125513328,
|
||||
"hash": "sha256",
|
||||
"digest": "2477ee80171fc9f1c1f0b57418502bc103c6b40138f97f74d0b6470bdfee634c",
|
||||
"providers": [
|
||||
{
|
||||
"type": "http",
|
||||
"url": "https://scontent.xx.fbcdn.net/mci_ab/uap/?ab_b=m&ab_page=react_native_devtools_binaries&ab_entry=AQMwDMTAKLhSOn54OBOfhm6F9HDvQN0kUXp7ekfFik2NdgGoL_eVBu9ZcB2LM8x3YNpk0KxCAJE75jDfNTvGD2CnmLLRNOPCkdQP3tko7TTNkTSA2uTSd-ov8butKl1w4RP7ZMOy-0tmjyUoJMQtE1DlN5_4FdCo9DsHmQ7aB3wq_nz90nH0V_iS"
|
||||
}
|
||||
],
|
||||
"format": "tar.gz",
|
||||
"path": "React Native DevTools-win32-x64/React Native DevTools.exe"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -26,20 +26,14 @@
|
||||
},
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
"node": ">= 20.19.4"
|
||||
"node": ">= 20.19.4",
|
||||
"electron": ">=36.3.0"
|
||||
},
|
||||
"dependencies": {
|
||||
"cross-spawn": "^7.0.6",
|
||||
"fb-dotslash": "0.5.8"
|
||||
"electron": "36.3.0"
|
||||
},
|
||||
"devDependencies": {
|
||||
"electron": "37.2.6",
|
||||
"semver": "^7.1.3"
|
||||
},
|
||||
"files": [
|
||||
"!**/__tests__/**",
|
||||
"bin",
|
||||
"dist",
|
||||
"!src/electron"
|
||||
]
|
||||
}
|
||||
}
|
||||
|
||||
@@ -36,7 +36,7 @@ function handleLaunchArgs(argv: string[]) {
|
||||
});
|
||||
|
||||
// Find an existing window for this app and launch configuration.
|
||||
let frontendWindow = BrowserWindow.getAllWindows().find(window => {
|
||||
const existingWindow = BrowserWindow.getAllWindows().find(window => {
|
||||
const metadata = windowMetadata.get(window);
|
||||
if (!metadata) {
|
||||
return false;
|
||||
@@ -44,37 +44,41 @@ function handleLaunchArgs(argv: string[]) {
|
||||
return metadata.windowKey === windowKey;
|
||||
});
|
||||
|
||||
if (frontendWindow) {
|
||||
if (existingWindow) {
|
||||
// If the window is already visible, flash it.
|
||||
if (frontendWindow.isVisible()) {
|
||||
frontendWindow.flashFrame(true);
|
||||
if (existingWindow.isVisible()) {
|
||||
existingWindow.flashFrame(true);
|
||||
setTimeout(() => {
|
||||
frontendWindow.flashFrame(false);
|
||||
existingWindow.flashFrame(false);
|
||||
}, 1000);
|
||||
}
|
||||
} else {
|
||||
// Create the browser window.
|
||||
frontendWindow = new BrowserWindow({
|
||||
width: 1200,
|
||||
height: 600,
|
||||
webPreferences: {
|
||||
partition: 'persist:react-native-devtools',
|
||||
preload: require.resolve('./preload.js'),
|
||||
},
|
||||
// Icon for Linux
|
||||
icon: path.join(__dirname, 'resources', 'icon.png'),
|
||||
});
|
||||
if (process.platform === 'darwin') {
|
||||
app.focus({
|
||||
steal: true,
|
||||
});
|
||||
}
|
||||
existingWindow.focus();
|
||||
return;
|
||||
}
|
||||
|
||||
// Create the browser window.
|
||||
const frontendWindow = new BrowserWindow({
|
||||
width: 1200,
|
||||
height: 600,
|
||||
webPreferences: {
|
||||
partition: 'persist:react-native-devtools',
|
||||
preload: require.resolve('./preload.js'),
|
||||
},
|
||||
// Icon for Linux
|
||||
icon: path.join(__dirname, 'resources', 'icon.png'),
|
||||
});
|
||||
|
||||
// Open links in the default browser instead of in new Electron windows.
|
||||
frontendWindow.webContents.setWindowOpenHandler(({url}) => {
|
||||
shell.openExternal(url);
|
||||
return {action: 'deny'};
|
||||
});
|
||||
|
||||
// TODO: If the window contains a live, working frontend instance with a valid connection to the backend,
|
||||
// we should avoid this reload and instead send the frontend a message to handle the launch arguments
|
||||
// dynamically (e.g. update the launch ID for telemetry purposes, handle deeplinking to a specific CDT panel, etc).
|
||||
frontendWindow.loadURL(frontendUrl);
|
||||
|
||||
windowMetadata.set(frontendWindow, {
|
||||
@@ -86,7 +90,6 @@ function handleLaunchArgs(argv: string[]) {
|
||||
steal: true,
|
||||
});
|
||||
}
|
||||
frontendWindow.focus();
|
||||
}
|
||||
|
||||
app.whenReady().then(() => {
|
||||
|
||||
@@ -1,48 +0,0 @@
|
||||
/**
|
||||
* Copyright (c) Meta Platforms, Inc. and affiliates.
|
||||
*
|
||||
* This source code is licensed under the MIT license found in the
|
||||
* LICENSE file in the root directory of this source tree.
|
||||
*
|
||||
* @flow strict-local
|
||||
* @format
|
||||
* @oncall react_native
|
||||
*/
|
||||
|
||||
const {unstable_spawnDebuggerShellWithArgs} = require('../../');
|
||||
|
||||
describe('debugger-shell Node package', () => {
|
||||
test('can spawn in detached+prebuilt mode without crashing', async () => {
|
||||
await expect(
|
||||
unstable_spawnDebuggerShellWithArgs(['--version'], {
|
||||
flavor: 'prebuilt',
|
||||
mode: 'detached',
|
||||
}),
|
||||
).resolves.toBeUndefined();
|
||||
});
|
||||
|
||||
// When running in the internal react-native-oss-js job, Electron isn't
|
||||
// installed correctly (postinstall scripts don't run) but the internal
|
||||
// `electron` workspace isn't available either. Detecting this dynamically
|
||||
// weakens the test somewhat in environments where it *should* pass, but this
|
||||
// is a dev-only feature anyway so this is fine.
|
||||
if (isElectronInstalled()) {
|
||||
test('can spawn in detached+dev mode without crashing', async () => {
|
||||
await expect(
|
||||
unstable_spawnDebuggerShellWithArgs(['--version'], {
|
||||
flavor: 'dev',
|
||||
mode: 'detached',
|
||||
}),
|
||||
).resolves.toBeUndefined();
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
function isElectronInstalled() {
|
||||
try {
|
||||
require('electron');
|
||||
return true;
|
||||
} catch {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
@@ -8,54 +8,44 @@
|
||||
* @format
|
||||
*/
|
||||
|
||||
import {
|
||||
prepareDebuggerShellFromDotSlashFile,
|
||||
spawnAndGetStderr,
|
||||
} from './private/LaunchUtils';
|
||||
|
||||
const {spawn} = require('cross-spawn');
|
||||
const path = require('path');
|
||||
|
||||
// The 'prebuilt' flavor will use the prebuilt shell binary (and the JavaScript embedded in it).
|
||||
// The 'dev' flavor will use a stock Electron binary and run the shell code from the `electron/` directory.
|
||||
type DebuggerShellFlavor = 'prebuilt' | 'dev';
|
||||
|
||||
const DEVTOOLS_BINARY_DOTSLASH_FILE = path.join(
|
||||
__dirname,
|
||||
'../../bin/react-native-devtools',
|
||||
);
|
||||
|
||||
async function unstable_spawnDebuggerShellWithArgs(
|
||||
args: string[],
|
||||
{
|
||||
mode = 'detached',
|
||||
flavor = 'prebuilt',
|
||||
}: $ReadOnly<{
|
||||
// In 'syncAndExit' mode, the current process will block until the spawned process exits, and then it will exit
|
||||
// with the same exit code as the spawned process.
|
||||
// In 'detached' mode, the spawned process will be detached from the current process and the current process will
|
||||
// continue to run normally.
|
||||
mode?: 'syncThenExit' | 'detached',
|
||||
flavor?: DebuggerShellFlavor,
|
||||
}> = {},
|
||||
): Promise<void> {
|
||||
const [binaryPath, baseArgs] = getShellBinaryAndArgs(flavor);
|
||||
// NOTE: Internally at Meta, this is aliased to a workspace that is
|
||||
// API-compatible with the 'electron' package, but contains prebuilt binaries
|
||||
// that do not need to be downloaded in a postinstall action.
|
||||
const electronPath = require('electron');
|
||||
|
||||
return new Promise((resolve, reject) => {
|
||||
const child = spawn(binaryPath, [...baseArgs, ...args], {
|
||||
stdio: 'inherit',
|
||||
windowsHide: true,
|
||||
detached: mode === 'detached',
|
||||
});
|
||||
const child = spawn(
|
||||
electronPath,
|
||||
[require.resolve('../electron'), ...args],
|
||||
{
|
||||
stdio: 'inherit',
|
||||
windowsHide: true,
|
||||
detached: mode === 'detached',
|
||||
},
|
||||
);
|
||||
if (mode === 'detached') {
|
||||
child.on('spawn', () => {
|
||||
resolve();
|
||||
});
|
||||
child.on('close', (code: number) => {
|
||||
child.on('close', (code /*: number */) => {
|
||||
if (code !== 0) {
|
||||
reject(
|
||||
new Error(
|
||||
`Failed to open debugger shell: exited with code ${code}`,
|
||||
`Failed to open debugger shell: ${electronPath} exited with code ${code}`,
|
||||
),
|
||||
);
|
||||
}
|
||||
@@ -64,7 +54,7 @@ async function unstable_spawnDebuggerShellWithArgs(
|
||||
} else if (mode === 'syncThenExit') {
|
||||
child.on('close', function (code, signal) {
|
||||
if (code === null) {
|
||||
console.error('Debugger shell exited with signal', signal);
|
||||
console.error(electronPath, 'exited with signal', signal);
|
||||
process.exit(1);
|
||||
}
|
||||
process.exit(code);
|
||||
@@ -84,91 +74,4 @@ async function unstable_spawnDebuggerShellWithArgs(
|
||||
});
|
||||
}
|
||||
|
||||
export type DebuggerShellPreparationResult = $ReadOnly<{
|
||||
code:
|
||||
| 'success'
|
||||
| 'not_implemented'
|
||||
| 'likely_offline'
|
||||
| 'platform_not_supported'
|
||||
| 'possible_corruption'
|
||||
| 'unexpected_error',
|
||||
humanReadableMessage?: string,
|
||||
verboseInfo?: string,
|
||||
}>;
|
||||
|
||||
/**
|
||||
* Attempts to prepare the debugger shell for use and returns a coded result
|
||||
* that can be used to advise the user on how to proceed in case of failure.
|
||||
* In particular, this function will attempt to download and extract an
|
||||
* appropriate binary for the "prebuilt" flavor.
|
||||
*
|
||||
* This function should be called early during dev server startup, in parallel
|
||||
* with other initialization steps, so that the debugger shell is ready to use
|
||||
* instantly when the user tries to open it (and conversely, the user is
|
||||
* informed ASAP if it is not ready to use).
|
||||
*/
|
||||
async function unstable_prepareDebuggerShell(
|
||||
flavor: DebuggerShellFlavor,
|
||||
): Promise<DebuggerShellPreparationResult> {
|
||||
const [binaryPath, baseArgs] = getShellBinaryAndArgs(flavor);
|
||||
|
||||
try {
|
||||
switch (flavor) {
|
||||
case 'prebuilt':
|
||||
const prebuiltResult = await prepareDebuggerShellFromDotSlashFile(
|
||||
DEVTOOLS_BINARY_DOTSLASH_FILE,
|
||||
);
|
||||
if (prebuiltResult.code !== 'success') {
|
||||
return prebuiltResult;
|
||||
}
|
||||
break;
|
||||
case 'dev':
|
||||
break;
|
||||
default:
|
||||
flavor as empty;
|
||||
throw new Error(`Unknown flavor: ${flavor}`);
|
||||
}
|
||||
const {code, stderr} = await spawnAndGetStderr(binaryPath, [
|
||||
...baseArgs,
|
||||
'--version',
|
||||
]);
|
||||
if (code !== 0) {
|
||||
return {
|
||||
code: 'unexpected_error',
|
||||
verboseInfo: stderr,
|
||||
};
|
||||
}
|
||||
return {code: 'success'};
|
||||
} catch (e) {
|
||||
return {
|
||||
code: 'unexpected_error',
|
||||
verboseInfo: e.message,
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
function getShellBinaryAndArgs(
|
||||
flavor: DebuggerShellFlavor,
|
||||
): [string, Array<string>] {
|
||||
switch (flavor) {
|
||||
case 'prebuilt':
|
||||
return [
|
||||
// $FlowIssue[cannot-resolve-module] fb-dotslash includes Flow types but Flow does not pick them up
|
||||
require('fb-dotslash'),
|
||||
[DEVTOOLS_BINARY_DOTSLASH_FILE],
|
||||
];
|
||||
case 'dev':
|
||||
return [
|
||||
// NOTE: Internally at Meta, this is aliased to a workspace that is
|
||||
// API-compatible with the 'electron' package, but contains prebuilt binaries
|
||||
// that do not need to be downloaded in a postinstall action.
|
||||
require('electron'),
|
||||
[require.resolve('../electron')],
|
||||
];
|
||||
default:
|
||||
flavor as empty;
|
||||
throw new Error(`Unknown flavor: ${flavor}`);
|
||||
}
|
||||
}
|
||||
|
||||
export {unstable_spawnDebuggerShellWithArgs, unstable_prepareDebuggerShell};
|
||||
export {unstable_spawnDebuggerShellWithArgs};
|
||||
|
||||
@@ -1,98 +0,0 @@
|
||||
/**
|
||||
* Copyright (c) Meta Platforms, Inc. and affiliates.
|
||||
*
|
||||
* This source code is licensed under the MIT license found in the
|
||||
* LICENSE file in the root directory of this source tree.
|
||||
*
|
||||
* @flow strict-local
|
||||
* @format
|
||||
*/
|
||||
|
||||
import type {DebuggerShellPreparationResult} from '../';
|
||||
|
||||
const {spawn} = require('cross-spawn');
|
||||
|
||||
async function spawnAndGetStderr(
|
||||
command: string,
|
||||
args: string[],
|
||||
): Promise<{
|
||||
code: number,
|
||||
stderr: string,
|
||||
}> {
|
||||
return new Promise((resolve, reject) => {
|
||||
const child = spawn(command, args, {
|
||||
stdio: ['ignore', 'ignore', 'pipe'],
|
||||
encoding: 'utf8',
|
||||
windowsHide: true,
|
||||
});
|
||||
let stderr = '';
|
||||
child.stderr.on('data', data => {
|
||||
stderr += data;
|
||||
});
|
||||
child.on('error', error => {
|
||||
reject(error);
|
||||
});
|
||||
child.on('close', (code, signal) => {
|
||||
resolve({
|
||||
code,
|
||||
stderr,
|
||||
});
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
async function prepareDebuggerShellFromDotSlashFile(
|
||||
filePath: string,
|
||||
): Promise<DebuggerShellPreparationResult> {
|
||||
const {code, stderr} = await spawnAndGetStderr(
|
||||
// $FlowIssue[cannot-resolve-module] fb-dotslash includes Flow types but Flow does not pick them up
|
||||
require('fb-dotslash'),
|
||||
['--', 'fetch', filePath],
|
||||
);
|
||||
if (code === 0) {
|
||||
return {code: 'success'};
|
||||
}
|
||||
if (
|
||||
stderr.includes('dotslash error') &&
|
||||
stderr.includes('no providers succeeded')
|
||||
) {
|
||||
if (stderr.includes('failed to verify artifact')) {
|
||||
return {
|
||||
code: 'possible_corruption',
|
||||
humanReadableMessage:
|
||||
'Failed to verify the latest version of React Native DevTools. ' +
|
||||
'Using a fallback version instead. ',
|
||||
verboseInfo: stderr,
|
||||
};
|
||||
}
|
||||
return {
|
||||
code: 'likely_offline',
|
||||
humanReadableMessage:
|
||||
'Failed to download the latest version of React Native DevTools. ' +
|
||||
'Using a fallback version instead. ' +
|
||||
'Connect to the internet or check your network settings.',
|
||||
verboseInfo: stderr,
|
||||
};
|
||||
}
|
||||
if (
|
||||
stderr.includes('dotslash error') &&
|
||||
stderr.includes('platform not supported')
|
||||
) {
|
||||
return {
|
||||
code: 'platform_not_supported',
|
||||
humanReadableMessage:
|
||||
'The latest version of React Native DevTools is not supported on this platform. ' +
|
||||
'Using a fallback version instead.',
|
||||
verboseInfo: stderr,
|
||||
};
|
||||
}
|
||||
return {
|
||||
code: 'unexpected_error',
|
||||
humanReadableMessage:
|
||||
'An unexpected error occured while installing the latest version of React Native DevTools. ' +
|
||||
'Using a fallback version instead.',
|
||||
verboseInfo: stderr,
|
||||
};
|
||||
}
|
||||
|
||||
export {spawnAndGetStderr, prepareDebuggerShellFromDotSlashFile};
|
||||
@@ -38,7 +38,6 @@
|
||||
"node": ">= 20.19.4"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@react-native/debugger-shell": "0.82.0-main",
|
||||
"selfsigned": "^2.4.1",
|
||||
"undici": "^5.29.0",
|
||||
"wait-for-expect": "^3.0.2"
|
||||
|
||||
@@ -105,9 +105,8 @@ export class DebuggerMock extends DebuggerAgent {
|
||||
: this.handle.mock.calls;
|
||||
// $FlowFixMe[incompatible-use]
|
||||
// $FlowFixMe[prop-missing]
|
||||
// $FlowFixMe[incompatible-type]
|
||||
const [response] = newHandleCalls.find(args => args[0].id === message.id);
|
||||
// $FlowFixMe[incompatible-type]
|
||||
// $FlowFixMe[incompatible-return]
|
||||
// $FlowFixMe[incompatible-indexer]
|
||||
return response;
|
||||
}
|
||||
|
||||
@@ -152,12 +152,12 @@ export class DeviceMock extends DeviceAgent {
|
||||
if (!payload) {
|
||||
return;
|
||||
}
|
||||
// $FlowFixMe[incompatible-type] TODO(moti) Figure out the right way to type maybePayload generically
|
||||
// $FlowFixMe[incompatible-call] TODO(moti) Figure out the right way to type maybePayload generically
|
||||
this.send({event, payload});
|
||||
});
|
||||
return;
|
||||
}
|
||||
// $FlowFixMe[incompatible-type] TODO(moti) Figure out the right way to type maybePayload generically
|
||||
// $FlowFixMe[incompatible-call] TODO(moti) Figure out the right way to type maybePayload generically
|
||||
this.send({event, payload: maybePayload});
|
||||
}
|
||||
}
|
||||
|
||||
@@ -66,7 +66,7 @@ export async function sendFromTargetToDebugger<Message: CdpMessageFromTarget>(
|
||||
: debugger_.handle.mock.calls;
|
||||
// $FlowFixMe[incompatible-type]
|
||||
const [receivedMessage]: [Message] = newHandleCalls.find(
|
||||
// $FlowFixMe[incompatible-type]
|
||||
// $FlowFixMe[incompatible-call]
|
||||
(call: [Message]) => call[0].method === message.method,
|
||||
);
|
||||
return receivedMessage;
|
||||
@@ -103,7 +103,7 @@ export async function sendFromDebuggerToTarget<Message: CdpMessageToTarget>(
|
||||
// $FlowFixMe[incompatible-use]
|
||||
call => call[0].wrappedEvent.id === message.id,
|
||||
);
|
||||
// $FlowFixMe[incompatible-type]
|
||||
// $FlowFixMe[incompatible-return]
|
||||
return receivedMessage.wrappedEvent;
|
||||
}
|
||||
|
||||
|
||||
@@ -93,7 +93,7 @@ export async function createServer(options: CreateServerOptions): Promise<{
|
||||
);
|
||||
httpServer = https.createServer(
|
||||
{cert, key},
|
||||
// $FlowFixMe[incompatible-type] The types for `connect` and `https` are subtly incompatible as written.
|
||||
// $FlowFixMe[incompatible-call] The types for `connect` and `https` are subtly incompatible as written.
|
||||
app,
|
||||
);
|
||||
} else {
|
||||
|
||||
@@ -40,7 +40,7 @@ function makeRequest(
|
||||
host: ?string,
|
||||
encrypted: boolean,
|
||||
): http$IncomingMessage<> | http$IncomingMessage<tls$TLSSocket> {
|
||||
// $FlowFixMe[incompatible-type] Partial mock of request
|
||||
// $FlowFixMe[incompatible-return] Partial mock of request
|
||||
return {
|
||||
socket: encrypted ? {encrypted: true} : {},
|
||||
headers: host != null ? {host} : {},
|
||||
|
||||
@@ -81,7 +81,7 @@ export default function createDevMiddleware({
|
||||
projectRoot,
|
||||
serverBaseUrl,
|
||||
logger,
|
||||
// $FlowFixMe[incompatible-type]
|
||||
// $FlowFixMe[prop-missing]
|
||||
unstable_browserLauncher = DefaultBrowserLauncher,
|
||||
unstable_eventReporter,
|
||||
unstable_experiments: experimentConfig = {},
|
||||
@@ -170,35 +170,6 @@ function createWrappedEventReporter(
|
||||
'\u001B[27m',
|
||||
);
|
||||
break;
|
||||
case 'fusebox_shell_preparation_attempt':
|
||||
switch (event.result.code) {
|
||||
case 'success':
|
||||
case 'not_implemented':
|
||||
break;
|
||||
case 'unexpected_error': {
|
||||
let message =
|
||||
event.result.humanReadableMessage ??
|
||||
'An unknown error occurred while installing React Native DevTools.';
|
||||
if (event.result.verboseInfo != null) {
|
||||
message += ` Details:\n\n${event.result.verboseInfo}`;
|
||||
} else {
|
||||
message += '.';
|
||||
}
|
||||
logger?.error(message);
|
||||
break;
|
||||
}
|
||||
case 'possible_corruption':
|
||||
case 'platform_not_supported':
|
||||
case 'likely_offline':
|
||||
logger?.warn(
|
||||
event.result.humanReadableMessage ??
|
||||
`An error of type ${event.result.code} occurred while installing React Native DevTools.`,
|
||||
);
|
||||
break;
|
||||
default:
|
||||
(event.result.code: empty);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
reporter?.logEvent(event);
|
||||
|
||||
@@ -10,10 +10,7 @@
|
||||
|
||||
export {default as createDevMiddleware} from './createDevMiddleware';
|
||||
|
||||
export type {
|
||||
BrowserLauncher,
|
||||
DebuggerShellPreparationResult,
|
||||
} from './types/BrowserLauncher';
|
||||
export type {BrowserLauncher} from './types/BrowserLauncher';
|
||||
export type {EventReporter, ReportableEvent} from './types/EventReporter';
|
||||
export type {
|
||||
CustomMessageHandler,
|
||||
|
||||
@@ -179,7 +179,7 @@ export default class Device {
|
||||
this.#deviceEventReporter?.logProfilingTargetRegistered();
|
||||
}
|
||||
|
||||
// $FlowFixMe[incompatible-type]
|
||||
// $FlowFixMe[incompatible-call]
|
||||
this.#deviceSocket.on('message', (message: string) => {
|
||||
try {
|
||||
const parsedMessage = JSON.parse(message);
|
||||
@@ -346,7 +346,7 @@ export default class Device {
|
||||
frontendUserAgent: userAgent,
|
||||
});
|
||||
|
||||
const debuggerInfo: ?DebuggerConnection & DebuggerConnection = {
|
||||
const debuggerInfo = {
|
||||
socket,
|
||||
prependedFilePrefix: false,
|
||||
pageId,
|
||||
@@ -408,7 +408,7 @@ export default class Device {
|
||||
|
||||
this.#sendConnectEventToDevice(this.#mapToDevicePageId(pageId));
|
||||
|
||||
// $FlowFixMe[incompatible-type]
|
||||
// $FlowFixMe[incompatible-call]
|
||||
socket.on('message', (message: string) => {
|
||||
this.#cdpDebugLogging.log('DebuggerToProxy', message);
|
||||
const debuggerRequest = JSON.parse(message);
|
||||
@@ -809,8 +809,6 @@ export default class Device {
|
||||
}
|
||||
|
||||
if (
|
||||
/* $FlowFixMe[invalid-compare] Error discovered during Constant Condition
|
||||
* roll out. See https://fburl.com/workplace/4oq3zi07. */
|
||||
payload.method === 'Runtime.executionContextCreated' &&
|
||||
this.#isLegacyPageReloading
|
||||
) {
|
||||
@@ -970,10 +968,7 @@ export default class Device {
|
||||
socket: WS,
|
||||
): void {
|
||||
const sendSuccessResponse = (scriptSource: string) => {
|
||||
const result: {
|
||||
scriptSource: string,
|
||||
bytecode?: string,
|
||||
} = {scriptSource};
|
||||
const result = {scriptSource};
|
||||
const response: CDPResponse<'Debugger.getScriptSource'> = {
|
||||
id: req.id,
|
||||
result,
|
||||
|
||||
@@ -10,10 +10,7 @@
|
||||
|
||||
import type {InspectorProxyQueries} from '../inspector-proxy/InspectorProxy';
|
||||
import type {PageDescription} from '../inspector-proxy/types';
|
||||
import type {
|
||||
BrowserLauncher,
|
||||
DebuggerShellPreparationResult,
|
||||
} from '../types/BrowserLauncher';
|
||||
import type {BrowserLauncher} from '../types/BrowserLauncher';
|
||||
import type {EventReporter} from '../types/EventReporter';
|
||||
import type {Experiments} from '../types/Experiments';
|
||||
import type {Logger} from '../types/Logger';
|
||||
@@ -51,19 +48,6 @@ export default function openDebuggerMiddleware({
|
||||
experiments,
|
||||
inspectorProxy,
|
||||
}: Options): NextHandleFunction {
|
||||
let shellPreparationPromise: Promise<DebuggerShellPreparationResult>;
|
||||
if (experiments.enableStandaloneFuseboxShell) {
|
||||
shellPreparationPromise =
|
||||
browserLauncher?.unstable_prepareFuseboxShell?.() ??
|
||||
Promise.resolve({code: 'not_implemented'});
|
||||
shellPreparationPromise = shellPreparationPromise.then(result => {
|
||||
eventReporter?.logEvent({
|
||||
type: 'fusebox_shell_preparation_attempt',
|
||||
result,
|
||||
});
|
||||
return result;
|
||||
});
|
||||
}
|
||||
return async (
|
||||
req: IncomingMessage,
|
||||
res: ServerResponse,
|
||||
@@ -73,8 +57,7 @@ export default function openDebuggerMiddleware({
|
||||
req.method === 'POST' ||
|
||||
(experiments.enableOpenDebuggerRedirect && req.method === 'GET')
|
||||
) {
|
||||
const parsedUrl = url.parse(req.url, true);
|
||||
|
||||
const paresedUrl = url.parse(req.url, true);
|
||||
const query: {
|
||||
/** @deprecated Will only match legacy Hermes targets */
|
||||
appId?: string,
|
||||
@@ -83,9 +66,8 @@ export default function openDebuggerMiddleware({
|
||||
launchId?: string,
|
||||
telemetryInfo?: string,
|
||||
target?: string,
|
||||
panel?: string,
|
||||
...
|
||||
} = parsedUrl.query;
|
||||
} = paresedUrl.query;
|
||||
|
||||
const targets = inspectorProxy
|
||||
.getPageDescriptions({requestorRelativeBaseUrl: new URL(serverBaseUrl)})
|
||||
@@ -168,28 +150,12 @@ export default function openDebuggerMiddleware({
|
||||
telemetryInfo: query.telemetryInfo,
|
||||
appId: target.appId,
|
||||
useFuseboxEntryPoint,
|
||||
panel: query.panel,
|
||||
},
|
||||
);
|
||||
let shouldUseStandaloneFuseboxShell =
|
||||
useFuseboxEntryPoint && experiments.enableStandaloneFuseboxShell;
|
||||
if (shouldUseStandaloneFuseboxShell) {
|
||||
const shellPreparationResult = await shellPreparationPromise;
|
||||
switch (shellPreparationResult.code) {
|
||||
case 'success':
|
||||
case 'not_implemented':
|
||||
break;
|
||||
case 'platform_not_supported':
|
||||
case 'possible_corruption':
|
||||
case 'likely_offline':
|
||||
case 'unexpected_error':
|
||||
shouldUseStandaloneFuseboxShell = false;
|
||||
break;
|
||||
default:
|
||||
(shellPreparationResult.code: empty);
|
||||
}
|
||||
}
|
||||
if (shouldUseStandaloneFuseboxShell) {
|
||||
if (
|
||||
useFuseboxEntryPoint &&
|
||||
experiments.enableStandaloneFuseboxShell
|
||||
) {
|
||||
const windowKey = [
|
||||
serverBaseUrl,
|
||||
target.webSocketDebuggerUrl,
|
||||
|
||||
@@ -8,10 +8,6 @@
|
||||
* @format
|
||||
*/
|
||||
|
||||
import type {DebuggerShellPreparationResult} from '@react-native/debugger-shell';
|
||||
|
||||
export type {DebuggerShellPreparationResult};
|
||||
|
||||
/**
|
||||
* An interface for integrators to provide a custom implementation for
|
||||
* opening URLs in a web browser.
|
||||
@@ -47,17 +43,4 @@ export interface BrowserLauncher {
|
||||
* this as necessary where the server is remote.
|
||||
*/
|
||||
unstable_showFuseboxShell?: (url: string, windowKey: string) => Promise<void>;
|
||||
|
||||
/**
|
||||
* Attempt to prepare the debugger shell for use and returns a coded result
|
||||
* that can be used to advise the user on how to proceed in case of failure.
|
||||
*
|
||||
* This function MAY be called multiple times or not at all. Implementers
|
||||
* SHOULD use the opportunity to prefetch and cache any expensive resources (e.g
|
||||
* platform-specific binaries needed in order to show the Fusebox shell). After a
|
||||
* successful call, subsequent calls SHOULD complete quickly. The implementation
|
||||
* SHOULD NOT return a rejecting promise in any case, and instead SHOULD report
|
||||
* errors via the returned result object.
|
||||
*/
|
||||
unstable_prepareFuseboxShell?: () => Promise<DebuggerShellPreparationResult>;
|
||||
}
|
||||
|
||||
@@ -8,8 +8,6 @@
|
||||
* @format
|
||||
*/
|
||||
|
||||
import type {DebuggerShellPreparationResult} from './BrowserLauncher';
|
||||
|
||||
type SuccessResult<Props: {...} | void = {}> = {
|
||||
status: 'success',
|
||||
...Props,
|
||||
@@ -134,10 +132,6 @@ export type ReportableEvent =
|
||||
duration: number,
|
||||
...ConnectionUptime,
|
||||
...DebuggerSessionIDs,
|
||||
}
|
||||
| {
|
||||
type: 'fusebox_shell_preparation_attempt',
|
||||
result: DebuggerShellPreparationResult,
|
||||
};
|
||||
|
||||
/**
|
||||
|
||||
@@ -24,7 +24,6 @@ export default function getDevToolsFrontendUrl(
|
||||
/** Whether to use the modern `rn_fusebox.html` entry point. */
|
||||
useFuseboxEntryPoint?: boolean,
|
||||
appId?: string,
|
||||
panel?: string,
|
||||
}>,
|
||||
): string {
|
||||
const wsParam = getWsParam({
|
||||
@@ -55,9 +54,6 @@ export default function getDevToolsFrontendUrl(
|
||||
if (options?.telemetryInfo != null && options.telemetryInfo !== '') {
|
||||
searchParams.append('telemetryInfo', options.telemetryInfo);
|
||||
}
|
||||
if (options?.panel != null && options.panel !== '') {
|
||||
searchParams.append('panel', options.panel);
|
||||
}
|
||||
|
||||
return appUrl + '?' + searchParams.toString();
|
||||
}
|
||||
|
||||
@@ -28,7 +28,7 @@
|
||||
"eslint-config-prettier": "^8.5.0",
|
||||
"eslint-plugin-eslint-comments": "^3.2.0",
|
||||
"eslint-plugin-ft-flow": "^2.0.1",
|
||||
"eslint-plugin-jest": "^29.0.1",
|
||||
"eslint-plugin-jest": "^27.9.0",
|
||||
"eslint-plugin-react": "^7.30.1",
|
||||
"eslint-plugin-react-hooks": "^5.2.0",
|
||||
"eslint-plugin-react-native": "^4.0.0"
|
||||
|
||||
@@ -18,8 +18,8 @@
|
||||
"bugs": "https://github.com/facebook/react-native/issues",
|
||||
"main": "index.js",
|
||||
"devDependencies": {
|
||||
"babel-plugin-syntax-hermes-parser": "0.32.0",
|
||||
"hermes-eslint": "0.32.0"
|
||||
"babel-plugin-syntax-hermes-parser": "0.30.0",
|
||||
"hermes-eslint": "0.30.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">= 20.19.4"
|
||||
|
||||
@@ -32,8 +32,8 @@
|
||||
"source-map-support": "0.5.0"
|
||||
},
|
||||
"devDependencies": {
|
||||
"babel-plugin-syntax-hermes-parser": "0.32.0",
|
||||
"hermes-eslint": "0.32.0"
|
||||
"babel-plugin-syntax-hermes-parser": "0.30.0",
|
||||
"hermes-eslint": "0.30.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">= 20.19.4"
|
||||
|
||||
@@ -45,7 +45,3 @@ tasks.named("ktfmtFormat") {
|
||||
":shared:ktfmtFormat",
|
||||
)
|
||||
}
|
||||
|
||||
// We intentionally disable the `ktfmtCheck` tasks as the formatting is primarly handled inside
|
||||
// fbsource
|
||||
allprojects { tasks.withType<com.ncorti.ktfmt.gradle.tasks.KtfmtCheckTask>() { enabled = false } }
|
||||
|
||||
Binary file not shown.
@@ -1,6 +1,6 @@
|
||||
distributionBase=GRADLE_USER_HOME
|
||||
distributionPath=wrapper/dists
|
||||
distributionUrl=https\://services.gradle.org/distributions/gradle-9.0.0-bin.zip
|
||||
distributionUrl=https\://services.gradle.org/distributions/gradle-8.14.3-bin.zip
|
||||
networkTimeout=10000
|
||||
validateDistributionUrl=true
|
||||
zipStoreBase=GRADLE_USER_HOME
|
||||
|
||||
Vendored
+1
-1
@@ -1,7 +1,7 @@
|
||||
#!/bin/sh
|
||||
|
||||
#
|
||||
# Copyright © 2015 the original authors.
|
||||
# Copyright © 2015-2021 the original authors.
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||
# you may not use this file except in compliance with the License.
|
||||
|
||||
@@ -68,8 +68,7 @@ tasks.withType<KotlinCompile>().configureEach {
|
||||
// See comment above on JDK 11 support
|
||||
jvmTarget.set(JvmTarget.JVM_11)
|
||||
allWarningsAsErrors.set(
|
||||
project.properties["enableWarningsAsErrors"]?.toString()?.toBoolean() ?: false
|
||||
)
|
||||
project.properties["enableWarningsAsErrors"]?.toString()?.toBoolean() ?: false)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
+1
-2
@@ -208,8 +208,7 @@ abstract class ReactExtension @Inject constructor(val project: Project) {
|
||||
} else {
|
||||
buildTypes.forEach { buildType ->
|
||||
result.add(
|
||||
(dependencyConfiguration ?: "${buildType}Implementation") to ":$nameCleansed"
|
||||
)
|
||||
(dependencyConfiguration ?: "${buildType}Implementation") to ":$nameCleansed")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+97
-83
@@ -29,6 +29,7 @@ import com.facebook.react.utils.JdkConfiguratorUtils.configureJavaToolChains
|
||||
import com.facebook.react.utils.JsonUtils
|
||||
import com.facebook.react.utils.NdkConfiguratorUtils.configureReactNativeNdk
|
||||
import com.facebook.react.utils.ProjectUtils.needsCodegenFromPackageJson
|
||||
import com.facebook.react.utils.PropertyUtils
|
||||
import com.facebook.react.utils.findPackageJsonFile
|
||||
import java.io.File
|
||||
import kotlin.system.exitProcess
|
||||
@@ -42,6 +43,7 @@ import org.gradle.internal.jvm.Jvm
|
||||
class ReactPlugin : Plugin<Project> {
|
||||
override fun apply(project: Project) {
|
||||
checkJvmVersion(project)
|
||||
checkLegacyArchProperty(project)
|
||||
val extension = project.extensions.create("react", ReactExtension::class.java, project)
|
||||
|
||||
// We register a private extension on the rootProject so that project wide configs
|
||||
@@ -49,10 +51,7 @@ class ReactPlugin : Plugin<Project> {
|
||||
val rootExtension =
|
||||
project.rootProject.extensions.findByType(PrivateReactExtension::class.java)
|
||||
?: project.rootProject.extensions.create(
|
||||
"privateReact",
|
||||
PrivateReactExtension::class.java,
|
||||
project,
|
||||
)
|
||||
"privateReact", PrivateReactExtension::class.java, project)
|
||||
|
||||
// App Only Configuration
|
||||
project.pluginManager.withPlugin("com.android.application") {
|
||||
@@ -112,12 +111,35 @@ class ReactPlugin : Plugin<Project> {
|
||||
********************************************************************************
|
||||
|
||||
"""
|
||||
.trimIndent()
|
||||
)
|
||||
.trimIndent())
|
||||
exitProcess(1)
|
||||
}
|
||||
}
|
||||
|
||||
private fun checkLegacyArchProperty(project: Project) {
|
||||
if ((project.hasProperty(PropertyUtils.NEW_ARCH_ENABLED) &&
|
||||
!project.property(PropertyUtils.NEW_ARCH_ENABLED).toString().toBoolean()) ||
|
||||
(project.hasProperty(PropertyUtils.SCOPED_NEW_ARCH_ENABLED) &&
|
||||
!project.property(PropertyUtils.SCOPED_NEW_ARCH_ENABLED).toString().toBoolean())) {
|
||||
project.logger.error(
|
||||
"""
|
||||
|
||||
********************************************************************************
|
||||
|
||||
WARNING: Setting `newArchEnabled=false` in your `gradle.properties` file is not
|
||||
supported anymore since React Native 0.82.
|
||||
|
||||
You can remove the line from your `gradle.properties` file.
|
||||
|
||||
The application will run with the New Architecture enabled by default.
|
||||
|
||||
********************************************************************************
|
||||
|
||||
"""
|
||||
.trimIndent())
|
||||
}
|
||||
}
|
||||
|
||||
/** This function configures Android resources - in this case just the bundle */
|
||||
private fun configureResources(project: Project, reactExtension: ReactExtension) {
|
||||
project.extensions.getByType(ApplicationAndroidComponentsExtension::class.java).finalizeDsl {
|
||||
@@ -135,7 +157,7 @@ class ReactPlugin : Plugin<Project> {
|
||||
project: Project,
|
||||
localExtension: ReactExtension,
|
||||
rootExtension: PrivateReactExtension,
|
||||
isLibrary: Boolean,
|
||||
isLibrary: Boolean
|
||||
) {
|
||||
// First, we set up the output dir for the codegen.
|
||||
val generatedSrcDir: Provider<Directory> =
|
||||
@@ -153,72 +175,70 @@ class ReactPlugin : Plugin<Project> {
|
||||
// We create the task to produce schema from JS files.
|
||||
val generateCodegenSchemaTask =
|
||||
project.tasks.register(
|
||||
"generateCodegenSchemaFromJavaScript",
|
||||
GenerateCodegenSchemaTask::class.java,
|
||||
) { it ->
|
||||
it.nodeExecutableAndArgs.set(rootExtension.nodeExecutableAndArgs)
|
||||
it.codegenDir.set(rootExtension.codegenDir)
|
||||
it.generatedSrcDir.set(generatedSrcDir)
|
||||
it.nodeWorkingDir.set(project.layout.projectDirectory.asFile.absolutePath)
|
||||
"generateCodegenSchemaFromJavaScript", GenerateCodegenSchemaTask::class.java) { it ->
|
||||
it.nodeExecutableAndArgs.set(rootExtension.nodeExecutableAndArgs)
|
||||
it.codegenDir.set(rootExtension.codegenDir)
|
||||
it.generatedSrcDir.set(generatedSrcDir)
|
||||
it.nodeWorkingDir.set(project.layout.projectDirectory.asFile.absolutePath)
|
||||
|
||||
// We're reading the package.json at configuration time to properly feed
|
||||
// the `jsRootDir` @Input property of this task & the onlyIf. Therefore, the
|
||||
// parsePackageJson should be invoked inside this lambda.
|
||||
val packageJson = findPackageJsonFile(project, rootExtension.root)
|
||||
val parsedPackageJson = packageJson?.let { JsonUtils.fromPackageJson(it) }
|
||||
// We're reading the package.json at configuration time to properly feed
|
||||
// the `jsRootDir` @Input property of this task & the onlyIf. Therefore, the
|
||||
// parsePackageJson should be invoked inside this lambda.
|
||||
val packageJson = findPackageJsonFile(project, rootExtension.root)
|
||||
val parsedPackageJson = packageJson?.let { JsonUtils.fromPackageJson(it) }
|
||||
|
||||
val jsSrcsDirInPackageJson = parsedPackageJson?.codegenConfig?.jsSrcsDir
|
||||
val includesGeneratedCode =
|
||||
parsedPackageJson?.codegenConfig?.includesGeneratedCode ?: false
|
||||
if (jsSrcsDirInPackageJson != null) {
|
||||
it.jsRootDir.set(File(packageJson.parentFile, jsSrcsDirInPackageJson))
|
||||
} else {
|
||||
it.jsRootDir.set(localExtension.jsRootDir)
|
||||
}
|
||||
it.jsInputFiles.set(
|
||||
project.fileTree(it.jsRootDir) { tree ->
|
||||
tree.include("**/*.js")
|
||||
tree.include("**/*.jsx")
|
||||
tree.include("**/*.ts")
|
||||
tree.include("**/*.tsx")
|
||||
|
||||
tree.exclude("node_modules/**/*")
|
||||
tree.exclude("**/*.d.ts")
|
||||
// We want to exclude the build directory, to don't pick them up for execution
|
||||
// avoidance.
|
||||
tree.exclude("**/build/**/*")
|
||||
val jsSrcsDirInPackageJson = parsedPackageJson?.codegenConfig?.jsSrcsDir
|
||||
val includesGeneratedCode =
|
||||
parsedPackageJson?.codegenConfig?.includesGeneratedCode ?: false
|
||||
if (jsSrcsDirInPackageJson != null) {
|
||||
it.jsRootDir.set(File(packageJson.parentFile, jsSrcsDirInPackageJson))
|
||||
} else {
|
||||
it.jsRootDir.set(localExtension.jsRootDir)
|
||||
}
|
||||
)
|
||||
it.jsInputFiles.set(
|
||||
project.fileTree(it.jsRootDir) { tree ->
|
||||
tree.include("**/*.js")
|
||||
tree.include("**/*.jsx")
|
||||
tree.include("**/*.ts")
|
||||
tree.include("**/*.tsx")
|
||||
|
||||
val needsCodegenFromPackageJson = project.needsCodegenFromPackageJson(rootExtension.root)
|
||||
it.onlyIf { (isLibrary || needsCodegenFromPackageJson) && !includesGeneratedCode }
|
||||
}
|
||||
tree.exclude("node_modules/**/*")
|
||||
tree.exclude("**/*.d.ts")
|
||||
// We want to exclude the build directory, to don't pick them up for execution
|
||||
// avoidance.
|
||||
tree.exclude("**/build/**/*")
|
||||
})
|
||||
|
||||
val needsCodegenFromPackageJson =
|
||||
project.needsCodegenFromPackageJson(rootExtension.root)
|
||||
it.onlyIf { (isLibrary || needsCodegenFromPackageJson) && !includesGeneratedCode }
|
||||
}
|
||||
|
||||
// We create the task to generate Java code from schema.
|
||||
val generateCodegenArtifactsTask =
|
||||
project.tasks.register(
|
||||
"generateCodegenArtifactsFromSchema",
|
||||
GenerateCodegenArtifactsTask::class.java,
|
||||
) { task ->
|
||||
task.dependsOn(generateCodegenSchemaTask)
|
||||
task.reactNativeDir.set(rootExtension.reactNativeDir)
|
||||
task.nodeExecutableAndArgs.set(rootExtension.nodeExecutableAndArgs)
|
||||
task.generatedSrcDir.set(generatedSrcDir)
|
||||
task.packageJsonFile.set(findPackageJsonFile(project, rootExtension.root))
|
||||
task.codegenJavaPackageName.set(localExtension.codegenJavaPackageName)
|
||||
task.libraryName.set(localExtension.libraryName)
|
||||
task.nodeWorkingDir.set(project.layout.projectDirectory.asFile.absolutePath)
|
||||
"generateCodegenArtifactsFromSchema", GenerateCodegenArtifactsTask::class.java) { task
|
||||
->
|
||||
task.dependsOn(generateCodegenSchemaTask)
|
||||
task.reactNativeDir.set(rootExtension.reactNativeDir)
|
||||
task.nodeExecutableAndArgs.set(rootExtension.nodeExecutableAndArgs)
|
||||
task.generatedSrcDir.set(generatedSrcDir)
|
||||
task.packageJsonFile.set(findPackageJsonFile(project, rootExtension.root))
|
||||
task.codegenJavaPackageName.set(localExtension.codegenJavaPackageName)
|
||||
task.libraryName.set(localExtension.libraryName)
|
||||
task.nodeWorkingDir.set(project.layout.projectDirectory.asFile.absolutePath)
|
||||
|
||||
// Please note that appNeedsCodegen is triggering a read of the package.json at
|
||||
// configuration time as we need to feed the onlyIf condition of this task.
|
||||
// Therefore, the appNeedsCodegen needs to be invoked inside this lambda.
|
||||
val needsCodegenFromPackageJson = project.needsCodegenFromPackageJson(rootExtension.root)
|
||||
val packageJson = findPackageJsonFile(project, rootExtension.root)
|
||||
val parsedPackageJson = packageJson?.let { JsonUtils.fromPackageJson(it) }
|
||||
val includesGeneratedCode =
|
||||
parsedPackageJson?.codegenConfig?.includesGeneratedCode ?: false
|
||||
task.onlyIf { (isLibrary || needsCodegenFromPackageJson) && !includesGeneratedCode }
|
||||
}
|
||||
// Please note that appNeedsCodegen is triggering a read of the package.json at
|
||||
// configuration time as we need to feed the onlyIf condition of this task.
|
||||
// Therefore, the appNeedsCodegen needs to be invoked inside this lambda.
|
||||
val needsCodegenFromPackageJson =
|
||||
project.needsCodegenFromPackageJson(rootExtension.root)
|
||||
val packageJson = findPackageJsonFile(project, rootExtension.root)
|
||||
val parsedPackageJson = packageJson?.let { JsonUtils.fromPackageJson(it) }
|
||||
val includesGeneratedCode =
|
||||
parsedPackageJson?.codegenConfig?.includesGeneratedCode ?: false
|
||||
task.onlyIf { (isLibrary || needsCodegenFromPackageJson) && !includesGeneratedCode }
|
||||
}
|
||||
|
||||
// We update the android configuration to include the generated sources.
|
||||
// This equivalent to this DSL:
|
||||
@@ -261,34 +281,29 @@ class ReactPlugin : Plugin<Project> {
|
||||
// dependency.
|
||||
val generatePackageListTask =
|
||||
project.tasks.register(
|
||||
"generateAutolinkingPackageList",
|
||||
GeneratePackageListTask::class.java,
|
||||
) { task ->
|
||||
task.autolinkInputFile.set(rootGeneratedAutolinkingFile)
|
||||
task.generatedOutputDirectory.set(generatedAutolinkingJavaDir)
|
||||
}
|
||||
"generateAutolinkingPackageList", GeneratePackageListTask::class.java) { task ->
|
||||
task.autolinkInputFile.set(rootGeneratedAutolinkingFile)
|
||||
task.generatedOutputDirectory.set(generatedAutolinkingJavaDir)
|
||||
}
|
||||
|
||||
// We add a task called generateAutolinkingPackageList to do not clash with the existing task
|
||||
// called generatePackageList. This can to be renamed once we unlink the rn <-> cli
|
||||
// dependency.
|
||||
val generateEntryPointTask =
|
||||
project.tasks.register(
|
||||
"generateReactNativeEntryPoint",
|
||||
GenerateEntryPointTask::class.java,
|
||||
) { task ->
|
||||
task.autolinkInputFile.set(rootGeneratedAutolinkingFile)
|
||||
task.generatedOutputDirectory.set(generatedAutolinkingJavaDir)
|
||||
}
|
||||
"generateReactNativeEntryPoint", GenerateEntryPointTask::class.java) { task ->
|
||||
task.autolinkInputFile.set(rootGeneratedAutolinkingFile)
|
||||
task.generatedOutputDirectory.set(generatedAutolinkingJavaDir)
|
||||
}
|
||||
|
||||
// We also need to generate code for C++ Autolinking
|
||||
val generateAutolinkingNewArchitectureFilesTask =
|
||||
project.tasks.register(
|
||||
"generateAutolinkingNewArchitectureFiles",
|
||||
GenerateAutolinkingNewArchitecturesFileTask::class.java,
|
||||
) { task ->
|
||||
task.autolinkInputFile.set(rootGeneratedAutolinkingFile)
|
||||
task.generatedOutputDirectory.set(generatedAutolinkingJniDir)
|
||||
}
|
||||
GenerateAutolinkingNewArchitecturesFileTask::class.java) { task ->
|
||||
task.autolinkInputFile.set(rootGeneratedAutolinkingFile)
|
||||
task.generatedOutputDirectory.set(generatedAutolinkingJniDir)
|
||||
}
|
||||
project.tasks
|
||||
.named("preBuild", Task::class.java)
|
||||
.dependsOn(generateAutolinkingNewArchitectureFilesTask)
|
||||
@@ -305,8 +320,7 @@ class ReactPlugin : Plugin<Project> {
|
||||
project.extensions.getByType(ApplicationAndroidComponentsExtension::class.java).apply {
|
||||
onVariants(selector().all()) { variant ->
|
||||
variant.sources.java?.addStaticSourceDirectory(
|
||||
generatedAutolinkingJavaDir.get().asFile.absolutePath
|
||||
)
|
||||
generatedAutolinkingJavaDir.get().asFile.absolutePath)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+3
-43
@@ -7,10 +7,8 @@
|
||||
|
||||
package com.facebook.react
|
||||
|
||||
import com.facebook.react.utils.PropertyUtils
|
||||
import org.gradle.api.Plugin
|
||||
import org.gradle.api.Project
|
||||
import org.jetbrains.kotlin.gradle.plugin.extraProperties
|
||||
|
||||
/**
|
||||
* Gradle plugin applied to the `android/build.gradle` file.
|
||||
@@ -20,25 +18,13 @@ import org.jetbrains.kotlin.gradle.plugin.extraProperties
|
||||
*/
|
||||
class ReactRootProjectPlugin : Plugin<Project> {
|
||||
override fun apply(project: Project) {
|
||||
checkLegacyArchProperty(project)
|
||||
project.subprojects { subproject ->
|
||||
project.subprojects {
|
||||
// As the :app project (i.e. ReactPlugin) configures both namespaces and JVM toolchains
|
||||
// for libraries, its evaluation must happen before the libraries' evaluation.
|
||||
// Eventually the configuration of namespace/JVM toolchain can be moved inside this plugin.
|
||||
if (subproject.path != ":app") {
|
||||
subproject.evaluationDependsOn(":app")
|
||||
if (it.path != ":app") {
|
||||
it.evaluationDependsOn(":app")
|
||||
}
|
||||
// We set the New Architecture properties to true for all subprojects. So that
|
||||
// libraries don't need to be modified and can keep on using the isNewArchEnabled()
|
||||
// function to check if property is set.
|
||||
if (subproject.hasProperty(PropertyUtils.SCOPED_NEW_ARCH_ENABLED)) {
|
||||
subproject.setProperty(PropertyUtils.SCOPED_NEW_ARCH_ENABLED, "true")
|
||||
}
|
||||
if (subproject.hasProperty(PropertyUtils.NEW_ARCH_ENABLED)) {
|
||||
subproject.setProperty(PropertyUtils.NEW_ARCH_ENABLED, "true")
|
||||
}
|
||||
subproject.extraProperties.set(PropertyUtils.NEW_ARCH_ENABLED, "true")
|
||||
subproject.extraProperties.set(PropertyUtils.SCOPED_NEW_ARCH_ENABLED, "true")
|
||||
}
|
||||
// We need to make sure that `:app:preBuild` task depends on all other subprojects' preBuild
|
||||
// tasks. This is necessary in order to have all the codegen generated code before the CMake
|
||||
@@ -57,30 +43,4 @@ class ReactRootProjectPlugin : Plugin<Project> {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun checkLegacyArchProperty(project: Project) {
|
||||
if (
|
||||
(project.hasProperty(PropertyUtils.NEW_ARCH_ENABLED) &&
|
||||
!project.property(PropertyUtils.NEW_ARCH_ENABLED).toString().toBoolean()) ||
|
||||
(project.hasProperty(PropertyUtils.SCOPED_NEW_ARCH_ENABLED) &&
|
||||
!project.property(PropertyUtils.SCOPED_NEW_ARCH_ENABLED).toString().toBoolean())
|
||||
) {
|
||||
project.logger.error(
|
||||
"""
|
||||
********************************************************************************
|
||||
|
||||
WARNING: Setting `newArchEnabled=false` in your `gradle.properties` file is not
|
||||
supported anymore since React Native 0.82.
|
||||
|
||||
You can remove the line from your `gradle.properties` file.
|
||||
|
||||
The application will run with the New Architecture enabled by default.
|
||||
|
||||
********************************************************************************
|
||||
|
||||
"""
|
||||
.trimIndent()
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+3
-5
@@ -54,11 +54,9 @@ internal fun Project.configureReactTasks(variant: Variant, config: ReactExtensio
|
||||
|
||||
configureNewArchPackagingOptions(project, config, variant)
|
||||
configureJsEnginePackagingOptions(config, variant, isHermesEnabledInThisVariant, useThirdPartyJSC)
|
||||
if (
|
||||
!isHermesEnabledInThisVariant &&
|
||||
!useThirdPartyJSC &&
|
||||
rootProject.name != "react-native-github"
|
||||
) {
|
||||
if (!isHermesEnabledInThisVariant &&
|
||||
!useThirdPartyJSC &&
|
||||
rootProject.name != "react-native-github") {
|
||||
showJSCRemovalMessage(project)
|
||||
}
|
||||
|
||||
|
||||
+3
-6
@@ -39,15 +39,12 @@ abstract class PrivateReactExtension @Inject constructor(project: Project) {
|
||||
// - We're inside a user project, so inside the ./android folder. Default should be
|
||||
// ../
|
||||
// User can always override this default by setting a `root =` inside the template.
|
||||
if (
|
||||
project.rootProject.name == "react-native-github" ||
|
||||
project.rootProject.name == "react-native-build-from-source"
|
||||
) {
|
||||
if (project.rootProject.name == "react-native-github" ||
|
||||
project.rootProject.name == "react-native-build-from-source") {
|
||||
project.rootProject.layout.projectDirectory.dir("../../")
|
||||
} else {
|
||||
project.rootProject.layout.projectDirectory.dir("../")
|
||||
}
|
||||
)
|
||||
})
|
||||
|
||||
val reactNativeDir: DirectoryProperty =
|
||||
objects.directoryProperty().convention(root.dir("node_modules/react-native"))
|
||||
|
||||
+5
-11
@@ -111,11 +111,7 @@ abstract class BundleHermesCTask : DefaultTask() {
|
||||
val composeScriptFile = File(reactNativeDir, "scripts/compose-source-maps.js")
|
||||
val composeSourceMapsCommand =
|
||||
getComposeSourceMapsCommand(
|
||||
composeScriptFile,
|
||||
packagerSourceMap,
|
||||
compilerSourceMap,
|
||||
outputSourceMap,
|
||||
)
|
||||
composeScriptFile, packagerSourceMap, compilerSourceMap, outputSourceMap)
|
||||
runCommand(composeSourceMapsCommand)
|
||||
}
|
||||
}
|
||||
@@ -176,7 +172,7 @@ abstract class BundleHermesCTask : DefaultTask() {
|
||||
internal fun getHermescCommand(
|
||||
hermesCommand: String,
|
||||
bytecodeFile: File,
|
||||
bundleFile: File,
|
||||
bundleFile: File
|
||||
): List<Any> {
|
||||
val rootFile = root.get().asFile
|
||||
return windowsAwareCommandLine(
|
||||
@@ -187,15 +183,14 @@ abstract class BundleHermesCTask : DefaultTask() {
|
||||
"-out",
|
||||
bytecodeFile.cliPath(rootFile),
|
||||
bundleFile.cliPath(rootFile),
|
||||
*hermesFlags.get().toTypedArray(),
|
||||
)
|
||||
*hermesFlags.get().toTypedArray())
|
||||
}
|
||||
|
||||
internal fun getComposeSourceMapsCommand(
|
||||
composeScript: File,
|
||||
packagerSourceMap: File,
|
||||
compilerSourceMap: File,
|
||||
outputSourceMap: File,
|
||||
outputSourceMap: File
|
||||
): List<Any> {
|
||||
val rootFile = root.get().asFile
|
||||
return windowsAwareCommandLine(
|
||||
@@ -204,7 +199,6 @@ abstract class BundleHermesCTask : DefaultTask() {
|
||||
packagerSourceMap.cliPath(rootFile),
|
||||
compilerSourceMap.cliPath(rootFile),
|
||||
"-o",
|
||||
outputSourceMap.cliPath(rootFile),
|
||||
)
|
||||
outputSourceMap.cliPath(rootFile))
|
||||
}
|
||||
}
|
||||
|
||||
+72
-72
@@ -165,88 +165,88 @@ abstract class GenerateAutolinkingNewArchitecturesFileTask : DefaultTask() {
|
||||
// language=cmake
|
||||
val CMAKE_TEMPLATE =
|
||||
"""
|
||||
# This code was generated by [React Native](https://www.npmjs.com/package/@react-native/gradle-plugin)
|
||||
cmake_minimum_required(VERSION 3.13)
|
||||
set(CMAKE_VERBOSE_MAKEFILE on)
|
||||
|
||||
# We set REACTNATIVE_MERGED_SO so libraries/apps can selectively decide to depend on either libreactnative.so
|
||||
# or link against a old prefab target (this is needed for React Native 0.76 on).
|
||||
set(REACTNATIVE_MERGED_SO true)
|
||||
|
||||
{{ libraryIncludes }}
|
||||
|
||||
set(AUTOLINKED_LIBRARIES
|
||||
{{ libraryModules }}
|
||||
)
|
||||
"""
|
||||
# This code was generated by [React Native](https://www.npmjs.com/package/@react-native/gradle-plugin)
|
||||
cmake_minimum_required(VERSION 3.13)
|
||||
set(CMAKE_VERBOSE_MAKEFILE on)
|
||||
|
||||
# We set REACTNATIVE_MERGED_SO so libraries/apps can selectively decide to depend on either libreactnative.so
|
||||
# or link against a old prefab target (this is needed for React Native 0.76 on).
|
||||
set(REACTNATIVE_MERGED_SO true)
|
||||
|
||||
{{ libraryIncludes }}
|
||||
|
||||
set(AUTOLINKED_LIBRARIES
|
||||
{{ libraryModules }}
|
||||
)
|
||||
"""
|
||||
.trimIndent()
|
||||
|
||||
// language=cpp
|
||||
val CPP_TEMPLATE =
|
||||
"""
|
||||
/**
|
||||
* This code was generated by [React Native](https://www.npmjs.com/package/@react-native/gradle-plugin).
|
||||
*
|
||||
* Do not edit this file as changes may cause incorrect behavior and will be lost
|
||||
* once the code is regenerated.
|
||||
*
|
||||
*/
|
||||
|
||||
#include "autolinking.h"
|
||||
{{ autolinkingCppIncludes }}
|
||||
|
||||
namespace facebook {
|
||||
namespace react {
|
||||
|
||||
std::shared_ptr<TurboModule> autolinking_ModuleProvider(const std::string moduleName, const JavaTurboModule::InitParams ¶ms) {
|
||||
{{ autolinkingCppTurboModuleJavaProviders }}
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
std::shared_ptr<TurboModule> autolinking_cxxModuleProvider(const std::string moduleName, const std::shared_ptr<CallInvoker>& jsInvoker) {
|
||||
{{ autolinkingCppTurboModuleCxxProviders }}
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
void autolinking_registerProviders(std::shared_ptr<ComponentDescriptorProviderRegistry const> providerRegistry) {
|
||||
{{ autolinkingCppComponentDescriptors }}
|
||||
return;
|
||||
}
|
||||
|
||||
} // namespace react
|
||||
} // namespace facebook
|
||||
"""
|
||||
/**
|
||||
* This code was generated by [React Native](https://www.npmjs.com/package/@react-native/gradle-plugin).
|
||||
*
|
||||
* Do not edit this file as changes may cause incorrect behavior and will be lost
|
||||
* once the code is regenerated.
|
||||
*
|
||||
*/
|
||||
|
||||
#include "autolinking.h"
|
||||
{{ autolinkingCppIncludes }}
|
||||
|
||||
namespace facebook {
|
||||
namespace react {
|
||||
|
||||
std::shared_ptr<TurboModule> autolinking_ModuleProvider(const std::string moduleName, const JavaTurboModule::InitParams ¶ms) {
|
||||
{{ autolinkingCppTurboModuleJavaProviders }}
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
std::shared_ptr<TurboModule> autolinking_cxxModuleProvider(const std::string moduleName, const std::shared_ptr<CallInvoker>& jsInvoker) {
|
||||
{{ autolinkingCppTurboModuleCxxProviders }}
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
void autolinking_registerProviders(std::shared_ptr<ComponentDescriptorProviderRegistry const> providerRegistry) {
|
||||
{{ autolinkingCppComponentDescriptors }}
|
||||
return;
|
||||
}
|
||||
|
||||
} // namespace react
|
||||
} // namespace facebook
|
||||
"""
|
||||
.trimIndent()
|
||||
|
||||
// language=cpp
|
||||
val hTemplate =
|
||||
"""
|
||||
/**
|
||||
* This code was generated by [React Native](https://www.npmjs.com/package/@react-native/gradle-plugin).
|
||||
*
|
||||
* Do not edit this file as changes may cause incorrect behavior and will be lost
|
||||
* once the code is regenerated.
|
||||
*
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <ReactCommon/CallInvoker.h>
|
||||
#include <ReactCommon/JavaTurboModule.h>
|
||||
#include <ReactCommon/TurboModule.h>
|
||||
#include <jsi/jsi.h>
|
||||
#include <react/renderer/componentregistry/ComponentDescriptorProviderRegistry.h>
|
||||
|
||||
namespace facebook {
|
||||
namespace react {
|
||||
|
||||
std::shared_ptr<TurboModule> autolinking_ModuleProvider(const std::string moduleName, const JavaTurboModule::InitParams ¶ms);
|
||||
std::shared_ptr<TurboModule> autolinking_cxxModuleProvider(const std::string moduleName, const std::shared_ptr<CallInvoker>& jsInvoker);
|
||||
void autolinking_registerProviders(std::shared_ptr<ComponentDescriptorProviderRegistry const> providerRegistry);
|
||||
|
||||
} // namespace react
|
||||
} // namespace facebook
|
||||
"""
|
||||
/**
|
||||
* This code was generated by [React Native](https://www.npmjs.com/package/@react-native/gradle-plugin).
|
||||
*
|
||||
* Do not edit this file as changes may cause incorrect behavior and will be lost
|
||||
* once the code is regenerated.
|
||||
*
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <ReactCommon/CallInvoker.h>
|
||||
#include <ReactCommon/JavaTurboModule.h>
|
||||
#include <ReactCommon/TurboModule.h>
|
||||
#include <jsi/jsi.h>
|
||||
#include <react/renderer/componentregistry/ComponentDescriptorProviderRegistry.h>
|
||||
|
||||
namespace facebook {
|
||||
namespace react {
|
||||
|
||||
std::shared_ptr<TurboModule> autolinking_ModuleProvider(const std::string moduleName, const JavaTurboModule::InitParams ¶ms);
|
||||
std::shared_ptr<TurboModule> autolinking_cxxModuleProvider(const std::string moduleName, const std::shared_ptr<CallInvoker>& jsInvoker);
|
||||
void autolinking_registerProviders(std::shared_ptr<ComponentDescriptorProviderRegistry const> providerRegistry);
|
||||
|
||||
} // namespace react
|
||||
} // namespace facebook
|
||||
"""
|
||||
.trimIndent()
|
||||
}
|
||||
}
|
||||
|
||||
+1
-3
@@ -81,8 +81,6 @@ abstract class GenerateCodegenArtifactsTask : Exec() {
|
||||
"--libraryName",
|
||||
libraryName,
|
||||
"--javaPackageName",
|
||||
codegenJavaPackageName,
|
||||
)
|
||||
)
|
||||
codegenJavaPackageName))
|
||||
}
|
||||
}
|
||||
|
||||
+1
-2
@@ -69,7 +69,6 @@ abstract class GenerateCodegenSchemaTask : Exec() {
|
||||
"NativeSampleTurboModule",
|
||||
generatedSchemaFile.get().asFile.cliPath(workingDir),
|
||||
jsRootDir.asFile.get().cliPath(workingDir),
|
||||
)
|
||||
)
|
||||
))
|
||||
}
|
||||
}
|
||||
|
||||
+44
-46
@@ -32,19 +32,17 @@ abstract class GenerateEntryPointTask : DefaultTask() {
|
||||
JsonUtils.fromAutolinkingConfigJson(autolinkInputFile.get().asFile)
|
||||
?: error(
|
||||
"""
|
||||
RNGP - Autolinking: Could not parse autolinking config file:
|
||||
${autolinkInputFile.get().asFile.absolutePath}
|
||||
|
||||
The file is either missing or not containing valid JSON so the build won't succeed.
|
||||
"""
|
||||
.trimIndent()
|
||||
)
|
||||
RNGP - Autolinking: Could not parse autolinking config file:
|
||||
${autolinkInputFile.get().asFile.absolutePath}
|
||||
|
||||
The file is either missing or not containing valid JSON so the build won't succeed.
|
||||
"""
|
||||
.trimIndent())
|
||||
|
||||
val packageName =
|
||||
model.project?.android?.packageName
|
||||
?: error(
|
||||
"RNGP - Autolinking: Could not find project.android.packageName in react-native config output! Could not autolink packages without this field."
|
||||
)
|
||||
"RNGP - Autolinking: Could not find project.android.packageName in react-native config output! Could not autolink packages without this field.")
|
||||
val generatedFileContents = composeFileContent(packageName)
|
||||
|
||||
val outputDir = generatedOutputDirectory.get().asFile
|
||||
@@ -64,45 +62,45 @@ abstract class GenerateEntryPointTask : DefaultTask() {
|
||||
// language=java
|
||||
val generatedFileContentsTemplate =
|
||||
"""
|
||||
package com.facebook.react;
|
||||
|
||||
import android.app.Application;
|
||||
import android.content.Context;
|
||||
import android.content.res.Resources;
|
||||
|
||||
import com.facebook.react.defaults.DefaultNewArchitectureEntryPoint;
|
||||
import com.facebook.react.common.annotations.internal.LegacyArchitectureLogger;
|
||||
import com.facebook.react.views.view.WindowUtilKt;
|
||||
import com.facebook.react.soloader.OpenSourceMergedSoMapping;
|
||||
import com.facebook.soloader.SoLoader;
|
||||
|
||||
import java.io.IOException;
|
||||
|
||||
/**
|
||||
* This class is the entry point for loading React Native using the configuration
|
||||
* that the users specifies in their .gradle files.
|
||||
*
|
||||
* The `loadReactNative(this)` method invocation should be called inside the
|
||||
* application onCreate otherwise the app won't load correctly.
|
||||
*/
|
||||
public class ReactNativeApplicationEntryPoint {
|
||||
public static void loadReactNative(Context context) {
|
||||
try {
|
||||
SoLoader.init(context, OpenSourceMergedSoMapping.INSTANCE);
|
||||
} catch (IOException error) {
|
||||
throw new RuntimeException(error);
|
||||
}
|
||||
|
||||
if ({{packageName}}.BuildConfig.IS_NEW_ARCHITECTURE_ENABLED) {
|
||||
DefaultNewArchitectureEntryPoint.load();
|
||||
}
|
||||
|
||||
if ({{packageName}}.BuildConfig.IS_EDGE_TO_EDGE_ENABLED) {
|
||||
WindowUtilKt.setEdgeToEdgeFeatureFlagOn();
|
||||
}
|
||||
package com.facebook.react;
|
||||
|
||||
import android.app.Application;
|
||||
import android.content.Context;
|
||||
import android.content.res.Resources;
|
||||
|
||||
import com.facebook.react.defaults.DefaultNewArchitectureEntryPoint;
|
||||
import com.facebook.react.common.annotations.internal.LegacyArchitectureLogger;
|
||||
import com.facebook.react.views.view.WindowUtilKt;
|
||||
import com.facebook.react.soloader.OpenSourceMergedSoMapping;
|
||||
import com.facebook.soloader.SoLoader;
|
||||
|
||||
import java.io.IOException;
|
||||
|
||||
/**
|
||||
* This class is the entry point for loading React Native using the configuration
|
||||
* that the users specifies in their .gradle files.
|
||||
*
|
||||
* The `loadReactNative(this)` method invocation should be called inside the
|
||||
* application onCreate otherwise the app won't load correctly.
|
||||
*/
|
||||
public class ReactNativeApplicationEntryPoint {
|
||||
public static void loadReactNative(Context context) {
|
||||
try {
|
||||
SoLoader.init(context, OpenSourceMergedSoMapping.INSTANCE);
|
||||
} catch (IOException error) {
|
||||
throw new RuntimeException(error);
|
||||
}
|
||||
|
||||
if ({{packageName}}.BuildConfig.IS_NEW_ARCHITECTURE_ENABLED) {
|
||||
DefaultNewArchitectureEntryPoint.load();
|
||||
}
|
||||
|
||||
if ({{packageName}}.BuildConfig.IS_EDGE_TO_EDGE_ENABLED) {
|
||||
WindowUtilKt.setEdgeToEdgeFeatureFlagOn();
|
||||
}
|
||||
}
|
||||
"""
|
||||
}
|
||||
"""
|
||||
.trimIndent()
|
||||
}
|
||||
}
|
||||
|
||||
+72
-74
@@ -34,19 +34,17 @@ abstract class GeneratePackageListTask : DefaultTask() {
|
||||
JsonUtils.fromAutolinkingConfigJson(autolinkInputFile.get().asFile)
|
||||
?: error(
|
||||
"""
|
||||
RNGP - Autolinking: Could not parse autolinking config file:
|
||||
${autolinkInputFile.get().asFile.absolutePath}
|
||||
|
||||
The file is either missing or not containing valid JSON so the build won't succeed.
|
||||
"""
|
||||
.trimIndent()
|
||||
)
|
||||
RNGP - Autolinking: Could not parse autolinking config file:
|
||||
${autolinkInputFile.get().asFile.absolutePath}
|
||||
|
||||
The file is either missing or not containing valid JSON so the build won't succeed.
|
||||
"""
|
||||
.trimIndent())
|
||||
|
||||
val packageName =
|
||||
model.project?.android?.packageName
|
||||
?: error(
|
||||
"RNGP - Autolinking: Could not find project.android.packageName in react-native config output! Could not autolink packages without this field."
|
||||
)
|
||||
"RNGP - Autolinking: Could not find project.android.packageName in react-native config output! Could not autolink packages without this field.")
|
||||
|
||||
val androidPackages = filterAndroidPackages(model)
|
||||
val packageImports = composePackageImports(packageName, androidPackages)
|
||||
@@ -63,7 +61,7 @@ abstract class GeneratePackageListTask : DefaultTask() {
|
||||
|
||||
internal fun composePackageImports(
|
||||
packageName: String,
|
||||
packages: Map<String, ModelAutolinkingDependenciesPlatformAndroidJson>,
|
||||
packages: Map<String, ModelAutolinkingDependenciesPlatformAndroidJson>
|
||||
) =
|
||||
packages.entries.joinToString("\n") { (name, dep) ->
|
||||
val packageImportPath =
|
||||
@@ -75,7 +73,7 @@ abstract class GeneratePackageListTask : DefaultTask() {
|
||||
|
||||
internal fun composePackageInstance(
|
||||
packageName: String,
|
||||
packages: Map<String, ModelAutolinkingDependenciesPlatformAndroidJson>,
|
||||
packages: Map<String, ModelAutolinkingDependenciesPlatformAndroidJson>
|
||||
) =
|
||||
if (packages.isEmpty()) {
|
||||
""
|
||||
@@ -136,69 +134,69 @@ abstract class GeneratePackageListTask : DefaultTask() {
|
||||
// language=java
|
||||
val generatedFileContentsTemplate =
|
||||
"""
|
||||
package com.facebook.react;
|
||||
|
||||
import android.app.Application;
|
||||
import android.content.Context;
|
||||
import android.content.res.Resources;
|
||||
|
||||
import com.facebook.react.ReactPackage;
|
||||
import com.facebook.react.shell.MainPackageConfig;
|
||||
import com.facebook.react.shell.MainReactPackage;
|
||||
import java.util.Arrays;
|
||||
import java.util.ArrayList;
|
||||
|
||||
{{ packageImports }}
|
||||
|
||||
@SuppressWarnings("deprecation")
|
||||
public class PackageList {
|
||||
private Application application;
|
||||
private ReactNativeHost reactNativeHost;
|
||||
private MainPackageConfig mConfig;
|
||||
|
||||
public PackageList(ReactNativeHost reactNativeHost) {
|
||||
this(reactNativeHost, null);
|
||||
}
|
||||
|
||||
public PackageList(Application application) {
|
||||
this(application, null);
|
||||
}
|
||||
|
||||
public PackageList(ReactNativeHost reactNativeHost, MainPackageConfig config) {
|
||||
this.reactNativeHost = reactNativeHost;
|
||||
mConfig = config;
|
||||
}
|
||||
|
||||
public PackageList(Application application, MainPackageConfig config) {
|
||||
this.reactNativeHost = null;
|
||||
this.application = application;
|
||||
mConfig = config;
|
||||
}
|
||||
|
||||
private ReactNativeHost getReactNativeHost() {
|
||||
return this.reactNativeHost;
|
||||
}
|
||||
|
||||
private Resources getResources() {
|
||||
return this.getApplication().getResources();
|
||||
}
|
||||
|
||||
private Application getApplication() {
|
||||
if (this.reactNativeHost == null) return this.application;
|
||||
return this.reactNativeHost.getApplication();
|
||||
}
|
||||
|
||||
private Context getApplicationContext() {
|
||||
return this.getApplication().getApplicationContext();
|
||||
}
|
||||
|
||||
public ArrayList<ReactPackage> getPackages() {
|
||||
return new ArrayList<>(Arrays.<ReactPackage>asList(
|
||||
new MainReactPackage(mConfig){{ packageClassInstances }}
|
||||
));
|
||||
}
|
||||
}
|
||||
"""
|
||||
package com.facebook.react;
|
||||
|
||||
import android.app.Application;
|
||||
import android.content.Context;
|
||||
import android.content.res.Resources;
|
||||
|
||||
import com.facebook.react.ReactPackage;
|
||||
import com.facebook.react.shell.MainPackageConfig;
|
||||
import com.facebook.react.shell.MainReactPackage;
|
||||
import java.util.Arrays;
|
||||
import java.util.ArrayList;
|
||||
|
||||
{{ packageImports }}
|
||||
|
||||
@SuppressWarnings("deprecation")
|
||||
public class PackageList {
|
||||
private Application application;
|
||||
private ReactNativeHost reactNativeHost;
|
||||
private MainPackageConfig mConfig;
|
||||
|
||||
public PackageList(ReactNativeHost reactNativeHost) {
|
||||
this(reactNativeHost, null);
|
||||
}
|
||||
|
||||
public PackageList(Application application) {
|
||||
this(application, null);
|
||||
}
|
||||
|
||||
public PackageList(ReactNativeHost reactNativeHost, MainPackageConfig config) {
|
||||
this.reactNativeHost = reactNativeHost;
|
||||
mConfig = config;
|
||||
}
|
||||
|
||||
public PackageList(Application application, MainPackageConfig config) {
|
||||
this.reactNativeHost = null;
|
||||
this.application = application;
|
||||
mConfig = config;
|
||||
}
|
||||
|
||||
private ReactNativeHost getReactNativeHost() {
|
||||
return this.reactNativeHost;
|
||||
}
|
||||
|
||||
private Resources getResources() {
|
||||
return this.getApplication().getResources();
|
||||
}
|
||||
|
||||
private Application getApplication() {
|
||||
if (this.reactNativeHost == null) return this.application;
|
||||
return this.reactNativeHost.getApplication();
|
||||
}
|
||||
|
||||
private Context getApplicationContext() {
|
||||
return this.getApplication().getApplicationContext();
|
||||
}
|
||||
|
||||
public ArrayList<ReactPackage> getPackages() {
|
||||
return new ArrayList<>(Arrays.<ReactPackage>asList(
|
||||
new MainReactPackage(mConfig){{ packageClassInstances }}
|
||||
));
|
||||
}
|
||||
}
|
||||
"""
|
||||
.trimIndent()
|
||||
}
|
||||
}
|
||||
|
||||
+1
-2
@@ -54,8 +54,7 @@ abstract class BuildCodegenCLITask : Exec() {
|
||||
windowsAwareBashCommandLine(
|
||||
codegenDir.asFile.get().canonicalPath.unixifyPath().plus(BUILD_SCRIPT_PATH),
|
||||
bashWindowsHome = bashWindowsHome.orNull,
|
||||
)
|
||||
)
|
||||
))
|
||||
super.exec()
|
||||
}
|
||||
|
||||
|
||||
+2
-4
@@ -29,10 +29,8 @@ abstract class CustomExecTask : Exec() {
|
||||
@get:Input @get:Optional abstract val onlyIfProvidedPathDoesNotExists: Property<String>
|
||||
|
||||
override fun exec() {
|
||||
if (
|
||||
onlyIfProvidedPathDoesNotExists.isPresent &&
|
||||
File(onlyIfProvidedPathDoesNotExists.get()).exists()
|
||||
) {
|
||||
if (onlyIfProvidedPathDoesNotExists.isPresent &&
|
||||
File(onlyIfProvidedPathDoesNotExists.get()).exists()) {
|
||||
return
|
||||
}
|
||||
if (standardOutputFile.isPresent) {
|
||||
|
||||
+1
-2
@@ -40,8 +40,7 @@ abstract class PrepareBoostTask : DefaultTask() {
|
||||
"CMakeLists.txt",
|
||||
"boost_${boostVersion.get()}/boost/**/*.hpp",
|
||||
"boost/boost/**/*.hpp",
|
||||
"asm/**/*.S",
|
||||
)
|
||||
"asm/**/*.S")
|
||||
it.includeEmptyDirs = false
|
||||
it.into(outputDir)
|
||||
}
|
||||
|
||||
+3
-6
@@ -46,8 +46,7 @@ abstract class PrepareGflagsTask : DefaultTask() {
|
||||
action.include(
|
||||
"gflags-${gflagsVersion.get()}/src/*.h",
|
||||
"gflags-${gflagsVersion.get()}/src/*.cc",
|
||||
"CMakeLists.txt",
|
||||
)
|
||||
"CMakeLists.txt")
|
||||
action.filesMatching("*/src/*") { matchedFile ->
|
||||
matchedFile.path = "gflags/${matchedFile.name}"
|
||||
}
|
||||
@@ -64,10 +63,8 @@ abstract class PrepareGflagsTask : DefaultTask() {
|
||||
.replace(Regex("@GFLAGS_NAMESPACE@"), "gflags")
|
||||
.replace(
|
||||
Regex(
|
||||
"@(HAVE_STDINT_H|HAVE_SYS_TYPES_H|HAVE_INTTYPES_H|GFLAGS_INTTYPES_FORMAT_C99)@"
|
||||
),
|
||||
"1",
|
||||
)
|
||||
"@(HAVE_STDINT_H|HAVE_SYS_TYPES_H|HAVE_INTTYPES_H|GFLAGS_INTTYPES_FORMAT_C99)@"),
|
||||
"1")
|
||||
.replace(Regex("@([A-Z0-9_]+)@"), "1")
|
||||
}
|
||||
matchedFile.path = "gflags/${matchedFile.name.removeSuffix(".in")}"
|
||||
|
||||
+3
-7
@@ -60,11 +60,8 @@ abstract class PrepareGlogTask : DefaultTask() {
|
||||
"ac_cv___attribute___noinline" to "__attribute__ ((noinline))",
|
||||
"ac_cv___attribute___noreturn" to "__attribute__ ((noreturn))",
|
||||
"ac_cv___attribute___printf_4_5" to
|
||||
"__attribute__((__format__ (__printf__, 4, 5)))",
|
||||
)
|
||||
),
|
||||
ReplaceTokens::class.java,
|
||||
)
|
||||
"__attribute__((__format__ (__printf__, 4, 5)))")),
|
||||
ReplaceTokens::class.java)
|
||||
matchedFile.path = (matchedFile.name.removeSuffix(".in"))
|
||||
}
|
||||
action.into(outputDir)
|
||||
@@ -77,8 +74,7 @@ abstract class PrepareGlogTask : DefaultTask() {
|
||||
"logging.h",
|
||||
"raw_logging.h",
|
||||
"vlog_is_on.h",
|
||||
"**/src/glog/log_severity.h",
|
||||
)
|
||||
"**/src/glog/log_severity.h")
|
||||
action.eachFile { file -> file.path = file.name }
|
||||
action.includeEmptyDirs = false
|
||||
action.into(exportedDir)
|
||||
|
||||
+1
-1
@@ -23,6 +23,6 @@ data class PrefabPreprocessingEntry(
|
||||
) : Serializable {
|
||||
constructor(
|
||||
libraryName: String,
|
||||
pathToPrefixCouple: Pair<String, String>,
|
||||
pathToPrefixCouple: Pair<String, String>
|
||||
) : this(libraryName, listOf(pathToPrefixCouple))
|
||||
}
|
||||
|
||||
+3
-12
@@ -66,15 +66,9 @@ internal object AgpConfiguratorUtils {
|
||||
ext.buildFeatures.buildConfig = true
|
||||
ext.defaultConfig.buildConfigField("boolean", "IS_NEW_ARCHITECTURE_ENABLED", "true")
|
||||
ext.defaultConfig.buildConfigField(
|
||||
"boolean",
|
||||
"IS_HERMES_ENABLED",
|
||||
project.isHermesEnabled.toString(),
|
||||
)
|
||||
"boolean", "IS_HERMES_ENABLED", project.isHermesEnabled.toString())
|
||||
ext.defaultConfig.buildConfigField(
|
||||
"boolean",
|
||||
"IS_EDGE_TO_EDGE_ENABLED",
|
||||
project.isEdgeToEdgeEnabled.toString(),
|
||||
)
|
||||
"boolean", "IS_EDGE_TO_EDGE_ENABLED", project.isEdgeToEdgeEnabled.toString())
|
||||
}
|
||||
}
|
||||
project.pluginManager.withPlugin("com.android.application", action)
|
||||
@@ -101,10 +95,7 @@ internal object AgpConfiguratorUtils {
|
||||
.getByType(ApplicationAndroidComponentsExtension::class.java)
|
||||
.finalizeDsl { ext ->
|
||||
ext.defaultConfig.resValue(
|
||||
"string",
|
||||
"react_native_dev_server_ip",
|
||||
getHostIpAddress(),
|
||||
)
|
||||
"string", "react_native_dev_server_ip", getHostIpAddress())
|
||||
ext.defaultConfig.resValue("integer", "react_native_dev_server_port", devServerPort)
|
||||
}
|
||||
}
|
||||
|
||||
+8
-9
@@ -39,8 +39,7 @@ internal object BackwardCompatUtils {
|
||||
|
||||
********************************************************************************
|
||||
"""
|
||||
.trimIndent()
|
||||
)
|
||||
.trimIndent())
|
||||
}
|
||||
}
|
||||
|
||||
@@ -56,14 +55,14 @@ internal object BackwardCompatUtils {
|
||||
val message =
|
||||
"""
|
||||
|
||||
=============== JavaScriptCore is being moved ===============
|
||||
JavaScriptCore has been extracted from react-native core
|
||||
and will be removed in a future release. It can now be
|
||||
installed from `@react-native-community/javascriptcore`
|
||||
See: https://github.com/react-native-community/javascriptcore
|
||||
=============================================================
|
||||
=============== JavaScriptCore is being moved ===============
|
||||
JavaScriptCore has been extracted from react-native core
|
||||
and will be removed in a future release. It can now be
|
||||
installed from `@react-native-community/javascriptcore`
|
||||
See: https://github.com/react-native-community/javascriptcore
|
||||
=============================================================
|
||||
|
||||
"""
|
||||
"""
|
||||
.trimIndent()
|
||||
project.logger.warn(message)
|
||||
hasShownJSCRemovalMessage = true
|
||||
|
||||
+9
-18
@@ -33,8 +33,7 @@ internal object DependencyUtils {
|
||||
val exclusiveEnterpriseRepository = project.rootProject.exclusiveEnterpriseRepository()
|
||||
if (exclusiveEnterpriseRepository != null) {
|
||||
project.logger.lifecycle(
|
||||
"Replacing ALL Maven Repositories with: $exclusiveEnterpriseRepository"
|
||||
)
|
||||
"Replacing ALL Maven Repositories with: $exclusiveEnterpriseRepository")
|
||||
}
|
||||
|
||||
project.rootProject.allprojects { eachProject ->
|
||||
@@ -100,7 +99,7 @@ internal object DependencyUtils {
|
||||
fun configureDependencies(
|
||||
project: Project,
|
||||
versionString: String,
|
||||
groupString: String = DEFAULT_INTERNAL_PUBLISHING_GROUP,
|
||||
groupString: String = DEFAULT_INTERNAL_PUBLISHING_GROUP
|
||||
) {
|
||||
if (versionString.isBlank()) return
|
||||
project.rootProject.allprojects { eachProject ->
|
||||
@@ -128,38 +127,30 @@ internal object DependencyUtils {
|
||||
|
||||
internal fun getDependencySubstitutions(
|
||||
versionString: String,
|
||||
groupString: String = DEFAULT_INTERNAL_PUBLISHING_GROUP,
|
||||
groupString: String = DEFAULT_INTERNAL_PUBLISHING_GROUP
|
||||
): List<Triple<String, String, String>> {
|
||||
val dependencySubstitution = mutableListOf<Triple<String, String, String>>()
|
||||
dependencySubstitution.add(
|
||||
Triple(
|
||||
"com.facebook.react:react-native",
|
||||
"${groupString}:react-android:${versionString}",
|
||||
"The react-native artifact was deprecated in favor of react-android due to https://github.com/facebook/react-native/issues/35210.",
|
||||
)
|
||||
)
|
||||
"The react-native artifact was deprecated in favor of react-android due to https://github.com/facebook/react-native/issues/35210."))
|
||||
dependencySubstitution.add(
|
||||
Triple(
|
||||
"com.facebook.react:hermes-engine",
|
||||
"${groupString}:hermes-android:${versionString}",
|
||||
"The hermes-engine artifact was deprecated in favor of hermes-android due to https://github.com/facebook/react-native/issues/35210.",
|
||||
)
|
||||
)
|
||||
"The hermes-engine artifact was deprecated in favor of hermes-android due to https://github.com/facebook/react-native/issues/35210."))
|
||||
if (groupString != DEFAULT_INTERNAL_PUBLISHING_GROUP) {
|
||||
dependencySubstitution.add(
|
||||
Triple(
|
||||
"com.facebook.react:react-android",
|
||||
"${groupString}:react-android:${versionString}",
|
||||
"The react-android dependency was modified to use the correct Maven group.",
|
||||
)
|
||||
)
|
||||
"The react-android dependency was modified to use the correct Maven group."))
|
||||
dependencySubstitution.add(
|
||||
Triple(
|
||||
"com.facebook.react:hermes-android",
|
||||
"${groupString}:hermes-android:${versionString}",
|
||||
"The hermes-android dependency was modified to use the correct Maven group.",
|
||||
)
|
||||
)
|
||||
"The hermes-android dependency was modified to use the correct Maven group."))
|
||||
}
|
||||
return dependencySubstitution
|
||||
}
|
||||
@@ -184,7 +175,7 @@ internal object DependencyUtils {
|
||||
|
||||
fun Project.mavenRepoFromUrl(
|
||||
url: String,
|
||||
action: (MavenArtifactRepository) -> Unit = {},
|
||||
action: (MavenArtifactRepository) -> Unit = {}
|
||||
): MavenArtifactRepository =
|
||||
project.repositories.maven {
|
||||
it.url = URI.create(url)
|
||||
@@ -193,7 +184,7 @@ internal object DependencyUtils {
|
||||
|
||||
fun Project.mavenRepoFromURI(
|
||||
uri: URI,
|
||||
action: (MavenArtifactRepository) -> Unit = {},
|
||||
action: (MavenArtifactRepository) -> Unit = {}
|
||||
): MavenArtifactRepository =
|
||||
project.repositories.maven {
|
||||
it.url = uri
|
||||
|
||||
+8
-11
@@ -30,8 +30,7 @@ internal object NdkConfiguratorUtils {
|
||||
ext.externalNativeBuild.cmake.path =
|
||||
File(
|
||||
extension.reactNativeDir.get().asFile,
|
||||
"ReactAndroid/cmake-utils/default-app-setup/CMakeLists.txt",
|
||||
)
|
||||
"ReactAndroid/cmake-utils/default-app-setup/CMakeLists.txt")
|
||||
}
|
||||
|
||||
// Parameters should be provided in an additive manner (do not override what
|
||||
@@ -45,8 +44,7 @@ internal object NdkConfiguratorUtils {
|
||||
}
|
||||
if (cmakeArgs.none { it.startsWith("-DREACT_ANDROID_DIR") }) {
|
||||
cmakeArgs.add(
|
||||
"-DREACT_ANDROID_DIR=${extension.reactNativeDir.file("ReactAndroid").get().asFile}"
|
||||
)
|
||||
"-DREACT_ANDROID_DIR=${extension.reactNativeDir.file("ReactAndroid").get().asFile}")
|
||||
}
|
||||
if (cmakeArgs.none { it.startsWith("-DANDROID_STL") }) {
|
||||
cmakeArgs.add("-DANDROID_STL=c++_shared")
|
||||
@@ -73,7 +71,7 @@ internal object NdkConfiguratorUtils {
|
||||
fun configureNewArchPackagingOptions(
|
||||
project: Project,
|
||||
extension: ReactExtension,
|
||||
variant: Variant,
|
||||
variant: Variant
|
||||
) {
|
||||
// We set some packagingOptions { pickFirst ... } for our users for libraries we own.
|
||||
variant.packaging.jniLibs.pickFirsts.addAll(
|
||||
@@ -87,8 +85,7 @@ internal object NdkConfiguratorUtils {
|
||||
"**/libjsi.so",
|
||||
// AGP will give priority of libc++_shared coming from App modules.
|
||||
"**/libc++_shared.so",
|
||||
)
|
||||
)
|
||||
))
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -110,7 +107,7 @@ internal object NdkConfiguratorUtils {
|
||||
|
||||
fun getPackagingOptionsForVariant(
|
||||
hermesEnabled: Boolean,
|
||||
useThirdPartyJSC: Boolean,
|
||||
useThirdPartyJSC: Boolean
|
||||
): Pair<List<String>, List<String>> {
|
||||
val excludes = mutableListOf<String>()
|
||||
val includes = mutableListOf<String>()
|
||||
@@ -120,17 +117,17 @@ internal object NdkConfiguratorUtils {
|
||||
hermesEnabled -> {
|
||||
excludes.add("**/libjsc.so")
|
||||
excludes.add("**/libjsctooling.so")
|
||||
includes.add("**/libhermesvm.so")
|
||||
includes.add("**/libhermes.so")
|
||||
includes.add("**/libhermestooling.so")
|
||||
}
|
||||
useThirdPartyJSC -> {
|
||||
excludes.add("**/libhermesvm.so")
|
||||
excludes.add("**/libhermes.so")
|
||||
excludes.add("**/libhermestooling.so")
|
||||
excludes.add("**/libjsctooling.so")
|
||||
includes.add("**/libjsc.so")
|
||||
}
|
||||
else -> {
|
||||
excludes.add("**/libhermesvm.so")
|
||||
excludes.add("**/libhermes.so")
|
||||
excludes.add("**/libhermestooling.so")
|
||||
includes.add("**/libjsc.so")
|
||||
includes.add("**/libjsctooling.so")
|
||||
|
||||
+13
-19
@@ -30,8 +30,7 @@ internal fun detectedEntryFile(config: ReactExtension, envVariableOverride: Stri
|
||||
detectEntryFile(
|
||||
entryFile = config.entryFile.orNull?.asFile,
|
||||
reactRoot = config.root.get().asFile,
|
||||
envVariableOverride = envVariableOverride,
|
||||
)
|
||||
envVariableOverride = envVariableOverride)
|
||||
|
||||
/**
|
||||
* Computes the CLI file for React Native. The Algo follows this order:
|
||||
@@ -43,8 +42,7 @@ internal fun detectedEntryFile(config: ReactExtension, envVariableOverride: Stri
|
||||
internal fun detectedCliFile(config: ReactExtension): File =
|
||||
detectCliFile(
|
||||
reactNativeRoot = config.root.get().asFile,
|
||||
preconfiguredCliFile = config.cliFile.asFile.orNull,
|
||||
)
|
||||
preconfiguredCliFile = config.cliFile.asFile.orNull)
|
||||
|
||||
/**
|
||||
* Computes the `hermesc` command location. The Algo follows this order:
|
||||
@@ -62,7 +60,7 @@ internal fun detectedHermesCommand(config: ReactExtension): String =
|
||||
private fun detectEntryFile(
|
||||
entryFile: File?,
|
||||
reactRoot: File,
|
||||
envVariableOverride: String? = null,
|
||||
envVariableOverride: String? = null
|
||||
): File =
|
||||
when {
|
||||
envVariableOverride != null -> File(reactRoot, envVariableOverride)
|
||||
@@ -85,8 +83,7 @@ private fun detectCliFile(reactNativeRoot: File, preconfiguredCliFile: File?): F
|
||||
.exec(
|
||||
arrayOf("node", "--print", "require.resolve('react-native/cli');"),
|
||||
emptyArray(),
|
||||
reactNativeRoot,
|
||||
)
|
||||
reactNativeRoot)
|
||||
|
||||
val nodeProcessOutput = nodeProcess.inputStream.use { it.bufferedReader().readText().trim() }
|
||||
|
||||
@@ -105,14 +102,13 @@ private fun detectCliFile(reactNativeRoot: File, preconfiguredCliFile: File?): F
|
||||
|
||||
error(
|
||||
"""
|
||||
Couldn't determine CLI location!
|
||||
Couldn't determine CLI location!
|
||||
|
||||
Please set `react { cliFile = file(...) }` inside your
|
||||
build.gradle to the path of the react-native cli.js file.
|
||||
This file typically resides in `node_modules/react-native/cli.js`
|
||||
"""
|
||||
.trimIndent()
|
||||
)
|
||||
Please set `react { cliFile = file(...) }` inside your
|
||||
build.gradle to the path of the react-native cli.js file.
|
||||
This file typically resides in `node_modules/react-native/cli.js`
|
||||
"""
|
||||
.trimIndent())
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -161,8 +157,7 @@ internal fun detectOSAwareHermesCommand(projectRoot: File, hermesCommand: String
|
||||
error(
|
||||
"Couldn't determine Hermesc location. " +
|
||||
"Please set `react.hermesCommand` to the path of the hermesc binary file. " +
|
||||
"node_modules/react-native/sdks/hermesc/%OS-BIN%/hermesc"
|
||||
)
|
||||
"node_modules/react-native/sdks/hermesc/%OS-BIN%/hermesc")
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -188,8 +183,7 @@ internal fun getHermesOSBin(): String {
|
||||
if (Os.isLinuxAmd64()) return "linux64-bin"
|
||||
error(
|
||||
"OS not recognized. Please set project.react.hermesCommand " +
|
||||
"to the path of a working Hermes compiler."
|
||||
)
|
||||
"to the path of a working Hermes compiler.")
|
||||
}
|
||||
|
||||
internal fun projectPathToLibraryName(projectPath: String): String =
|
||||
@@ -227,7 +221,7 @@ internal fun findPackageJsonFile(project: Project, rootProperty: DirectoryProper
|
||||
*/
|
||||
internal fun readPackageJsonFile(
|
||||
project: Project,
|
||||
rootProperty: DirectoryProperty,
|
||||
rootProperty: DirectoryProperty
|
||||
): ModelPackageJson? {
|
||||
val packageJson = findPackageJsonFile(project, rootProperty)
|
||||
return packageJson?.let { JsonUtils.fromPackageJson(it) }
|
||||
|
||||
+124
-133
@@ -23,12 +23,11 @@ class ReactExtensionTest {
|
||||
val validJsonFile =
|
||||
createJsonFile(
|
||||
"""
|
||||
{
|
||||
"reactNativeVersion": "1000.0.0"
|
||||
}
|
||||
"""
|
||||
.trimIndent()
|
||||
)
|
||||
{
|
||||
"reactNativeVersion": "1000.0.0"
|
||||
}
|
||||
"""
|
||||
.trimIndent())
|
||||
|
||||
val deps = getGradleDependenciesToApply(validJsonFile)
|
||||
assertThat(deps).isEmpty()
|
||||
@@ -39,24 +38,23 @@ class ReactExtensionTest {
|
||||
val validJsonFile =
|
||||
createJsonFile(
|
||||
"""
|
||||
{
|
||||
"reactNativeVersion": "1000.0.0",
|
||||
"dependencies": {
|
||||
"@react-native/oss-library-example": {
|
||||
"root": "./node_modules/@react-native/oss-library-example",
|
||||
"name": "@react-native/oss-library-example",
|
||||
"platforms": {
|
||||
"android": {
|
||||
"sourceDir": "src/main/java",
|
||||
"packageImportPath": "com.facebook.react"
|
||||
}
|
||||
}
|
||||
}
|
||||
{
|
||||
"reactNativeVersion": "1000.0.0",
|
||||
"dependencies": {
|
||||
"@react-native/oss-library-example": {
|
||||
"root": "./node_modules/@react-native/oss-library-example",
|
||||
"name": "@react-native/oss-library-example",
|
||||
"platforms": {
|
||||
"android": {
|
||||
"sourceDir": "src/main/java",
|
||||
"packageImportPath": "com.facebook.react"
|
||||
}
|
||||
}
|
||||
"""
|
||||
.trimIndent()
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
"""
|
||||
.trimIndent())
|
||||
|
||||
val deps = getGradleDependenciesToApply(validJsonFile)
|
||||
assertThat(deps).containsExactly("implementation" to ":react-native_oss-library-example")
|
||||
@@ -67,25 +65,24 @@ class ReactExtensionTest {
|
||||
val validJsonFile =
|
||||
createJsonFile(
|
||||
"""
|
||||
{
|
||||
"reactNativeVersion": "1000.0.0",
|
||||
"dependencies": {
|
||||
"@react-native/oss-library-example": {
|
||||
"root": "./node_modules/@react-native/oss-library-example",
|
||||
"name": "@react-native/oss-library-example",
|
||||
"platforms": {
|
||||
"android": {
|
||||
"sourceDir": "src/main/java",
|
||||
"packageImportPath": "com.facebook.react",
|
||||
"dependencyConfiguration": "compileOnly"
|
||||
}
|
||||
}
|
||||
}
|
||||
{
|
||||
"reactNativeVersion": "1000.0.0",
|
||||
"dependencies": {
|
||||
"@react-native/oss-library-example": {
|
||||
"root": "./node_modules/@react-native/oss-library-example",
|
||||
"name": "@react-native/oss-library-example",
|
||||
"platforms": {
|
||||
"android": {
|
||||
"sourceDir": "src/main/java",
|
||||
"packageImportPath": "com.facebook.react",
|
||||
"dependencyConfiguration": "compileOnly"
|
||||
}
|
||||
}
|
||||
"""
|
||||
.trimIndent()
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
"""
|
||||
.trimIndent())
|
||||
|
||||
val deps = getGradleDependenciesToApply(validJsonFile)
|
||||
assertThat(deps).containsExactly("compileOnly" to ":react-native_oss-library-example")
|
||||
@@ -96,32 +93,30 @@ class ReactExtensionTest {
|
||||
val validJsonFile =
|
||||
createJsonFile(
|
||||
"""
|
||||
{
|
||||
"reactNativeVersion": "1000.0.0",
|
||||
"dependencies": {
|
||||
"@react-native/oss-library-example": {
|
||||
"root": "./node_modules/@react-native/oss-library-example",
|
||||
"name": "@react-native/oss-library-example",
|
||||
"platforms": {
|
||||
"android": {
|
||||
"sourceDir": "src/main/java",
|
||||
"packageImportPath": "com.facebook.react",
|
||||
"buildTypes": ["debug", "release"]
|
||||
}
|
||||
}
|
||||
}
|
||||
{
|
||||
"reactNativeVersion": "1000.0.0",
|
||||
"dependencies": {
|
||||
"@react-native/oss-library-example": {
|
||||
"root": "./node_modules/@react-native/oss-library-example",
|
||||
"name": "@react-native/oss-library-example",
|
||||
"platforms": {
|
||||
"android": {
|
||||
"sourceDir": "src/main/java",
|
||||
"packageImportPath": "com.facebook.react",
|
||||
"buildTypes": ["debug", "release"]
|
||||
}
|
||||
}
|
||||
"""
|
||||
.trimIndent()
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
"""
|
||||
.trimIndent())
|
||||
|
||||
val deps = getGradleDependenciesToApply(validJsonFile)
|
||||
assertThat(deps)
|
||||
.containsExactly(
|
||||
"debugImplementation" to ":react-native_oss-library-example",
|
||||
"releaseImplementation" to ":react-native_oss-library-example",
|
||||
)
|
||||
"releaseImplementation" to ":react-native_oss-library-example")
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -129,41 +124,39 @@ class ReactExtensionTest {
|
||||
val validJsonFile =
|
||||
createJsonFile(
|
||||
"""
|
||||
{
|
||||
"reactNativeVersion": "1000.0.0",
|
||||
"dependencies": {
|
||||
"@react-native/oss-library-example": {
|
||||
"root": "./node_modules/@react-native/oss-library-example",
|
||||
"name": "@react-native/oss-library-example",
|
||||
"platforms": {
|
||||
"android": {
|
||||
"sourceDir": "src/main/java",
|
||||
"packageImportPath": "com.facebook.react"
|
||||
}
|
||||
}
|
||||
},
|
||||
"@react-native/another-library-for-testing": {
|
||||
"root": "./node_modules/@react-native/another-library-for-testing",
|
||||
"name": "@react-native/another-library-for-testing",
|
||||
"platforms": {
|
||||
"android": {
|
||||
"sourceDir": "src/main/java",
|
||||
"packageImportPath": "com.facebook.react"
|
||||
}
|
||||
}
|
||||
}
|
||||
{
|
||||
"reactNativeVersion": "1000.0.0",
|
||||
"dependencies": {
|
||||
"@react-native/oss-library-example": {
|
||||
"root": "./node_modules/@react-native/oss-library-example",
|
||||
"name": "@react-native/oss-library-example",
|
||||
"platforms": {
|
||||
"android": {
|
||||
"sourceDir": "src/main/java",
|
||||
"packageImportPath": "com.facebook.react"
|
||||
}
|
||||
}
|
||||
"""
|
||||
.trimIndent()
|
||||
)
|
||||
},
|
||||
"@react-native/another-library-for-testing": {
|
||||
"root": "./node_modules/@react-native/another-library-for-testing",
|
||||
"name": "@react-native/another-library-for-testing",
|
||||
"platforms": {
|
||||
"android": {
|
||||
"sourceDir": "src/main/java",
|
||||
"packageImportPath": "com.facebook.react"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
"""
|
||||
.trimIndent())
|
||||
|
||||
val deps = getGradleDependenciesToApply(validJsonFile)
|
||||
assertThat(deps)
|
||||
.containsExactly(
|
||||
"implementation" to ":react-native_oss-library-example",
|
||||
"implementation" to ":react-native_another-library-for-testing",
|
||||
)
|
||||
"implementation" to ":react-native_another-library-for-testing")
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -171,27 +164,26 @@ class ReactExtensionTest {
|
||||
val validJsonFile =
|
||||
createJsonFile(
|
||||
"""
|
||||
{
|
||||
"reactNativeVersion": "1000.0.0",
|
||||
"dependencies": {
|
||||
"@react-native/oss-library-example": {
|
||||
"root": "./node_modules/@react-native/oss-library-example",
|
||||
"name": "@react-native/oss-library-example",
|
||||
"platforms": {
|
||||
"ios": {
|
||||
"podspecPath": "./node_modules/@react-native/oss-library-example/oss-library-example.podspec",
|
||||
"version": "0.0.0",
|
||||
"configurations": [],
|
||||
"scriptPhases": []
|
||||
},
|
||||
"android": null
|
||||
}
|
||||
}
|
||||
}
|
||||
{
|
||||
"reactNativeVersion": "1000.0.0",
|
||||
"dependencies": {
|
||||
"@react-native/oss-library-example": {
|
||||
"root": "./node_modules/@react-native/oss-library-example",
|
||||
"name": "@react-native/oss-library-example",
|
||||
"platforms": {
|
||||
"ios": {
|
||||
"podspecPath": "./node_modules/@react-native/oss-library-example/oss-library-example.podspec",
|
||||
"version": "0.0.0",
|
||||
"configurations": [],
|
||||
"scriptPhases": []
|
||||
},
|
||||
"android": null
|
||||
}
|
||||
"""
|
||||
.trimIndent()
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
"""
|
||||
.trimIndent())
|
||||
|
||||
val deps = getGradleDependenciesToApply(validJsonFile)
|
||||
assertThat(deps).isEmpty()
|
||||
@@ -202,35 +194,34 @@ class ReactExtensionTest {
|
||||
val validJsonFile =
|
||||
createJsonFile(
|
||||
"""
|
||||
{
|
||||
"reactNativeVersion": "1000.0.0",
|
||||
"dependencies": {
|
||||
"@react-native/oss-library-example": {
|
||||
"root": "./node_modules/@react-native/android-example",
|
||||
"name": "@react-native/android-example",
|
||||
"platforms": {
|
||||
"android": {
|
||||
"sourceDir": "src/main/java",
|
||||
"packageImportPath": "com.facebook.react"
|
||||
}
|
||||
}
|
||||
},
|
||||
"@react-native/another-library-for-testing": {
|
||||
"root": "./node_modules/@react-native/cxx-testing",
|
||||
"name": "@react-native/cxx-testing",
|
||||
"platforms": {
|
||||
"android": {
|
||||
"sourceDir": "src/main/java",
|
||||
"packageImportPath": "com.facebook.react",
|
||||
"isPureCxxDependency": true
|
||||
}
|
||||
}
|
||||
}
|
||||
{
|
||||
"reactNativeVersion": "1000.0.0",
|
||||
"dependencies": {
|
||||
"@react-native/oss-library-example": {
|
||||
"root": "./node_modules/@react-native/android-example",
|
||||
"name": "@react-native/android-example",
|
||||
"platforms": {
|
||||
"android": {
|
||||
"sourceDir": "src/main/java",
|
||||
"packageImportPath": "com.facebook.react"
|
||||
}
|
||||
}
|
||||
"""
|
||||
.trimIndent()
|
||||
)
|
||||
},
|
||||
"@react-native/another-library-for-testing": {
|
||||
"root": "./node_modules/@react-native/cxx-testing",
|
||||
"name": "@react-native/cxx-testing",
|
||||
"platforms": {
|
||||
"android": {
|
||||
"sourceDir": "src/main/java",
|
||||
"packageImportPath": "com.facebook.react",
|
||||
"isPureCxxDependency": true
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
"""
|
||||
.trimIndent())
|
||||
|
||||
val deps = getGradleDependenciesToApply(validJsonFile)
|
||||
assertThat(deps).containsExactly("implementation" to ":react-native_android-example")
|
||||
|
||||
+2
-6
@@ -37,12 +37,8 @@ class ModelAutolinkingDependenciesJsonTest {
|
||||
.isEqualTo("react-native_package")
|
||||
assertThat(
|
||||
ModelAutolinkingDependenciesJson(
|
||||
"",
|
||||
"@this*is~a(more)complicated/example!of~weird)packages",
|
||||
null,
|
||||
)
|
||||
.nameCleansed
|
||||
)
|
||||
"", "@this*is~a(more)complicated/example!of~weird)packages", null)
|
||||
.nameCleansed)
|
||||
.isEqualTo("this_is_a_more_complicated_example_of_weird_packages")
|
||||
}
|
||||
}
|
||||
|
||||
+8
-19
@@ -47,8 +47,7 @@ class BundleHermesCTaskTest {
|
||||
File(rootDir, "file.js"),
|
||||
File(rootDir, "file.jsx"),
|
||||
File(rootDir, "file.ts"),
|
||||
File(rootDir, "file.tsx"),
|
||||
)
|
||||
File(rootDir, "file.tsx"))
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -72,11 +71,7 @@ class BundleHermesCTaskTest {
|
||||
|
||||
assertThat(task.sources.excludes)
|
||||
.containsExactlyInAnyOrder(
|
||||
"**/android/**/*",
|
||||
"**/ios/**/*",
|
||||
"**/build/**/*",
|
||||
"**/node_modules/**/*",
|
||||
)
|
||||
"**/android/**/*", "**/ios/**/*", "**/build/**/*", "**/node_modules/**/*")
|
||||
assertThat(task.sources.files.size).isEqualTo(1)
|
||||
assertThat(task.sources.files).containsExactly(File(rootDir, "afolder/includedfile.js"))
|
||||
}
|
||||
@@ -241,8 +236,7 @@ class BundleHermesCTaskTest {
|
||||
"--minify",
|
||||
"true",
|
||||
"--read-global-cache",
|
||||
"--verbose",
|
||||
)
|
||||
"--verbose")
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -297,8 +291,7 @@ class BundleHermesCTaskTest {
|
||||
"--minify",
|
||||
"true",
|
||||
"--read-global-cache",
|
||||
"--verbose",
|
||||
)
|
||||
"--verbose")
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -348,8 +341,7 @@ class BundleHermesCTaskTest {
|
||||
"-out",
|
||||
bytecodeFile.absolutePath,
|
||||
bundleFile.absolutePath,
|
||||
"my-custom-hermes-flag",
|
||||
)
|
||||
"my-custom-hermes-flag")
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -377,8 +369,7 @@ class BundleHermesCTaskTest {
|
||||
"-out",
|
||||
bytecodeFile.relativeTo(tempFolder.root).path,
|
||||
bundleFile.relativeTo(tempFolder.root).path,
|
||||
"my-custom-hermes-flag",
|
||||
)
|
||||
"my-custom-hermes-flag")
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -406,8 +397,7 @@ class BundleHermesCTaskTest {
|
||||
packagerMap.absolutePath,
|
||||
compilerMap.absolutePath,
|
||||
"-o",
|
||||
outputMap.absolutePath,
|
||||
)
|
||||
outputMap.absolutePath)
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -438,7 +428,6 @@ class BundleHermesCTaskTest {
|
||||
packagerMap.relativeTo(tempFolder.root).path,
|
||||
compilerMap.relativeTo(tempFolder.root).path,
|
||||
"-o",
|
||||
outputMap.relativeTo(tempFolder.root).path,
|
||||
)
|
||||
outputMap.relativeTo(tempFolder.root).path)
|
||||
}
|
||||
}
|
||||
|
||||
+9
-22
@@ -71,12 +71,8 @@ class GenerateAutolinkingNewArchitecturesFileTaskTest {
|
||||
root = "./a/directory",
|
||||
name = "a-dependency",
|
||||
platforms =
|
||||
ModelAutolinkingDependenciesPlatformJson(android = null),
|
||||
)
|
||||
),
|
||||
project = null,
|
||||
)
|
||||
)
|
||||
ModelAutolinkingDependenciesPlatformJson(android = null))),
|
||||
project = null))
|
||||
assertThat(result).isEmpty()
|
||||
}
|
||||
|
||||
@@ -102,12 +98,8 @@ class GenerateAutolinkingNewArchitecturesFileTaskTest {
|
||||
root = "./a/directory",
|
||||
name = "a-dependency",
|
||||
platforms =
|
||||
ModelAutolinkingDependenciesPlatformJson(android = android),
|
||||
)
|
||||
),
|
||||
project = null,
|
||||
)
|
||||
)
|
||||
ModelAutolinkingDependenciesPlatformJson(android = android))),
|
||||
project = null))
|
||||
assertThat(result).containsExactly(android)
|
||||
}
|
||||
|
||||
@@ -134,8 +126,7 @@ class GenerateAutolinkingNewArchitecturesFileTaskTest {
|
||||
|
||||
)
|
||||
"""
|
||||
.trimIndent()
|
||||
)
|
||||
.trimIndent())
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -165,8 +156,7 @@ class GenerateAutolinkingNewArchitecturesFileTaskTest {
|
||||
another_cxxModule
|
||||
)
|
||||
"""
|
||||
.trimIndent()
|
||||
)
|
||||
.trimIndent())
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -210,8 +200,7 @@ class GenerateAutolinkingNewArchitecturesFileTaskTest {
|
||||
} // namespace react
|
||||
} // namespace facebook
|
||||
"""
|
||||
.trimIndent()
|
||||
)
|
||||
.trimIndent())
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -267,8 +256,7 @@ class GenerateAutolinkingNewArchitecturesFileTaskTest {
|
||||
} // namespace react
|
||||
} // namespace facebook
|
||||
"""
|
||||
.trimIndent()
|
||||
)
|
||||
.trimIndent())
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -311,6 +299,5 @@ class GenerateAutolinkingNewArchitecturesFileTaskTest {
|
||||
cxxModuleCMakeListsPath = "./another/directory/cxx/CMakeLists.txt",
|
||||
cxxModuleHeaderName = "AnotherCxxModule",
|
||||
cxxModuleCMakeListsModuleName = "another_cxxModule",
|
||||
),
|
||||
)
|
||||
))
|
||||
}
|
||||
|
||||
+6
-8
@@ -148,8 +148,7 @@ class GenerateCodegenArtifactsTaskTest {
|
||||
}
|
||||
}
|
||||
"""
|
||||
.trimIndent()
|
||||
)
|
||||
.trimIndent())
|
||||
}
|
||||
|
||||
val task =
|
||||
@@ -178,8 +177,7 @@ class GenerateCodegenArtifactsTaskTest {
|
||||
}
|
||||
}
|
||||
"""
|
||||
.trimIndent()
|
||||
)
|
||||
.trimIndent())
|
||||
}
|
||||
|
||||
val task =
|
||||
@@ -198,10 +196,10 @@ class GenerateCodegenArtifactsTaskTest {
|
||||
@Test
|
||||
fun resolveTaskParameters_withMissingPackageJson_usesGradleOne() {
|
||||
val task =
|
||||
createTestTask<GenerateCodegenArtifactsTask> { task ->
|
||||
task.packageJsonFile.set(File(tempFolder.root, "package.json"))
|
||||
task.codegenJavaPackageName.set("com.example.test")
|
||||
task.libraryName.set("a-library-name-from-gradle")
|
||||
createTestTask<GenerateCodegenArtifactsTask> {
|
||||
it.packageJsonFile.set(File(tempFolder.root, "package.json"))
|
||||
it.codegenJavaPackageName.set("com.example.test")
|
||||
it.libraryName.set("a-library-name-from-gradle")
|
||||
}
|
||||
|
||||
val (libraryName, javaPackageName) = task.resolveTaskParameters()
|
||||
|
||||
+1
-2
@@ -86,7 +86,6 @@ class GenerateEntryPointTaskTest {
|
||||
}
|
||||
}
|
||||
"""
|
||||
.trimIndent()
|
||||
)
|
||||
.trimIndent())
|
||||
}
|
||||
}
|
||||
|
||||
+12
-30
@@ -64,8 +64,7 @@ class GeneratePackageListTaskTest {
|
||||
// @react-native/another-package
|
||||
import com.facebook.react.anotherPackage;
|
||||
"""
|
||||
.trimIndent()
|
||||
)
|
||||
.trimIndent())
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -89,8 +88,7 @@ class GeneratePackageListTaskTest {
|
||||
new APackage(),
|
||||
new AnotherPackage()
|
||||
"""
|
||||
.trimIndent()
|
||||
)
|
||||
.trimIndent())
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -148,12 +146,8 @@ class GeneratePackageListTaskTest {
|
||||
root = "./a/directory",
|
||||
name = "a-dependency",
|
||||
platforms =
|
||||
ModelAutolinkingDependenciesPlatformJson(android = null),
|
||||
)
|
||||
),
|
||||
project = null,
|
||||
)
|
||||
)
|
||||
ModelAutolinkingDependenciesPlatformJson(android = null))),
|
||||
project = null))
|
||||
assertThat(result)
|
||||
.isEqualTo(emptyMap<String, ModelAutolinkingDependenciesPlatformAndroidJson>())
|
||||
}
|
||||
@@ -180,12 +174,8 @@ class GeneratePackageListTaskTest {
|
||||
root = "./a/directory",
|
||||
name = "a-dependency",
|
||||
platforms =
|
||||
ModelAutolinkingDependenciesPlatformJson(android = android),
|
||||
)
|
||||
),
|
||||
project = null,
|
||||
)
|
||||
)
|
||||
ModelAutolinkingDependenciesPlatformJson(android = android))),
|
||||
project = null))
|
||||
assertThat(result.entries.size).isEqualTo(1)
|
||||
assertThat(result["a-dependency"]).isEqualTo(android)
|
||||
}
|
||||
@@ -199,8 +189,7 @@ class GeneratePackageListTaskTest {
|
||||
packageImportPath = "import com.facebook.react.aPackage;",
|
||||
packageInstance = "new APackage()",
|
||||
buildTypes = emptyList(),
|
||||
isPureCxxDependency = true,
|
||||
)
|
||||
isPureCxxDependency = true)
|
||||
|
||||
val result =
|
||||
task.filterAndroidPackages(
|
||||
@@ -213,12 +202,8 @@ class GeneratePackageListTaskTest {
|
||||
root = "./a/directory",
|
||||
name = "a-pure-cxx-dependency",
|
||||
platforms =
|
||||
ModelAutolinkingDependenciesPlatformJson(android = android),
|
||||
)
|
||||
),
|
||||
project = null,
|
||||
)
|
||||
)
|
||||
ModelAutolinkingDependenciesPlatformJson(android = android))),
|
||||
project = null))
|
||||
assertThat(result)
|
||||
.isEqualTo(emptyMap<String, ModelAutolinkingDependenciesPlatformAndroidJson>())
|
||||
}
|
||||
@@ -297,8 +282,7 @@ class GeneratePackageListTaskTest {
|
||||
}
|
||||
}
|
||||
"""
|
||||
.trimIndent()
|
||||
)
|
||||
.trimIndent())
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -380,8 +364,7 @@ class GeneratePackageListTaskTest {
|
||||
}
|
||||
}
|
||||
"""
|
||||
.trimIndent()
|
||||
)
|
||||
.trimIndent())
|
||||
}
|
||||
|
||||
private val testDependencies =
|
||||
@@ -405,6 +388,5 @@ class GeneratePackageListTaskTest {
|
||||
libraryName = "anotherPackage",
|
||||
componentDescriptors = emptyList(),
|
||||
cmakeListsPath = "./another/directory/CMakeLists.txt",
|
||||
),
|
||||
)
|
||||
))
|
||||
}
|
||||
|
||||
+16
-17
@@ -26,8 +26,7 @@ class PrepareBoostTaskTest {
|
||||
assertThatThrownBy { task.taskAction() }
|
||||
.isInstanceOf(IllegalStateException::class.java)
|
||||
.hasMessage(
|
||||
"Cannot query the value of task ':PrepareBoostTask' property 'boostVersion' because it has no value available."
|
||||
)
|
||||
"Cannot query the value of task ':PrepareBoostTask' property 'boostVersion' because it has no value available.")
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -58,11 +57,11 @@ class PrepareBoostTaskTest {
|
||||
val boostThirdPartyJniPath = tempFolder.newFolder("boostpath/jni")
|
||||
val output = tempFolder.newFolder("output")
|
||||
val task =
|
||||
createTestTask<PrepareBoostTask> { task ->
|
||||
task.boostPath.setFrom(boostpath)
|
||||
task.boostThirdPartyJniPath.set(boostThirdPartyJniPath)
|
||||
task.boostVersion.set("1.0.0")
|
||||
task.outputDir.set(output)
|
||||
createTestTask<PrepareBoostTask> {
|
||||
it.boostPath.setFrom(boostpath)
|
||||
it.boostThirdPartyJniPath.set(boostThirdPartyJniPath)
|
||||
it.boostVersion.set("1.0.0")
|
||||
it.outputDir.set(output)
|
||||
}
|
||||
File(boostpath, "asm/asm.S").apply {
|
||||
parentFile.mkdirs()
|
||||
@@ -79,11 +78,11 @@ class PrepareBoostTaskTest {
|
||||
val boostThirdPartyJniPath = tempFolder.newFolder("boostpath/jni")
|
||||
val output = tempFolder.newFolder("output")
|
||||
val task =
|
||||
createTestTask<PrepareBoostTask> { task ->
|
||||
task.boostPath.setFrom(boostpath)
|
||||
task.boostThirdPartyJniPath.set(boostThirdPartyJniPath)
|
||||
task.boostVersion.set("1.0.0")
|
||||
task.outputDir.set(output)
|
||||
createTestTask<PrepareBoostTask> {
|
||||
it.boostPath.setFrom(boostpath)
|
||||
it.boostThirdPartyJniPath.set(boostThirdPartyJniPath)
|
||||
it.boostVersion.set("1.0.0")
|
||||
it.outputDir.set(output)
|
||||
}
|
||||
File(boostpath, "boost_1.0.0/boost/config.hpp").apply {
|
||||
parentFile.mkdirs()
|
||||
@@ -100,11 +99,11 @@ class PrepareBoostTaskTest {
|
||||
val boostThirdPartyJniPath = tempFolder.newFolder("boostpath/jni")
|
||||
val output = tempFolder.newFolder("output")
|
||||
val task =
|
||||
createTestTask<PrepareBoostTask> { task ->
|
||||
task.boostPath.setFrom(boostpath)
|
||||
task.boostThirdPartyJniPath.set(boostThirdPartyJniPath)
|
||||
task.boostVersion.set("1.0.0")
|
||||
task.outputDir.set(output)
|
||||
createTestTask<PrepareBoostTask> {
|
||||
it.boostPath.setFrom(boostpath)
|
||||
it.boostThirdPartyJniPath.set(boostThirdPartyJniPath)
|
||||
it.boostVersion.set("1.0.0")
|
||||
it.outputDir.set(output)
|
||||
}
|
||||
File(boostpath, "boost/boost/config.hpp").apply {
|
||||
parentFile.mkdirs()
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user