mirror of
https://github.com/facebook/react-native.git
synced 2025-11-01 09:14:26 +00:00
Compare commits
92
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
a19fd24132 | ||
|
|
b45a3e5cd8 | ||
|
|
7b7c45030b | ||
|
|
8bd01c7d01 | ||
|
|
f40d69f06d | ||
|
|
722f5ba786 | ||
|
|
94b5d4b53f | ||
|
|
58e163c74e | ||
|
|
847f8902ff | ||
|
|
6e0e72df71 | ||
|
|
83fd1742da | ||
|
|
fc15260f1c | ||
|
|
14540e6abf | ||
|
|
947d9c3897 | ||
|
|
68a17f2651 | ||
|
|
4fa2064905 | ||
|
|
f96f1a6e11 | ||
|
|
0d7379b9fe | ||
|
|
a7a513fc96 | ||
|
|
5635d5c0a3 | ||
|
|
57c291bbc4 | ||
|
|
10e47e69aa | ||
|
|
b006080949 | ||
|
|
9a1dadf799 | ||
|
|
03c7316ab0 | ||
|
|
09740c9001 | ||
|
|
2e7c84ba00 | ||
|
|
5ab6e7ad3f | ||
|
|
f25e35ae4a | ||
|
|
2f784ce9a5 | ||
|
|
5a4962a0c1 | ||
|
|
2aed264695 | ||
|
|
b34e63539d | ||
|
|
e2a776f322 | ||
|
|
36adaf4c0b | ||
|
|
99212cf6f3 | ||
|
|
5d4f9467d9 | ||
|
|
442a368af5 | ||
|
|
ea876054cf | ||
|
|
e9e0d8c2f7 | ||
|
|
a5d9044158 | ||
|
|
1e9ac296a5 | ||
|
|
b54efb8d0d | ||
|
|
a1b05c5b86 | ||
|
|
1d5cdf10fc | ||
|
|
5506441df9 | ||
|
|
62c9ff6264 | ||
|
|
c169250a36 | ||
|
|
1902c3c4d5 | ||
|
|
e96396bd18 | ||
|
|
ecad90ad8b | ||
|
|
eae7d3c6a1 | ||
|
|
9d4c4b2741 | ||
|
|
5f110c416b | ||
|
|
091f8cf506 | ||
|
|
d5c1647a29 | ||
|
|
04279cea78 | ||
|
|
ca5ce205f7 | ||
|
|
fb8a6a5bb0 | ||
|
|
bdb394f754 | ||
|
|
dab8f6097e | ||
|
|
2080f64f03 | ||
|
|
6c87b748f3 | ||
|
|
b30a5f8ab2 | ||
|
|
3033aaaef1 | ||
|
|
07699e5838 | ||
|
|
1dd464d84e | ||
|
|
33ff0c4789 | ||
|
|
289bdb6b1b | ||
|
|
43f07ae9a5 | ||
|
|
87bdfda020 | ||
|
|
c4822419c4 | ||
|
|
a52f5514ed | ||
|
|
bf4c887e1d | ||
|
|
8b8b05cd1e | ||
|
|
eacd793930 | ||
|
|
628002205c | ||
|
|
6554a99c0d | ||
|
|
95160c1e6b | ||
|
|
1def9fdbc9 | ||
|
|
62ea6e891c | ||
|
|
75097b2599 | ||
|
|
b345cecaaa | ||
|
|
b0501a5be2 | ||
|
|
6a683cb268 | ||
|
|
0cea462113 | ||
|
|
b985831702 | ||
|
|
68946780d0 | ||
|
|
1624cdb29e | ||
|
|
f77fced5e2 | ||
|
|
6482204523 | ||
|
|
966f2a2983 |
@@ -3,6 +3,7 @@
|
||||
docs/generatedComponentApiDocs.js
|
||||
packages/react-native/flow/
|
||||
packages/react-native/sdks/
|
||||
packages/react-native/types_generated/
|
||||
packages/react-native/ReactAndroid/build
|
||||
packages/react-native/ReactAndroid/hermes-engine/build/
|
||||
packages/react-native/Libraries/Renderer/*
|
||||
|
||||
@@ -15,9 +15,8 @@ runs:
|
||||
uses: ./.github/actions/setup-node
|
||||
with:
|
||||
node-version: ${{ inputs.node-version }}
|
||||
- name: Yarn install
|
||||
shell: bash
|
||||
run: yarn install --non-interactive --frozen-lockfile
|
||||
- name: Run yarn install
|
||||
uses: ./.github/actions/yarn-install
|
||||
- name: Run linters against modified files (analysis-bot)
|
||||
shell: bash
|
||||
run: yarn lint-ci
|
||||
|
||||
@@ -42,12 +42,12 @@ runs:
|
||||
with:
|
||||
java-version: '17'
|
||||
distribution: 'zulu'
|
||||
- name: Run yarn install
|
||||
uses: ./.github/actions/yarn-install
|
||||
- name: Start Metro in Debug
|
||||
shell: bash
|
||||
if: ${{ inputs.flavor == 'Debug' }}
|
||||
run: |
|
||||
yarn install
|
||||
|
||||
# build codegen or we will see a redbox
|
||||
./packages/react-native-codegen/scripts/oss/build.sh
|
||||
|
||||
|
||||
@@ -4,4 +4,17 @@ runs:
|
||||
steps:
|
||||
- name: Install dependencies
|
||||
shell: bash
|
||||
run: yarn install --non-interactive --frozen-lockfile
|
||||
run: |
|
||||
MAX_ATTEMPTS=2
|
||||
ATTEMPT=0
|
||||
WAIT_TIME=20
|
||||
while [ $ATTEMPT -lt $MAX_ATTEMPTS ]; do
|
||||
yarn install --non-interactive --frozen-lockfile && break
|
||||
echo "yarn install failed. Retrying in $WAIT_TIME seconds..."
|
||||
sleep $WAIT_TIME
|
||||
ATTEMPT=$((ATTEMPT + 1))
|
||||
done
|
||||
if [ $ATTEMPT -eq $MAX_ATTEMPTS ]; then
|
||||
echo "All attempts to invoke yarn install failed - Aborting the workflow"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
@@ -15,12 +15,14 @@ const {
|
||||
const mockRun = jest.fn();
|
||||
const mockSleep = jest.fn();
|
||||
const mockGetNpmPackageInfo = jest.fn();
|
||||
const mockVerifyPublishedPackage = jest.fn();
|
||||
const silence = () => {};
|
||||
|
||||
jest.mock('../utils.js', () => ({
|
||||
log: silence,
|
||||
run: mockRun,
|
||||
sleep: mockSleep,
|
||||
verifyPublishedPackage: mockVerifyPublishedPackage,
|
||||
getNpmPackageInfo: mockGetNpmPackageInfo,
|
||||
}));
|
||||
|
||||
@@ -82,77 +84,43 @@ describe('#verifyPublishedTemplate', () => {
|
||||
|
||||
it("waits on npm updating for version and not 'latest'", async () => {
|
||||
const NOT_LATEST = false;
|
||||
mockGetNpmPackageInfo
|
||||
// template@<version>
|
||||
.mockReturnValueOnce(Promise.reject('mock http/404'))
|
||||
.mockReturnValueOnce(Promise.resolve());
|
||||
mockSleep.mockReturnValueOnce(Promise.resolve()).mockImplementation(() => {
|
||||
throw new Error('Should not be called again!');
|
||||
});
|
||||
|
||||
const version = '0.77.0';
|
||||
|
||||
await verifyPublishedTemplate(version, NOT_LATEST);
|
||||
|
||||
expect(mockGetNpmPackageInfo).toHaveBeenLastCalledWith(
|
||||
expect(mockVerifyPublishedPackage).toHaveBeenCalledWith(
|
||||
'@react-native-community/template',
|
||||
version,
|
||||
null,
|
||||
18,
|
||||
);
|
||||
});
|
||||
|
||||
it('waits on npm updating version and latest tag', async () => {
|
||||
const IS_LATEST = true;
|
||||
const version = '0.77.0';
|
||||
mockGetNpmPackageInfo
|
||||
// template@latest → unknown tag
|
||||
.mockReturnValueOnce(Promise.reject('mock http/404'))
|
||||
// template@latest != version → old tag
|
||||
.mockReturnValueOnce(Promise.resolve({version: '0.76.5'}))
|
||||
// template@latest == version → correct tag
|
||||
.mockReturnValueOnce(Promise.resolve({version}));
|
||||
mockSleep
|
||||
.mockReturnValueOnce(Promise.resolve())
|
||||
.mockReturnValueOnce(Promise.resolve())
|
||||
.mockImplementation(() => {
|
||||
throw new Error('Should not be called again!');
|
||||
});
|
||||
|
||||
await verifyPublishedTemplate(version, IS_LATEST);
|
||||
|
||||
expect(mockGetNpmPackageInfo).toHaveBeenCalledWith(
|
||||
expect(mockVerifyPublishedPackage).toHaveBeenCalledWith(
|
||||
'@react-native-community/template',
|
||||
version,
|
||||
'latest',
|
||||
18,
|
||||
);
|
||||
});
|
||||
|
||||
describe('timeouts', () => {
|
||||
let mockProcess;
|
||||
beforeEach(() => {
|
||||
mockProcess = jest.spyOn(process, 'exit').mockImplementation(code => {
|
||||
throw new Error(`process.exit(${code}) called!`);
|
||||
});
|
||||
});
|
||||
afterEach(() => mockProcess.mockRestore());
|
||||
describe('retries', () => {
|
||||
it('will timeout if npm does not update package version after a set number of retries', async () => {
|
||||
const RETRIES = 2;
|
||||
mockGetNpmPackageInfo.mockReturnValue(Promise.reject('mock http/404'));
|
||||
mockSleep.mockReturnValue(Promise.resolve());
|
||||
await expect(() =>
|
||||
verifyPublishedTemplate('0.77.0', true, RETRIES),
|
||||
).rejects.toThrowError('process.exit(1) called!');
|
||||
expect(mockGetNpmPackageInfo).toHaveBeenCalledTimes(RETRIES);
|
||||
});
|
||||
|
||||
it('will timeout if npm does not update latest tag after a set number of retries', async () => {
|
||||
const RETRIES = 7;
|
||||
const IS_LATEST = true;
|
||||
mockGetNpmPackageInfo.mockReturnValue(
|
||||
Promise.resolve({version: '0.76.5'}),
|
||||
);
|
||||
mockSleep.mockReturnValue(Promise.resolve());
|
||||
await expect(async () => {
|
||||
await verifyPublishedTemplate('0.77.0', IS_LATEST, RETRIES);
|
||||
}).rejects.toThrowError('process.exit(1) called!');
|
||||
expect(mockGetNpmPackageInfo).toHaveBeenCalledTimes(RETRIES);
|
||||
await verifyPublishedTemplate('0.77.0', true, RETRIES),
|
||||
expect(mockVerifyPublishedPackage).toHaveBeenCalledWith(
|
||||
'@react-native-community/template',
|
||||
'0.77.0',
|
||||
'latest',
|
||||
2,
|
||||
);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
@@ -0,0 +1,135 @@
|
||||
/**
|
||||
* 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 {verifyPublishedPackage} = require('../verifyPublishedPackage');
|
||||
|
||||
const mockRun = jest.fn();
|
||||
const mockSleep = jest.fn();
|
||||
const mockGetNpmPackageInfo = jest.fn();
|
||||
const silence = () => {};
|
||||
|
||||
const REACT_NATIVE_PACKAGE = 'react-native';
|
||||
|
||||
jest.mock('../utils.js', () => ({
|
||||
log: silence,
|
||||
run: mockRun,
|
||||
sleep: mockSleep,
|
||||
getNpmPackageInfo: mockGetNpmPackageInfo,
|
||||
}));
|
||||
|
||||
describe('#verifyPublishedPackage', () => {
|
||||
beforeEach(jest.clearAllMocks);
|
||||
|
||||
it("waits on npm updating for version and not 'latest'", async () => {
|
||||
mockGetNpmPackageInfo
|
||||
// template@<version>
|
||||
.mockReturnValueOnce(Promise.reject('mock http/404'))
|
||||
.mockReturnValueOnce(Promise.resolve());
|
||||
mockSleep.mockReturnValueOnce(Promise.resolve()).mockImplementation(() => {
|
||||
throw new Error('Should not be called again!');
|
||||
});
|
||||
|
||||
const version = '0.78.0';
|
||||
await verifyPublishedPackage(REACT_NATIVE_PACKAGE, version, null);
|
||||
|
||||
expect(mockGetNpmPackageInfo).toHaveBeenLastCalledWith(
|
||||
REACT_NATIVE_PACKAGE,
|
||||
version,
|
||||
);
|
||||
});
|
||||
|
||||
it('waits on npm updating version and latest tag', async () => {
|
||||
const version = '0.78.0';
|
||||
mockGetNpmPackageInfo
|
||||
// template@latest → unknown tag
|
||||
.mockReturnValueOnce(Promise.reject('mock http/404'))
|
||||
// template@latest != version → old tag
|
||||
.mockReturnValueOnce(Promise.resolve({version: '0.76.5'}))
|
||||
// template@latest == version → correct tag
|
||||
.mockReturnValueOnce(Promise.resolve({version}));
|
||||
mockSleep
|
||||
.mockReturnValueOnce(Promise.resolve())
|
||||
.mockReturnValueOnce(Promise.resolve())
|
||||
.mockImplementation(() => {
|
||||
throw new Error('Should not be called again!');
|
||||
});
|
||||
|
||||
await verifyPublishedPackage(REACT_NATIVE_PACKAGE, version, 'latest');
|
||||
|
||||
expect(mockGetNpmPackageInfo).toHaveBeenCalledWith(
|
||||
REACT_NATIVE_PACKAGE,
|
||||
'latest',
|
||||
);
|
||||
});
|
||||
|
||||
it('waits on npm updating version and next tag', async () => {
|
||||
const version = '0.78.0-rc.0';
|
||||
mockGetNpmPackageInfo
|
||||
// template@latest → unknown tag
|
||||
.mockReturnValueOnce(Promise.reject('mock http/404'))
|
||||
// template@latest != version → old tag
|
||||
.mockReturnValueOnce(Promise.resolve({version: '0.76.5'}))
|
||||
// template@latest == version → correct tag
|
||||
.mockReturnValueOnce(Promise.resolve({version}));
|
||||
mockSleep
|
||||
.mockReturnValueOnce(Promise.resolve())
|
||||
.mockReturnValueOnce(Promise.resolve())
|
||||
.mockImplementation(() => {
|
||||
throw new Error('Should not be called again!');
|
||||
});
|
||||
await verifyPublishedPackage(REACT_NATIVE_PACKAGE, version, 'next');
|
||||
|
||||
expect(mockGetNpmPackageInfo).toHaveBeenCalledWith(
|
||||
REACT_NATIVE_PACKAGE,
|
||||
'next',
|
||||
);
|
||||
});
|
||||
|
||||
describe('timeouts', () => {
|
||||
let mockProcess;
|
||||
beforeEach(() => {
|
||||
mockProcess = jest.spyOn(process, 'exit').mockImplementation(code => {
|
||||
throw new Error(`process.exit(${code}) called!`);
|
||||
});
|
||||
});
|
||||
afterEach(() => mockProcess.mockRestore());
|
||||
it('will timeout if npm does not update package version after a set number of retries', async () => {
|
||||
const RETRIES = 2;
|
||||
mockGetNpmPackageInfo.mockReturnValue(Promise.reject('mock http/404'));
|
||||
mockSleep.mockReturnValue(Promise.resolve());
|
||||
await expect(() =>
|
||||
verifyPublishedPackage(
|
||||
REACT_NATIVE_PACKAGE,
|
||||
'0.77.0',
|
||||
'latest',
|
||||
RETRIES,
|
||||
),
|
||||
).rejects.toThrowError('process.exit(1) called!');
|
||||
expect(mockGetNpmPackageInfo).toHaveBeenCalledTimes(RETRIES);
|
||||
});
|
||||
|
||||
it('will timeout if npm does not update latest tag after a set number of retries', async () => {
|
||||
const RETRIES = 7;
|
||||
const IS_LATEST = true;
|
||||
mockGetNpmPackageInfo.mockReturnValue(
|
||||
Promise.resolve({version: '0.76.5'}),
|
||||
);
|
||||
mockSleep.mockReturnValue(Promise.resolve());
|
||||
await expect(async () => {
|
||||
await verifyPublishedPackage(
|
||||
REACT_NATIVE_PACKAGE,
|
||||
'0.77.0',
|
||||
'latest',
|
||||
RETRIES,
|
||||
);
|
||||
}).rejects.toThrowError('process.exit(1) called!');
|
||||
expect(mockGetNpmPackageInfo).toHaveBeenCalledTimes(RETRIES);
|
||||
});
|
||||
});
|
||||
});
|
||||
@@ -0,0 +1,104 @@
|
||||
/**
|
||||
* 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 {verifyReleaseOnNpm} = require('../verifyReleaseOnNpm');
|
||||
|
||||
const mockVerifyPublishedPackage = jest.fn();
|
||||
const silence = () => {};
|
||||
|
||||
jest.mock('../utils.js', () => ({
|
||||
verifyPublishedPackage: mockVerifyPublishedPackage,
|
||||
}));
|
||||
|
||||
describe('#verifyReleaseOnNPM', () => {
|
||||
beforeEach(jest.clearAllMocks);
|
||||
|
||||
it("waits on npm updating for version and not 'latest'", async () => {
|
||||
const NOT_LATEST = false;
|
||||
const version = '0.78.0';
|
||||
await verifyReleaseOnNpm(version, NOT_LATEST);
|
||||
|
||||
expect(mockVerifyPublishedPackage).toHaveBeenLastCalledWith(
|
||||
'react-native',
|
||||
version,
|
||||
null,
|
||||
18,
|
||||
);
|
||||
});
|
||||
|
||||
it('waits on npm updating version and latest tag', async () => {
|
||||
const IS_LATEST = true;
|
||||
const version = '0.78.0';
|
||||
|
||||
await verifyReleaseOnNpm(version, IS_LATEST);
|
||||
|
||||
expect(mockVerifyPublishedPackage).toHaveBeenCalledWith(
|
||||
'react-native',
|
||||
version,
|
||||
'latest',
|
||||
18,
|
||||
);
|
||||
});
|
||||
|
||||
it('waits on npm updating version, not latest and next tag', async () => {
|
||||
const IS_LATEST = false;
|
||||
const version = '0.78.0-rc.0';
|
||||
|
||||
await verifyReleaseOnNpm(version, IS_LATEST);
|
||||
|
||||
expect(mockVerifyPublishedPackage).toHaveBeenCalledWith(
|
||||
'react-native',
|
||||
version,
|
||||
'next',
|
||||
18,
|
||||
);
|
||||
});
|
||||
|
||||
it('waits on npm updating version, latest and next tag', async () => {
|
||||
const IS_LATEST = true;
|
||||
const version = '0.78.0-rc.0';
|
||||
|
||||
await verifyReleaseOnNpm(version, IS_LATEST);
|
||||
|
||||
expect(mockVerifyPublishedPackage).toHaveBeenCalledWith(
|
||||
'react-native',
|
||||
version,
|
||||
'next',
|
||||
18,
|
||||
);
|
||||
});
|
||||
|
||||
describe('timeouts', () => {
|
||||
it('will timeout if npm does not update package version after a set number of retries', async () => {
|
||||
const RETRIES = 2;
|
||||
|
||||
await verifyReleaseOnNpm('0.77.0', true, RETRIES),
|
||||
expect(mockVerifyPublishedPackage).toHaveBeenCalledWith(
|
||||
'react-native',
|
||||
'0.77.0',
|
||||
'latest',
|
||||
2,
|
||||
);
|
||||
});
|
||||
|
||||
it('will timeout if npm does not update latest tag after a set number of retries', async () => {
|
||||
const RETRIES = 7;
|
||||
const IS_LATEST = true;
|
||||
|
||||
await verifyReleaseOnNpm('0.77.0', IS_LATEST, RETRIES);
|
||||
|
||||
expect(mockVerifyPublishedPackage).toHaveBeenCalledWith(
|
||||
'react-native',
|
||||
'0.77.0',
|
||||
'latest',
|
||||
7,
|
||||
);
|
||||
});
|
||||
});
|
||||
});
|
||||
@@ -4,8 +4,8 @@
|
||||
# This source code is licensed under the MIT license found in the
|
||||
# LICENSE file in the root directory of this source tree.
|
||||
|
||||
GITHUB_OWNER=${CIRCLE_PROJECT_USERNAME:-facebook}
|
||||
GITHUB_REPO=${CIRCLE_PROJECT_REPONAME:-react-native}
|
||||
GITHUB_OWNER=-facebook
|
||||
GITHUB_REPO=-react-native
|
||||
export GITHUB_OWNER
|
||||
export GITHUB_REPO
|
||||
|
||||
|
||||
@@ -4,34 +4,20 @@
|
||||
# This source code is licensed under the MIT license found in the
|
||||
# LICENSE file in the root directory of this source tree.
|
||||
|
||||
GITHUB_OWNER=${CIRCLE_PROJECT_USERNAME:-facebook}
|
||||
GITHUB_REPO=${CIRCLE_PROJECT_REPONAME:-react-native}
|
||||
GITHUB_OWNER=-facebook
|
||||
GITHUB_REPO=-react-native
|
||||
export GITHUB_OWNER
|
||||
export GITHUB_REPO
|
||||
|
||||
if [ -x "$(command -v shellcheck)" ]; then
|
||||
IFS=$'\n'
|
||||
|
||||
if [ -n "$CIRCLE_CI" ]; then
|
||||
results=( "$(find . -type f -not -path "*node_modules*" -not -path "*third-party*" -name '*.sh' -exec sh -c 'shellcheck "$1" -f json' -- {} \;)" )
|
||||
|
||||
cat <(echo shellcheck; printf '%s\n' "${results[@]}" | jq .,[] | jq -s . | jq --compact-output --raw-output '[ (.[] | .[] | . ) ]') | GITHUB_PR_NUMBER="$GITHUB_PR_NUMBER" node packages/react-native-bots/code-analysis-bot.js
|
||||
# check status
|
||||
STATUS=$?
|
||||
if [ $STATUS == 0 ]; then
|
||||
echo "Shell scripts analyzed successfully."
|
||||
else
|
||||
echo "Shell script analysis failed, error status $STATUS."
|
||||
fi
|
||||
|
||||
else
|
||||
find . \
|
||||
-type f \
|
||||
-not -path "*node_modules*" \
|
||||
-not -path "*third-party*" \
|
||||
-name '*.sh' \
|
||||
find . \
|
||||
-type f \
|
||||
-not -path "*node_modules*" \
|
||||
-not -path "*third-party*" \
|
||||
-name '*.sh' \
|
||||
-exec sh -c 'shellcheck "$1"' -- {} \;
|
||||
fi
|
||||
|
||||
else
|
||||
echo 'shellcheck is not installed. See https://github.com/facebook/react-native/wiki/Development-Dependencies#shellcheck for instructions.'
|
||||
|
||||
@@ -7,7 +7,7 @@
|
||||
* @format
|
||||
*/
|
||||
|
||||
const {run, sleep, getNpmPackageInfo, log} = require('./utils.js');
|
||||
const {run, sleep, log, verifyPublishedPackage} = require('./utils.js');
|
||||
|
||||
const TAG_AS_LATEST_REGEX = /#publish-packages-to-npm&latest/;
|
||||
|
||||
@@ -53,8 +53,7 @@ module.exports.publishTemplate = async (github, version, dryRun = true) => {
|
||||
});
|
||||
};
|
||||
|
||||
const SLEEP_S = 10;
|
||||
const MAX_RETRIES = 3 * 6; // 3 minutes
|
||||
const MAX_RETRIES = 3 * 6; // 18 attempts. Waiting between attempt: 10 s. Total time: 3 mins.
|
||||
const TEMPLATE_NPM_PKG = '@react-native-community/template';
|
||||
|
||||
/**
|
||||
@@ -68,36 +67,15 @@ module.exports.verifyPublishedTemplate = async (
|
||||
latest = false,
|
||||
retries = MAX_RETRIES,
|
||||
) => {
|
||||
log(`🔍 Is ${TEMPLATE_NPM_PKG}@${version} on npm?`);
|
||||
|
||||
let count = retries;
|
||||
while (count-- > 0) {
|
||||
try {
|
||||
const json = await getNpmPackageInfo(
|
||||
TEMPLATE_NPM_PKG,
|
||||
latest ? 'latest' : version,
|
||||
);
|
||||
log(`🎉 Found ${TEMPLATE_NPM_PKG}@${version} on npm`);
|
||||
if (!latest) {
|
||||
return;
|
||||
}
|
||||
if (json.version === version) {
|
||||
log(`🎉 ${TEMPLATE_NPM_PKG}@latest → ${version} on npm`);
|
||||
return;
|
||||
}
|
||||
log(
|
||||
`🐌 ${TEMPLATE_NPM_PKG}@latest → ${pkg.version} on npm and not ${version} as expected, retrying...`,
|
||||
);
|
||||
} catch (e) {
|
||||
log(`Nope, fetch failed: ${e.message}`);
|
||||
}
|
||||
await sleep(SLEEP_S);
|
||||
try {
|
||||
await verifyPublishedPackage(
|
||||
TEMPLATE_NPM_PKG,
|
||||
version,
|
||||
latest ? 'latest' : null,
|
||||
retries,
|
||||
);
|
||||
} catch (e) {
|
||||
console.error(e.message);
|
||||
process.exit(1);
|
||||
}
|
||||
|
||||
let msg = `🚨 Timed out when trying to verify ${TEMPLATE_NPM_PKG}@${version} on npm`;
|
||||
if (latest) {
|
||||
msg += ' and latest tag points to this version.';
|
||||
}
|
||||
log(msg);
|
||||
process.exit(1);
|
||||
};
|
||||
|
||||
@@ -12,18 +12,22 @@ const {execSync} = require('child_process');
|
||||
function run(cmd) {
|
||||
return execSync(cmd, 'utf8').toString().trim();
|
||||
}
|
||||
module.exports.run = run;
|
||||
|
||||
async function sleep(seconds) {
|
||||
return new Promise(resolve => setTimeout(resolve, seconds * 1000));
|
||||
}
|
||||
module.exports.sleep = sleep;
|
||||
|
||||
async function getNpmPackageInfo(pkg, versionOrTag) {
|
||||
return fetch(`https://registry.npmjs.org/${pkg}/${versionOrTag}`).then(resp =>
|
||||
resp.json(),
|
||||
);
|
||||
}
|
||||
module.exports.getNpmPackageInfo = getNpmPackageInfo;
|
||||
|
||||
module.exports.log = (...args) => console.log(...args);
|
||||
const log = (...args) => console.log(...args);
|
||||
|
||||
module.exports = {
|
||||
log,
|
||||
getNpmPackageInfo,
|
||||
sleep,
|
||||
run,
|
||||
};
|
||||
|
||||
@@ -0,0 +1,63 @@
|
||||
/**
|
||||
* 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 {log, getNpmPackageInfo, sleep} = require('./utils');
|
||||
|
||||
const SLEEP_S = 10;
|
||||
const MAX_RETRIES = 3 * 6; // 18 attempts. Waiting between attempt: 10 s. Total time: 3 mins.
|
||||
|
||||
async function verifyPublishedPackage(
|
||||
packageName,
|
||||
version,
|
||||
tag = null,
|
||||
retries = MAX_RETRIES,
|
||||
) {
|
||||
log(`🔍 Is ${packageName}@${version} on npm?`);
|
||||
|
||||
let count = retries;
|
||||
while (count-- > 0) {
|
||||
try {
|
||||
const json = await getNpmPackageInfo(packageName, tag ? tag : version);
|
||||
log(`🎉 Found ${packageName}@${version} on npm`);
|
||||
if (!tag) {
|
||||
return;
|
||||
}
|
||||
|
||||
// check for next tag
|
||||
if (tag === 'next' && json.version === version) {
|
||||
log(`🎉 ${packageName}@next → ${version} on npm`);
|
||||
return;
|
||||
}
|
||||
|
||||
// Check for latest tag
|
||||
if (tag === 'latest' && json.version === version) {
|
||||
log(`🎉 ${packageName}@latest → ${version} on npm`);
|
||||
return;
|
||||
}
|
||||
|
||||
log(
|
||||
`🐌 ${packageName}@${tag} → ${pkg.version} on npm and not ${version} as expected, retrying...`,
|
||||
);
|
||||
} catch (e) {
|
||||
log(`Nope, fetch failed: ${e.message}`);
|
||||
}
|
||||
await sleep(SLEEP_S);
|
||||
}
|
||||
|
||||
let msg = `🚨 Timed out when trying to verify ${packageName}@${version} on npm`;
|
||||
if (tag) {
|
||||
msg += ` and ${tag} tag points to this version.`;
|
||||
}
|
||||
log(msg);
|
||||
process.exit(1);
|
||||
}
|
||||
|
||||
module.exports = {
|
||||
verifyPublishedPackage,
|
||||
};
|
||||
@@ -0,0 +1,26 @@
|
||||
/**
|
||||
* 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 {run, sleep, log, verifyPublishedPackage} = require('./utils.js');
|
||||
const REACT_NATIVE_NPM_PKG = 'react-native';
|
||||
const MAX_RETRIES = 3 * 6; // 18 attempts. Waiting between attempt: 10 s. Total time: 3 mins.
|
||||
/**
|
||||
* Will verify that @latest, @next and the @<version> have been published.
|
||||
*
|
||||
* NOTE: This will infinitely query each step until successful, make sure the
|
||||
* calling job has a timeout.
|
||||
*/
|
||||
module.exports.verifyReleaseOnNpm = async (
|
||||
version,
|
||||
latest = false,
|
||||
retries = MAX_RETRIES,
|
||||
) => {
|
||||
const tag = version.includes('-rc.') ? 'next' : latest ? 'latest' : null;
|
||||
await verifyPublishedPackage(REACT_NATIVE_NPM_PKG, version, tag, retries);
|
||||
};
|
||||
@@ -18,9 +18,8 @@ jobs:
|
||||
if: github.repository == 'facebook/react-native'
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- name: Run Yarn Install on Root
|
||||
run: yarn install
|
||||
working-directory: .
|
||||
- name: Run yarn install
|
||||
uses: ./.github/actions/yarn-install
|
||||
- name: Danger
|
||||
run: yarn danger ci --use-github-checks --failOnErrors
|
||||
working-directory: packages/react-native-bots
|
||||
|
||||
@@ -0,0 +1,27 @@
|
||||
name: Monitor React Native New Issues
|
||||
|
||||
on:
|
||||
schedule:
|
||||
- cron: "0 */6 * * *"
|
||||
workflow_dispatch:
|
||||
|
||||
jobs:
|
||||
monitor-issues:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Set up Node.js
|
||||
uses: actions/setup-node@v4
|
||||
with:
|
||||
node-version: '20'
|
||||
- name: Monitor New Issues
|
||||
uses: react-native-community/repo-monitor@v1.0.0
|
||||
with:
|
||||
task: "monitor-issues"
|
||||
git_secret: ${{ secrets.GITHUB_TOKEN }}
|
||||
notifier: "discord"
|
||||
fetch_data_interval: 6
|
||||
repo_owner: "facebook"
|
||||
repo_name: "react-native"
|
||||
discord_webhook_url: "${{ secrets.DISCORD_WEBHOOK_URL }}"
|
||||
discord_id_type: "role"
|
||||
discord_ids: "1295340673779630141"
|
||||
@@ -214,3 +214,13 @@ jobs:
|
||||
-H "Accept: application/vnd.github.v3+json" \
|
||||
-H "Authorization: Bearer $REACT_NATIVE_BOT_GITHUB_TOKEN" \
|
||||
-d "{\"event_type\": \"publish\", \"client_payload\": { \"version\": \"${{ github.ref_name }}\" }}"
|
||||
- name: Verify Release is on NPM
|
||||
timeout-minutes: 3
|
||||
uses: actions/github-script@v6
|
||||
with:
|
||||
github-token: ${{ secrets.REACT_NATIVE_BOT_GITHUB_TOKEN }}
|
||||
script: |
|
||||
const {verifyReleaseOnNpm} = require('./.github/workflow-scripts/verifyReleaseOnNpm.js');
|
||||
const {isLatest()} = require('./.github/workflow-scripts/publishTemplate.js');
|
||||
const version = "${{ github.ref_name }}";
|
||||
await verifyReleaseOnNpm(version, isLatest());
|
||||
|
||||
@@ -7,5 +7,6 @@
|
||||
|
||||
packages/*/dist
|
||||
vendor
|
||||
packages/react-native/types_generated/
|
||||
|
||||
packages/react-native-codegen/e2e/__test_fixtures__/modules/NativeEnumTurboModule.js
|
||||
|
||||
+61
-1
@@ -49,7 +49,7 @@
|
||||
#### Android specific
|
||||
|
||||
- Changed visibility of FrescoBasedReactTextInlineImageViewManager to internal ([d5f33c19cb](https://github.com/facebook/react-native/commit/d5f33c19cb33e2f2c7d2470cc90872c1f065f20d) by [@alanleedev](https://github.com/alanleedev))
|
||||
- Mikgrating pointerEvents API breaks compatibility for kotlin usages of this api as a val ([45e4a3afce](https://github.com/facebook/react-native/commit/45e4a3afceb4be3047cd01a60ec2c9f806ed30fe) by [@mdvacca](https://github.com/mdvacca))
|
||||
- Migrating pointerEvents API breaks compatibility for kotlin usages of this api as a val ([45e4a3afce](https://github.com/facebook/react-native/commit/45e4a3afceb4be3047cd01a60ec2c9f806ed30fe) by [@mdvacca](https://github.com/mdvacca))
|
||||
- Convert RootView to Kotlin ([21c9491926](https://github.com/facebook/react-native/commit/21c94919260a68409f82081740169d0409e78933) by [@fabriziocucci](https://github.com/fabriziocucci))
|
||||
- Delete unused abstract class GuardedResultAsyncTask ([67bff8734f](https://github.com/facebook/react-native/commit/67bff8734f4b92fe399910eecad5b67511a749c1) by [@mdvacca](https://github.com/mdvacca))
|
||||
- Delete deprecated class FabricViewStateManager ([b25b65ba19](https://github.com/facebook/react-native/commit/b25b65ba19f3c674fd2efe5c01123ccc0ae55cbf) by [@mdvacca](https://github.com/mdvacca))
|
||||
@@ -547,6 +547,32 @@ github.com/robhogan))
|
||||
- **TextInput:** Workaround for Mac Catalyst TextInput crash due to serialization attempt of WeakEventEmitter ([e04738b7ec](https://github.com/facebook/react-native/commit/e04738b7ecec9e7da3aab49bb24a6336b9496b94) by [@rozele](https://github.com/rozele))
|
||||
- **TextInput:** Fix `maxLength` not working in old arch ([4b3ef3b00c](https://github.com/facebook/react-native/commit/4b3ef3b00ce0026c0d1e1f2a5546fcec249255d8) by [@mateoguzmana](https://github.com/mateoguzmana))
|
||||
|
||||
## v0.76.7
|
||||
|
||||
### Changed
|
||||
|
||||
#### iOS specific
|
||||
|
||||
- **Deps:** Pin 'concurrent-ruby' to a working version ([198adb47af](https://github.com/facebook/react-native/commit/198adb47af3676c85b35adb308c110c1d87120c8) by [@cipolleschi](https://github.com/cipolleschi))
|
||||
|
||||
### Fixed
|
||||
|
||||
- **Text** Fix `maxFontSizeMultiplier` prop on `Text` and `TextInput` components in Fabric / New Architecture ([ea49d4d1b01107a5ecbbbd4904f1d935e51d6b32](https://github.com/facebook/react-native/commit/ea49d4d1b01107a5ecbbbd4904f1d935e51d6b32) by [@RickardZrinski](https://github.com/RickardZrinski))
|
||||
- **Appearance:** Fix `Appearance.setColorScheme(null)` not resetting color scheme value ([7d63235086](https://github.com/facebook/react-native/commit/7d63235086352d8c424d634c7039551f0a5025dc) by [@sangonz193](https://github.com/sangonz193))
|
||||
|
||||
#### Android specific
|
||||
|
||||
- **Deps:** Add missing `invariant` dependency ([ee8088b615](https://github.com/facebook/react-native/commit/ee8088b6157837c239db47ac5bd3a8603ceefc3c) by [@tido64](https://github.com/tido64))
|
||||
- **Turbomodule** Fix execution of early InteropEvents ([4ed2b35bf6](https://github.com/facebook/react-native/commit/4ed2b35bf61426c81c9f8b30a142d77b44988fdb) by [@mdvacca](https://github.com/mdvacca))
|
||||
- **Deps:** Bump Kotlin to 1.9.25 to mitigate #49115 ([f8857ba3b5](https://github.com/facebook/react-native/commit/f8857ba3b51f26871d0a0b82b9581a0c35b6273d) by [@cortinico](https://github.com/cortinico))
|
||||
|
||||
#### iOS specific
|
||||
|
||||
- **runtime:** `RCTSurfaceHostingProxyRootView` no longer has different behavior (whether it calls `start` on the provided *surface*) depending on which initializer is used. Call `start` yourself on the *surface* instead. ([13b93cfdda](https://github.com/facebook/react-native/commit/13b93cfddaa559697968ac1c19e55f7aaa053070) by Nolan O'Brien)
|
||||
- Be less strict with method parsing of TurboModule Interop Layer
|
||||
- **Turbomodule:** Avoid crashing the app when the InteropLayer can't find some methods in the native implementation. ([3bd3f101b9](https://github.com/facebook/react-native/commit/3bd3f101b9dcff8551a2f8259ddeed9843fd69b8) by [@cipolleschi](https://github.com/cipolleschi))
|
||||
- **Runtime:** Fix applicationDidEnterBackground not being called ([adaceba546](https://github.com/facebook/react-native/commit/adaceba5462b4ad8676745f34e0be2bf5bb25166) by [@alextoudic](https://github.com/alextoudic))
|
||||
|
||||
## v0.76.6
|
||||
|
||||
### Fixed
|
||||
@@ -952,6 +978,40 @@ created on the mqt_native thread. ([c4a6bbc8fd](https://github.com/facebook/reac
|
||||
- **infra:** Update ws from 7.5.1 to 7.5.10 (CVE-2024-37890) ([13f1b9e10f](https://github.com/facebook/react-native/commit/13f1b9e10f6045421808714f7e62aa17bfb3e891) by [@GijsWeterings](https://github.com/GijsWeterings))
|
||||
- **infra:** Update ws from 6.2.2 to 6.2.3 (CVE-2024-37890) ([80cfacef78](https://github.com/facebook/react-native/commit/80cfacef78f34d3786d955084a8bf4d42ea37f1b) by [@GijsWeterings](https://github.com/GijsWeterings))
|
||||
|
||||
## v0.75.5
|
||||
|
||||
### Added
|
||||
|
||||
- **Hermes:** Implement more missing methods on WithRuntimeDecorator ([80f67ca03c](https://github.com/facebook/react-native/commit/80f67ca03c99c688e2a3127e9b3dddd02625848e) by [@neildhar](https://github.com/neildhar))
|
||||
|
||||
|
||||
### Changed
|
||||
|
||||
#### Android specific
|
||||
|
||||
- **Deps:** Bump Kotlin to 1.9.25 to mitigate [#49115](https://github.com/facebook/react-native/issues/49115) ([25e76a2717](https://github.com/facebook/react-native/commit/25e76a271781b3ffe8002108d8b12aa3d47442b5) by [@riteshshukla04](https://github.com/riteshshukla04))
|
||||
|
||||
#### iOS specific
|
||||
|
||||
- **Deps:** Pin Xcodeproj to < 1.26.0 ([2922af2e7e](https://github.com/facebook/react-native/commit/2922af2e7e8527a93c7956b10ddb314f25c334fa) by [@cipolleschi](https://github.com/cipolleschi))
|
||||
- **Deps:** Pin concurrent-ruby to <= 1.3.4 ([794bf34e60](https://github.com/facebook/react-native/commit/794bf34e60cea8146aebad1fefe051d4140fc28b) by [@cipolleschi](https://github.com/cipolleschi))
|
||||
|
||||
### Fixed
|
||||
|
||||
- **FormData:** fix: FormData filename in content-disposition ([78ef1e2bc2](https://github.com/facebook/react-native/commit/78ef1e2bc2ed30321745e2505713915b9015d920) by [@foyarash](https://github.com/@foyarash))
|
||||
|
||||
|
||||
#### Android specific
|
||||
|
||||
- **TextInput:** Set TextInput selection correctly when attached to window in Android ([1656394bae](https://github.com/facebook/react-native/commit/1656394bae16cc54fb38687d38bcbf85138c98a2) by [@QichenZhu](https://github.com/QichenZhu))
|
||||
|
||||
#### iOS specific
|
||||
|
||||
- **Animation:** Fabric: Fixes animations strict weak ordering sorted check failed ([ea0bc54115](https://github.com/facebook/react-native/commit/ea0bc541155700e0973d960c94d01918d6b28c6b) by [@zhongwuzw](https://github.com/zhongwuzw))
|
||||
- **Hermes** Exclude dSYM from the archive ([fdb2631b5e](https://github.com/facebook/react-native/commit/fdb2631b5ea27765663046b94f84956d30ebaaeb) by [@cipolleschi](https://github.com/cipolleschi))
|
||||
- **Image** Fix images not displayed when extension is implicit ([b6ed0d351e](https://github.com/facebook/react-native/commit/b6ed0d351e246c431bdc88a6c3d154ba35220c25) by [@cipolleschi](https://github.com/cipolleschi))
|
||||
- **Xcode:** Fix the generation of .xcode.env.local ([dbffbf72d7](https://github.com/facebook/react-native/commit/dbffbf72d7287e021e965b6639e455e8555bbf2e) by [@cipolleschi](https://github.com/cipolleschi))
|
||||
|
||||
## v0.75.4
|
||||
|
||||
### Fixed
|
||||
|
||||
Vendored
+5
-1
@@ -12,5 +12,9 @@
|
||||
// https://www.npmjs.com/package/debug
|
||||
|
||||
declare module 'debug' {
|
||||
declare module.exports: (namespace: string) => (...Array<mixed>) => void;
|
||||
declare module.exports: {
|
||||
(namespace: string): (...Array<mixed>) => void,
|
||||
enable(match: string): void,
|
||||
disable(): void,
|
||||
};
|
||||
}
|
||||
|
||||
Vendored
+43
-42
@@ -1,29 +1,28 @@
|
||||
// flow-typed signature: e556c06e721548417501c08b01fec911
|
||||
// flow-typed version: ad3adf2de8/react-dom_v17.x.x/flow_>=v0.127.x
|
||||
|
||||
declare module 'react-dom' {
|
||||
import type {Component} from 'react';
|
||||
|
||||
declare var version: string;
|
||||
|
||||
declare function findDOMNode(
|
||||
componentOrElement: Element | ?React$Component<any, any>
|
||||
componentOrElement: Element | ?Component<any, any>
|
||||
): null | Element | Text;
|
||||
|
||||
declare function render<ElementType: React$ElementType>(
|
||||
element: React$Element<ElementType>,
|
||||
declare function render<ElementType: React.ElementType>(
|
||||
element: ExactReactElement_DEPRECATED<ElementType>,
|
||||
container: Element,
|
||||
callback?: () => void
|
||||
): React$ElementRef<ElementType>;
|
||||
): React.ElementRef<ElementType>;
|
||||
|
||||
declare function hydrate<ElementType: React$ElementType>(
|
||||
element: React$Element<ElementType>,
|
||||
declare function hydrate<ElementType: React.ElementType>(
|
||||
element: ExactReactElement_DEPRECATED<ElementType>,
|
||||
container: Element,
|
||||
callback?: () => void
|
||||
): React$ElementRef<ElementType>;
|
||||
): React.ElementRef<ElementType>;
|
||||
|
||||
declare function createPortal(
|
||||
node: React$Node,
|
||||
node: React.Node,
|
||||
container: Element
|
||||
): React$Portal;
|
||||
): React.Portal;
|
||||
|
||||
declare function unmountComponentAtNode(container: any): boolean;
|
||||
|
||||
@@ -37,30 +36,32 @@ declare module 'react-dom' {
|
||||
): void;
|
||||
|
||||
declare function unstable_renderSubtreeIntoContainer<
|
||||
ElementType: React$ElementType
|
||||
ElementType: React.ElementType
|
||||
>(
|
||||
parentComponent: React$Component<any, any>,
|
||||
nextElement: React$Element<ElementType>,
|
||||
parentComponent: Component<any, any>,
|
||||
nextElement: ExactReactElement_DEPRECATED<ElementType>,
|
||||
container: any,
|
||||
callback?: () => void
|
||||
): React$ElementRef<ElementType>;
|
||||
): React.ElementRef<ElementType>;
|
||||
}
|
||||
|
||||
declare module 'react-dom/server' {
|
||||
declare var version: string;
|
||||
|
||||
declare function renderToString(element: React$Node): string;
|
||||
declare function renderToString(element: React.Node): string;
|
||||
|
||||
declare function renderToStaticMarkup(element: React$Node): string;
|
||||
declare function renderToStaticMarkup(element: React.Node): string;
|
||||
|
||||
declare function renderToNodeStream(element: React$Node): stream$Readable;
|
||||
declare function renderToNodeStream(element: React.Node): stream$Readable;
|
||||
|
||||
declare function renderToStaticNodeStream(
|
||||
element: React$Node
|
||||
element: React.Node
|
||||
): stream$Readable;
|
||||
}
|
||||
|
||||
declare module 'react-dom/test-utils' {
|
||||
import type {Component} from 'react';
|
||||
|
||||
declare interface Thenable {
|
||||
then(resolve: () => mixed, reject?: () => mixed): mixed,
|
||||
}
|
||||
@@ -74,66 +75,66 @@ declare module 'react-dom/test-utils' {
|
||||
};
|
||||
|
||||
declare function renderIntoDocument(
|
||||
instance: React$Element<any>
|
||||
): React$Component<any, any>;
|
||||
instance: React.MixedElement
|
||||
): Component<any, any>;
|
||||
|
||||
declare function mockComponent(
|
||||
componentClass: React$ElementType,
|
||||
componentClass: React.ElementType,
|
||||
mockTagName?: string
|
||||
): { [key: string]: mixed, ... };
|
||||
|
||||
declare function isElement(element: React$Element<any>): boolean;
|
||||
declare function isElement(element: React.MixedElement): boolean;
|
||||
|
||||
declare function isElementOfType(
|
||||
element: React$Element<any>,
|
||||
componentClass: React$ElementType
|
||||
element: React.MixedElement,
|
||||
componentClass: React.ElementType
|
||||
): boolean;
|
||||
|
||||
declare function isDOMComponent(instance: any): boolean;
|
||||
|
||||
declare function isCompositeComponent(
|
||||
instance: React$Component<any, any>
|
||||
instance: Component<any, any>
|
||||
): boolean;
|
||||
|
||||
declare function isCompositeComponentWithType(
|
||||
instance: React$Component<any, any>,
|
||||
componentClass: React$ElementType
|
||||
instance: Component<any, any>,
|
||||
componentClass: React.ElementType
|
||||
): boolean;
|
||||
|
||||
declare function findAllInRenderedTree(
|
||||
tree: React$Component<any, any>,
|
||||
test: (child: React$Component<any, any>) => boolean
|
||||
): Array<React$Component<any, any>>;
|
||||
tree: Component<any, any>,
|
||||
test: (child: Component<any, any>) => boolean
|
||||
): Array<Component<any, any>>;
|
||||
|
||||
declare function scryRenderedDOMComponentsWithClass(
|
||||
tree: React$Component<any, any>,
|
||||
tree: Component<any, any>,
|
||||
className: string
|
||||
): Array<Element>;
|
||||
|
||||
declare function findRenderedDOMComponentWithClass(
|
||||
tree: React$Component<any, any>,
|
||||
tree: Component<any, any>,
|
||||
className: string
|
||||
): ?Element;
|
||||
|
||||
declare function scryRenderedDOMComponentsWithTag(
|
||||
tree: React$Component<any, any>,
|
||||
tree: Component<any, any>,
|
||||
tagName: string
|
||||
): Array<Element>;
|
||||
|
||||
declare function findRenderedDOMComponentWithTag(
|
||||
tree: React$Component<any, any>,
|
||||
tree: Component<any, any>,
|
||||
tagName: string
|
||||
): ?Element;
|
||||
|
||||
declare function scryRenderedComponentsWithType(
|
||||
tree: React$Component<any, any>,
|
||||
componentClass: React$ElementType
|
||||
): Array<React$Component<any, any>>;
|
||||
tree: Component<any, any>,
|
||||
componentClass: React.ElementType
|
||||
): Array<Component<any, any>>;
|
||||
|
||||
declare function findRenderedComponentWithType(
|
||||
tree: React$Component<any, any>,
|
||||
componentClass: React$ElementType
|
||||
): ?React$Component<any, any>;
|
||||
tree: Component<any, any>,
|
||||
componentClass: React.ElementType
|
||||
): ?Component<any, any>;
|
||||
|
||||
declare function act(callback: () => void | Thenable): Thenable;
|
||||
}
|
||||
|
||||
+45
-41
@@ -1,50 +1,52 @@
|
||||
// Type definitions for react-test-renderer 16.x.x
|
||||
// Ported from: https://github.com/DefinitelyTyped/DefinitelyTyped/blob/master/types/react-test-renderer
|
||||
|
||||
type ReactComponentInstance = React$Component<any>;
|
||||
|
||||
type ReactTestRendererJSON = {
|
||||
type: string,
|
||||
props: { [propName: string]: any, ... },
|
||||
children: null | ReactTestRendererJSON[],
|
||||
...
|
||||
};
|
||||
|
||||
type ReactTestRendererTree = ReactTestRendererJSON & {
|
||||
nodeType: "component" | "host",
|
||||
instance: ?ReactComponentInstance,
|
||||
rendered: null | ReactTestRendererTree,
|
||||
...
|
||||
};
|
||||
|
||||
type ReactTestInstance = {
|
||||
instance: ?ReactComponentInstance,
|
||||
type: string,
|
||||
props: { [propName: string]: any, ... },
|
||||
parent: null | ReactTestInstance,
|
||||
children: Array<ReactTestInstance | string>,
|
||||
find(predicate: (node: ReactTestInstance) => boolean): ReactTestInstance,
|
||||
findByType(type: React$ElementType): ReactTestInstance,
|
||||
findByProps(props: { [propName: string]: any, ... }): ReactTestInstance,
|
||||
findAll(
|
||||
predicate: (node: ReactTestInstance) => boolean,
|
||||
options?: { deep: boolean, ... }
|
||||
): ReactTestInstance[],
|
||||
findAllByType(
|
||||
type: React$ElementType,
|
||||
options?: { deep: boolean, ... }
|
||||
): ReactTestInstance[],
|
||||
findAllByProps(
|
||||
props: { [propName: string]: any, ... },
|
||||
options?: { deep: boolean, ... }
|
||||
): ReactTestInstance[],
|
||||
...
|
||||
};
|
||||
|
||||
type TestRendererOptions = { createNodeMock(element: React.MixedElement): any, ... };
|
||||
|
||||
declare module "react-test-renderer" {
|
||||
declare export type ReactTestRenderer = {
|
||||
import type {Component as ReactComponent} from 'react';
|
||||
|
||||
type ReactComponentInstance = ReactComponent<any>;
|
||||
|
||||
export type ReactTestRendererJSON = {
|
||||
type: string,
|
||||
props: { [propName: string]: any, ... },
|
||||
children: null | ReactTestRendererJSON[],
|
||||
...
|
||||
};
|
||||
|
||||
export type ReactTestRendererTree = ReactTestRendererJSON & {
|
||||
nodeType: "component" | "host",
|
||||
instance: ?ReactComponentInstance,
|
||||
rendered: null | ReactTestRendererTree,
|
||||
...
|
||||
};
|
||||
|
||||
export type ReactTestInstance = {
|
||||
instance: ?ReactComponentInstance,
|
||||
type: string,
|
||||
props: { [propName: string]: any, ... },
|
||||
parent: null | ReactTestInstance,
|
||||
children: Array<ReactTestInstance | string>,
|
||||
find(predicate: (node: ReactTestInstance) => boolean): ReactTestInstance,
|
||||
findByType(type: React.ElementType): ReactTestInstance,
|
||||
findByProps(props: { [propName: string]: any, ... }): ReactTestInstance,
|
||||
findAll(
|
||||
predicate: (node: ReactTestInstance) => boolean,
|
||||
options?: { deep: boolean, ... }
|
||||
): ReactTestInstance[],
|
||||
findAllByType(
|
||||
type: React.ElementType,
|
||||
options?: { deep: boolean, ... }
|
||||
): ReactTestInstance[],
|
||||
findAllByProps(
|
||||
props: { [propName: string]: any, ... },
|
||||
options?: { deep: boolean, ... }
|
||||
): ReactTestInstance[],
|
||||
...
|
||||
};
|
||||
|
||||
export type ReactTestRenderer = {
|
||||
toJSON(): null | ReactTestRendererJSON,
|
||||
toTree(): null | ReactTestRendererTree,
|
||||
unmount(nextElement?: React.MixedElement): void,
|
||||
@@ -65,6 +67,8 @@ declare module "react-test-renderer" {
|
||||
}
|
||||
|
||||
declare module "react-test-renderer/shallow" {
|
||||
import type {ReactTestInstance} from 'react-test-renderer';
|
||||
|
||||
declare export default class ShallowRenderer {
|
||||
static createRenderer(): ShallowRenderer;
|
||||
getMountedInstance(): ReactTestInstance;
|
||||
|
||||
+3
-2
@@ -49,7 +49,7 @@
|
||||
"@babel/preset-env": "^7.25.3",
|
||||
"@babel/preset-flow": "^7.24.7",
|
||||
"@definitelytyped/dtslint": "^0.0.127",
|
||||
"@jest/create-cache-key-function": "^29.6.3",
|
||||
"@jest/create-cache-key-function": "^29.7.0",
|
||||
"@react-native/metro-babel-transformer": "0.79.0-main",
|
||||
"@react-native/metro-config": "0.79.0-main",
|
||||
"@tsconfig/node18": "1.0.1",
|
||||
@@ -63,6 +63,7 @@
|
||||
"chalk": "^4.0.0",
|
||||
"clang-format": "^1.8.0",
|
||||
"connect": "^3.6.5",
|
||||
"debug": "^2.2.0",
|
||||
"deep-equal": "1.1.1",
|
||||
"eslint": "^8.57.0",
|
||||
"eslint-config-prettier": "^8.5.0",
|
||||
@@ -83,7 +84,7 @@
|
||||
"hermes-eslint": "0.25.1",
|
||||
"hermes-transform": "0.25.1",
|
||||
"inquirer": "^7.1.0",
|
||||
"jest": "^29.6.3",
|
||||
"jest": "^29.7.0",
|
||||
"jest-diff": "^29.7.0",
|
||||
"jest-junit": "^10.0.0",
|
||||
"jest-snapshot": "^29.7.0",
|
||||
|
||||
+9
-4
@@ -32,11 +32,16 @@ abstract class GenerateCodegenSchemaTask : Exec() {
|
||||
|
||||
@get:InputFiles
|
||||
val jsInputFiles =
|
||||
project.fileTree(jsRootDir) {
|
||||
it.include("**/*.js")
|
||||
it.include("**/*.ts")
|
||||
project.fileTree(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.
|
||||
it.exclude("**/build/**/*")
|
||||
tree.exclude("**/build/**/*")
|
||||
}
|
||||
|
||||
@get:OutputFile
|
||||
|
||||
+13
-3
@@ -27,16 +27,23 @@ class GenerateCodegenSchemaTaskTest {
|
||||
val jsRootDir =
|
||||
tempFolder.newFolder("js").apply {
|
||||
File(this, "file.js").createNewFile()
|
||||
File(this, "file.jsx").createNewFile()
|
||||
File(this, "file.ts").createNewFile()
|
||||
File(this, "file.tsx").createNewFile()
|
||||
File(this, "ignore.txt").createNewFile()
|
||||
}
|
||||
|
||||
val task = createTestTask<GenerateCodegenSchemaTask> { it.jsRootDir.set(jsRootDir) }
|
||||
|
||||
assertThat(task.jsInputFiles.dir).isEqualTo(jsRootDir)
|
||||
assertThat(task.jsInputFiles.includes).isEqualTo(setOf("**/*.js", "**/*.ts"))
|
||||
assertThat(task.jsInputFiles.includes)
|
||||
.isEqualTo(setOf("**/*.js", "**/*.jsx", "**/*.ts", "**/*.tsx"))
|
||||
assertThat(task.jsInputFiles.files)
|
||||
.containsExactlyInAnyOrder(File(jsRootDir, "file.js"), File(jsRootDir, "file.ts"))
|
||||
.containsExactlyInAnyOrder(
|
||||
File(jsRootDir, "file.js"),
|
||||
File(jsRootDir, "file.jsx"),
|
||||
File(jsRootDir, "file.ts"),
|
||||
File(jsRootDir, "file.tsx"))
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -60,12 +67,15 @@ class GenerateCodegenSchemaTaskTest {
|
||||
.createFileAndPath()
|
||||
File(this, "afolder/build/intermediates/sourcemaps/react/anotherfolder/excludedfile.js")
|
||||
.createFileAndPath()
|
||||
File(this, "node_modules/excludedfile.js").createFileAndPath()
|
||||
File(this, "afolder/excludedfile.d.ts").createFileAndPath()
|
||||
}
|
||||
|
||||
val task = createTestTask<GenerateCodegenSchemaTask> { it.jsRootDir.set(jsRootDir) }
|
||||
|
||||
assertThat(task.jsInputFiles.dir).isEqualTo(jsRootDir)
|
||||
assertThat(task.jsInputFiles.excludes).isEqualTo(setOf("**/build/**/*"))
|
||||
assertThat(task.jsInputFiles.excludes)
|
||||
.isEqualTo(setOf("node_modules/**/*", "**/*.d.ts", "**/build/**/*"))
|
||||
assertThat(task.jsInputFiles.files).containsExactly(File(jsRootDir, "afolder/includedfile.js"))
|
||||
}
|
||||
|
||||
|
||||
@@ -26,7 +26,7 @@
|
||||
"chalk": "^4.1.2",
|
||||
"commander": "^12.0.0",
|
||||
"eslint": "^8.19.0",
|
||||
"jest": "^29.6.3",
|
||||
"jest": "^29.7.0",
|
||||
"listr2": "^8.2.1",
|
||||
"react-test-renderer": "19.0.0",
|
||||
"rxjs": "^7.8.1"
|
||||
|
||||
@@ -198,13 +198,6 @@ async function sendReview(
|
||||
return;
|
||||
}
|
||||
|
||||
if (process.env.CIRCLE_CI) {
|
||||
console.error(
|
||||
'Code analysis found issues, but the review cannot be posted to GitHub without an access token.',
|
||||
);
|
||||
process.exit(1);
|
||||
}
|
||||
|
||||
let results = body + '\n';
|
||||
comments.forEach(comment => {
|
||||
results +=
|
||||
|
||||
@@ -33,7 +33,7 @@
|
||||
"@babel/plugin-transform-flow-strip-types": "^7.25.2",
|
||||
"@babel/preset-env": "^7.25.3",
|
||||
"@types/jest": "^29.5.3",
|
||||
"jest": "^29.6.3",
|
||||
"jest": "^29.7.0",
|
||||
"rimraf": "^3.0.2"
|
||||
},
|
||||
"peerDependencies": {
|
||||
|
||||
@@ -20,6 +20,7 @@ const rnTesterConfig = getDefaultConfig(
|
||||
const JS_DIR = process.env.JS_DIR
|
||||
? path.resolve(process.cwd(), process.env.JS_DIR)
|
||||
: null;
|
||||
const NODE_MODULES = path.sep + 'node_modules' + path.sep;
|
||||
|
||||
const config = {
|
||||
projectRoot: path.resolve(__dirname, '../../..'),
|
||||
@@ -28,12 +29,22 @@ const config = {
|
||||
},
|
||||
resolver: {
|
||||
blockList: /\/RendererProxy\.fb\.js$/, // Disable dependency injection for the renderer
|
||||
disableHierarchicalLookup: !!JS_DIR,
|
||||
sourceExts: ['fb.js', ...rnTesterConfig.resolver.sourceExts],
|
||||
nodeModulesPaths: JS_DIR
|
||||
? [path.join(JS_DIR, 'public', 'node_modules')]
|
||||
: [],
|
||||
hasteImplModulePath: path.resolve(__dirname, 'hasteImpl.js'),
|
||||
resolveRequest: JS_DIR
|
||||
? (ctx, dep, platform) =>
|
||||
ctx.originModulePath.includes(NODE_MODULES)
|
||||
? ctx.resolveRequest(ctx, dep, platform)
|
||||
: // Disable hierarchical node_modules lookup from 1P code.
|
||||
ctx.resolveRequest(
|
||||
{...ctx, disableHierarchicalLookup: true},
|
||||
dep,
|
||||
platform,
|
||||
)
|
||||
: null,
|
||||
},
|
||||
transformer: {
|
||||
// We need to wrap the default transformer so we can run it from source
|
||||
@@ -43,8 +54,7 @@ const config = {
|
||||
watchFolders: JS_DIR
|
||||
? [
|
||||
path.join(JS_DIR, 'RKJSModules', 'vendor', 'react'),
|
||||
path.join(JS_DIR, 'tools', 'metro'),
|
||||
path.join(JS_DIR, 'node_modules'),
|
||||
path.join(JS_DIR, 'tools', 'metro', 'packages', 'metro-runtime'),
|
||||
path.join(JS_DIR, 'public', 'node_modules'),
|
||||
]
|
||||
: [],
|
||||
|
||||
+128
-14
@@ -230,10 +230,7 @@ class Expect {
|
||||
}
|
||||
}
|
||||
|
||||
toBeCalled(): void {
|
||||
return this.toHaveBeenCalled();
|
||||
}
|
||||
|
||||
toBeCalled: () => void;
|
||||
toHaveBeenCalled(): void {
|
||||
const mock = this.#requireMock();
|
||||
const pass = mock.calls.length > 0;
|
||||
@@ -244,10 +241,7 @@ class Expect {
|
||||
}
|
||||
}
|
||||
|
||||
toBeCalledTimes(times: number): void {
|
||||
return this.toHaveBeenCalledTimes(times);
|
||||
}
|
||||
|
||||
toBeCalledTimes: (times: number) => void;
|
||||
toHaveBeenCalledTimes(times: number): void {
|
||||
const mock = this.#requireMock();
|
||||
const pass = mock.calls.length === times;
|
||||
@@ -258,22 +252,63 @@ class Expect {
|
||||
}
|
||||
}
|
||||
|
||||
toBeCalledWith(...args: mixed[]): void {
|
||||
return this.toHaveBeenCalledWith(...args);
|
||||
}
|
||||
|
||||
toHaveBeenCalledWith(...args: mixed[]): void {
|
||||
toBeCalledWith: (...args: Array<mixed>) => void;
|
||||
toHaveBeenCalledWith(...args: Array<mixed>): void {
|
||||
const mock = this.#requireMock();
|
||||
const pass = mock.calls.some(callArgs =>
|
||||
deepEqual(callArgs, args, {strict: true}),
|
||||
);
|
||||
if (!this.#isExpectedResult(pass)) {
|
||||
throw new ErrorWithCustomBlame(
|
||||
`Expected ${String(this.#received)}${this.#maybeNotLabel()} to have been called with ${stringify(args)}, but it was called with ${stringify(mock.calls)}`,
|
||||
`Expected ${String(this.#received)}${this.#maybeNotLabel()} to have been called with ${stringify(
|
||||
args,
|
||||
)}, but it was called with ${stringify(mock.calls)}`,
|
||||
).blameToPreviousFrame();
|
||||
}
|
||||
}
|
||||
|
||||
lastCalledWith: (...args: Array<mixed>) => void;
|
||||
toHaveBeenLastCalledWith(...args: mixed[]): void {
|
||||
const mock = this.#requireMock();
|
||||
if (mock.calls.length === 0) {
|
||||
if (this.#isNot) {
|
||||
return;
|
||||
}
|
||||
|
||||
throw new ErrorWithCustomBlame(
|
||||
`Expected ${String(this.#received)} to have been last called with ${stringify(args)}, but it was not called a single time.`,
|
||||
);
|
||||
}
|
||||
|
||||
const pass = deepEqual(mock.lastCall, args, {strict: true});
|
||||
if (!this.#isExpectedResult(pass)) {
|
||||
throw new ErrorWithCustomBlame(
|
||||
`Expected ${String(this.#received)}${this.#maybeNotLabel()} to have been last called with ${stringify(args)}, but it was last called with ${stringify(mock.lastCall)}.`,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
nthCalledWith: (index: number, ...args: mixed[]) => void;
|
||||
toHaveBeenNthCalledWith(index: number, ...args: mixed[]): void {
|
||||
if (index < 1) {
|
||||
throw new ErrorWithCustomBlame(
|
||||
`Expected index to be positive number, got ${index}.`,
|
||||
).blameToPreviousFrame();
|
||||
}
|
||||
|
||||
const mock = this.#requireMock();
|
||||
if (this.#isNot && mock.calls.length < index) {
|
||||
return;
|
||||
}
|
||||
|
||||
const pass = deepEqual(mock.calls[index - 1], args, {strict: true});
|
||||
if (!this.#isExpectedResult(pass)) {
|
||||
throw new ErrorWithCustomBlame(
|
||||
`Expected ${String(this.#received)}${this.#maybeNotLabel()} to have been nth(${index}) called with ${stringify(args)}, but it was called with ${stringify(mock.calls[index - 1])}.`,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
toBeGreaterThan(expected: number): void {
|
||||
if (typeof this.#received !== 'number') {
|
||||
throw new ErrorWithCustomBlame(
|
||||
@@ -358,6 +393,70 @@ class Expect {
|
||||
}
|
||||
}
|
||||
|
||||
toContain(item: mixed): void {
|
||||
if (typeof this.#received === 'string') {
|
||||
if (typeof item !== 'string') {
|
||||
throw new ErrorWithCustomBlame(
|
||||
`Expected ${String(item)} to be a string but it was a ${typeof item}`,
|
||||
).blameToPreviousFrame();
|
||||
}
|
||||
|
||||
const pass = this.#received.includes(item);
|
||||
if (!this.#isExpectedResult(pass)) {
|
||||
throw new ErrorWithCustomBlame(
|
||||
`Expected ${String(this.#received)}${this.#maybeNotLabel()} to contain ${item}`,
|
||||
).blameToPreviousFrame();
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
if (!Array.isArray(this.#received)) {
|
||||
throw new ErrorWithCustomBlame(
|
||||
`Expected ${String(this.#received)} to be an array`,
|
||||
).blameToPreviousFrame();
|
||||
}
|
||||
|
||||
const pass = this.#received.includes(item);
|
||||
if (!this.#isExpectedResult(pass)) {
|
||||
throw new ErrorWithCustomBlame(
|
||||
`Expected ${String(this.#received)}${this.#maybeNotLabel()} to contain ${String(item)}`,
|
||||
).blameToPreviousFrame();
|
||||
}
|
||||
}
|
||||
|
||||
toContainEqual(item: mixed): void {
|
||||
if (typeof this.#received === 'string') {
|
||||
if (typeof item !== 'string') {
|
||||
throw new ErrorWithCustomBlame(
|
||||
`Expected ${String(item)} to be a string but it was a ${typeof item}`,
|
||||
).blameToPreviousFrame();
|
||||
}
|
||||
|
||||
const pass = this.#received.includes(item);
|
||||
if (!this.#isExpectedResult(pass)) {
|
||||
throw new ErrorWithCustomBlame(
|
||||
`Expected ${String(this.#received)}${this.#maybeNotLabel()} to contain ${item}`,
|
||||
).blameToPreviousFrame();
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
if (!Array.isArray(this.#received)) {
|
||||
throw new ErrorWithCustomBlame(
|
||||
`Expected ${String(this.#received)} to be an array`,
|
||||
).blameToPreviousFrame();
|
||||
}
|
||||
|
||||
const pass = this.#received.some(value =>
|
||||
deepEqual(value, item, {strict: true}),
|
||||
);
|
||||
if (!this.#isExpectedResult(pass)) {
|
||||
throw new ErrorWithCustomBlame(
|
||||
`Expected ${String(this.#received)}${this.#maybeNotLabel()} to contain item equal to ${String(item)}`,
|
||||
).blameToPreviousFrame();
|
||||
}
|
||||
}
|
||||
|
||||
toMatchSnapshot(expected?: string): void {
|
||||
if (this.#isNot) {
|
||||
throw new ErrorWithCustomBlame(
|
||||
@@ -399,6 +498,21 @@ class Expect {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Base methods can't be implemented as an arrow function because they
|
||||
* will not be added to the prototype.
|
||||
*/
|
||||
// $FlowExpectedError[method-unbinding]
|
||||
Expect.prototype.toBeCalled = Expect.prototype.toHaveBeenCalled;
|
||||
// $FlowExpectedError[method-unbinding]
|
||||
Expect.prototype.toBeCalledTimes = Expect.prototype.toHaveBeenCalledTimes;
|
||||
// $FlowExpectedError[method-unbinding]
|
||||
Expect.prototype.toBeCalledWith = Expect.prototype.toHaveBeenCalledWith;
|
||||
// $FlowExpectedError[method-unbinding]
|
||||
Expect.prototype.lastCalledWith = Expect.prototype.toHaveBeenLastCalledWith;
|
||||
// $FlowExpectedError[method-unbinding]
|
||||
Expect.prototype.nthCalledWith = Expect.prototype.toHaveBeenNthCalledWith;
|
||||
|
||||
const expect: mixed => Expect = (received: mixed) => new Expect(received);
|
||||
|
||||
export default expect;
|
||||
|
||||
+62
-24
@@ -25,12 +25,23 @@ type SuiteOptions = $ReadOnly<{
|
||||
minWarmupDuration?: number,
|
||||
minWarmupIterations?: number,
|
||||
disableOptimizedBuildCheck?: boolean,
|
||||
testOnly?: boolean,
|
||||
}>;
|
||||
|
||||
type TestOptions = $ReadOnly<{
|
||||
...FnOptions,
|
||||
only?: boolean,
|
||||
}>;
|
||||
|
||||
type SuiteResults = Array<$ReadOnly<TaskResult>>;
|
||||
|
||||
interface TestFunction {
|
||||
(name: string, fn: () => void, options?: FnOptions): SuiteAPI;
|
||||
only: (name: string, fn: () => void, options?: FnOptions) => SuiteAPI;
|
||||
}
|
||||
|
||||
interface SuiteAPI {
|
||||
add(name: string, fn: () => void, options?: FnOptions): SuiteAPI;
|
||||
+test: TestFunction;
|
||||
verify(fn: (results: SuiteResults) => void): SuiteAPI;
|
||||
}
|
||||
|
||||
@@ -41,7 +52,7 @@ export function suite(
|
||||
const tasks: Array<{
|
||||
name: string,
|
||||
fn: () => void,
|
||||
options: FnOptions | void,
|
||||
options: TestOptions | void,
|
||||
}> = [];
|
||||
const verifyFns = [];
|
||||
|
||||
@@ -56,12 +67,13 @@ export function suite(
|
||||
// no point in running the benchmark.
|
||||
// We still run a single iteration of each test just to make sure that the
|
||||
// logic in the benchmark doesn't break.
|
||||
const isTestOnly = isRunningFromCI && verifyFns.length === 0;
|
||||
const isTestOnly =
|
||||
suiteOptions.testOnly === true ||
|
||||
(isRunningFromCI && verifyFns.length === 0);
|
||||
|
||||
const benchOptions: BenchOptions = isTestOnly
|
||||
? {
|
||||
warmupIterations: 1,
|
||||
warmupTime: 0,
|
||||
warmup: false,
|
||||
iterations: 1,
|
||||
time: 0,
|
||||
}
|
||||
@@ -71,30 +83,39 @@ export function suite(
|
||||
benchOptions.throws = true;
|
||||
benchOptions.now = () => NativeCPUTime.getCPUTimeNanos() / 1000000;
|
||||
|
||||
if (suiteOptions.minIterations != null) {
|
||||
benchOptions.iterations = suiteOptions.minIterations;
|
||||
}
|
||||
if (!isTestOnly) {
|
||||
if (suiteOptions.minIterations != null) {
|
||||
benchOptions.iterations = suiteOptions.minIterations;
|
||||
}
|
||||
|
||||
if (suiteOptions.minDuration != null) {
|
||||
benchOptions.time = suiteOptions.minDuration;
|
||||
}
|
||||
if (suiteOptions.minDuration != null) {
|
||||
benchOptions.time = suiteOptions.minDuration;
|
||||
}
|
||||
|
||||
if (suiteOptions.warmup != null) {
|
||||
benchOptions.warmup = suiteOptions.warmup;
|
||||
}
|
||||
if (suiteOptions.warmup != null) {
|
||||
benchOptions.warmup = suiteOptions.warmup;
|
||||
}
|
||||
|
||||
if (suiteOptions.minWarmupDuration != null) {
|
||||
benchOptions.warmupTime = suiteOptions.minWarmupDuration;
|
||||
}
|
||||
if (suiteOptions.minWarmupDuration != null) {
|
||||
benchOptions.warmupTime = suiteOptions.minWarmupDuration;
|
||||
}
|
||||
|
||||
if (suiteOptions.minWarmupIterations != null) {
|
||||
benchOptions.warmupIterations = suiteOptions.minWarmupIterations;
|
||||
if (suiteOptions.minWarmupIterations != null) {
|
||||
benchOptions.warmupIterations = suiteOptions.minWarmupIterations;
|
||||
}
|
||||
}
|
||||
|
||||
const bench = new Bench(benchOptions);
|
||||
|
||||
const isFocused = tasks.find(task => task.options?.only === true) != null;
|
||||
|
||||
for (const task of tasks) {
|
||||
bench.add(task.name, task.fn, task.options);
|
||||
if (isFocused && task.options?.only !== true) {
|
||||
continue;
|
||||
}
|
||||
|
||||
const {only, ...options} = task.options ?? {};
|
||||
bench.add(task.name, task.fn, options);
|
||||
}
|
||||
|
||||
bench.runSync();
|
||||
@@ -116,13 +137,30 @@ export function suite(
|
||||
if (__DEV__ && suiteOptions.disableOptimizedBuildCheck !== true) {
|
||||
throw new Error('Benchmarks should not be run in development mode');
|
||||
}
|
||||
|
||||
if (isFocused) {
|
||||
throw new Error(
|
||||
'Failing focused test to prevent it from being committed',
|
||||
);
|
||||
}
|
||||
});
|
||||
|
||||
const test = (
|
||||
name: string,
|
||||
fn: () => void,
|
||||
options?: FnOptions,
|
||||
): SuiteAPI => {
|
||||
tasks.push({name, fn, options});
|
||||
return suiteAPI;
|
||||
};
|
||||
|
||||
test.only = (name: string, fn: () => void, options?: FnOptions): SuiteAPI => {
|
||||
tasks.push({name, fn, options: {...options, only: true}});
|
||||
return suiteAPI;
|
||||
};
|
||||
|
||||
const suiteAPI = {
|
||||
add(name: string, fn: () => void, options?: FnOptions): SuiteAPI {
|
||||
tasks.push({name, fn, options});
|
||||
return suiteAPI;
|
||||
},
|
||||
test,
|
||||
verify(fn: (results: SuiteResults) => void): SuiteAPI {
|
||||
verifyFns.push(fn);
|
||||
return suiteAPI;
|
||||
|
||||
+33
-5
@@ -381,7 +381,7 @@ describe('Fantom', () => {
|
||||
});
|
||||
});
|
||||
|
||||
describe('runOnUIThread + dispatchNativeEvent', () => {
|
||||
describe('runOnUIThread + enqueueNativeEvent', () => {
|
||||
it('sends event without payload', () => {
|
||||
const root = Fantom.createRoot();
|
||||
let maybeNode;
|
||||
@@ -404,7 +404,7 @@ describe('Fantom', () => {
|
||||
expect(focusEvent).toHaveBeenCalledTimes(0);
|
||||
|
||||
Fantom.runOnUIThread(() => {
|
||||
Fantom.dispatchNativeEvent(element, 'focus');
|
||||
Fantom.enqueueNativeEvent(element, 'focus');
|
||||
});
|
||||
|
||||
// The tasks have not run.
|
||||
@@ -437,7 +437,7 @@ describe('Fantom', () => {
|
||||
const element = ensureInstance(maybeNode, ReactNativeElement);
|
||||
|
||||
Fantom.runOnUIThread(() => {
|
||||
Fantom.dispatchNativeEvent(element, 'change', {
|
||||
Fantom.enqueueNativeEvent(element, 'change', {
|
||||
text: 'Hello World',
|
||||
});
|
||||
});
|
||||
@@ -470,13 +470,13 @@ describe('Fantom', () => {
|
||||
const element = ensureInstance(maybeNode, ReactNativeElement);
|
||||
|
||||
Fantom.runOnUIThread(() => {
|
||||
Fantom.dispatchNativeEvent(element, 'scroll', {
|
||||
Fantom.enqueueNativeEvent(element, 'scroll', {
|
||||
contentOffset: {
|
||||
x: 0,
|
||||
y: 1,
|
||||
},
|
||||
});
|
||||
Fantom.dispatchNativeEvent(
|
||||
Fantom.enqueueNativeEvent(
|
||||
element,
|
||||
'scroll',
|
||||
{
|
||||
@@ -501,6 +501,34 @@ describe('Fantom', () => {
|
||||
});
|
||||
});
|
||||
|
||||
describe('dispatchNativeEvent', () => {
|
||||
it('flushes the event and runs the work loop', () => {
|
||||
const root = Fantom.createRoot();
|
||||
let maybeNode;
|
||||
|
||||
let focusEvent = jest.fn();
|
||||
|
||||
Fantom.runTask(() => {
|
||||
root.render(
|
||||
<TextInput
|
||||
onFocus={focusEvent}
|
||||
ref={node => {
|
||||
maybeNode = node;
|
||||
}}
|
||||
/>,
|
||||
);
|
||||
});
|
||||
|
||||
const element = ensureInstance(maybeNode, ReactNativeElement);
|
||||
|
||||
expect(focusEvent).toHaveBeenCalledTimes(0);
|
||||
|
||||
Fantom.dispatchNativeEvent(element, 'focus');
|
||||
|
||||
expect(focusEvent).toHaveBeenCalledTimes(1);
|
||||
});
|
||||
});
|
||||
|
||||
describe('scrollTo', () => {
|
||||
it('throws error if called on node that is not scroll view', () => {
|
||||
const root = Fantom.createRoot();
|
||||
|
||||
Vendored
+35
@@ -0,0 +1,35 @@
|
||||
/**
|
||||
* 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
|
||||
*/
|
||||
|
||||
import Fantom from '../..';
|
||||
|
||||
let runs = 0;
|
||||
|
||||
// We need to use `afterAll` because the benchmark API defines tests in Jest,
|
||||
// and we can't call it from within other tests.
|
||||
afterAll(() => {
|
||||
expect(runs).toBe(1);
|
||||
});
|
||||
|
||||
Fantom.unstable_benchmark
|
||||
.suite('Benchmark test', {
|
||||
testOnly: true,
|
||||
|
||||
// Ignores warmup, iterations and duration
|
||||
warmup: true,
|
||||
minWarmupIterations: 10,
|
||||
minIterations: 10,
|
||||
minDuration: 1000,
|
||||
minWarmupDuration: 1000,
|
||||
})
|
||||
.test('test', () => {
|
||||
runs++;
|
||||
});
|
||||
@@ -358,6 +358,100 @@ describe('expect', () => {
|
||||
}),
|
||||
);
|
||||
|
||||
['lastCalledWith', 'toHaveBeenLastCalledWith'].map(
|
||||
toHaveBeenLastCalledWithAlias =>
|
||||
test(toHaveBeenLastCalledWithAlias, () => {
|
||||
const fn = jest.fn();
|
||||
|
||||
expect(fn).not[toHaveBeenLastCalledWithAlias]();
|
||||
expect(fn).not[toHaveBeenLastCalledWithAlias]({});
|
||||
|
||||
expect(() => {
|
||||
expect(fn)[toHaveBeenLastCalledWithAlias]();
|
||||
}).toThrow();
|
||||
|
||||
fn('happy');
|
||||
expect(fn)[toHaveBeenLastCalledWithAlias]('happy');
|
||||
expect(fn).not[toHaveBeenLastCalledWithAlias]();
|
||||
|
||||
fn();
|
||||
expect(fn)[toHaveBeenLastCalledWithAlias]();
|
||||
expect(fn).not[toHaveBeenLastCalledWithAlias]('happy');
|
||||
|
||||
fn({a: 1}, 2);
|
||||
|
||||
expect(fn)[toHaveBeenLastCalledWithAlias]({a: 1}, 2);
|
||||
expect(fn).not[toHaveBeenLastCalledWithAlias]();
|
||||
expect(fn).not[toHaveBeenLastCalledWithAlias]({a: 1});
|
||||
expect(fn).not[toHaveBeenLastCalledWithAlias]({a: 2}, 2);
|
||||
expect(fn).not[toHaveBeenLastCalledWithAlias]({a: 1}, 2, undefined);
|
||||
|
||||
expect(() => {
|
||||
expect(fn).not[toHaveBeenLastCalledWithAlias]({a: 1}, 2);
|
||||
}).toThrow();
|
||||
|
||||
expect(() => {
|
||||
expect(fn)[toHaveBeenLastCalledWithAlias](1);
|
||||
}).toThrow();
|
||||
|
||||
// Passing functions that aren't mocks should always fail
|
||||
expect(() => {
|
||||
expect(() => {})[toHaveBeenLastCalledWithAlias]();
|
||||
}).toThrow();
|
||||
|
||||
expect(() => {
|
||||
expect(() => {}).not[toHaveBeenLastCalledWithAlias]();
|
||||
}).toThrow();
|
||||
}),
|
||||
);
|
||||
|
||||
['nthCalledWith', 'toHaveBeenNthCalledWith'].map(
|
||||
toHaveBeenNthCalledWithAlias =>
|
||||
test(toHaveBeenNthCalledWithAlias, () => {
|
||||
const fn = jest.fn();
|
||||
|
||||
expect(fn).not[toHaveBeenNthCalledWithAlias](1);
|
||||
expect(fn).not[toHaveBeenNthCalledWithAlias](1, {});
|
||||
|
||||
expect(() => {
|
||||
expect(fn)[toHaveBeenNthCalledWithAlias](0);
|
||||
}).toThrow();
|
||||
|
||||
expect(() => {
|
||||
expect(fn)[toHaveBeenNthCalledWithAlias](1);
|
||||
}).toThrow();
|
||||
|
||||
fn('happy');
|
||||
fn();
|
||||
fn({a: 1}, 2);
|
||||
|
||||
expect(fn)[toHaveBeenNthCalledWithAlias](1, 'happy');
|
||||
expect(fn)[toHaveBeenNthCalledWithAlias](2);
|
||||
expect(fn)[toHaveBeenNthCalledWithAlias](3, {a: 1}, 2);
|
||||
expect(fn).not[toHaveBeenNthCalledWithAlias](1);
|
||||
expect(fn).not[toHaveBeenNthCalledWithAlias](3, {a: 1});
|
||||
expect(fn).not[toHaveBeenNthCalledWithAlias](3, {a: 2}, 2);
|
||||
expect(fn).not[toHaveBeenNthCalledWithAlias](3, {a: 1}, 2, undefined);
|
||||
|
||||
expect(() => {
|
||||
expect(fn).not[toHaveBeenNthCalledWithAlias](3, {a: 1}, 2);
|
||||
}).toThrow();
|
||||
|
||||
expect(() => {
|
||||
expect(fn)[toHaveBeenNthCalledWithAlias](1);
|
||||
}).toThrow();
|
||||
|
||||
// Passing functions that aren't mocks should always fail
|
||||
expect(() => {
|
||||
expect(() => {})[toHaveBeenNthCalledWithAlias](1);
|
||||
}).toThrow();
|
||||
|
||||
expect(() => {
|
||||
expect(() => {}).not[toHaveBeenNthCalledWithAlias](1);
|
||||
}).toThrow();
|
||||
}),
|
||||
);
|
||||
|
||||
describe('jest.fn()', () => {
|
||||
it('tracks execution of functions without implementations', () => {
|
||||
const fn = jest.fn();
|
||||
@@ -661,6 +755,46 @@ describe('expect', () => {
|
||||
}).toThrow();
|
||||
});
|
||||
|
||||
test('toContain', () => {
|
||||
expect('hello').toContain('he');
|
||||
expect('hello').not.toContain('lol');
|
||||
expect([1, 2, 3]).toContain(1);
|
||||
expect([1, 2, 3]).not.toContain(4);
|
||||
|
||||
const obj = {a: 1};
|
||||
expect([obj, {a: 2}, {a: 3}]).toContain(obj);
|
||||
expect([obj]).not.toContain({a: 1});
|
||||
|
||||
expect(() => {
|
||||
expect([]).toContain(obj);
|
||||
}).toThrow();
|
||||
|
||||
expect(() => {
|
||||
expect('hello').not.toContain('e');
|
||||
}).toThrow();
|
||||
});
|
||||
|
||||
test('toContainEqual', () => {
|
||||
expect('hello').toContainEqual('he');
|
||||
expect('hello').not.toContainEqual('lol');
|
||||
expect([1, 2, 3]).toContainEqual(1);
|
||||
expect([1, 2, 3]).not.toContainEqual(4);
|
||||
|
||||
const obj = {a: 1};
|
||||
expect([obj, {a: 2}, {a: 3}]).toContainEqual(obj);
|
||||
expect([obj]).toContainEqual({a: 1});
|
||||
expect([[obj]]).toContainEqual([{a: 1}]);
|
||||
expect([obj]).not.toContainEqual({a: 2});
|
||||
|
||||
expect(() => {
|
||||
expect([]).toContainEqual(obj);
|
||||
}).toThrow();
|
||||
|
||||
expect(() => {
|
||||
expect([{a: 1}]).not.toContainEqual({a: 1});
|
||||
}).toThrow();
|
||||
});
|
||||
|
||||
describe('toMatchSnapshot()', () => {
|
||||
test('primitive types', () => {
|
||||
expect(undefined).toMatchSnapshot();
|
||||
|
||||
+29
-2
@@ -13,10 +13,12 @@ import type {
|
||||
RenderOutputConfig,
|
||||
} from './getFantomRenderedOutput';
|
||||
import type {MixedElement} from 'react';
|
||||
import type {RootTag} from 'react-native/Libraries/ReactNative/RootTag';
|
||||
|
||||
import ReactNativeElement from '../../react-native/src/private/webapis/dom/nodes/ReadOnlyNode';
|
||||
import * as Benchmark from './Benchmark';
|
||||
import getFantomRenderedOutput from './getFantomRenderedOutput';
|
||||
import {createRootTag} from 'react-native/Libraries/ReactNative/RootTag';
|
||||
import ReactFabric from 'react-native/Libraries/Renderer/shims/ReactFabric';
|
||||
import NativeFantom, {
|
||||
NativeEventCategory,
|
||||
@@ -91,6 +93,10 @@ class Root {
|
||||
return getFantomRenderedOutput(this.#surfaceId, config);
|
||||
}
|
||||
|
||||
getRootTag(): RootTag {
|
||||
return createRootTag(this.#surfaceId);
|
||||
}
|
||||
|
||||
// TODO: add an API to check if all surfaces were deallocated when tests are finished.
|
||||
}
|
||||
|
||||
@@ -166,14 +172,21 @@ function createRoot(rootConfig?: RootConfig): Root {
|
||||
return new Root(rootConfig);
|
||||
}
|
||||
|
||||
function dispatchNativeEvent(
|
||||
/**
|
||||
* This is a low level method to enqueue a native event to a node.
|
||||
* It does not wait for it to be flushed in the UI thread or for it to be
|
||||
* processed by JS.
|
||||
*
|
||||
* For a higher level API, use `dispatchNativeEvent`.
|
||||
*/
|
||||
function enqueueNativeEvent(
|
||||
node: ReactNativeElement,
|
||||
type: string,
|
||||
payload?: {[key: string]: mixed},
|
||||
options?: {category?: NativeEventCategory, isUnique?: boolean},
|
||||
) {
|
||||
const shadowNode = getNativeNodeReference(node);
|
||||
NativeFantom.dispatchNativeEvent(
|
||||
NativeFantom.enqueueNativeEvent(
|
||||
shadowNode,
|
||||
type,
|
||||
payload,
|
||||
@@ -182,6 +195,19 @@ function dispatchNativeEvent(
|
||||
);
|
||||
}
|
||||
|
||||
function dispatchNativeEvent(
|
||||
node: ReactNativeElement,
|
||||
type: string,
|
||||
payload?: {[key: string]: mixed},
|
||||
options?: {category?: NativeEventCategory, isUnique?: boolean},
|
||||
) {
|
||||
runOnUIThread(() => {
|
||||
enqueueNativeEvent(node, type, payload, options);
|
||||
});
|
||||
|
||||
runWorkLoop();
|
||||
}
|
||||
|
||||
function scrollTo(
|
||||
node: ReactNativeElement,
|
||||
options: {x: number, y: number, zoomScale?: number},
|
||||
@@ -287,6 +313,7 @@ export default {
|
||||
runWorkLoop,
|
||||
createRoot,
|
||||
dispatchNativeEvent,
|
||||
enqueueNativeEvent,
|
||||
flushAllNativeEvents,
|
||||
unstable_benchmark: Benchmark,
|
||||
scrollTo,
|
||||
|
||||
@@ -7,8 +7,8 @@
|
||||
|
||||
plugins {
|
||||
id("com.facebook.react")
|
||||
alias(libs.plugins.android.library)
|
||||
alias(libs.plugins.kotlin.android)
|
||||
id("com.android.library")
|
||||
id("org.jetbrains.kotlin.android")
|
||||
}
|
||||
|
||||
android {
|
||||
|
||||
+2
-1
@@ -12,8 +12,9 @@ package com.facebook.react.viewmanagers;
|
||||
import android.view.View;
|
||||
import androidx.annotation.Nullable;
|
||||
import com.facebook.react.bridge.ReadableArray;
|
||||
import com.facebook.react.uimanager.ViewManagerWithGeneratedInterface;
|
||||
|
||||
public interface AndroidPopupMenuManagerInterface<T extends View> {
|
||||
public interface AndroidPopupMenuManagerInterface<T extends View> extends ViewManagerWithGeneratedInterface {
|
||||
void setMenuItems(T view, @Nullable ReadableArray value);
|
||||
void show(T view);
|
||||
}
|
||||
|
||||
@@ -6,6 +6,7 @@
|
||||
"files": [
|
||||
"js",
|
||||
"android",
|
||||
"react-native.config.js",
|
||||
"!android/build",
|
||||
"!**/__tests__",
|
||||
"!**/__fixtures__",
|
||||
@@ -15,6 +16,9 @@
|
||||
"react-native",
|
||||
"android"
|
||||
],
|
||||
"scripts": {
|
||||
"prepublishOnly": "node ./scripts/prepublish-popup-menu-android.js"
|
||||
},
|
||||
"license": "MIT",
|
||||
"devDependencies": {
|
||||
"@react-native/codegen": "0.79.0-main"
|
||||
|
||||
+46
@@ -0,0 +1,46 @@
|
||||
/**
|
||||
* 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.
|
||||
*/
|
||||
|
||||
/*
|
||||
* This script is used to update the android/build.gradle.kts file
|
||||
* with the versions from the libs.versions.toml file.
|
||||
*
|
||||
* This is needed because this package is consumed from source from
|
||||
* external users and we don't want to have several SDK version around to
|
||||
* maintain.
|
||||
*
|
||||
* It's invoked as a prepublish script for this package.
|
||||
*/
|
||||
|
||||
function extractVersion(tomlContent, regex) {
|
||||
const match = tomlContent.match(regex);
|
||||
return match && match[1] ? match[1] : null;
|
||||
}
|
||||
|
||||
const fs = require('fs');
|
||||
|
||||
const buildGradleKtsPath = 'android/build.gradle.kts';
|
||||
const libsVersionsTomlPath = '../react-native/gradle/libs.versions.toml';
|
||||
|
||||
console.log(`Updating ${buildGradleKtsPath} with versions from ${libsVersionsTomlPath}...`);
|
||||
|
||||
let gradleContent = fs.readFileSync(buildGradleKtsPath, 'utf8');
|
||||
const tomlContent = fs.readFileSync(libsVersionsTomlPath, 'utf8');
|
||||
|
||||
const compileSdk = extractVersion(tomlContent, /compileSdk\s*=\s*"(\d+)"/);
|
||||
const minSdk = extractVersion(tomlContent, /minSdk\s*=\s*"(\d+)"/);
|
||||
const buildTools = extractVersion(tomlContent, /buildTools\s*=\s*"([\d.]+)"/);
|
||||
|
||||
gradleContent = gradleContent
|
||||
.replace('libs.versions.compileSdk.get().toInt()', compileSdk)
|
||||
.replace('libs.versions.minSdk.get().toInt()', minSdk)
|
||||
.replace('libs.versions.buildTools.get()', `"${buildTools}"`)
|
||||
.replace('project(":packages:react-native:ReactAndroid")', '"com.facebook.react:react-android"');
|
||||
|
||||
fs.writeFileSync(buildGradleKtsPath, gradleContent);
|
||||
|
||||
console.log('Done!');
|
||||
+2
-1
@@ -12,8 +12,9 @@ package com.facebook.react.viewmanagers;
|
||||
import android.view.View;
|
||||
import androidx.annotation.Nullable;
|
||||
import com.facebook.react.bridge.ReadableArray;
|
||||
import com.facebook.react.uimanager.ViewManagerWithGeneratedInterface;
|
||||
|
||||
public interface SampleNativeComponentManagerInterface<T extends View> {
|
||||
public interface SampleNativeComponentManagerInterface<T extends View> extends ViewManagerWithGeneratedInterface {
|
||||
void setOpacity(T view, float value);
|
||||
void setValues(T view, @Nullable ReadableArray value);
|
||||
void changeBackgroundColor(T view, String color);
|
||||
|
||||
@@ -1,62 +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.
|
||||
*
|
||||
* @format
|
||||
* @flow
|
||||
*/
|
||||
|
||||
export type AlertType =
|
||||
| 'default'
|
||||
| 'plain-text'
|
||||
| 'secure-text'
|
||||
| 'login-password';
|
||||
|
||||
export type AlertButtonStyle = 'default' | 'cancel' | 'destructive';
|
||||
|
||||
export type AlertButton = {
|
||||
text?: string,
|
||||
onPress?: ?((value?: string) => any) | ?Function,
|
||||
isPreferred?: boolean,
|
||||
style?: AlertButtonStyle,
|
||||
...
|
||||
};
|
||||
|
||||
export type Buttons = Array<AlertButton>;
|
||||
|
||||
export type AlertOptions = {
|
||||
/** @platform android */
|
||||
cancelable?: ?boolean,
|
||||
userInterfaceStyle?: 'unspecified' | 'light' | 'dark',
|
||||
/** @platform android */
|
||||
onDismiss?: ?() => void,
|
||||
...
|
||||
};
|
||||
|
||||
/**
|
||||
* Launches an alert dialog with the specified title and message.
|
||||
*
|
||||
* See https://reactnative.dev/docs/alert
|
||||
*/
|
||||
declare class Alert {
|
||||
static alert(
|
||||
title: ?string,
|
||||
message?: ?string,
|
||||
buttons?: Buttons,
|
||||
options?: AlertOptions,
|
||||
): void;
|
||||
|
||||
static prompt(
|
||||
title: ?string,
|
||||
message?: ?string,
|
||||
callbackOrButtons?: ?(((text: string) => void) | Buttons),
|
||||
type?: ?AlertType,
|
||||
defaultValue?: string,
|
||||
keyboardType?: string,
|
||||
options?: AlertOptions,
|
||||
): void;
|
||||
}
|
||||
|
||||
export default Alert;
|
||||
+51
-5
@@ -9,18 +9,61 @@
|
||||
*/
|
||||
|
||||
import type {DialogOptions} from '../NativeModules/specs/NativeDialogManagerAndroid';
|
||||
import type {AlertOptions, AlertType, Buttons} from './Alert.flow';
|
||||
|
||||
import Platform from '../Utilities/Platform';
|
||||
import RCTAlertManager from './RCTAlertManager';
|
||||
|
||||
export type * from './Alert.flow';
|
||||
/**
|
||||
* @platform ios
|
||||
*/
|
||||
export type AlertType =
|
||||
| 'default'
|
||||
| 'plain-text'
|
||||
| 'secure-text'
|
||||
| 'login-password';
|
||||
|
||||
/**
|
||||
* @platform ios
|
||||
*/
|
||||
export type AlertButtonStyle = 'default' | 'cancel' | 'destructive';
|
||||
|
||||
export type AlertButton = {
|
||||
text?: string,
|
||||
onPress?: ?((value?: string) => any) | ?Function,
|
||||
isPreferred?: boolean,
|
||||
style?: AlertButtonStyle,
|
||||
...
|
||||
};
|
||||
|
||||
export type AlertButtons = Array<AlertButton>;
|
||||
|
||||
export type AlertOptions = {
|
||||
/** @platform android */
|
||||
cancelable?: ?boolean,
|
||||
userInterfaceStyle?: 'unspecified' | 'light' | 'dark',
|
||||
/** @platform android */
|
||||
onDismiss?: ?() => void,
|
||||
...
|
||||
};
|
||||
|
||||
/**
|
||||
* Launches an alert dialog with the specified title and message.
|
||||
*
|
||||
* Optionally provide a list of buttons. Tapping any button will fire the
|
||||
* respective onPress callback and dismiss the alert. By default, the only
|
||||
* button will be an 'OK' button.
|
||||
*
|
||||
* This is an API that works both on iOS and Android and can show static
|
||||
* alerts. On iOS, you can show an alert that prompts the user to enter
|
||||
* some information.
|
||||
*
|
||||
* See https://reactnative.dev/docs/alert
|
||||
*/
|
||||
class Alert {
|
||||
static alert(
|
||||
title: ?string,
|
||||
message?: ?string,
|
||||
buttons?: Buttons,
|
||||
buttons?: AlertButtons,
|
||||
options?: AlertOptions,
|
||||
): void {
|
||||
if (Platform.OS === 'ios') {
|
||||
@@ -53,7 +96,7 @@ class Alert {
|
||||
// At most three buttons (neutral, negative, positive). Ignore rest.
|
||||
// The text 'OK' should be probably localized. iOS Alert does that in native.
|
||||
const defaultPositiveText = 'OK';
|
||||
const validButtons: Buttons = buttons
|
||||
const validButtons: AlertButtons = buttons
|
||||
? buttons.slice(0, 3)
|
||||
: [{text: defaultPositiveText}];
|
||||
const buttonPositive = validButtons.pop();
|
||||
@@ -93,10 +136,13 @@ class Alert {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @platform ios
|
||||
*/
|
||||
static prompt(
|
||||
title: ?string,
|
||||
message?: ?string,
|
||||
callbackOrButtons?: ?(((text: string) => void) | Buttons),
|
||||
callbackOrButtons?: ?(((text: string) => void) | AlertButtons),
|
||||
type?: ?AlertType = 'plain-text',
|
||||
defaultValue?: string,
|
||||
keyboardType?: string,
|
||||
|
||||
@@ -1,20 +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.
|
||||
*
|
||||
* @format
|
||||
* @flow strict-local
|
||||
*/
|
||||
|
||||
import type {Args} from './NativeAlertManager';
|
||||
|
||||
declare const RCTAlertManager: {
|
||||
alertWithArgs(
|
||||
args: Args,
|
||||
callback: (id: number, value: string) => void,
|
||||
): void,
|
||||
};
|
||||
|
||||
export default RCTAlertManager;
|
||||
@@ -228,6 +228,21 @@ using namespace facebook::react;
|
||||
};
|
||||
}
|
||||
|
||||
if ([self.delegate respondsToSelector:@selector(loadSourceForBridge:onProgress:onComplete:)]) {
|
||||
configuration.loadSourceForBridgeWithProgress =
|
||||
^(RCTBridge *_Nonnull bridge,
|
||||
RCTSourceLoadProgressBlock _Nonnull onProgress,
|
||||
RCTSourceLoadBlock _Nonnull loadCallback) {
|
||||
[weakSelf.delegate loadSourceForBridge:bridge onProgress:onProgress onComplete:loadCallback];
|
||||
};
|
||||
}
|
||||
|
||||
if ([self.delegate respondsToSelector:@selector(loadSourceForBridge:withBlock:)]) {
|
||||
configuration.loadSourceForBridge = ^(RCTBridge *_Nonnull bridge, RCTSourceLoadBlock _Nonnull loadCallback) {
|
||||
[weakSelf.delegate loadSourceForBridge:bridge withBlock:loadCallback];
|
||||
};
|
||||
}
|
||||
|
||||
return [[RCTRootViewFactory alloc] initWithTurboModuleDelegate:self hostDelegate:self configuration:configuration];
|
||||
}
|
||||
|
||||
|
||||
@@ -31,6 +31,11 @@ typedef NSURL *_Nullable (^RCTBundleURLBlock)(void);
|
||||
typedef NSArray<id<RCTBridgeModule>> *_Nonnull (^RCTExtraModulesForBridgeBlock)(RCTBridge *bridge);
|
||||
typedef NSDictionary<NSString *, Class> *_Nonnull (^RCTExtraLazyModuleClassesForBridge)(RCTBridge *bridge);
|
||||
typedef BOOL (^RCTBridgeDidNotFindModuleBlock)(RCTBridge *bridge, NSString *moduleName);
|
||||
typedef void (^RCTLoadSourceForBridgeWithProgressBlock)(
|
||||
RCTBridge *bridge,
|
||||
RCTSourceLoadProgressBlock onProgress,
|
||||
RCTSourceLoadBlock loadCallback);
|
||||
typedef void (^RCTLoadSourceForBridgeBlock)(RCTBridge *bridge, RCTSourceLoadBlock loadCallback);
|
||||
|
||||
#pragma mark - RCTRootViewFactory Configuration
|
||||
@interface RCTRootViewFactoryConfiguration : NSObject
|
||||
@@ -145,6 +150,19 @@ typedef BOOL (^RCTBridgeDidNotFindModuleBlock)(RCTBridge *bridge, NSString *modu
|
||||
*/
|
||||
@property (nonatomic, nullable) RCTBridgeDidNotFindModuleBlock bridgeDidNotFindModule;
|
||||
|
||||
/**
|
||||
* The bridge will automatically attempt to load the JS source code from the
|
||||
* location specified by the `sourceURLForBridge:` method, however, if you want
|
||||
* to handle loading the JS yourself, you can do so by setting this property.
|
||||
*/
|
||||
@property (nonatomic, nullable) RCTLoadSourceForBridgeWithProgressBlock loadSourceForBridgeWithProgress;
|
||||
|
||||
/**
|
||||
* Similar to loadSourceForBridgeWithProgress but without progress
|
||||
* reporting.
|
||||
*/
|
||||
@property (nonatomic, nullable) RCTLoadSourceForBridgeBlock loadSourceForBridge;
|
||||
|
||||
@end
|
||||
|
||||
#pragma mark - RCTRootViewFactory
|
||||
|
||||
@@ -302,6 +302,22 @@
|
||||
return NO;
|
||||
}
|
||||
|
||||
- (void)loadSourceForBridge:(RCTBridge *)bridge withBlock:(RCTSourceLoadBlock)loadCallback
|
||||
{
|
||||
if (_configuration.loadSourceForBridge != nil) {
|
||||
_configuration.loadSourceForBridge(bridge, loadCallback);
|
||||
}
|
||||
}
|
||||
|
||||
- (void)loadSourceForBridge:(RCTBridge *)bridge
|
||||
onProgress:(RCTSourceLoadProgressBlock)onProgress
|
||||
onComplete:(RCTSourceLoadBlock)loadCallback
|
||||
{
|
||||
if (_configuration.loadSourceForBridgeWithProgress != nil) {
|
||||
_configuration.loadSourceForBridgeWithProgress(bridge, onProgress, loadCallback);
|
||||
}
|
||||
}
|
||||
|
||||
- (NSURL *)bundleURL
|
||||
{
|
||||
return self->_configuration.bundleURLBlock();
|
||||
|
||||
+3
-3
@@ -39,7 +39,7 @@ describe('onScroll', () => {
|
||||
const element = ensureInstance(maybeNode, ReactNativeElement);
|
||||
|
||||
Fantom.runOnUIThread(() => {
|
||||
Fantom.dispatchNativeEvent(
|
||||
Fantom.enqueueNativeEvent(
|
||||
element,
|
||||
'scroll',
|
||||
{
|
||||
@@ -85,13 +85,13 @@ describe('onScroll', () => {
|
||||
const element = ensureInstance(maybeNode, ReactNativeElement);
|
||||
|
||||
Fantom.runOnUIThread(() => {
|
||||
Fantom.dispatchNativeEvent(element, 'scroll', {
|
||||
Fantom.enqueueNativeEvent(element, 'scroll', {
|
||||
contentOffset: {
|
||||
x: 0,
|
||||
y: 1,
|
||||
},
|
||||
});
|
||||
Fantom.dispatchNativeEvent(
|
||||
Fantom.enqueueNativeEvent(
|
||||
element,
|
||||
'scroll',
|
||||
{
|
||||
|
||||
Vendored
+660
@@ -0,0 +1,660 @@
|
||||
/**
|
||||
* 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
|
||||
* @fantom_flags enableAccessToHostTreeInFabric:true
|
||||
* @fantom_flags enableViewCulling:true
|
||||
* @fantom_flags enableSynchronousStateUpdates:true
|
||||
*/
|
||||
|
||||
import '../../../Core/InitializeCore.js';
|
||||
import ensureInstance from '../../../../src/private/utilities/ensureInstance';
|
||||
import ReactNativeElement from '../../../../src/private/webapis/dom/nodes/ReactNativeElement';
|
||||
import View from '../../View/View';
|
||||
import ScrollView from '../ScrollView';
|
||||
import Fantom from '@react-native/fantom';
|
||||
import * as React from 'react';
|
||||
|
||||
test('basic culling', () => {
|
||||
const root = Fantom.createRoot({viewportWidth: 100, viewportHeight: 100});
|
||||
let maybeNode;
|
||||
|
||||
Fantom.runTask(() => {
|
||||
root.render(
|
||||
<ScrollView
|
||||
style={{height: 100, width: 100}}
|
||||
ref={node => {
|
||||
maybeNode = node;
|
||||
}}>
|
||||
<View
|
||||
nativeID={'child'}
|
||||
style={{height: 10, width: 10, marginTop: 45}}
|
||||
/>
|
||||
</ScrollView>,
|
||||
);
|
||||
});
|
||||
|
||||
expect(root.takeMountingManagerLogs()).toEqual([
|
||||
'Update {type: "RootView", nativeID: (root)}',
|
||||
'Create {type: "ScrollView", nativeID: (N/A)}',
|
||||
'Create {type: "View", nativeID: (N/A)}',
|
||||
'Create {type: "View", nativeID: "child"}',
|
||||
'Insert {type: "View", parentNativeID: (N/A), index: 0, nativeID: "child"}',
|
||||
'Insert {type: "View", parentNativeID: (N/A), index: 0, nativeID: (N/A)}',
|
||||
'Insert {type: "ScrollView", parentNativeID: (root), index: 0, nativeID: (N/A)}',
|
||||
]);
|
||||
|
||||
const element = ensureInstance(maybeNode, ReactNativeElement);
|
||||
|
||||
Fantom.runOnUIThread(() => {
|
||||
Fantom.scrollTo(element, {
|
||||
x: 0,
|
||||
y: 60,
|
||||
});
|
||||
});
|
||||
Fantom.runWorkLoop();
|
||||
|
||||
expect(root.takeMountingManagerLogs()).toEqual([
|
||||
'Remove {type: "View", parentNativeID: (N/A), index: 0, nativeID: "child"}',
|
||||
'Delete {type: "View", nativeID: "child"}',
|
||||
'Remove {type: "View", parentNativeID: (N/A), index: 0, nativeID: (N/A)}',
|
||||
'Delete {type: "View", nativeID: (N/A)}',
|
||||
'Update {type: "ScrollView", nativeID: (N/A)}',
|
||||
]);
|
||||
|
||||
Fantom.runOnUIThread(() => {
|
||||
Fantom.scrollTo(element, {
|
||||
x: 0,
|
||||
y: 0,
|
||||
});
|
||||
});
|
||||
Fantom.runWorkLoop();
|
||||
|
||||
expect(root.takeMountingManagerLogs()).toEqual([
|
||||
'Update {type: "ScrollView", nativeID: (N/A)}',
|
||||
'Create {type: "View", nativeID: (N/A)}',
|
||||
'Create {type: "View", nativeID: "child"}',
|
||||
'Insert {type: "View", parentNativeID: (N/A), index: 0, nativeID: "child"}',
|
||||
'Insert {type: "View", parentNativeID: (N/A), index: 0, nativeID: (N/A)}',
|
||||
]);
|
||||
});
|
||||
|
||||
test('recursive culling', () => {
|
||||
const root = Fantom.createRoot({viewportHeight: 100, viewportWidth: 100});
|
||||
let maybeNode;
|
||||
|
||||
Fantom.runTask(() => {
|
||||
root.render(
|
||||
<ScrollView
|
||||
style={{height: 100, width: 100}}
|
||||
ref={node => {
|
||||
maybeNode = node;
|
||||
}}>
|
||||
<View
|
||||
nativeID={'element A'}
|
||||
style={{height: 30, width: 30, marginTop: 25}}>
|
||||
<View nativeID={'child AA'} style={{height: 10, width: 10}} />
|
||||
<View
|
||||
nativeID={'child AB'}
|
||||
style={{height: 10, width: 10, marginTop: 5}}
|
||||
/>
|
||||
</View>
|
||||
<View
|
||||
nativeID={'element B'}
|
||||
style={{height: 30, width: 30, marginTop: 195}}>
|
||||
<View nativeID={'child BA'} style={{height: 10, width: 10}} />
|
||||
<View
|
||||
nativeID={'child BB'}
|
||||
style={{height: 10, width: 10, marginTop: 5}}
|
||||
/>
|
||||
</View>
|
||||
</ScrollView>,
|
||||
);
|
||||
});
|
||||
|
||||
expect(root.takeMountingManagerLogs()).toEqual([
|
||||
'Update {type: "RootView", nativeID: (root)}',
|
||||
'Create {type: "ScrollView", nativeID: (N/A)}',
|
||||
'Create {type: "View", nativeID: (N/A)}',
|
||||
'Create {type: "View", nativeID: "element A"}',
|
||||
'Create {type: "View", nativeID: "child AA"}',
|
||||
'Create {type: "View", nativeID: "child AB"}',
|
||||
'Insert {type: "View", parentNativeID: "element A", index: 0, nativeID: "child AA"}',
|
||||
'Insert {type: "View", parentNativeID: "element A", index: 1, nativeID: "child AB"}',
|
||||
'Insert {type: "View", parentNativeID: (N/A), index: 0, nativeID: "element A"}',
|
||||
'Insert {type: "View", parentNativeID: (N/A), index: 0, nativeID: (N/A)}',
|
||||
'Insert {type: "ScrollView", parentNativeID: (root), index: 0, nativeID: (N/A)}',
|
||||
]);
|
||||
|
||||
const element = ensureInstance(maybeNode, ReactNativeElement);
|
||||
|
||||
// === Scroll down to the edge of child AA ===
|
||||
Fantom.runOnUIThread(() => {
|
||||
Fantom.scrollTo(element, {
|
||||
x: 0,
|
||||
y: 30,
|
||||
});
|
||||
});
|
||||
Fantom.runWorkLoop();
|
||||
|
||||
expect(root.takeMountingManagerLogs()).toEqual([
|
||||
'Update {type: "ScrollView", nativeID: (N/A)}',
|
||||
]);
|
||||
|
||||
// === Scroll down past child AA ===
|
||||
Fantom.runOnUIThread(() => {
|
||||
Fantom.scrollTo(element, {
|
||||
x: 0,
|
||||
y: 36,
|
||||
});
|
||||
});
|
||||
Fantom.runWorkLoop();
|
||||
|
||||
expect(root.takeMountingManagerLogs()).toEqual([
|
||||
'Update {type: "ScrollView", nativeID: (N/A)}',
|
||||
'Remove {type: "View", parentNativeID: "element A", index: 0, nativeID: "child AA"}',
|
||||
'Delete {type: "View", nativeID: "child AA"}',
|
||||
]);
|
||||
|
||||
// === Scroll down past child AB ===
|
||||
Fantom.runOnUIThread(() => {
|
||||
Fantom.scrollTo(element, {
|
||||
x: 0,
|
||||
y: 51,
|
||||
});
|
||||
});
|
||||
Fantom.runWorkLoop();
|
||||
|
||||
expect(root.takeMountingManagerLogs()).toEqual([
|
||||
'Update {type: "ScrollView", nativeID: (N/A)}',
|
||||
'Remove {type: "View", parentNativeID: "element A", index: 0, nativeID: "child AB"}',
|
||||
'Delete {type: "View", nativeID: "child AB"}',
|
||||
]);
|
||||
|
||||
// === Scroll down past element A ===
|
||||
Fantom.runOnUIThread(() => {
|
||||
Fantom.scrollTo(element, {
|
||||
x: 0,
|
||||
y: 56,
|
||||
});
|
||||
});
|
||||
Fantom.runWorkLoop();
|
||||
|
||||
expect(root.takeMountingManagerLogs()).toEqual([
|
||||
'Update {type: "ScrollView", nativeID: (N/A)}',
|
||||
'Remove {type: "View", parentNativeID: (N/A), index: 0, nativeID: "element A"}',
|
||||
'Delete {type: "View", nativeID: "element A"}',
|
||||
]);
|
||||
|
||||
// Scroll element B into viewport. Just child BA should be created.
|
||||
Fantom.runOnUIThread(() => {
|
||||
Fantom.scrollTo(element, {
|
||||
x: 0,
|
||||
y: 155,
|
||||
});
|
||||
});
|
||||
Fantom.runWorkLoop();
|
||||
|
||||
expect(root.takeMountingManagerLogs()).toEqual([
|
||||
'Update {type: "ScrollView", nativeID: (N/A)}',
|
||||
'Create {type: "View", nativeID: "element B"}',
|
||||
'Create {type: "View", nativeID: "child BA"}',
|
||||
'Insert {type: "View", parentNativeID: "element B", index: 0, nativeID: "child BA"}',
|
||||
'Insert {type: "View", parentNativeID: (N/A), index: 0, nativeID: "element B"}',
|
||||
]);
|
||||
|
||||
// Scroll child BA into viewport.
|
||||
Fantom.runOnUIThread(() => {
|
||||
Fantom.scrollTo(element, {
|
||||
x: 0,
|
||||
y: 165,
|
||||
});
|
||||
});
|
||||
Fantom.runWorkLoop();
|
||||
|
||||
expect(root.takeMountingManagerLogs()).toEqual([
|
||||
'Update {type: "ScrollView", nativeID: (N/A)}',
|
||||
'Create {type: "View", nativeID: "child BB"}',
|
||||
'Insert {type: "View", parentNativeID: "element B", index: 1, nativeID: "child BB"}',
|
||||
]);
|
||||
|
||||
// Scroll back to start
|
||||
Fantom.runOnUIThread(() => {
|
||||
Fantom.scrollTo(element, {
|
||||
x: 0,
|
||||
y: 0,
|
||||
});
|
||||
});
|
||||
Fantom.runWorkLoop();
|
||||
|
||||
expect(root.takeMountingManagerLogs()).toEqual([
|
||||
'Update {type: "ScrollView", nativeID: (N/A)}',
|
||||
'Remove {type: "View", parentNativeID: "element B", index: 1, nativeID: "child BB"}',
|
||||
'Remove {type: "View", parentNativeID: "element B", index: 0, nativeID: "child BA"}',
|
||||
'Delete {type: "View", nativeID: "child BA"}',
|
||||
'Delete {type: "View", nativeID: "child BB"}',
|
||||
'Remove {type: "View", parentNativeID: (N/A), index: 0, nativeID: "element B"}',
|
||||
'Delete {type: "View", nativeID: "element B"}',
|
||||
'Create {type: "View", nativeID: "element A"}',
|
||||
'Create {type: "View", nativeID: "child AA"}',
|
||||
'Create {type: "View", nativeID: "child AB"}',
|
||||
'Insert {type: "View", parentNativeID: "element A", index: 0, nativeID: "child AA"}',
|
||||
'Insert {type: "View", parentNativeID: "element A", index: 1, nativeID: "child AB"}',
|
||||
'Insert {type: "View", parentNativeID: (N/A), index: 0, nativeID: "element A"}',
|
||||
]);
|
||||
|
||||
// Scroll past element A
|
||||
Fantom.runOnUIThread(() => {
|
||||
Fantom.scrollTo(element, {
|
||||
x: 0,
|
||||
y: 85,
|
||||
});
|
||||
});
|
||||
Fantom.runWorkLoop();
|
||||
|
||||
expect(root.takeMountingManagerLogs()).toEqual([
|
||||
'Update {type: "ScrollView", nativeID: (N/A)}',
|
||||
'Remove {type: "View", parentNativeID: "element A", index: 1, nativeID: "child AB"}',
|
||||
'Remove {type: "View", parentNativeID: "element A", index: 0, nativeID: "child AA"}',
|
||||
'Delete {type: "View", nativeID: "child AA"}',
|
||||
'Delete {type: "View", nativeID: "child AB"}',
|
||||
'Remove {type: "View", parentNativeID: (N/A), index: 0, nativeID: "element A"}',
|
||||
'Delete {type: "View", nativeID: "element A"}',
|
||||
]);
|
||||
});
|
||||
|
||||
test('recursive culling when initial offset is negative', () => {
|
||||
const root = Fantom.createRoot({viewportHeight: 874, viewportWidth: 402});
|
||||
let maybeNode;
|
||||
|
||||
Fantom.runTask(() => {
|
||||
root.render(
|
||||
<ScrollView
|
||||
style={{height: 874, width: 402}}
|
||||
contentOffset={{x: 0, y: -10000}}
|
||||
ref={node => {
|
||||
maybeNode = node;
|
||||
}}>
|
||||
<View
|
||||
nativeID={'child A'}
|
||||
style={{height: 100, width: 100, marginTop: 235}}
|
||||
/>
|
||||
<View
|
||||
nativeID={'child B'}
|
||||
style={{height: 100, width: 100, marginTop: 235}}>
|
||||
<View nativeID={'child BA'} style={{height: 17, width: 100}} />
|
||||
<View
|
||||
nativeID={'child BB'}
|
||||
style={{height: 17, width: 100, marginTop: 60}}
|
||||
/>
|
||||
</View>
|
||||
</ScrollView>,
|
||||
);
|
||||
});
|
||||
|
||||
expect(root.takeMountingManagerLogs()).toEqual([
|
||||
'Update {type: "RootView", nativeID: (root)}',
|
||||
'Create {type: "ScrollView", nativeID: (N/A)}',
|
||||
'Insert {type: "ScrollView", parentNativeID: (root), index: 0, nativeID: (N/A)}',
|
||||
]);
|
||||
|
||||
const element = ensureInstance(maybeNode, ReactNativeElement);
|
||||
|
||||
Fantom.runOnUIThread(() => {
|
||||
Fantom.scrollTo(element, {
|
||||
x: 0,
|
||||
y: 0,
|
||||
});
|
||||
});
|
||||
Fantom.runWorkLoop();
|
||||
|
||||
expect(root.takeMountingManagerLogs()).toEqual([
|
||||
'Update {type: "ScrollView", nativeID: (N/A)}',
|
||||
'Create {type: "View", nativeID: (N/A)}',
|
||||
'Create {type: "View", nativeID: "child A"}',
|
||||
'Create {type: "View", nativeID: "child B"}',
|
||||
'Create {type: "View", nativeID: "child BA"}',
|
||||
'Create {type: "View", nativeID: "child BB"}',
|
||||
'Insert {type: "View", parentNativeID: "child B", index: 0, nativeID: "child BA"}',
|
||||
'Insert {type: "View", parentNativeID: "child B", index: 1, nativeID: "child BB"}',
|
||||
'Insert {type: "View", parentNativeID: (N/A), index: 0, nativeID: "child A"}',
|
||||
'Insert {type: "View", parentNativeID: (N/A), index: 1, nativeID: "child B"}',
|
||||
'Insert {type: "View", parentNativeID: (N/A), index: 0, nativeID: (N/A)}',
|
||||
]);
|
||||
});
|
||||
|
||||
test('deep nesting', () => {
|
||||
const root = Fantom.createRoot({viewportHeight: 100, viewportWidth: 100});
|
||||
let maybeNode;
|
||||
|
||||
Fantom.runTask(() => {
|
||||
root.render(
|
||||
<ScrollView
|
||||
style={{height: 100, width: 100}}
|
||||
ref={node => {
|
||||
maybeNode = node;
|
||||
}}>
|
||||
<View
|
||||
nativeID={'element A'}
|
||||
style={{height: 10, width: 100, marginTop: 30}}
|
||||
/>
|
||||
<View
|
||||
nativeID={'element B'}
|
||||
style={{height: 50, width: 100, marginTop: 85}}>
|
||||
<View
|
||||
nativeID={'child BA'}
|
||||
style={{height: 30, width: 80, marginTop: 10, marginLeft: 10}}>
|
||||
<View
|
||||
nativeID={'child BAA'}
|
||||
style={{height: 10, width: 75, marginTop: 5, marginLeft: 5}}
|
||||
/>
|
||||
<View
|
||||
nativeID={'child BAB'}
|
||||
style={{height: 10, width: 75, marginTop: 15, marginLeft: 5}}
|
||||
/>
|
||||
</View>
|
||||
</View>
|
||||
</ScrollView>,
|
||||
);
|
||||
});
|
||||
|
||||
expect(root.takeMountingManagerLogs()).toEqual([
|
||||
'Update {type: "RootView", nativeID: (root)}',
|
||||
'Create {type: "ScrollView", nativeID: (N/A)}',
|
||||
'Create {type: "View", nativeID: (N/A)}',
|
||||
'Create {type: "View", nativeID: "element A"}',
|
||||
'Insert {type: "View", parentNativeID: (N/A), index: 0, nativeID: "element A"}',
|
||||
'Insert {type: "View", parentNativeID: (N/A), index: 0, nativeID: (N/A)}',
|
||||
'Insert {type: "ScrollView", parentNativeID: (root), index: 0, nativeID: (N/A)}',
|
||||
]);
|
||||
|
||||
const element = ensureInstance(maybeNode, ReactNativeElement);
|
||||
|
||||
Fantom.runOnUIThread(() => {
|
||||
Fantom.scrollTo(element, {
|
||||
x: 0,
|
||||
y: 40,
|
||||
});
|
||||
});
|
||||
Fantom.runWorkLoop();
|
||||
|
||||
expect(root.takeMountingManagerLogs()).toEqual([
|
||||
'Update {type: "ScrollView", nativeID: (N/A)}',
|
||||
'Create {type: "View", nativeID: "element B"}',
|
||||
'Create {type: "View", nativeID: "child BA"}',
|
||||
'Create {type: "View", nativeID: "child BAA"}',
|
||||
'Insert {type: "View", parentNativeID: "child BA", index: 0, nativeID: "child BAA"}',
|
||||
'Insert {type: "View", parentNativeID: "element B", index: 0, nativeID: "child BA"}',
|
||||
'Insert {type: "View", parentNativeID: (N/A), index: 1, nativeID: "element B"}',
|
||||
]);
|
||||
|
||||
Fantom.runOnUIThread(() => {
|
||||
Fantom.scrollTo(element, {
|
||||
x: 0,
|
||||
y: 150,
|
||||
});
|
||||
});
|
||||
Fantom.runWorkLoop();
|
||||
|
||||
expect(root.takeMountingManagerLogs()).toEqual([
|
||||
'Update {type: "ScrollView", nativeID: (N/A)}',
|
||||
'Remove {type: "View", parentNativeID: (N/A), index: 0, nativeID: "element A"}',
|
||||
'Delete {type: "View", nativeID: "element A"}',
|
||||
'Create {type: "View", nativeID: "child BAB"}',
|
||||
'Insert {type: "View", parentNativeID: "child BA", index: 1, nativeID: "child BAB"}',
|
||||
]);
|
||||
});
|
||||
|
||||
test('adding new item into area that is not culled', () => {
|
||||
const root = Fantom.createRoot({viewportHeight: 100, viewportWidth: 100});
|
||||
|
||||
Fantom.runTask(() => {
|
||||
root.render(
|
||||
<ScrollView style={{height: 100, width: 100}}>
|
||||
<View
|
||||
nativeID={'element A'}
|
||||
style={{height: 20, width: 20, marginTop: 30}}
|
||||
/>
|
||||
</ScrollView>,
|
||||
);
|
||||
});
|
||||
|
||||
expect(root.takeMountingManagerLogs()).toEqual([
|
||||
'Update {type: "RootView", nativeID: (root)}',
|
||||
'Create {type: "ScrollView", nativeID: (N/A)}',
|
||||
'Create {type: "View", nativeID: (N/A)}',
|
||||
'Create {type: "View", nativeID: "element A"}',
|
||||
'Insert {type: "View", parentNativeID: (N/A), index: 0, nativeID: "element A"}',
|
||||
'Insert {type: "View", parentNativeID: (N/A), index: 0, nativeID: (N/A)}',
|
||||
'Insert {type: "ScrollView", parentNativeID: (root), index: 0, nativeID: (N/A)}',
|
||||
]);
|
||||
|
||||
Fantom.runTask(() => {
|
||||
root.render(
|
||||
<ScrollView style={{height: 100, width: 100}}>
|
||||
<View
|
||||
nativeID={'element A'}
|
||||
style={{height: 20, width: 20, marginTop: 30}}>
|
||||
<View nativeID={'child AA'} style={{height: 20, width: 20}} />
|
||||
</View>
|
||||
</ScrollView>,
|
||||
);
|
||||
});
|
||||
|
||||
expect(root.takeMountingManagerLogs()).toEqual([
|
||||
'Create {type: "View", nativeID: "child AA"}',
|
||||
'Insert {type: "View", parentNativeID: "element A", index: 0, nativeID: "child AA"}',
|
||||
]);
|
||||
});
|
||||
|
||||
test('adding new item into area that is culled', () => {
|
||||
const root = Fantom.createRoot({viewportHeight: 100, viewportWidth: 100});
|
||||
|
||||
Fantom.runTask(() => {
|
||||
root.render(
|
||||
<ScrollView
|
||||
contentOffset={{x: 0, y: 45}}
|
||||
style={{height: 100, width: 100}}>
|
||||
<View
|
||||
key="element B"
|
||||
nativeID={'element B'}
|
||||
style={{height: 20, width: 20, marginTop: 30}}
|
||||
/>
|
||||
</ScrollView>,
|
||||
);
|
||||
});
|
||||
|
||||
expect(root.takeMountingManagerLogs()).toEqual([
|
||||
'Update {type: "RootView", nativeID: (root)}',
|
||||
'Create {type: "ScrollView", nativeID: (N/A)}',
|
||||
'Create {type: "View", nativeID: (N/A)}',
|
||||
'Create {type: "View", nativeID: "element B"}',
|
||||
'Insert {type: "View", parentNativeID: (N/A), index: 0, nativeID: "element B"}',
|
||||
'Insert {type: "View", parentNativeID: (N/A), index: 0, nativeID: (N/A)}',
|
||||
'Insert {type: "ScrollView", parentNativeID: (root), index: 0, nativeID: (N/A)}',
|
||||
]);
|
||||
|
||||
Fantom.runTask(() => {
|
||||
root.render(
|
||||
<ScrollView
|
||||
contentOffset={{x: 0, y: 45}}
|
||||
style={{height: 100, width: 100}}>
|
||||
<View
|
||||
key="element A"
|
||||
nativeID={'element A'}
|
||||
style={{height: 20, width: 20}}
|
||||
/>
|
||||
<View
|
||||
key="element B"
|
||||
nativeID={'element B'}
|
||||
style={{height: 20, width: 20, marginTop: 10}}
|
||||
/>
|
||||
</ScrollView>,
|
||||
);
|
||||
});
|
||||
|
||||
// element B is updated but it should be inconsequential.
|
||||
// Differentiator generates an update for it because Yoga cloned
|
||||
// shadow node backing element B.
|
||||
expect(root.takeMountingManagerLogs()).toEqual([
|
||||
'Update {type: "View", nativeID: "element B"}',
|
||||
]);
|
||||
});
|
||||
|
||||
test('initial render', () => {
|
||||
let maybeNode;
|
||||
const root = Fantom.createRoot({viewportHeight: 100, viewportWidth: 100});
|
||||
|
||||
Fantom.runTask(() => {
|
||||
root.render(
|
||||
<ScrollView
|
||||
contentOffset={{x: 0, y: 45}}
|
||||
ref={node => {
|
||||
maybeNode = node;
|
||||
}}
|
||||
style={{height: 100, width: 100}}>
|
||||
<View nativeID={'element A'} style={{height: 50, width: 100}} />
|
||||
<View
|
||||
nativeID={'element B'}
|
||||
style={{height: 50, width: 100, marginTop: 100}}>
|
||||
<View nativeID={'child BA'} style={{height: 20, width: 100}} />
|
||||
<View
|
||||
nativeID={'child BB'}
|
||||
style={{height: 20, width: 100, marginTop: 10}}
|
||||
/>
|
||||
</View>
|
||||
</ScrollView>,
|
||||
);
|
||||
});
|
||||
|
||||
expect(root.takeMountingManagerLogs()).toEqual([
|
||||
'Update {type: "RootView", nativeID: (root)}',
|
||||
'Create {type: "ScrollView", nativeID: (N/A)}',
|
||||
'Create {type: "View", nativeID: (N/A)}',
|
||||
'Create {type: "View", nativeID: "element A"}',
|
||||
'Insert {type: "View", parentNativeID: (N/A), index: 0, nativeID: "element A"}',
|
||||
'Insert {type: "View", parentNativeID: (N/A), index: 0, nativeID: (N/A)}',
|
||||
'Insert {type: "ScrollView", parentNativeID: (root), index: 0, nativeID: (N/A)}',
|
||||
]);
|
||||
|
||||
const element = ensureInstance(maybeNode, ReactNativeElement);
|
||||
|
||||
Fantom.runOnUIThread(() => {
|
||||
Fantom.scrollTo(element, {
|
||||
x: 0,
|
||||
y: 100,
|
||||
});
|
||||
});
|
||||
Fantom.runWorkLoop();
|
||||
|
||||
expect(root.takeMountingManagerLogs()).toEqual([
|
||||
'Update {type: "ScrollView", nativeID: (N/A)}',
|
||||
'Remove {type: "View", parentNativeID: (N/A), index: 0, nativeID: "element A"}',
|
||||
'Delete {type: "View", nativeID: "element A"}',
|
||||
'Create {type: "View", nativeID: "element B"}',
|
||||
'Create {type: "View", nativeID: "child BA"}',
|
||||
'Create {type: "View", nativeID: "child BB"}',
|
||||
'Insert {type: "View", parentNativeID: "element B", index: 0, nativeID: "child BA"}',
|
||||
'Insert {type: "View", parentNativeID: "element B", index: 1, nativeID: "child BB"}',
|
||||
'Insert {type: "View", parentNativeID: (N/A), index: 0, nativeID: "element B"}',
|
||||
]);
|
||||
});
|
||||
|
||||
test('unmounting culled elements', () => {
|
||||
const root = Fantom.createRoot({viewportWidth: 100, viewportHeight: 100});
|
||||
|
||||
Fantom.runTask(() => {
|
||||
root.render(
|
||||
<ScrollView
|
||||
style={{height: 100, width: 100}}
|
||||
contentOffset={{x: 0, y: 20}}>
|
||||
<View nativeID={'element 1'} style={{height: 10, width: 10}} />
|
||||
</ScrollView>,
|
||||
);
|
||||
});
|
||||
|
||||
expect(root.takeMountingManagerLogs()).toEqual([
|
||||
'Update {type: "RootView", nativeID: (root)}',
|
||||
'Create {type: "ScrollView", nativeID: (N/A)}',
|
||||
'Insert {type: "ScrollView", parentNativeID: (root), index: 0, nativeID: (N/A)}',
|
||||
]);
|
||||
|
||||
Fantom.runTask(() => {
|
||||
root.render(<></>);
|
||||
});
|
||||
|
||||
expect(root.takeMountingManagerLogs()).toEqual([
|
||||
'Remove {type: "ScrollView", parentNativeID: (root), index: 0, nativeID: (N/A)}',
|
||||
'Delete {type: "ScrollView", nativeID: (N/A)}',
|
||||
]);
|
||||
});
|
||||
|
||||
// TODO: only elements in ScrollView are culled.
|
||||
test('basic culling smaller ScrollView', () => {
|
||||
let maybeNode;
|
||||
const root = Fantom.createRoot({viewportWidth: 100, viewportHeight: 100});
|
||||
|
||||
Fantom.runTask(() => {
|
||||
root.render(
|
||||
<ScrollView
|
||||
ref={node => {
|
||||
maybeNode = node;
|
||||
}}
|
||||
style={{height: 50, width: 50, marginTop: 25}}>
|
||||
<View nativeID={'element 1'} style={{height: 10, width: 10}} />
|
||||
</ScrollView>,
|
||||
);
|
||||
});
|
||||
|
||||
expect(root.takeMountingManagerLogs()).toEqual([
|
||||
'Update {type: "RootView", nativeID: (root)}',
|
||||
'Create {type: "ScrollView", nativeID: (N/A)}',
|
||||
'Create {type: "View", nativeID: (N/A)}',
|
||||
'Create {type: "View", nativeID: "element 1"}',
|
||||
'Insert {type: "View", parentNativeID: (N/A), index: 0, nativeID: "element 1"}',
|
||||
'Insert {type: "View", parentNativeID: (N/A), index: 0, nativeID: (N/A)}',
|
||||
'Insert {type: "ScrollView", parentNativeID: (root), index: 0, nativeID: (N/A)}',
|
||||
]);
|
||||
|
||||
const element = ensureInstance(maybeNode, ReactNativeElement);
|
||||
|
||||
Fantom.runOnUIThread(() => {
|
||||
Fantom.scrollTo(element, {
|
||||
x: 0,
|
||||
y: 11,
|
||||
});
|
||||
});
|
||||
Fantom.runWorkLoop();
|
||||
|
||||
expect(root.takeMountingManagerLogs()).toEqual([
|
||||
'Remove {type: "View", parentNativeID: (N/A), index: 0, nativeID: "element 1"}',
|
||||
'Delete {type: "View", nativeID: "element 1"}',
|
||||
'Remove {type: "View", parentNativeID: (N/A), index: 0, nativeID: (N/A)}',
|
||||
'Delete {type: "View", nativeID: (N/A)}',
|
||||
'Update {type: "ScrollView", nativeID: (N/A)}',
|
||||
]);
|
||||
});
|
||||
|
||||
test('views are not culled when outside of viewport', () => {
|
||||
const root = Fantom.createRoot({viewportWidth: 100, viewportHeight: 100});
|
||||
|
||||
Fantom.runTask(() => {
|
||||
root.render(
|
||||
<View
|
||||
nativeID={'child'}
|
||||
style={{height: 10, width: 10, marginTop: 101}}
|
||||
/>,
|
||||
);
|
||||
});
|
||||
|
||||
expect(root.takeMountingManagerLogs()).toEqual([
|
||||
'Update {type: "RootView", nativeID: (root)}',
|
||||
'Create {type: "View", nativeID: "child"}',
|
||||
'Insert {type: "View", parentNativeID: (root), index: 0, nativeID: "child"}',
|
||||
]);
|
||||
});
|
||||
+4
-4
@@ -124,7 +124,7 @@ describe('focus and blur event', () => {
|
||||
expect(blurEvent).toHaveBeenCalledTimes(0);
|
||||
|
||||
Fantom.runOnUIThread(() => {
|
||||
Fantom.dispatchNativeEvent(element, 'focus');
|
||||
Fantom.enqueueNativeEvent(element, 'focus');
|
||||
});
|
||||
|
||||
// The tasks have not run.
|
||||
@@ -137,7 +137,7 @@ describe('focus and blur event', () => {
|
||||
expect(blurEvent).toHaveBeenCalledTimes(0);
|
||||
|
||||
Fantom.runOnUIThread(() => {
|
||||
Fantom.dispatchNativeEvent(element, 'blur');
|
||||
Fantom.enqueueNativeEvent(element, 'blur');
|
||||
});
|
||||
|
||||
Fantom.runWorkLoop();
|
||||
@@ -169,7 +169,7 @@ describe('onChange', () => {
|
||||
const element = ensureInstance(maybeNode, ReactNativeElement);
|
||||
|
||||
Fantom.runOnUIThread(() => {
|
||||
Fantom.dispatchNativeEvent(element, 'change', {
|
||||
Fantom.enqueueNativeEvent(element, 'change', {
|
||||
text: 'Hello World',
|
||||
});
|
||||
});
|
||||
@@ -202,7 +202,7 @@ describe('onChangeText', () => {
|
||||
const element = ensureInstance(maybeNode, ReactNativeElement);
|
||||
|
||||
Fantom.runOnUIThread(() => {
|
||||
Fantom.dispatchNativeEvent(element, 'change', {
|
||||
Fantom.enqueueNativeEvent(element, 'change', {
|
||||
text: 'Hello World',
|
||||
});
|
||||
});
|
||||
|
||||
@@ -76,20 +76,6 @@ export interface ViewPropsIOS extends TVViewPropsIOS {
|
||||
}
|
||||
|
||||
export interface ViewPropsAndroid {
|
||||
/**
|
||||
* Views that are only used to layout their children or otherwise don't draw anything
|
||||
* may be automatically removed from the native hierarchy as an optimization.
|
||||
* Set this property to false to disable this optimization and ensure that this View exists in the native view hierarchy.
|
||||
*/
|
||||
collapsable?: boolean | undefined;
|
||||
|
||||
/**
|
||||
* Setting to false prevents direct children of the view from being removed
|
||||
* from the native view hierarchy, similar to the effect of setting
|
||||
* `collapsable={false}` on each child.
|
||||
*/
|
||||
collapsableChildren?: boolean | undefined;
|
||||
|
||||
/**
|
||||
* Whether this view should render itself (and all of its children) into a single hardware texture on the GPU.
|
||||
*
|
||||
@@ -211,4 +197,18 @@ export interface ViewProps
|
||||
* Used to reference react managed views from native code.
|
||||
*/
|
||||
nativeID?: string | undefined;
|
||||
|
||||
/**
|
||||
* Views that are only used to layout their children or otherwise don't draw anything
|
||||
* may be automatically removed from the native hierarchy as an optimization.
|
||||
* Set this property to false to disable this optimization and ensure that this View exists in the native view hierarchy.
|
||||
*/
|
||||
collapsable?: boolean | undefined;
|
||||
|
||||
/**
|
||||
* Setting to false prevents direct children of the view from being removed
|
||||
* from the native view hierarchy, similar to the effect of setting
|
||||
* `collapsable={false}` on each child.
|
||||
*/
|
||||
collapsableChildren?: boolean | undefined;
|
||||
}
|
||||
|
||||
+2
-2
@@ -21,7 +21,7 @@ let thousandViews: React.MixedElement;
|
||||
|
||||
Fantom.unstable_benchmark
|
||||
.suite('View')
|
||||
.add(
|
||||
.test(
|
||||
'render 100 uncollapsable views',
|
||||
() => {
|
||||
Fantom.runTask(() => root.render(thousandViews));
|
||||
@@ -51,7 +51,7 @@ Fantom.unstable_benchmark
|
||||
},
|
||||
},
|
||||
)
|
||||
.add(
|
||||
.test(
|
||||
'render 1000 uncollapsable views',
|
||||
() => {
|
||||
Fantom.runTask(() => root.render(thousandViews));
|
||||
|
||||
@@ -25,6 +25,14 @@ type Task =
|
||||
}
|
||||
| (() => void);
|
||||
|
||||
// NOTE: The original implementation of `InteractionManager` never rejected
|
||||
// the returned promise. This preserves that behavior in the stub.
|
||||
function reject(error: Error): void {
|
||||
setTimeout(() => {
|
||||
throw error;
|
||||
}, 0);
|
||||
}
|
||||
|
||||
/**
|
||||
* InteractionManager allows long-running work to be scheduled after any
|
||||
* interactions/animations have completed. In particular, this allows JavaScript
|
||||
@@ -97,7 +105,7 @@ const InteractionManagerStub = {
|
||||
...
|
||||
} {
|
||||
let immediateID: ?$FlowIssue;
|
||||
const promise = new Promise((resolve, reject) => {
|
||||
const promise = new Promise(resolve => {
|
||||
immediateID = setImmediate(() => {
|
||||
if (typeof task === 'object' && task !== null) {
|
||||
if (typeof task.gen === 'function') {
|
||||
|
||||
@@ -19,6 +19,7 @@ import * as LogBoxStyle from './LogBoxStyle';
|
||||
import * as React from 'react';
|
||||
|
||||
type Props = $ReadOnly<{
|
||||
id?: string,
|
||||
backgroundColor: $ReadOnly<{
|
||||
default: string,
|
||||
pressed: string,
|
||||
@@ -42,6 +43,7 @@ function LogBoxButton(props: Props): React.Node {
|
||||
|
||||
const content = (
|
||||
<View
|
||||
id={props.id}
|
||||
style={StyleSheet.compose(
|
||||
{
|
||||
backgroundColor: pressed
|
||||
|
||||
@@ -36,7 +36,9 @@ export default function LogBoxInspectorHeader(props: Props): React.Node {
|
||||
<LogBoxInspectorHeaderSafeArea style={styles[props.level]}>
|
||||
<View style={styles.header}>
|
||||
<View style={styles.title}>
|
||||
<Text style={styles.titleText}>Failed to compile</Text>
|
||||
<Text style={styles.titleText} id="logbox_header_title_text">
|
||||
Failed to compile
|
||||
</Text>
|
||||
</View>
|
||||
</View>
|
||||
</LogBoxInspectorHeaderSafeArea>
|
||||
@@ -60,7 +62,9 @@ export default function LogBoxInspectorHeader(props: Props): React.Node {
|
||||
onPress={() => props.onSelectIndex(prevIndex)}
|
||||
/>
|
||||
<View style={styles.title}>
|
||||
<Text style={styles.titleText}>{titleText}</Text>
|
||||
<Text style={styles.titleText} id="logbox_header_title_text">
|
||||
{titleText}
|
||||
</Text>
|
||||
</View>
|
||||
<LogBoxInspectorHeaderButton
|
||||
disabled={props.total <= 1}
|
||||
|
||||
@@ -46,11 +46,13 @@ function LogBoxInspectorMessageHeader(props: Props): React.Node {
|
||||
return (
|
||||
<View style={messageStyles.body}>
|
||||
<View style={messageStyles.heading}>
|
||||
<Text style={[messageStyles.headingText, messageStyles[props.level]]}>
|
||||
<Text
|
||||
style={[messageStyles.headingText, messageStyles[props.level]]}
|
||||
id="logbox_message_title_text">
|
||||
{props.title}
|
||||
</Text>
|
||||
</View>
|
||||
<Text style={messageStyles.bodyText}>
|
||||
<Text style={messageStyles.bodyText} id="logbox_message_contents_text">
|
||||
<LogBoxMessage
|
||||
maxLength={props.collapsed ? SHOW_MORE_MESSAGE_LENGTH : Infinity}
|
||||
message={props.message}
|
||||
|
||||
@@ -39,6 +39,7 @@ export default function LogBoxNotification(props: Props): React.Node {
|
||||
return (
|
||||
<View style={styles.container}>
|
||||
<LogBoxButton
|
||||
id={`logbox_button_${level}`}
|
||||
onPress={props.onPressOpen}
|
||||
style={styles.press}
|
||||
backgroundColor={{
|
||||
|
||||
+4
@@ -36,6 +36,7 @@ exports[`LogBoxInspectorHeader should render both buttons for two total 1`] = `
|
||||
}
|
||||
>
|
||||
<Text
|
||||
id="logbox_header_title_text"
|
||||
style={
|
||||
Object {
|
||||
"color": "rgba(255, 255, 255, 1)",
|
||||
@@ -99,6 +100,7 @@ exports[`LogBoxInspectorHeader should render no buttons for one total 1`] = `
|
||||
}
|
||||
>
|
||||
<Text
|
||||
id="logbox_header_title_text"
|
||||
style={
|
||||
Object {
|
||||
"color": "rgba(255, 255, 255, 1)",
|
||||
@@ -152,6 +154,7 @@ exports[`LogBoxInspectorHeader should render syntax error header 1`] = `
|
||||
}
|
||||
>
|
||||
<Text
|
||||
id="logbox_header_title_text"
|
||||
style={
|
||||
Object {
|
||||
"color": "rgba(255, 255, 255, 1)",
|
||||
@@ -205,6 +208,7 @@ exports[`LogBoxInspectorHeader should render two buttons for three or more total
|
||||
}
|
||||
>
|
||||
<Text
|
||||
id="logbox_header_title_text"
|
||||
style={
|
||||
Object {
|
||||
"color": "rgba(255, 255, 255, 1)",
|
||||
|
||||
+12
@@ -28,6 +28,7 @@ exports[`LogBoxInspectorMessageHeader should not render "See More" if expanded 1
|
||||
}
|
||||
>
|
||||
<Text
|
||||
id="logbox_message_title_text"
|
||||
style={
|
||||
Array [
|
||||
Object {
|
||||
@@ -47,6 +48,7 @@ exports[`LogBoxInspectorMessageHeader should not render "See More" if expanded 1
|
||||
</Text>
|
||||
</View>
|
||||
<Text
|
||||
id="logbox_message_contents_text"
|
||||
style={
|
||||
Object {
|
||||
"color": "rgba(255, 255, 255, 1)",
|
||||
@@ -105,6 +107,7 @@ exports[`LogBoxInspectorMessageHeader should not render See More button for shor
|
||||
}
|
||||
>
|
||||
<Text
|
||||
id="logbox_message_title_text"
|
||||
style={
|
||||
Array [
|
||||
Object {
|
||||
@@ -124,6 +127,7 @@ exports[`LogBoxInspectorMessageHeader should not render See More button for shor
|
||||
</Text>
|
||||
</View>
|
||||
<Text
|
||||
id="logbox_message_contents_text"
|
||||
style={
|
||||
Object {
|
||||
"color": "rgba(255, 255, 255, 1)",
|
||||
@@ -182,6 +186,7 @@ exports[`LogBoxInspectorMessageHeader should render "See More" if collapsed 1`]
|
||||
}
|
||||
>
|
||||
<Text
|
||||
id="logbox_message_title_text"
|
||||
style={
|
||||
Array [
|
||||
Object {
|
||||
@@ -201,6 +206,7 @@ exports[`LogBoxInspectorMessageHeader should render "See More" if collapsed 1`]
|
||||
</Text>
|
||||
</View>
|
||||
<Text
|
||||
id="logbox_message_contents_text"
|
||||
style={
|
||||
Object {
|
||||
"color": "rgba(255, 255, 255, 1)",
|
||||
@@ -272,6 +278,7 @@ exports[`LogBoxInspectorMessageHeader should render error 1`] = `
|
||||
}
|
||||
>
|
||||
<Text
|
||||
id="logbox_message_title_text"
|
||||
style={
|
||||
Array [
|
||||
Object {
|
||||
@@ -291,6 +298,7 @@ exports[`LogBoxInspectorMessageHeader should render error 1`] = `
|
||||
</Text>
|
||||
</View>
|
||||
<Text
|
||||
id="logbox_message_contents_text"
|
||||
style={
|
||||
Object {
|
||||
"color": "rgba(255, 255, 255, 1)",
|
||||
@@ -349,6 +357,7 @@ exports[`LogBoxInspectorMessageHeader should render fatal 1`] = `
|
||||
}
|
||||
>
|
||||
<Text
|
||||
id="logbox_message_title_text"
|
||||
style={
|
||||
Array [
|
||||
Object {
|
||||
@@ -368,6 +377,7 @@ exports[`LogBoxInspectorMessageHeader should render fatal 1`] = `
|
||||
</Text>
|
||||
</View>
|
||||
<Text
|
||||
id="logbox_message_contents_text"
|
||||
style={
|
||||
Object {
|
||||
"color": "rgba(255, 255, 255, 1)",
|
||||
@@ -426,6 +436,7 @@ exports[`LogBoxInspectorMessageHeader should render syntax error 1`] = `
|
||||
}
|
||||
>
|
||||
<Text
|
||||
id="logbox_message_title_text"
|
||||
style={
|
||||
Array [
|
||||
Object {
|
||||
@@ -445,6 +456,7 @@ exports[`LogBoxInspectorMessageHeader should render syntax error 1`] = `
|
||||
</Text>
|
||||
</View>
|
||||
<Text
|
||||
id="logbox_message_contents_text"
|
||||
style={
|
||||
Object {
|
||||
"color": "rgba(255, 255, 255, 1)",
|
||||
|
||||
+1
@@ -20,6 +20,7 @@ exports[`LogBoxNotification should render log 1`] = `
|
||||
"pressed": "rgba(51, 51, 51, 0.9)",
|
||||
}
|
||||
}
|
||||
id="logbox_button_warn"
|
||||
onPress={[Function]}
|
||||
style={
|
||||
Object {
|
||||
|
||||
@@ -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.
|
||||
*
|
||||
* @flow strict-local
|
||||
* @format
|
||||
* @oncall react_native
|
||||
* @fantom_flags enableAccessToHostTreeInFabric:true
|
||||
*/
|
||||
|
||||
import ensureInstance from '../../../src/private/utilities/ensureInstance';
|
||||
import ReadOnlyElement from '../../../src/private/webapis/dom/nodes/ReadOnlyElement';
|
||||
import View from '../../Components/View/View';
|
||||
import AppContainer from '../../ReactNative/AppContainer';
|
||||
import LogBoxInspectorContainer from '../LogBoxInspectorContainer';
|
||||
import {
|
||||
ManualConsoleError,
|
||||
// $FlowExpectedError[untyped-import]
|
||||
} from './__fixtures__/ReactWarningFixtures';
|
||||
import Fantom from '@react-native/fantom';
|
||||
import nullthrows from 'nullthrows';
|
||||
import * as React from 'react';
|
||||
|
||||
import '../../Core/InitializeCore.js';
|
||||
|
||||
function findById(node: ReadOnlyElement, id: string): ?ReadOnlyElement {
|
||||
if (node.id === id) {
|
||||
return node;
|
||||
}
|
||||
|
||||
for (const child of node.children) {
|
||||
const found = findById(child, id);
|
||||
if (found) {
|
||||
return found;
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
describe('LogBox', () => {
|
||||
let originalConsoleError;
|
||||
let originalConsoleWarn;
|
||||
let mockError;
|
||||
let mockWarn;
|
||||
|
||||
beforeAll(() => {
|
||||
originalConsoleError = console.error;
|
||||
originalConsoleWarn = console.warn;
|
||||
});
|
||||
|
||||
beforeEach(() => {
|
||||
mockError = jest.fn((...args) => {
|
||||
originalConsoleError(...args);
|
||||
});
|
||||
mockWarn = jest.fn((...args) => {
|
||||
originalConsoleWarn(...args);
|
||||
});
|
||||
// $FlowExpectedError[cannot-write]
|
||||
console.error = mockError;
|
||||
// $FlowExpectedError[cannot-write]
|
||||
console.warn = mockWarn;
|
||||
});
|
||||
|
||||
afterEach(() => {
|
||||
// $FlowExpectedError[cannot-write]
|
||||
console.error = originalConsoleError;
|
||||
// $FlowExpectedError[cannot-write]
|
||||
console.warn = originalConsoleWarn;
|
||||
});
|
||||
|
||||
it('renders an empty screen if there are no errors', () => {
|
||||
const logBoxRoot = Fantom.createRoot();
|
||||
Fantom.runTask(() => {
|
||||
logBoxRoot.render(<LogBoxInspectorContainer />);
|
||||
});
|
||||
|
||||
expect(logBoxRoot.getRenderedOutput().toJSX()).toBe(null);
|
||||
});
|
||||
|
||||
it('handles a manual console.error without a component stack in LogBox', () => {
|
||||
let maybeViewNode;
|
||||
|
||||
const logBoxRoot = Fantom.createRoot();
|
||||
Fantom.runTask(() => {
|
||||
logBoxRoot.render(
|
||||
<View
|
||||
ref={node => {
|
||||
maybeViewNode = node;
|
||||
}}>
|
||||
<LogBoxInspectorContainer />
|
||||
</View>,
|
||||
);
|
||||
});
|
||||
|
||||
expect(logBoxRoot.getRenderedOutput().toJSX()).toBe(null);
|
||||
|
||||
const logBoxRootNode = ensureInstance(maybeViewNode, ReadOnlyElement);
|
||||
|
||||
const root = Fantom.createRoot();
|
||||
Fantom.runTask(() => {
|
||||
root.render(
|
||||
<View
|
||||
ref={node => {
|
||||
maybeViewNode = node;
|
||||
}}>
|
||||
<AppContainer rootTag={root.getRootTag()}>
|
||||
<ManualConsoleError />
|
||||
</AppContainer>
|
||||
</View>,
|
||||
);
|
||||
});
|
||||
|
||||
expect(logBoxRoot.getRenderedOutput().toJSX()).toBe(null);
|
||||
|
||||
const appRootNode = ensureInstance(maybeViewNode, ReadOnlyElement);
|
||||
const logBoxButton = nullthrows(
|
||||
findById(appRootNode, 'logbox_button_error'),
|
||||
);
|
||||
|
||||
Fantom.dispatchNativeEvent(logBoxButton, 'click');
|
||||
|
||||
const headerTitle = findById(logBoxRootNode, 'logbox_header_title_text');
|
||||
const messageTitle = findById(logBoxRootNode, 'logbox_message_title_text');
|
||||
const messageContents = findById(
|
||||
logBoxRootNode,
|
||||
'logbox_message_contents_text',
|
||||
);
|
||||
|
||||
expect(headerTitle?.textContent).toBe('Log 1 of 1');
|
||||
expect(messageTitle?.textContent).toBe('Console Error');
|
||||
expect(messageContents?.textContent).toBe('Manual console error');
|
||||
});
|
||||
});
|
||||
@@ -45,6 +45,19 @@ export type ResponseType =
|
||||
| 'text';
|
||||
export type Response = ?Object | string;
|
||||
|
||||
type XHRInterceptor = interface {
|
||||
requestSent(id: number, url: string, method: string, headers: Object): void,
|
||||
responseReceived(
|
||||
id: number,
|
||||
url: string,
|
||||
status: number,
|
||||
headers: Object,
|
||||
): void,
|
||||
dataReceived(id: number, data: string): void,
|
||||
loadingFinished(id: number, encodedDataLength: number): void,
|
||||
loadingFailed(id: number, error: string): void,
|
||||
};
|
||||
|
||||
// The native blob module is optional so inject it here if available.
|
||||
if (BlobManager.isAvailable) {
|
||||
BlobManager.addNetworkingHandler();
|
||||
@@ -120,6 +133,7 @@ class XMLHttpRequest extends EventTarget {
|
||||
static LOADING: number = LOADING;
|
||||
static DONE: number = DONE;
|
||||
|
||||
static _interceptor: ?XHRInterceptor = null;
|
||||
static _profiling: boolean = false;
|
||||
|
||||
UNSENT: number = UNSENT;
|
||||
@@ -157,6 +171,10 @@ class XMLHttpRequest extends EventTarget {
|
||||
_startTime: ?number = null;
|
||||
_performanceLogger: IPerformanceLogger = GlobalPerformanceLogger;
|
||||
|
||||
static __setInterceptor_DO_NOT_USE(interceptor: ?XHRInterceptor) {
|
||||
XMLHttpRequest._interceptor = interceptor;
|
||||
}
|
||||
|
||||
static enableProfiling(enableProfiling: boolean): void {
|
||||
XMLHttpRequest._profiling = enableProfiling;
|
||||
}
|
||||
@@ -283,10 +301,20 @@ class XMLHttpRequest extends EventTarget {
|
||||
return this._cachedResponse;
|
||||
}
|
||||
|
||||
// exposed for testing
|
||||
__didCreateRequest(requestId: number): void {
|
||||
this._requestId = requestId;
|
||||
|
||||
XMLHttpRequest._interceptor &&
|
||||
XMLHttpRequest._interceptor.requestSent(
|
||||
requestId,
|
||||
this._url || '',
|
||||
this._method || 'GET',
|
||||
this._headers,
|
||||
);
|
||||
}
|
||||
|
||||
// exposed for testing
|
||||
__didUploadProgress(
|
||||
requestId: number,
|
||||
progress: number,
|
||||
@@ -321,6 +349,14 @@ class XMLHttpRequest extends EventTarget {
|
||||
} else {
|
||||
delete this.responseURL;
|
||||
}
|
||||
|
||||
XMLHttpRequest._interceptor &&
|
||||
XMLHttpRequest._interceptor.responseReceived(
|
||||
requestId,
|
||||
responseURL || this._url || '',
|
||||
status,
|
||||
responseHeaders || {},
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -331,6 +367,9 @@ class XMLHttpRequest extends EventTarget {
|
||||
this._response = response;
|
||||
this._cachedResponse = undefined; // force lazy recomputation
|
||||
this.setReadyState(this.LOADING);
|
||||
|
||||
XMLHttpRequest._interceptor &&
|
||||
XMLHttpRequest._interceptor.dataReceived(requestId, response);
|
||||
}
|
||||
|
||||
__didReceiveIncrementalData(
|
||||
@@ -353,6 +392,8 @@ class XMLHttpRequest extends EventTarget {
|
||||
'Track:XMLHttpRequest:Incremental Data: ' + this._getMeasureURL(),
|
||||
);
|
||||
}
|
||||
XMLHttpRequest._interceptor &&
|
||||
XMLHttpRequest._interceptor.dataReceived(requestId, responseText);
|
||||
|
||||
this.setReadyState(this.LOADING);
|
||||
this.__didReceiveDataProgress(requestId, progress, total);
|
||||
@@ -376,6 +417,7 @@ class XMLHttpRequest extends EventTarget {
|
||||
);
|
||||
}
|
||||
|
||||
// exposed for testing
|
||||
__didCompleteResponse(
|
||||
requestId: number,
|
||||
error: string,
|
||||
@@ -401,6 +443,16 @@ class XMLHttpRequest extends EventTarget {
|
||||
end: performance.now(),
|
||||
});
|
||||
}
|
||||
if (error) {
|
||||
XMLHttpRequest._interceptor &&
|
||||
XMLHttpRequest._interceptor.loadingFailed(requestId, error);
|
||||
} else {
|
||||
XMLHttpRequest._interceptor &&
|
||||
XMLHttpRequest._interceptor.loadingFinished(
|
||||
requestId,
|
||||
this._response.length,
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -34,6 +34,19 @@ export type ResponseType =
|
||||
| 'text';
|
||||
export type Response = ?Object | string;
|
||||
|
||||
type XHRInterceptor = interface {
|
||||
requestSent(id: number, url: string, method: string, headers: Object): void,
|
||||
responseReceived(
|
||||
id: number,
|
||||
url: string,
|
||||
status: number,
|
||||
headers: Object,
|
||||
): void,
|
||||
dataReceived(id: number, data: string): void,
|
||||
loadingFinished(id: number, encodedDataLength: number): void,
|
||||
loadingFailed(id: number, error: string): void,
|
||||
};
|
||||
|
||||
// The native blob module is optional so inject it here if available.
|
||||
if (BlobManager.isAvailable) {
|
||||
BlobManager.addNetworkingHandler();
|
||||
@@ -88,6 +101,7 @@ class XMLHttpRequest extends (EventTarget(...XHR_EVENTS): typeof EventTarget) {
|
||||
static LOADING: number = LOADING;
|
||||
static DONE: number = DONE;
|
||||
|
||||
static _interceptor: ?XHRInterceptor = null;
|
||||
static _profiling: boolean = false;
|
||||
|
||||
UNSENT: number = UNSENT;
|
||||
@@ -135,6 +149,10 @@ class XMLHttpRequest extends (EventTarget(...XHR_EVENTS): typeof EventTarget) {
|
||||
_startTime: ?number = null;
|
||||
_performanceLogger: IPerformanceLogger = GlobalPerformanceLogger;
|
||||
|
||||
static __setInterceptor_DO_NOT_USE(interceptor: ?XHRInterceptor) {
|
||||
XMLHttpRequest._interceptor = interceptor;
|
||||
}
|
||||
|
||||
static enableProfiling(enableProfiling: boolean): void {
|
||||
XMLHttpRequest._profiling = enableProfiling;
|
||||
}
|
||||
@@ -261,10 +279,20 @@ class XMLHttpRequest extends (EventTarget(...XHR_EVENTS): typeof EventTarget) {
|
||||
return this._cachedResponse;
|
||||
}
|
||||
|
||||
// exposed for testing
|
||||
__didCreateRequest(requestId: number): void {
|
||||
this._requestId = requestId;
|
||||
|
||||
XMLHttpRequest._interceptor &&
|
||||
XMLHttpRequest._interceptor.requestSent(
|
||||
requestId,
|
||||
this._url || '',
|
||||
this._method || 'GET',
|
||||
this._headers,
|
||||
);
|
||||
}
|
||||
|
||||
// exposed for testing
|
||||
__didUploadProgress(
|
||||
requestId: number,
|
||||
progress: number,
|
||||
@@ -297,6 +325,14 @@ class XMLHttpRequest extends (EventTarget(...XHR_EVENTS): typeof EventTarget) {
|
||||
} else {
|
||||
delete this.responseURL;
|
||||
}
|
||||
|
||||
XMLHttpRequest._interceptor &&
|
||||
XMLHttpRequest._interceptor.responseReceived(
|
||||
requestId,
|
||||
responseURL || this._url || '',
|
||||
status,
|
||||
responseHeaders || {},
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -307,6 +343,9 @@ class XMLHttpRequest extends (EventTarget(...XHR_EVENTS): typeof EventTarget) {
|
||||
this._response = response;
|
||||
this._cachedResponse = undefined; // force lazy recomputation
|
||||
this.setReadyState(this.LOADING);
|
||||
|
||||
XMLHttpRequest._interceptor &&
|
||||
XMLHttpRequest._interceptor.dataReceived(requestId, response);
|
||||
}
|
||||
|
||||
__didReceiveIncrementalData(
|
||||
@@ -329,6 +368,8 @@ class XMLHttpRequest extends (EventTarget(...XHR_EVENTS): typeof EventTarget) {
|
||||
'Track:XMLHttpRequest:Incremental Data: ' + this._getMeasureURL(),
|
||||
);
|
||||
}
|
||||
XMLHttpRequest._interceptor &&
|
||||
XMLHttpRequest._interceptor.dataReceived(requestId, responseText);
|
||||
|
||||
this.setReadyState(this.LOADING);
|
||||
this.__didReceiveDataProgress(requestId, progress, total);
|
||||
@@ -376,6 +417,16 @@ class XMLHttpRequest extends (EventTarget(...XHR_EVENTS): typeof EventTarget) {
|
||||
end: performance.now(),
|
||||
});
|
||||
}
|
||||
if (error) {
|
||||
XMLHttpRequest._interceptor &&
|
||||
XMLHttpRequest._interceptor.loadingFailed(requestId, error);
|
||||
} else {
|
||||
XMLHttpRequest._interceptor &&
|
||||
XMLHttpRequest._interceptor.loadingFinished(
|
||||
requestId,
|
||||
this._response.length,
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -30,11 +30,11 @@ export type TaskProvider = () => Task;
|
||||
type TaskCanceller = () => void;
|
||||
type TaskCancelProvider = () => TaskCanceller;
|
||||
|
||||
export type ComponentProvider = () => React$ComponentType<any>;
|
||||
export type ComponentProvider = () => React.ComponentType<any>;
|
||||
export type ComponentProviderInstrumentationHook = (
|
||||
component_: ComponentProvider,
|
||||
scopedPerformanceLogger: IPerformanceLogger,
|
||||
) => React$ComponentType<any>;
|
||||
) => React.ComponentType<any>;
|
||||
export type AppConfig = {
|
||||
appKey: string,
|
||||
component?: ComponentProvider,
|
||||
@@ -59,7 +59,7 @@ export type Registry = {
|
||||
};
|
||||
export type WrapperComponentProvider = (
|
||||
appParameters: Object,
|
||||
) => React$ComponentType<any>;
|
||||
) => React.ComponentType<any>;
|
||||
export type RootViewStyleProvider = (appParameters: Object) => ViewStyleProp;
|
||||
|
||||
const runnables: Runnables = {};
|
||||
|
||||
+2
-2
@@ -36,7 +36,7 @@ const ownerDocument: ReactNativeDocument = {};
|
||||
/* eslint-disable no-new */
|
||||
Fantom.unstable_benchmark
|
||||
.suite('ReactNativeElement vs. ReactFabricHostComponent')
|
||||
.add('ReactNativeElement', () => {
|
||||
.test('ReactNativeElement', () => {
|
||||
new ReactNativeElement(
|
||||
tag,
|
||||
viewConfig,
|
||||
@@ -44,6 +44,6 @@ Fantom.unstable_benchmark
|
||||
ownerDocument,
|
||||
);
|
||||
})
|
||||
.add('ReactFabricHostComponent', () => {
|
||||
.test('ReactFabricHostComponent', () => {
|
||||
new ReactFabricHostComponent(tag, viewConfig, internalInstanceHandle);
|
||||
});
|
||||
|
||||
+1
-1
@@ -12,7 +12,7 @@ import * as React from 'react';
|
||||
|
||||
export opaque type RootTag = number;
|
||||
|
||||
export const RootTagContext: React$Context<RootTag> =
|
||||
export const RootTagContext: React.Context<RootTag> =
|
||||
React.createContext<RootTag>(0);
|
||||
|
||||
if (__DEV__) {
|
||||
|
||||
+2
-2
@@ -42,7 +42,7 @@ describe('discrete event category', () => {
|
||||
interruptRendering = false;
|
||||
const element = ensureReactNativeElement(maybeTextInputNode);
|
||||
Fantom.runOnUIThread(() => {
|
||||
Fantom.dispatchNativeEvent(
|
||||
Fantom.enqueueNativeEvent(
|
||||
element,
|
||||
'change',
|
||||
{
|
||||
@@ -161,7 +161,7 @@ describe('continuous event category', () => {
|
||||
interruptRendering = false;
|
||||
const element = ensureReactNativeElement(maybeTextInputNode);
|
||||
Fantom.runOnUIThread(() => {
|
||||
Fantom.dispatchNativeEvent(
|
||||
Fantom.enqueueNativeEvent(
|
||||
element,
|
||||
'selectionChange',
|
||||
{
|
||||
|
||||
+2
-2
@@ -11,7 +11,7 @@
|
||||
'use strict';
|
||||
|
||||
const Settings = {
|
||||
get(key: string): mixed {
|
||||
get(key: string): any {
|
||||
console.warn('Settings is not yet supported on this platform.');
|
||||
return null;
|
||||
},
|
||||
@@ -20,7 +20,7 @@ const Settings = {
|
||||
console.warn('Settings is not yet supported on this platform.');
|
||||
},
|
||||
|
||||
watchKeys(keys: string | Array<string>, callback: Function): number {
|
||||
watchKeys(keys: string | Array<string>, callback: () => void): number {
|
||||
console.warn('Settings is not yet supported on this platform.');
|
||||
return -1;
|
||||
},
|
||||
|
||||
+9
-2
@@ -8,6 +8,8 @@
|
||||
* @flow strict-local
|
||||
*/
|
||||
|
||||
import type {ColorValue} from '../StyleSheet/StyleSheet';
|
||||
|
||||
import NativeActionSheetManager from '../ActionSheetIOS/NativeActionSheetManager';
|
||||
import NativeShareModule from './NativeShareModule';
|
||||
|
||||
@@ -29,11 +31,16 @@ export type ShareContent =
|
||||
export type ShareOptions = {
|
||||
dialogTitle?: string,
|
||||
excludedActivityTypes?: Array<string>,
|
||||
tintColor?: string,
|
||||
tintColor?: ColorValue,
|
||||
subject?: string,
|
||||
anchor?: number,
|
||||
};
|
||||
|
||||
export type ShareAction = {
|
||||
action: 'sharedAction' | 'dismissedAction',
|
||||
activityType?: string | null,
|
||||
};
|
||||
|
||||
class Share {
|
||||
/**
|
||||
* Open a dialog to share text content.
|
||||
@@ -73,7 +80,7 @@ class Share {
|
||||
*/
|
||||
static share(
|
||||
content: ShareContent,
|
||||
options: ShareOptions = {},
|
||||
options?: ShareOptions = {},
|
||||
): Promise<{action: string, activityType: ?string}> {
|
||||
invariant(
|
||||
typeof content === 'object' && content !== null,
|
||||
|
||||
+17
-15
@@ -112,7 +112,7 @@ describe('processFilter', () => {
|
||||
});
|
||||
it('string multiple filters', () => {
|
||||
expect(
|
||||
processFilter('brightness(0.5) opacity(0.5) blur(5) hue-rotate(90deg)'),
|
||||
processFilter('brightness(0.5) opacity(0.5) blur(5px) hue-rotate(90deg)'),
|
||||
).toEqual([{brightness: 0.5}, {opacity: 0.5}, {blur: 5}, {hueRotate: 90}]);
|
||||
});
|
||||
it('string multiple filters with newlines', () => {
|
||||
@@ -124,7 +124,7 @@ describe('processFilter', () => {
|
||||
});
|
||||
it('string multiple filters one invalid', () => {
|
||||
expect(
|
||||
processFilter('brightness(0.5) opacity(0.5) blur(5) hue-rotate(90foo)'),
|
||||
processFilter('brightness(0.5) opacity(0.5) blur(5px) hue-rotate(90foo)'),
|
||||
).toEqual([]);
|
||||
});
|
||||
it('string multiple same filters', () => {
|
||||
@@ -233,7 +233,7 @@ function createFilterPrimitive(
|
||||
|
||||
function testDropShadow() {
|
||||
it('should parse string drop-shadow', () => {
|
||||
expect(processFilter('drop-shadow(4px 4 10px red)')).toEqual([
|
||||
expect(processFilter('drop-shadow(4px 4px 10px red)')).toEqual([
|
||||
{
|
||||
dropShadow: {
|
||||
offsetX: 4,
|
||||
@@ -246,7 +246,7 @@ function testDropShadow() {
|
||||
});
|
||||
|
||||
it('should parse string negative offsets drop-shadow', () => {
|
||||
expect(processFilter('drop-shadow(-4 -4)')).toEqual([
|
||||
expect(processFilter('drop-shadow(-4px -4px)')).toEqual([
|
||||
{
|
||||
dropShadow: {
|
||||
offsetX: -4,
|
||||
@@ -258,7 +258,9 @@ function testDropShadow() {
|
||||
|
||||
it('should parse string multiple drop-shadows', () => {
|
||||
expect(
|
||||
processFilter('drop-shadow(4 4) drop-shadow(4 4) drop-shadow(4 4)'),
|
||||
processFilter(
|
||||
'drop-shadow(4px 4px) drop-shadow(4px 4px) drop-shadow(4px 4px)',
|
||||
),
|
||||
).toEqual([
|
||||
{
|
||||
dropShadow: {
|
||||
@@ -283,7 +285,7 @@ function testDropShadow() {
|
||||
|
||||
it('should parse string drop-shadow with random whitespaces', () => {
|
||||
expect(
|
||||
processFilter(' drop-shadow(4px 4 10px red) '),
|
||||
processFilter(' drop-shadow(4px 4px 10px red) '),
|
||||
).toEqual([
|
||||
{
|
||||
dropShadow: {
|
||||
@@ -299,7 +301,7 @@ function testDropShadow() {
|
||||
it('should parse string drop-shadow with multiple filters', () => {
|
||||
expect(
|
||||
processFilter(
|
||||
'drop-shadow(4px 4 10px red) brightness(0.5) brightness(0.5)',
|
||||
'drop-shadow(4px 4px 10px red) brightness(0.5) brightness(0.5)',
|
||||
),
|
||||
).toEqual([
|
||||
{
|
||||
@@ -316,7 +318,7 @@ function testDropShadow() {
|
||||
});
|
||||
|
||||
it('should parse string drop-shadow with color', () => {
|
||||
expect(processFilter('drop-shadow(50 50 purple)')).toEqual([
|
||||
expect(processFilter('drop-shadow(50px 50px purple)')).toEqual([
|
||||
{
|
||||
dropShadow: {
|
||||
offsetX: 50,
|
||||
@@ -328,7 +330,7 @@ function testDropShadow() {
|
||||
});
|
||||
|
||||
it('should parse string drop-shadow with rgba color', () => {
|
||||
expect(processFilter('drop-shadow(50 50 rgba(0, 0, 0, 1))')).toEqual([
|
||||
expect(processFilter('drop-shadow(50px 50px rgba(0, 0, 0, 1))')).toEqual([
|
||||
{
|
||||
dropShadow: {
|
||||
offsetX: 50,
|
||||
@@ -340,7 +342,7 @@ function testDropShadow() {
|
||||
});
|
||||
|
||||
it('should parse string with mixed case drop-shadow', () => {
|
||||
expect(processFilter('DroP-sHaDOw(50 50 purple)')).toEqual([
|
||||
expect(processFilter('DroP-sHaDOw(50px 50px purple)')).toEqual([
|
||||
{
|
||||
dropShadow: {
|
||||
offsetX: 50,
|
||||
@@ -359,7 +361,7 @@ function testDropShadow() {
|
||||
offsetX: 4,
|
||||
offsetY: 4,
|
||||
color: '#FFFFFF',
|
||||
standardDeviation: '10',
|
||||
standardDeviation: '10px',
|
||||
},
|
||||
},
|
||||
]),
|
||||
@@ -376,7 +378,7 @@ function testDropShadow() {
|
||||
});
|
||||
|
||||
it('should fail to parse string comma separated drop-shadow', () => {
|
||||
expect(processFilter('drop-shadow(4px, 4, 10px, red)')).toEqual([]);
|
||||
expect(processFilter('drop-shadow(4px, 4px, 10px, red)')).toEqual([]);
|
||||
});
|
||||
|
||||
it('should fail to parse other symbols after args comma separated drop-shadow', () => {
|
||||
@@ -384,15 +386,15 @@ function testDropShadow() {
|
||||
});
|
||||
|
||||
it('should fail on color between lengths string drop-shadow', () => {
|
||||
expect(processFilter('drop-shadow(10 red 10 10')).toEqual([]);
|
||||
expect(processFilter('drop-shadow(10px red 10px 10px')).toEqual([]);
|
||||
});
|
||||
|
||||
it('should fail on color between offset & blur string drop-shadow', () => {
|
||||
expect(processFilter('drop-shadow(10 10 red 10')).toEqual([]);
|
||||
expect(processFilter('drop-shadow(10px 10px red 10px')).toEqual([]);
|
||||
});
|
||||
|
||||
it('should fail on negative blue', () => {
|
||||
expect(processFilter('drop-shadow(10 10 -10')).toEqual([]);
|
||||
expect(processFilter('drop-shadow(10px 10px -10px')).toEqual([]);
|
||||
});
|
||||
|
||||
it('should fail on invalid object drop-shadow', () => {
|
||||
|
||||
@@ -317,5 +317,9 @@ function parseLength(length: string): ?number {
|
||||
return null;
|
||||
}
|
||||
|
||||
if (match[3] == null && match[1] !== '0') {
|
||||
return null;
|
||||
}
|
||||
|
||||
return Number(match[1]);
|
||||
}
|
||||
|
||||
+1
-1
@@ -15,7 +15,7 @@ const React = require('react');
|
||||
/**
|
||||
* Whether the current element is the descendant of a <Text> element.
|
||||
*/
|
||||
const TextAncestorContext: React$Context<boolean> = React.createContext(false);
|
||||
const TextAncestorContext: React.Context<boolean> = React.createContext(false);
|
||||
if (__DEV__) {
|
||||
TextAncestorContext.displayName = 'TextAncestorContext';
|
||||
}
|
||||
|
||||
@@ -53,7 +53,7 @@ declare export default typeof NativeActionSheetManager;
|
||||
"
|
||||
`;
|
||||
|
||||
exports[`public API should not change unintentionally Libraries/Alert/Alert.flow.js 1`] = `
|
||||
exports[`public API should not change unintentionally Libraries/Alert/Alert.js 1`] = `
|
||||
"export type AlertType =
|
||||
| \\"default\\"
|
||||
| \\"plain-text\\"
|
||||
@@ -67,7 +67,7 @@ export type AlertButton = {
|
||||
style?: AlertButtonStyle,
|
||||
...
|
||||
};
|
||||
export type Buttons = Array<AlertButton>;
|
||||
export type AlertButtons = Array<AlertButton>;
|
||||
export type AlertOptions = {
|
||||
cancelable?: ?boolean,
|
||||
userInterfaceStyle?: \\"unspecified\\" | \\"light\\" | \\"dark\\",
|
||||
@@ -78,36 +78,13 @@ declare class Alert {
|
||||
static alert(
|
||||
title: ?string,
|
||||
message?: ?string,
|
||||
buttons?: Buttons,
|
||||
buttons?: AlertButtons,
|
||||
options?: AlertOptions
|
||||
): void;
|
||||
static prompt(
|
||||
title: ?string,
|
||||
message?: ?string,
|
||||
callbackOrButtons?: ?(((text: string) => void) | Buttons),
|
||||
type?: ?AlertType,
|
||||
defaultValue?: string,
|
||||
keyboardType?: string,
|
||||
options?: AlertOptions
|
||||
): void;
|
||||
}
|
||||
declare export default typeof Alert;
|
||||
"
|
||||
`;
|
||||
|
||||
exports[`public API should not change unintentionally Libraries/Alert/Alert.js 1`] = `
|
||||
"export type * from \\"./Alert.flow\\";
|
||||
declare class Alert {
|
||||
static alert(
|
||||
title: ?string,
|
||||
message?: ?string,
|
||||
buttons?: Buttons,
|
||||
options?: AlertOptions
|
||||
): void;
|
||||
static prompt(
|
||||
title: ?string,
|
||||
message?: ?string,
|
||||
callbackOrButtons?: ?(((text: string) => void) | Buttons),
|
||||
callbackOrButtons?: ?(((text: string) => void) | AlertButtons),
|
||||
type?: ?AlertType,
|
||||
defaultValue?: string,
|
||||
keyboardType?: string,
|
||||
@@ -124,17 +101,6 @@ declare export default typeof NativeAlertManager;
|
||||
"
|
||||
`;
|
||||
|
||||
exports[`public API should not change unintentionally Libraries/Alert/RCTAlertManager.flow.js 1`] = `
|
||||
"declare const RCTAlertManager: {
|
||||
alertWithArgs(
|
||||
args: Args,
|
||||
callback: (id: number, value: string) => void
|
||||
): void,
|
||||
};
|
||||
declare export default typeof RCTAlertManager;
|
||||
"
|
||||
`;
|
||||
|
||||
exports[`public API should not change unintentionally Libraries/Alert/RCTAlertManager.js.flow 1`] = `
|
||||
"declare export default {
|
||||
alertWithArgs(
|
||||
@@ -5514,6 +5480,7 @@ exports[`public API should not change unintentionally Libraries/LogBox/UI/AnsiHi
|
||||
|
||||
exports[`public API should not change unintentionally Libraries/LogBox/UI/LogBoxButton.js 1`] = `
|
||||
"type Props = $ReadOnly<{
|
||||
id?: string,
|
||||
backgroundColor: $ReadOnly<{
|
||||
default: string,
|
||||
pressed: string,
|
||||
@@ -6030,6 +5997,18 @@ export type ResponseType =
|
||||
| \\"json\\"
|
||||
| \\"text\\";
|
||||
export type Response = ?Object | string;
|
||||
type XHRInterceptor = interface {
|
||||
requestSent(id: number, url: string, method: string, headers: Object): void,
|
||||
responseReceived(
|
||||
id: number,
|
||||
url: string,
|
||||
status: number,
|
||||
headers: Object
|
||||
): void,
|
||||
dataReceived(id: number, data: string): void,
|
||||
loadingFinished(id: number, encodedDataLength: number): void,
|
||||
loadingFailed(id: number, error: string): void,
|
||||
};
|
||||
declare class XMLHttpRequestEventTarget extends EventTarget {
|
||||
get onload(): EventCallback | null;
|
||||
set onload(listener: ?EventCallback): void;
|
||||
@@ -6112,6 +6091,18 @@ export type ResponseType =
|
||||
| \\"json\\"
|
||||
| \\"text\\";
|
||||
export type Response = ?Object | string;
|
||||
type XHRInterceptor = interface {
|
||||
requestSent(id: number, url: string, method: string, headers: Object): void,
|
||||
responseReceived(
|
||||
id: number,
|
||||
url: string,
|
||||
status: number,
|
||||
headers: Object
|
||||
): void,
|
||||
dataReceived(id: number, data: string): void,
|
||||
loadingFinished(id: number, encodedDataLength: number): void,
|
||||
loadingFailed(id: number, error: string): void,
|
||||
};
|
||||
declare class XMLHttpRequestEventTarget extends EventTarget {
|
||||
onload: ?Function;
|
||||
onloadstart: ?Function;
|
||||
@@ -6542,11 +6533,11 @@ exports[`public API should not change unintentionally Libraries/ReactNative/AppR
|
||||
export type TaskProvider = () => Task;
|
||||
type TaskCanceller = () => void;
|
||||
type TaskCancelProvider = () => TaskCanceller;
|
||||
export type ComponentProvider = () => React$ComponentType<any>;
|
||||
export type ComponentProvider = () => React.ComponentType<any>;
|
||||
export type ComponentProviderInstrumentationHook = (
|
||||
component_: ComponentProvider,
|
||||
scopedPerformanceLogger: IPerformanceLogger
|
||||
) => React$ComponentType<any>;
|
||||
) => React.ComponentType<any>;
|
||||
export type AppConfig = {
|
||||
appKey: string,
|
||||
component?: ComponentProvider,
|
||||
@@ -6571,7 +6562,7 @@ export type Registry = {
|
||||
};
|
||||
export type WrapperComponentProvider = (
|
||||
appParameters: Object
|
||||
) => React$ComponentType<any>;
|
||||
) => React.ComponentType<any>;
|
||||
export type RootViewStyleProvider = (appParameters: Object) => ViewStyleProp;
|
||||
declare const AppRegistry: {
|
||||
setWrapperComponentProvider(provider: WrapperComponentProvider): void,
|
||||
@@ -6887,7 +6878,7 @@ exports[`public API should not change unintentionally Libraries/ReactNative/Rend
|
||||
|
||||
exports[`public API should not change unintentionally Libraries/ReactNative/RootTag.js 1`] = `
|
||||
"declare export opaque type RootTag;
|
||||
declare export const RootTagContext: React$Context<RootTag>;
|
||||
declare export const RootTagContext: React.Context<RootTag>;
|
||||
declare export function createRootTag(rootTag: number | RootTag): RootTag;
|
||||
"
|
||||
`;
|
||||
@@ -6951,9 +6942,9 @@ declare export default typeof NativeSettingsManager;
|
||||
|
||||
exports[`public API should not change unintentionally Libraries/Settings/Settings.js 1`] = `
|
||||
"declare const Settings: {
|
||||
get(key: string): mixed,
|
||||
get(key: string): any,
|
||||
set(settings: Object): void,
|
||||
watchKeys(keys: string | Array<string>, callback: Function): number,
|
||||
watchKeys(keys: string | Array<string>, callback: () => void): number,
|
||||
clearWatch(watchId: number): void,
|
||||
};
|
||||
declare export default typeof Settings;
|
||||
@@ -6981,14 +6972,18 @@ exports[`public API should not change unintentionally Libraries/Share/Share.js 1
|
||||
export type ShareOptions = {
|
||||
dialogTitle?: string,
|
||||
excludedActivityTypes?: Array<string>,
|
||||
tintColor?: string,
|
||||
tintColor?: ColorValue,
|
||||
subject?: string,
|
||||
anchor?: number,
|
||||
};
|
||||
export type ShareAction = {
|
||||
action: \\"sharedAction\\" | \\"dismissedAction\\",
|
||||
activityType?: string | null,
|
||||
};
|
||||
declare class Share {
|
||||
static share(
|
||||
content: ShareContent,
|
||||
options: ShareOptions
|
||||
options?: ShareOptions
|
||||
): Promise<{ action: string, activityType: ?string }>;
|
||||
static sharedAction: \\"sharedAction\\";
|
||||
static dismissedAction: \\"dismissedAction\\";
|
||||
@@ -7684,7 +7679,8 @@ declare export default typeof Text;
|
||||
`;
|
||||
|
||||
exports[`public API should not change unintentionally Libraries/Text/TextAncestor.js 1`] = `
|
||||
"declare const TextAncestorContext: React$Context<boolean>;
|
||||
"declare const React: $FlowFixMe;
|
||||
declare const TextAncestorContext: React.Context<boolean>;
|
||||
declare export default typeof TextAncestorContext;
|
||||
"
|
||||
`;
|
||||
|
||||
+8
-4
@@ -64,6 +64,7 @@ const CGFloat BACKGROUND_COLOR_ZPOSITION = -1024.0f;
|
||||
_reactSubviews = [NSMutableArray new];
|
||||
self.multipleTouchEnabled = YES;
|
||||
_useCustomContainerView = NO;
|
||||
_removeClippedSubviews = NO;
|
||||
}
|
||||
return self;
|
||||
}
|
||||
@@ -229,10 +230,13 @@ const CGFloat BACKGROUND_COLOR_ZPOSITION = -1024.0f;
|
||||
needsInvalidateLayer = YES;
|
||||
}
|
||||
|
||||
if (oldViewProps.removeClippedSubviews != newViewProps.removeClippedSubviews) {
|
||||
_removeClippedSubviews = newViewProps.removeClippedSubviews;
|
||||
if (_removeClippedSubviews && self.currentContainerView.subviews.count > 0) {
|
||||
_reactSubviews = [NSMutableArray arrayWithArray:self.currentContainerView.subviews];
|
||||
// Disable `removeClippedSubviews` when Fabric View Culling is enabled.
|
||||
if (!ReactNativeFeatureFlags::enableViewCulling()) {
|
||||
if (oldViewProps.removeClippedSubviews != newViewProps.removeClippedSubviews) {
|
||||
_removeClippedSubviews = newViewProps.removeClippedSubviews;
|
||||
if (_removeClippedSubviews && self.currentContainerView.subviews.count > 0) {
|
||||
_reactSubviews = [NSMutableArray arrayWithArray:self.currentContainerView.subviews];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -1387,6 +1387,7 @@ public final class com/facebook/react/bridge/ReactSoftExceptionLogger$Categories
|
||||
public static final field RVG_IS_VIEW_CLIPPED Ljava/lang/String;
|
||||
public static final field RVG_ON_VIEW_REMOVED Ljava/lang/String;
|
||||
public static final field SOFT_ASSERTIONS Ljava/lang/String;
|
||||
public static final field SURFACE_MOUNTING_MANAGER_MISSING_VIEWSTATE Ljava/lang/String;
|
||||
}
|
||||
|
||||
public abstract interface class com/facebook/react/bridge/ReactSoftExceptionLogger$ReactSoftExceptionListener {
|
||||
@@ -1643,7 +1644,7 @@ public final class com/facebook/react/bridge/queue/MessageQueueThreadImpl$Compan
|
||||
public final fun create (Lcom/facebook/react/bridge/queue/MessageQueueThreadSpec;Lcom/facebook/react/bridge/queue/QueueThreadExceptionHandler;)Lcom/facebook/react/bridge/queue/MessageQueueThreadImpl;
|
||||
}
|
||||
|
||||
public class com/facebook/react/bridge/queue/MessageQueueThreadPerfStats {
|
||||
public final class com/facebook/react/bridge/queue/MessageQueueThreadPerfStats {
|
||||
public field cpuTime J
|
||||
public field wallTime J
|
||||
public fun <init> ()V
|
||||
@@ -1873,6 +1874,7 @@ public final class com/facebook/react/common/build/ReactBuildConfig {
|
||||
public static final field INSTANCE Lcom/facebook/react/common/build/ReactBuildConfig;
|
||||
public static final field IS_INTERNAL_BUILD Z
|
||||
public static final field UNSTABLE_ENABLE_FUSEBOX_RELEASE Z
|
||||
public static final field UNSTABLE_ENABLE_MINIFY_LEGACY_ARCHITECTURE Z
|
||||
}
|
||||
|
||||
public abstract interface class com/facebook/react/common/mapbuffer/MapBuffer : java/lang/Iterable, kotlin/jvm/internal/markers/KMappedMarker {
|
||||
@@ -3370,6 +3372,14 @@ public class com/facebook/react/modules/network/ProgressResponseBody : okhttp3/R
|
||||
public fun totalBytesRead ()J
|
||||
}
|
||||
|
||||
public final class com/facebook/react/modules/network/ReactCookieJarContainer : com/facebook/react/modules/network/CookieJarContainer {
|
||||
public fun <init> ()V
|
||||
public fun loadForRequest (Lokhttp3/HttpUrl;)Ljava/util/List;
|
||||
public fun removeCookieJar ()V
|
||||
public fun saveFromResponse (Lokhttp3/HttpUrl;Ljava/util/List;)V
|
||||
public fun setCookieJar (Lokhttp3/CookieJar;)V
|
||||
}
|
||||
|
||||
public class com/facebook/react/modules/network/TLSSocketFactory : javax/net/ssl/SSLSocketFactory {
|
||||
public fun <init> ()V
|
||||
public fun createSocket (Ljava/lang/String;I)Ljava/net/Socket;
|
||||
@@ -5585,10 +5595,6 @@ public abstract interface class com/facebook/react/uimanager/events/RCTModernEve
|
||||
public abstract fun receiveTouches (Lcom/facebook/react/uimanager/events/TouchEvent;)V
|
||||
}
|
||||
|
||||
public abstract interface class com/facebook/react/uimanager/events/SynchronousEventReceiver {
|
||||
public abstract fun receiveEvent (IILjava/lang/String;ZLcom/facebook/react/bridge/WritableMap;IZ)V
|
||||
}
|
||||
|
||||
public final class com/facebook/react/uimanager/events/TouchEvent : com/facebook/react/uimanager/events/Event {
|
||||
public static final field Companion Lcom/facebook/react/uimanager/events/TouchEvent$Companion;
|
||||
public static final field UNSET J
|
||||
@@ -6040,11 +6046,6 @@ public final class com/facebook/react/views/common/ContextUtils {
|
||||
public static final fun findContextOfType (Landroid/content/Context;Ljava/lang/Class;)Ljava/lang/Object;
|
||||
}
|
||||
|
||||
public final class com/facebook/react/views/common/ViewUtils {
|
||||
public static final field INSTANCE Lcom/facebook/react/views/common/ViewUtils;
|
||||
public static final fun getTestId (Landroid/view/View;)Ljava/lang/String;
|
||||
}
|
||||
|
||||
public final class com/facebook/react/views/debuggingoverlay/DebuggingOverlay : android/view/View {
|
||||
public fun <init> (Landroid/content/Context;)V
|
||||
public final fun clearElementsHighlights ()V
|
||||
@@ -6240,19 +6241,6 @@ public final class com/facebook/react/views/image/ImageResizeMode {
|
||||
public static final fun toTileMode (Ljava/lang/String;)Landroid/graphics/Shader$TileMode;
|
||||
}
|
||||
|
||||
public final class com/facebook/react/views/image/MultiPostprocessor : com/facebook/imagepipeline/request/Postprocessor {
|
||||
public static final field Companion Lcom/facebook/react/views/image/MultiPostprocessor$Companion;
|
||||
public synthetic fun <init> (Ljava/util/List;Lkotlin/jvm/internal/DefaultConstructorMarker;)V
|
||||
public static final fun from (Ljava/util/List;)Lcom/facebook/imagepipeline/request/Postprocessor;
|
||||
public fun getName ()Ljava/lang/String;
|
||||
public fun getPostprocessorCacheKey ()Lcom/facebook/cache/common/CacheKey;
|
||||
public fun process (Landroid/graphics/Bitmap;Lcom/facebook/imagepipeline/bitmaps/PlatformBitmapFactory;)Lcom/facebook/common/references/CloseableReference;
|
||||
}
|
||||
|
||||
public final class com/facebook/react/views/image/MultiPostprocessor$Companion {
|
||||
public final fun from (Ljava/util/List;)Lcom/facebook/imagepipeline/request/Postprocessor;
|
||||
}
|
||||
|
||||
public abstract interface class com/facebook/react/views/image/ReactCallerContextFactory {
|
||||
public abstract fun getOrCreateCallerContext (Ljava/lang/String;Ljava/lang/String;)Ljava/lang/Object;
|
||||
}
|
||||
@@ -7654,9 +7642,3 @@ public final class com/facebook/react/views/view/ViewGroupClickEvent : com/faceb
|
||||
public fun getEventName ()Ljava/lang/String;
|
||||
}
|
||||
|
||||
public final class com/facebook/react/views/view/WindowUtilKt {
|
||||
public static final fun setStatusBarTranslucency (Landroid/view/Window;Z)V
|
||||
public static final fun setStatusBarVisibility (Landroid/view/Window;Z)V
|
||||
public static final fun setSystemBarsTranslucency (Landroid/view/Window;Z)V
|
||||
}
|
||||
|
||||
|
||||
@@ -523,6 +523,7 @@ android {
|
||||
buildConfigField("int", "EXOPACKAGE_FLAGS", "0")
|
||||
buildConfigField("boolean", "UNSTABLE_ENABLE_FUSEBOX_RELEASE", "false")
|
||||
buildConfigField("boolean", "ENABLE_PERFETTO", "false")
|
||||
buildConfigField("boolean", "UNSTABLE_ENABLE_MINIFY_LEGACY_ARCHITECTURE", "false")
|
||||
|
||||
resValue("integer", "react_native_dev_server_port", reactNativeDevServerPort())
|
||||
|
||||
|
||||
+2
-2
@@ -11,9 +11,9 @@ import javax.annotation.processing.AbstractProcessor
|
||||
import javax.annotation.processing.RoundEnvironment
|
||||
import javax.lang.model.element.TypeElement
|
||||
|
||||
public abstract class ProcessorBase : AbstractProcessor() {
|
||||
internal abstract class ProcessorBase : AbstractProcessor() {
|
||||
|
||||
public fun process(annotations: Set<TypeElement?>?, roundEnv: RoundEnvironment?): Boolean =
|
||||
fun process(annotations: Set<TypeElement?>?, roundEnv: RoundEnvironment?): Boolean =
|
||||
processImpl(annotations, roundEnv)
|
||||
|
||||
protected abstract fun processImpl(
|
||||
|
||||
+2
@@ -19,6 +19,8 @@ public object ReactSoftExceptionLogger {
|
||||
public const val RVG_IS_VIEW_CLIPPED: String = "ReactViewGroup.isViewClipped"
|
||||
public const val RVG_ON_VIEW_REMOVED: String = "ReactViewGroup.onViewRemoved"
|
||||
public const val SOFT_ASSERTIONS: String = "SoftAssertions"
|
||||
public const val SURFACE_MOUNTING_MANAGER_MISSING_VIEWSTATE: String =
|
||||
"SurfaceMountingManager:MissingViewState"
|
||||
}
|
||||
|
||||
// Use a list instead of a set here because we expect the number of listeners
|
||||
|
||||
+3
-3
@@ -5,10 +5,10 @@
|
||||
* LICENSE file in the root directory of this source tree.
|
||||
*/
|
||||
|
||||
package com.facebook.react.bridge.queue;
|
||||
package com.facebook.react.bridge.queue
|
||||
|
||||
/** This class holds perf counters' values at the beginning of an RN startup. */
|
||||
public class MessageQueueThreadPerfStats {
|
||||
public long wallTime;
|
||||
public long cpuTime;
|
||||
@JvmField public var wallTime: Long = 0
|
||||
@JvmField public var cpuTime: Long = 0
|
||||
}
|
||||
+4
-5
@@ -5,13 +5,12 @@
|
||||
* LICENSE file in the root directory of this source tree.
|
||||
*/
|
||||
|
||||
package com.facebook.react.bridge.queue;
|
||||
package com.facebook.react.bridge.queue
|
||||
|
||||
/**
|
||||
* Interface for a class that knows how to handle an Exception thrown while executing a Runnable
|
||||
* submitted via {@link MessageQueueThread#runOnQueue}.
|
||||
* submitted via [MessageQueueThread.runOnQueue].
|
||||
*/
|
||||
public interface QueueThreadExceptionHandler {
|
||||
|
||||
void handleException(Exception e);
|
||||
public fun interface QueueThreadExceptionHandler {
|
||||
public fun handleException(e: Exception)
|
||||
}
|
||||
-27
@@ -1,27 +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.
|
||||
*/
|
||||
|
||||
package com.facebook.react.bridge.queue;
|
||||
|
||||
/**
|
||||
* Specifies which {@link MessageQueueThread}s must be used to run the various contexts of execution
|
||||
* within catalyst (Main UI thread, native modules, and JS). Some of these queues *may* be the same
|
||||
* but should be coded against as if they are different.
|
||||
*
|
||||
* <p>UI Queue Thread: The standard Android main UI thread and Looper. Not configurable. Native
|
||||
* Modules Queue Thread: The thread and Looper that native modules are invoked on. JS Queue Thread:
|
||||
* The thread and Looper that JS is executed on.
|
||||
*/
|
||||
public interface ReactQueueConfiguration {
|
||||
MessageQueueThread getUIQueueThread();
|
||||
|
||||
MessageQueueThread getNativeModulesQueueThread();
|
||||
|
||||
MessageQueueThread getJSQueueThread();
|
||||
|
||||
void destroy();
|
||||
}
|
||||
+30
@@ -0,0 +1,30 @@
|
||||
/*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
package com.facebook.react.bridge.queue
|
||||
|
||||
/**
|
||||
* Specifies which [MessageQueueThread]s must be used to run the various contexts of execution
|
||||
* within catalyst (Main UI thread, native modules, and JS). Some of these queues *may* be the same
|
||||
* but should be coded against as if they are different.
|
||||
*
|
||||
* UI Queue Thread: The standard Android main UI thread and Looper. Not configurable.
|
||||
*
|
||||
* Native Modules Queue Thread: The thread and Looper that native modules are invoked on.
|
||||
*
|
||||
* JS Queue Thread: The thread and Looper that JS is executed on. thread and Looper that JS is
|
||||
* executed on.
|
||||
*/
|
||||
public interface ReactQueueConfiguration {
|
||||
public fun getUIQueueThread(): MessageQueueThread
|
||||
|
||||
public fun getNativeModulesQueueThread(): MessageQueueThread
|
||||
|
||||
public fun getJSQueueThread(): MessageQueueThread
|
||||
|
||||
public fun destroy()
|
||||
}
|
||||
+4
@@ -33,4 +33,8 @@ public object ReactBuildConfig {
|
||||
/** [Experimental] Enable React Native DevTools in release builds. */
|
||||
@JvmField
|
||||
public val UNSTABLE_ENABLE_FUSEBOX_RELEASE: Boolean = BuildConfig.UNSTABLE_ENABLE_FUSEBOX_RELEASE
|
||||
|
||||
@JvmField
|
||||
public val UNSTABLE_ENABLE_MINIFY_LEGACY_ARCHITECTURE: Boolean =
|
||||
BuildConfig.UNSTABLE_ENABLE_MINIFY_LEGACY_ARCHITECTURE
|
||||
}
|
||||
|
||||
+2
-2
@@ -286,11 +286,11 @@ public class MountingManager {
|
||||
* Send an accessibility eventType to a Native View. eventType is any valid `AccessibilityEvent.X`
|
||||
* value.
|
||||
*
|
||||
* <p>Why accept {@ViewUtils.NO_SURFACE_ID}(-1) SurfaceId? Currently there are calls to
|
||||
* <p>Why accept {@ViewUtil.NO_SURFACE_ID}(-1) SurfaceId? Currently there are calls to
|
||||
* UIManager.sendAccessibilityEvent which is a legacy API and accepts only reactTag. We will have
|
||||
* to investigate and migrate away from those calls over time.
|
||||
*
|
||||
* @param surfaceId {@link int} that identifies the surface or {@ViewUtils.NO_SURFACE_ID}(-1) to
|
||||
* @param surfaceId {@link int} that identifies the surface or {@ViewUtil.NO_SURFACE_ID}(-1) to
|
||||
* temporarily support backward compatibility.
|
||||
* @param reactTag {@link int} that identifies the react Tag of the view.
|
||||
* @param eventType {@link int} that identifies Android eventType. see {@link
|
||||
|
||||
+3
-2
@@ -21,6 +21,7 @@ import androidx.collection.SparseArrayCompat;
|
||||
import com.facebook.common.logging.FLog;
|
||||
import com.facebook.infer.annotation.Assertions;
|
||||
import com.facebook.infer.annotation.ThreadConfined;
|
||||
import com.facebook.react.bridge.ReactNoCrashSoftException;
|
||||
import com.facebook.react.bridge.ReactSoftExceptionLogger;
|
||||
import com.facebook.react.bridge.ReadableArray;
|
||||
import com.facebook.react.bridge.ReadableMap;
|
||||
@@ -1033,8 +1034,8 @@ public class SurfaceMountingManager {
|
||||
|
||||
if (viewState == null) {
|
||||
ReactSoftExceptionLogger.logSoftException(
|
||||
MountingManager.TAG,
|
||||
new IllegalStateException(
|
||||
ReactSoftExceptionLogger.Categories.SURFACE_MOUNTING_MANAGER_MISSING_VIEWSTATE,
|
||||
new ReactNoCrashSoftException(
|
||||
"Unable to find viewState for tag: " + reactTag + " for deleteView"));
|
||||
return;
|
||||
}
|
||||
|
||||
+3
-3
@@ -10,7 +10,7 @@ package com.facebook.react.internal
|
||||
import com.facebook.react.bridge.UiThreadUtil
|
||||
|
||||
/** An implementation of ChoreographerProvider that directly uses android.view.Choreographer. */
|
||||
public object AndroidChoreographerProvider : ChoreographerProvider {
|
||||
internal object AndroidChoreographerProvider : ChoreographerProvider {
|
||||
|
||||
private class AndroidChoreographer : ChoreographerProvider.Choreographer {
|
||||
private val instance: android.view.Choreographer = android.view.Choreographer.getInstance()
|
||||
@@ -24,9 +24,9 @@ public object AndroidChoreographerProvider : ChoreographerProvider {
|
||||
}
|
||||
}
|
||||
|
||||
@JvmStatic public fun getInstance(): AndroidChoreographerProvider = this
|
||||
@JvmStatic fun getInstance(): AndroidChoreographerProvider = this
|
||||
|
||||
override public fun getChoreographer(): ChoreographerProvider.Choreographer {
|
||||
override fun getChoreographer(): ChoreographerProvider.Choreographer {
|
||||
UiThreadUtil.assertOnUiThread()
|
||||
return AndroidChoreographer()
|
||||
}
|
||||
|
||||
+19
-13
@@ -4,7 +4,7 @@
|
||||
* This source code is licensed under the MIT license found in the
|
||||
* LICENSE file in the root directory of this source tree.
|
||||
*
|
||||
* @generated SignedSource<<a73d250c74505693246cd3309ef1d08a>>
|
||||
* @generated SignedSource<<ae55a0a7badfc9d80453d2737f0f87fd>>
|
||||
*/
|
||||
|
||||
/**
|
||||
@@ -166,12 +166,30 @@ public object ReactNativeFeatureFlags {
|
||||
@JvmStatic
|
||||
public fun enableUIConsistency(): Boolean = accessor.enableUIConsistency()
|
||||
|
||||
/**
|
||||
* Enables View Culling: as soon as a view goes off screen, it can be reused anywhere in the UI and pieced together with other items to create new UI elements.
|
||||
*/
|
||||
@JvmStatic
|
||||
public fun enableViewCulling(): Boolean = accessor.enableViewCulling()
|
||||
|
||||
/**
|
||||
* Enables View Recycling. When enabled, individual ViewManagers must still opt-in.
|
||||
*/
|
||||
@JvmStatic
|
||||
public fun enableViewRecycling(): Boolean = accessor.enableViewRecycling()
|
||||
|
||||
/**
|
||||
* Enables View Recycling for <Text> via ReactTextView/ReactTextViewManager.
|
||||
*/
|
||||
@JvmStatic
|
||||
public fun enableViewRecyclingForText(): Boolean = accessor.enableViewRecyclingForText()
|
||||
|
||||
/**
|
||||
* Enables View Recycling for <View> via ReactViewGroup/ReactViewManager.
|
||||
*/
|
||||
@JvmStatic
|
||||
public fun enableViewRecyclingForView(): Boolean = accessor.enableViewRecyclingForView()
|
||||
|
||||
/**
|
||||
* When enabled, rawProps in Props will not include Yoga specific props.
|
||||
*/
|
||||
@@ -214,12 +232,6 @@ public object ReactNativeFeatureFlags {
|
||||
@JvmStatic
|
||||
public fun lazyAnimationCallbacks(): Boolean = accessor.lazyAnimationCallbacks()
|
||||
|
||||
/**
|
||||
* Adds support for loading vector drawable assets in the Image component (only on Android)
|
||||
*/
|
||||
@JvmStatic
|
||||
public fun loadVectorDrawablesOnImages(): Boolean = accessor.loadVectorDrawablesOnImages()
|
||||
|
||||
/**
|
||||
* Enables storing js caller stack when creating promise in native module. This is useful in case of Promise rejection and tracing the cause.
|
||||
*/
|
||||
@@ -262,12 +274,6 @@ public object ReactNativeFeatureFlags {
|
||||
@JvmStatic
|
||||
public fun useRawPropsJsiValue(): Boolean = accessor.useRawPropsJsiValue()
|
||||
|
||||
/**
|
||||
* When enabled, cloning shadow nodes within react native will update the reference held by the current JS fiber tree.
|
||||
*/
|
||||
@JvmStatic
|
||||
public fun useRuntimeShadowNodeReferenceUpdate(): Boolean = accessor.useRuntimeShadowNodeReferenceUpdate()
|
||||
|
||||
/**
|
||||
* In Bridgeless mode, should legacy NativeModules use the TurboModule system?
|
||||
*/
|
||||
|
||||
+31
-21
@@ -4,7 +4,7 @@
|
||||
* This source code is licensed under the MIT license found in the
|
||||
* LICENSE file in the root directory of this source tree.
|
||||
*
|
||||
* @generated SignedSource<<0effb773b4902465909432a2f5576bdf>>
|
||||
* @generated SignedSource<<d7872ba2601906476aec3d08ebe1ab94>>
|
||||
*/
|
||||
|
||||
/**
|
||||
@@ -43,7 +43,10 @@ internal class ReactNativeFeatureFlagsCxxAccessor : ReactNativeFeatureFlagsAcces
|
||||
private var enableReportEventPaintTimeCache: Boolean? = null
|
||||
private var enableSynchronousStateUpdatesCache: Boolean? = null
|
||||
private var enableUIConsistencyCache: Boolean? = null
|
||||
private var enableViewCullingCache: Boolean? = null
|
||||
private var enableViewRecyclingCache: Boolean? = null
|
||||
private var enableViewRecyclingForTextCache: Boolean? = null
|
||||
private var enableViewRecyclingForViewCache: Boolean? = null
|
||||
private var excludeYogaFromRawPropsCache: Boolean? = null
|
||||
private var fixDifferentiatorEmittingUpdatesWithWrongParentTagCache: Boolean? = null
|
||||
private var fixMappingOfEventPrioritiesBetweenFabricAndReactCache: Boolean? = null
|
||||
@@ -51,7 +54,6 @@ internal class ReactNativeFeatureFlagsCxxAccessor : ReactNativeFeatureFlagsAcces
|
||||
private var fuseboxEnabledReleaseCache: Boolean? = null
|
||||
private var fuseboxNetworkInspectionEnabledCache: Boolean? = null
|
||||
private var lazyAnimationCallbacksCache: Boolean? = null
|
||||
private var loadVectorDrawablesOnImagesCache: Boolean? = null
|
||||
private var traceTurboModulePromiseRejectionsOnAndroidCache: Boolean? = null
|
||||
private var useAlwaysAvailableJSErrorHandlingCache: Boolean? = null
|
||||
private var useEditTextStockAndroidFocusBehaviorCache: Boolean? = null
|
||||
@@ -59,7 +61,6 @@ internal class ReactNativeFeatureFlagsCxxAccessor : ReactNativeFeatureFlagsAcces
|
||||
private var useNativeViewConfigsInBridgelessModeCache: Boolean? = null
|
||||
private var useOptimizedEventBatchingOnAndroidCache: Boolean? = null
|
||||
private var useRawPropsJsiValueCache: Boolean? = null
|
||||
private var useRuntimeShadowNodeReferenceUpdateCache: Boolean? = null
|
||||
private var useTurboModuleInteropCache: Boolean? = null
|
||||
private var useTurboModulesCache: Boolean? = null
|
||||
|
||||
@@ -270,6 +271,15 @@ internal class ReactNativeFeatureFlagsCxxAccessor : ReactNativeFeatureFlagsAcces
|
||||
return cached
|
||||
}
|
||||
|
||||
override fun enableViewCulling(): Boolean {
|
||||
var cached = enableViewCullingCache
|
||||
if (cached == null) {
|
||||
cached = ReactNativeFeatureFlagsCxxInterop.enableViewCulling()
|
||||
enableViewCullingCache = cached
|
||||
}
|
||||
return cached
|
||||
}
|
||||
|
||||
override fun enableViewRecycling(): Boolean {
|
||||
var cached = enableViewRecyclingCache
|
||||
if (cached == null) {
|
||||
@@ -279,6 +289,24 @@ internal class ReactNativeFeatureFlagsCxxAccessor : ReactNativeFeatureFlagsAcces
|
||||
return cached
|
||||
}
|
||||
|
||||
override fun enableViewRecyclingForText(): Boolean {
|
||||
var cached = enableViewRecyclingForTextCache
|
||||
if (cached == null) {
|
||||
cached = ReactNativeFeatureFlagsCxxInterop.enableViewRecyclingForText()
|
||||
enableViewRecyclingForTextCache = cached
|
||||
}
|
||||
return cached
|
||||
}
|
||||
|
||||
override fun enableViewRecyclingForView(): Boolean {
|
||||
var cached = enableViewRecyclingForViewCache
|
||||
if (cached == null) {
|
||||
cached = ReactNativeFeatureFlagsCxxInterop.enableViewRecyclingForView()
|
||||
enableViewRecyclingForViewCache = cached
|
||||
}
|
||||
return cached
|
||||
}
|
||||
|
||||
override fun excludeYogaFromRawProps(): Boolean {
|
||||
var cached = excludeYogaFromRawPropsCache
|
||||
if (cached == null) {
|
||||
@@ -342,15 +370,6 @@ internal class ReactNativeFeatureFlagsCxxAccessor : ReactNativeFeatureFlagsAcces
|
||||
return cached
|
||||
}
|
||||
|
||||
override fun loadVectorDrawablesOnImages(): Boolean {
|
||||
var cached = loadVectorDrawablesOnImagesCache
|
||||
if (cached == null) {
|
||||
cached = ReactNativeFeatureFlagsCxxInterop.loadVectorDrawablesOnImages()
|
||||
loadVectorDrawablesOnImagesCache = cached
|
||||
}
|
||||
return cached
|
||||
}
|
||||
|
||||
override fun traceTurboModulePromiseRejectionsOnAndroid(): Boolean {
|
||||
var cached = traceTurboModulePromiseRejectionsOnAndroidCache
|
||||
if (cached == null) {
|
||||
@@ -414,15 +433,6 @@ internal class ReactNativeFeatureFlagsCxxAccessor : ReactNativeFeatureFlagsAcces
|
||||
return cached
|
||||
}
|
||||
|
||||
override fun useRuntimeShadowNodeReferenceUpdate(): Boolean {
|
||||
var cached = useRuntimeShadowNodeReferenceUpdateCache
|
||||
if (cached == null) {
|
||||
cached = ReactNativeFeatureFlagsCxxInterop.useRuntimeShadowNodeReferenceUpdate()
|
||||
useRuntimeShadowNodeReferenceUpdateCache = cached
|
||||
}
|
||||
return cached
|
||||
}
|
||||
|
||||
override fun useTurboModuleInterop(): Boolean {
|
||||
var cached = useTurboModuleInteropCache
|
||||
if (cached == null) {
|
||||
|
||||
+7
-5
@@ -4,7 +4,7 @@
|
||||
* This source code is licensed under the MIT license found in the
|
||||
* LICENSE file in the root directory of this source tree.
|
||||
*
|
||||
* @generated SignedSource<<2a8b1617f45c251d8e6ceb7c70612104>>
|
||||
* @generated SignedSource<<c616ff84eacfbdd7640bc1516e72ad8f>>
|
||||
*/
|
||||
|
||||
/**
|
||||
@@ -74,8 +74,14 @@ public object ReactNativeFeatureFlagsCxxInterop {
|
||||
|
||||
@DoNotStrip @JvmStatic public external fun enableUIConsistency(): Boolean
|
||||
|
||||
@DoNotStrip @JvmStatic public external fun enableViewCulling(): Boolean
|
||||
|
||||
@DoNotStrip @JvmStatic public external fun enableViewRecycling(): Boolean
|
||||
|
||||
@DoNotStrip @JvmStatic public external fun enableViewRecyclingForText(): Boolean
|
||||
|
||||
@DoNotStrip @JvmStatic public external fun enableViewRecyclingForView(): Boolean
|
||||
|
||||
@DoNotStrip @JvmStatic public external fun excludeYogaFromRawProps(): Boolean
|
||||
|
||||
@DoNotStrip @JvmStatic public external fun fixDifferentiatorEmittingUpdatesWithWrongParentTag(): Boolean
|
||||
@@ -90,8 +96,6 @@ public object ReactNativeFeatureFlagsCxxInterop {
|
||||
|
||||
@DoNotStrip @JvmStatic public external fun lazyAnimationCallbacks(): Boolean
|
||||
|
||||
@DoNotStrip @JvmStatic public external fun loadVectorDrawablesOnImages(): Boolean
|
||||
|
||||
@DoNotStrip @JvmStatic public external fun traceTurboModulePromiseRejectionsOnAndroid(): Boolean
|
||||
|
||||
@DoNotStrip @JvmStatic public external fun useAlwaysAvailableJSErrorHandling(): Boolean
|
||||
@@ -106,8 +110,6 @@ public object ReactNativeFeatureFlagsCxxInterop {
|
||||
|
||||
@DoNotStrip @JvmStatic public external fun useRawPropsJsiValue(): Boolean
|
||||
|
||||
@DoNotStrip @JvmStatic public external fun useRuntimeShadowNodeReferenceUpdate(): Boolean
|
||||
|
||||
@DoNotStrip @JvmStatic public external fun useTurboModuleInterop(): Boolean
|
||||
|
||||
@DoNotStrip @JvmStatic public external fun useTurboModules(): Boolean
|
||||
|
||||
+7
-5
@@ -4,7 +4,7 @@
|
||||
* This source code is licensed under the MIT license found in the
|
||||
* LICENSE file in the root directory of this source tree.
|
||||
*
|
||||
* @generated SignedSource<<115a70b8b6854f14841deb0d98e43902>>
|
||||
* @generated SignedSource<<964ac42bbe930d8506dcb9d9834460bd>>
|
||||
*/
|
||||
|
||||
/**
|
||||
@@ -69,8 +69,14 @@ public open class ReactNativeFeatureFlagsDefaults : ReactNativeFeatureFlagsProvi
|
||||
|
||||
override fun enableUIConsistency(): Boolean = false
|
||||
|
||||
override fun enableViewCulling(): Boolean = false
|
||||
|
||||
override fun enableViewRecycling(): Boolean = false
|
||||
|
||||
override fun enableViewRecyclingForText(): Boolean = true
|
||||
|
||||
override fun enableViewRecyclingForView(): Boolean = true
|
||||
|
||||
override fun excludeYogaFromRawProps(): Boolean = false
|
||||
|
||||
override fun fixDifferentiatorEmittingUpdatesWithWrongParentTag(): Boolean = true
|
||||
@@ -85,8 +91,6 @@ public open class ReactNativeFeatureFlagsDefaults : ReactNativeFeatureFlagsProvi
|
||||
|
||||
override fun lazyAnimationCallbacks(): Boolean = false
|
||||
|
||||
override fun loadVectorDrawablesOnImages(): Boolean = true
|
||||
|
||||
override fun traceTurboModulePromiseRejectionsOnAndroid(): Boolean = false
|
||||
|
||||
override fun useAlwaysAvailableJSErrorHandling(): Boolean = false
|
||||
@@ -101,8 +105,6 @@ public open class ReactNativeFeatureFlagsDefaults : ReactNativeFeatureFlagsProvi
|
||||
|
||||
override fun useRawPropsJsiValue(): Boolean = false
|
||||
|
||||
override fun useRuntimeShadowNodeReferenceUpdate(): Boolean = true
|
||||
|
||||
override fun useTurboModuleInterop(): Boolean = false
|
||||
|
||||
override fun useTurboModules(): Boolean = false
|
||||
|
||||
+34
-23
@@ -4,7 +4,7 @@
|
||||
* This source code is licensed under the MIT license found in the
|
||||
* LICENSE file in the root directory of this source tree.
|
||||
*
|
||||
* @generated SignedSource<<7fde7cbd79c60ae151f7c4585363f654>>
|
||||
* @generated SignedSource<<39af73b5dd34ee875ac898945dc7b4e7>>
|
||||
*/
|
||||
|
||||
/**
|
||||
@@ -47,7 +47,10 @@ internal class ReactNativeFeatureFlagsLocalAccessor : ReactNativeFeatureFlagsAcc
|
||||
private var enableReportEventPaintTimeCache: Boolean? = null
|
||||
private var enableSynchronousStateUpdatesCache: Boolean? = null
|
||||
private var enableUIConsistencyCache: Boolean? = null
|
||||
private var enableViewCullingCache: Boolean? = null
|
||||
private var enableViewRecyclingCache: Boolean? = null
|
||||
private var enableViewRecyclingForTextCache: Boolean? = null
|
||||
private var enableViewRecyclingForViewCache: Boolean? = null
|
||||
private var excludeYogaFromRawPropsCache: Boolean? = null
|
||||
private var fixDifferentiatorEmittingUpdatesWithWrongParentTagCache: Boolean? = null
|
||||
private var fixMappingOfEventPrioritiesBetweenFabricAndReactCache: Boolean? = null
|
||||
@@ -55,7 +58,6 @@ internal class ReactNativeFeatureFlagsLocalAccessor : ReactNativeFeatureFlagsAcc
|
||||
private var fuseboxEnabledReleaseCache: Boolean? = null
|
||||
private var fuseboxNetworkInspectionEnabledCache: Boolean? = null
|
||||
private var lazyAnimationCallbacksCache: Boolean? = null
|
||||
private var loadVectorDrawablesOnImagesCache: Boolean? = null
|
||||
private var traceTurboModulePromiseRejectionsOnAndroidCache: Boolean? = null
|
||||
private var useAlwaysAvailableJSErrorHandlingCache: Boolean? = null
|
||||
private var useEditTextStockAndroidFocusBehaviorCache: Boolean? = null
|
||||
@@ -63,7 +65,6 @@ internal class ReactNativeFeatureFlagsLocalAccessor : ReactNativeFeatureFlagsAcc
|
||||
private var useNativeViewConfigsInBridgelessModeCache: Boolean? = null
|
||||
private var useOptimizedEventBatchingOnAndroidCache: Boolean? = null
|
||||
private var useRawPropsJsiValueCache: Boolean? = null
|
||||
private var useRuntimeShadowNodeReferenceUpdateCache: Boolean? = null
|
||||
private var useTurboModuleInteropCache: Boolean? = null
|
||||
private var useTurboModulesCache: Boolean? = null
|
||||
|
||||
@@ -297,6 +298,16 @@ internal class ReactNativeFeatureFlagsLocalAccessor : ReactNativeFeatureFlagsAcc
|
||||
return cached
|
||||
}
|
||||
|
||||
override fun enableViewCulling(): Boolean {
|
||||
var cached = enableViewCullingCache
|
||||
if (cached == null) {
|
||||
cached = currentProvider.enableViewCulling()
|
||||
accessedFeatureFlags.add("enableViewCulling")
|
||||
enableViewCullingCache = cached
|
||||
}
|
||||
return cached
|
||||
}
|
||||
|
||||
override fun enableViewRecycling(): Boolean {
|
||||
var cached = enableViewRecyclingCache
|
||||
if (cached == null) {
|
||||
@@ -307,6 +318,26 @@ internal class ReactNativeFeatureFlagsLocalAccessor : ReactNativeFeatureFlagsAcc
|
||||
return cached
|
||||
}
|
||||
|
||||
override fun enableViewRecyclingForText(): Boolean {
|
||||
var cached = enableViewRecyclingForTextCache
|
||||
if (cached == null) {
|
||||
cached = currentProvider.enableViewRecyclingForText()
|
||||
accessedFeatureFlags.add("enableViewRecyclingForText")
|
||||
enableViewRecyclingForTextCache = cached
|
||||
}
|
||||
return cached
|
||||
}
|
||||
|
||||
override fun enableViewRecyclingForView(): Boolean {
|
||||
var cached = enableViewRecyclingForViewCache
|
||||
if (cached == null) {
|
||||
cached = currentProvider.enableViewRecyclingForView()
|
||||
accessedFeatureFlags.add("enableViewRecyclingForView")
|
||||
enableViewRecyclingForViewCache = cached
|
||||
}
|
||||
return cached
|
||||
}
|
||||
|
||||
override fun excludeYogaFromRawProps(): Boolean {
|
||||
var cached = excludeYogaFromRawPropsCache
|
||||
if (cached == null) {
|
||||
@@ -377,16 +408,6 @@ internal class ReactNativeFeatureFlagsLocalAccessor : ReactNativeFeatureFlagsAcc
|
||||
return cached
|
||||
}
|
||||
|
||||
override fun loadVectorDrawablesOnImages(): Boolean {
|
||||
var cached = loadVectorDrawablesOnImagesCache
|
||||
if (cached == null) {
|
||||
cached = currentProvider.loadVectorDrawablesOnImages()
|
||||
accessedFeatureFlags.add("loadVectorDrawablesOnImages")
|
||||
loadVectorDrawablesOnImagesCache = cached
|
||||
}
|
||||
return cached
|
||||
}
|
||||
|
||||
override fun traceTurboModulePromiseRejectionsOnAndroid(): Boolean {
|
||||
var cached = traceTurboModulePromiseRejectionsOnAndroidCache
|
||||
if (cached == null) {
|
||||
@@ -457,16 +478,6 @@ internal class ReactNativeFeatureFlagsLocalAccessor : ReactNativeFeatureFlagsAcc
|
||||
return cached
|
||||
}
|
||||
|
||||
override fun useRuntimeShadowNodeReferenceUpdate(): Boolean {
|
||||
var cached = useRuntimeShadowNodeReferenceUpdateCache
|
||||
if (cached == null) {
|
||||
cached = currentProvider.useRuntimeShadowNodeReferenceUpdate()
|
||||
accessedFeatureFlags.add("useRuntimeShadowNodeReferenceUpdate")
|
||||
useRuntimeShadowNodeReferenceUpdateCache = cached
|
||||
}
|
||||
return cached
|
||||
}
|
||||
|
||||
override fun useTurboModuleInterop(): Boolean {
|
||||
var cached = useTurboModuleInteropCache
|
||||
if (cached == null) {
|
||||
|
||||
+7
-5
@@ -4,7 +4,7 @@
|
||||
* This source code is licensed under the MIT license found in the
|
||||
* LICENSE file in the root directory of this source tree.
|
||||
*
|
||||
* @generated SignedSource<<bf4fee68309fdc7f9270f44ab5535c14>>
|
||||
* @generated SignedSource<<28634dd2fab612ceddaa3c1a39e9c617>>
|
||||
*/
|
||||
|
||||
/**
|
||||
@@ -69,8 +69,14 @@ public interface ReactNativeFeatureFlagsProvider {
|
||||
|
||||
@DoNotStrip public fun enableUIConsistency(): Boolean
|
||||
|
||||
@DoNotStrip public fun enableViewCulling(): Boolean
|
||||
|
||||
@DoNotStrip public fun enableViewRecycling(): Boolean
|
||||
|
||||
@DoNotStrip public fun enableViewRecyclingForText(): Boolean
|
||||
|
||||
@DoNotStrip public fun enableViewRecyclingForView(): Boolean
|
||||
|
||||
@DoNotStrip public fun excludeYogaFromRawProps(): Boolean
|
||||
|
||||
@DoNotStrip public fun fixDifferentiatorEmittingUpdatesWithWrongParentTag(): Boolean
|
||||
@@ -85,8 +91,6 @@ public interface ReactNativeFeatureFlagsProvider {
|
||||
|
||||
@DoNotStrip public fun lazyAnimationCallbacks(): Boolean
|
||||
|
||||
@DoNotStrip public fun loadVectorDrawablesOnImages(): Boolean
|
||||
|
||||
@DoNotStrip public fun traceTurboModulePromiseRejectionsOnAndroid(): Boolean
|
||||
|
||||
@DoNotStrip public fun useAlwaysAvailableJSErrorHandling(): Boolean
|
||||
@@ -101,8 +105,6 @@ public interface ReactNativeFeatureFlagsProvider {
|
||||
|
||||
@DoNotStrip public fun useRawPropsJsiValue(): Boolean
|
||||
|
||||
@DoNotStrip public fun useRuntimeShadowNodeReferenceUpdate(): Boolean
|
||||
|
||||
@DoNotStrip public fun useTurboModuleInterop(): Boolean
|
||||
|
||||
@DoNotStrip public fun useTurboModules(): Boolean
|
||||
|
||||
+1
-4
@@ -20,7 +20,6 @@ import com.facebook.react.bridge.ReactApplicationContext
|
||||
import com.facebook.react.bridge.ReactContext
|
||||
import com.facebook.react.bridge.ReactContextBaseJavaModule
|
||||
import com.facebook.react.common.ReactConstants
|
||||
import com.facebook.react.internal.featureflags.ReactNativeFeatureFlags
|
||||
import com.facebook.react.module.annotations.ReactModule
|
||||
import com.facebook.react.modules.common.ModuleDataCleaner
|
||||
import com.facebook.react.modules.network.ForwardingCookieHandler
|
||||
@@ -164,9 +163,7 @@ constructor(
|
||||
.setNetworkFetcher(ReactOkHttpNetworkFetcher(client))
|
||||
.setDownsampleMode(DownsampleMode.AUTO)
|
||||
.setRequestListeners(requestListeners)
|
||||
builder
|
||||
.experiment()
|
||||
.setBinaryXmlEnabled(ReactNativeFeatureFlags.loadVectorDrawablesOnImages())
|
||||
builder.experiment().setBinaryXmlEnabled(true)
|
||||
return builder
|
||||
}
|
||||
}
|
||||
|
||||
+1
-1
@@ -16,7 +16,7 @@ import okhttp3.Headers
|
||||
import okhttp3.HttpUrl
|
||||
|
||||
/** Basic okhttp3 CookieJar container */
|
||||
internal class ReactCookieJarContainer : CookieJarContainer {
|
||||
public class ReactCookieJarContainer : CookieJarContainer {
|
||||
|
||||
private var cookieJar: CookieJar? = null
|
||||
|
||||
|
||||
+27
-36
@@ -21,14 +21,13 @@ internal object ResponseUtil {
|
||||
progress: Long,
|
||||
total: Long
|
||||
) {
|
||||
val args =
|
||||
reactContext?.emitDeviceEvent(
|
||||
"didSendNetworkData",
|
||||
Arguments.createArray().apply {
|
||||
pushInt(requestId)
|
||||
pushInt(progress.toInt())
|
||||
pushInt(total.toInt())
|
||||
}
|
||||
|
||||
reactContext?.emitDeviceEvent("didSendNetworkData", args)
|
||||
})
|
||||
}
|
||||
|
||||
@JvmStatic
|
||||
@@ -39,15 +38,14 @@ internal object ResponseUtil {
|
||||
progress: Long,
|
||||
total: Long
|
||||
) {
|
||||
val args =
|
||||
reactContext?.emitDeviceEvent(
|
||||
"didReceiveNetworkIncrementalData",
|
||||
Arguments.createArray().apply {
|
||||
pushInt(requestId)
|
||||
pushString(data)
|
||||
pushInt(progress.toInt())
|
||||
pushInt(total.toInt())
|
||||
}
|
||||
|
||||
reactContext?.emitDeviceEvent("didReceiveNetworkIncrementalData", args)
|
||||
})
|
||||
}
|
||||
|
||||
@JvmStatic
|
||||
@@ -57,36 +55,33 @@ internal object ResponseUtil {
|
||||
progress: Long,
|
||||
total: Long
|
||||
) {
|
||||
val args =
|
||||
reactContext?.emitDeviceEvent(
|
||||
"didReceiveNetworkDataProgress",
|
||||
Arguments.createArray().apply {
|
||||
pushInt(requestId)
|
||||
pushInt(progress.toInt())
|
||||
pushInt(total.toInt())
|
||||
}
|
||||
|
||||
reactContext?.emitDeviceEvent("didReceiveNetworkDataProgress", args)
|
||||
})
|
||||
}
|
||||
|
||||
@JvmStatic
|
||||
fun onDataReceived(reactContext: ReactApplicationContext?, requestId: Int, data: String?) {
|
||||
val args =
|
||||
reactContext?.emitDeviceEvent(
|
||||
"didReceiveNetworkData",
|
||||
Arguments.createArray().apply {
|
||||
pushInt(requestId)
|
||||
pushString(data)
|
||||
}
|
||||
|
||||
reactContext?.emitDeviceEvent("didReceiveNetworkData", args)
|
||||
})
|
||||
}
|
||||
|
||||
@JvmStatic
|
||||
fun onDataReceived(reactContext: ReactApplicationContext?, requestId: Int, data: WritableMap?) {
|
||||
val args =
|
||||
reactContext?.emitDeviceEvent(
|
||||
"didReceiveNetworkData",
|
||||
Arguments.createArray().apply {
|
||||
pushInt(requestId)
|
||||
pushMap(data)
|
||||
}
|
||||
|
||||
reactContext?.emitDeviceEvent("didReceiveNetworkData", args)
|
||||
})
|
||||
}
|
||||
|
||||
@JvmStatic
|
||||
@@ -96,28 +91,25 @@ internal object ResponseUtil {
|
||||
error: String?,
|
||||
e: Throwable?
|
||||
) {
|
||||
val args =
|
||||
reactContext?.emitDeviceEvent(
|
||||
"didCompleteNetworkResponse",
|
||||
Arguments.createArray().apply {
|
||||
pushInt(requestId)
|
||||
pushString(error)
|
||||
}
|
||||
|
||||
if ((e != null) && (e.javaClass == SocketTimeoutException::class.java)) {
|
||||
args.pushBoolean(true) // last argument is a time out boolean
|
||||
}
|
||||
|
||||
reactContext?.emitDeviceEvent("didCompleteNetworkResponse", args)
|
||||
if (e?.javaClass == SocketTimeoutException::class.java) {
|
||||
pushBoolean(true) // last argument is a time out boolean
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
@JvmStatic
|
||||
fun onRequestSuccess(reactContext: ReactApplicationContext?, requestId: Int) {
|
||||
val args =
|
||||
reactContext?.emitDeviceEvent(
|
||||
"didCompleteNetworkResponse",
|
||||
Arguments.createArray().apply {
|
||||
pushInt(requestId)
|
||||
pushNull()
|
||||
}
|
||||
|
||||
reactContext?.emitDeviceEvent("didCompleteNetworkResponse", args)
|
||||
})
|
||||
}
|
||||
|
||||
@JvmStatic
|
||||
@@ -128,14 +120,13 @@ internal object ResponseUtil {
|
||||
headers: WritableMap?,
|
||||
url: String?
|
||||
) {
|
||||
val args =
|
||||
reactContext?.emitDeviceEvent(
|
||||
"didReceiveNetworkResponse",
|
||||
Arguments.createArray().apply {
|
||||
pushInt(requestId)
|
||||
pushInt(statusCode)
|
||||
pushMap(headers)
|
||||
pushString(url)
|
||||
}
|
||||
|
||||
reactContext?.emitDeviceEvent("didReceiveNetworkResponse", args)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
+1
-1
@@ -12,5 +12,5 @@ import com.facebook.proguard.annotations.DoNotStripAny
|
||||
@DoNotStripAny
|
||||
internal interface ComponentNameResolver {
|
||||
/* returns a list of all the component names that are registered in React Native. */
|
||||
public val componentNames: Array<String>?
|
||||
val componentNames: Array<String>?
|
||||
}
|
||||
|
||||
+6
-5
@@ -37,6 +37,7 @@ import java.util.Set;
|
||||
public class JSPointerDispatcher {
|
||||
private static final int UNSELECTED_VIEW_TAG = -1;
|
||||
private static final int UNSET_POINTER_ID = -1;
|
||||
private static final int UNSET_CHILD_VIEW_ID = -1;
|
||||
private static final float ONMOVE_EPSILON = 0.1f;
|
||||
private static final String TAG = "PointerEvents";
|
||||
|
||||
@@ -45,7 +46,7 @@ public class JSPointerDispatcher {
|
||||
private Map<Integer, List<ViewTarget>> mCurrentlyDownPointerIdsToHitPath;
|
||||
private Set<Integer> mHoveringPointerIds = new HashSet<>();
|
||||
|
||||
private int mChildHandlingNativeGesture = -1;
|
||||
private int mChildHandlingNativeGesture = UNSET_CHILD_VIEW_ID;
|
||||
private int mPrimaryPointerId = UNSET_POINTER_ID;
|
||||
private int mCoalescingKey = 0;
|
||||
private int mLastButtonState = 0;
|
||||
@@ -62,7 +63,7 @@ public class JSPointerDispatcher {
|
||||
|
||||
public void onChildStartedNativeGesture(
|
||||
View childView, MotionEvent motionEvent, EventDispatcher eventDispatcher) {
|
||||
if (mChildHandlingNativeGesture != -1 || childView == null) {
|
||||
if (mChildHandlingNativeGesture != UNSET_CHILD_VIEW_ID || childView == null) {
|
||||
// This means we previously had another child start handling this native gesture and now a
|
||||
// different native parent of that child has decided to intercept the touch stream and handle
|
||||
// the gesture itself. Example where this can happen: HorizontalScrollView in a ScrollView.
|
||||
@@ -92,7 +93,7 @@ public class JSPointerDispatcher {
|
||||
|
||||
public void onChildEndedNativeGesture() {
|
||||
// There should be only one child gesture at any given time. We can safely turn off the flag.
|
||||
mChildHandlingNativeGesture = -1;
|
||||
mChildHandlingNativeGesture = UNSET_CHILD_VIEW_ID;
|
||||
}
|
||||
|
||||
// returns the section of the hit path shared by both lists, or an empty list if there's no such
|
||||
@@ -280,7 +281,7 @@ public class JSPointerDispatcher {
|
||||
public void handleMotionEvent(
|
||||
MotionEvent motionEvent, EventDispatcher eventDispatcher, boolean isCapture) {
|
||||
// Don't fire any pointer events if child view is handling native gesture
|
||||
if (mChildHandlingNativeGesture != -1) {
|
||||
if (mChildHandlingNativeGesture != UNSET_CHILD_VIEW_ID) {
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -609,7 +610,7 @@ public class JSPointerDispatcher {
|
||||
// expected to happen very often as it would mean some child View has decided to intercept the
|
||||
// touch stream and start a native gesture only upon receiving the UP/CANCEL event.
|
||||
Assertions.assertCondition(
|
||||
mChildHandlingNativeGesture == -1,
|
||||
mChildHandlingNativeGesture == UNSET_CHILD_VIEW_ID,
|
||||
"Expected to not have already sent a cancel for this gesture");
|
||||
|
||||
int activePointerId = eventState.getActivePointerId();
|
||||
|
||||
+2
-2
@@ -12,7 +12,7 @@ import com.facebook.yoga.YogaDirection
|
||||
|
||||
internal object LayoutDirectionUtil {
|
||||
@JvmStatic
|
||||
public fun toAndroidFromYoga(direction: YogaDirection): Int =
|
||||
fun toAndroidFromYoga(direction: YogaDirection): Int =
|
||||
when (direction) {
|
||||
YogaDirection.LTR -> View.LAYOUT_DIRECTION_LTR
|
||||
YogaDirection.RTL -> View.LAYOUT_DIRECTION_RTL
|
||||
@@ -20,7 +20,7 @@ internal object LayoutDirectionUtil {
|
||||
}
|
||||
|
||||
@JvmStatic
|
||||
public fun toYogaFromAndroid(direction: Int): YogaDirection =
|
||||
fun toYogaFromAndroid(direction: Int): YogaDirection =
|
||||
when (direction) {
|
||||
View.LAYOUT_DIRECTION_LTR -> YogaDirection.LTR
|
||||
View.LAYOUT_DIRECTION_RTL -> YogaDirection.RTL
|
||||
|
||||
+1
-1
@@ -17,5 +17,5 @@ public interface ReactOverflowView {
|
||||
* Gets the overflow state of a view. If set, this should be one of [ViewProps#HIDDEN],
|
||||
* [ViewProps#VISIBLE] or [ViewProps#SCROLL].
|
||||
*/
|
||||
public fun getOverflow(): String?
|
||||
public val overflow: String?
|
||||
}
|
||||
|
||||
+2
-2
@@ -13,6 +13,6 @@ package com.facebook.react.uimanager
|
||||
*/
|
||||
public interface ReactPointerEventsView {
|
||||
|
||||
/** Return the PointerEvents of the View. */
|
||||
public fun getPointerEvents(): PointerEvents
|
||||
/** The PointerEvents of the View. */
|
||||
public val pointerEvents: PointerEvents
|
||||
}
|
||||
|
||||
+2
-2
@@ -10,8 +10,8 @@ package com.facebook.react.uimanager.events
|
||||
import com.facebook.react.bridge.WritableMap
|
||||
|
||||
@Deprecated("Experimental")
|
||||
public interface SynchronousEventReceiver {
|
||||
public fun receiveEvent(
|
||||
internal interface SynchronousEventReceiver {
|
||||
fun receiveEvent(
|
||||
surfaceId: Int,
|
||||
reactTag: Int,
|
||||
eventName: String,
|
||||
|
||||
-24
@@ -1,24 +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.
|
||||
*/
|
||||
|
||||
package com.facebook.react.views.common
|
||||
|
||||
import android.view.View
|
||||
import com.facebook.react.R
|
||||
|
||||
/** Class containing static methods involving manipulations of Views */
|
||||
public object ViewUtils {
|
||||
|
||||
/**
|
||||
* Returns value of testId for the given view, if present
|
||||
*
|
||||
* @param view View to get the testId value for
|
||||
* @return the value of testId if defined for the view, otherwise null
|
||||
*/
|
||||
@JvmStatic
|
||||
public fun getTestId(view: View?): String? = view?.getTag(R.id.react_test_id) as? String
|
||||
}
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user