mirror of
https://github.com/zoriya/react-native-svg.git
synced 2025-12-06 07:06:11 +00:00
Feat: add scripts to sync archs (#2357)
Changes moved from: https://github.com/software-mansion/react-native-screens/pull/2224 ## Description When changing native props on Fabric, codegen generates corresponding interfaces and delegates. To make sure both implementations are consistent, we implement those interfaces on Paper too. Currently, after generating interfaces using codegen, developer needs to copy corresponding files for paper manually. This task adds Gradle task, that automates this. ## Changes Current assumption: Two scripts: `check-archs-consistency` and `sync-archs`. The first one generates codegen interfaces and compares them with what we have for paper, the second generates and copies for paper to sync the archs. - sync is run when staged on changes to `src/paper` - check is run on CI when `src/paper` or `android/src/paper/java/com/facebook/react/viewmanagers` changes ## Test code and steps to reproduce Open `src/fabric/LineNativeComponent.ts` or/and `src/fabric/NativeSvgRenderableModule.ts` and remove any proper form interface. Now: - when building paper, interface should be updated - when committing, interface should be updated - if committed and pushed, Test consistency between Paper & Fabric should fail :) Brining back the prop and repeating up should cause the interface back and CI green. Posting changes in other places should cause CI task to run. You can also run those commands yourself using `yarn check-archs-consistency` and `yarn sync-archs`
This commit is contained in:
27
.github/workflows/check-archs-consistency.yml
vendored
Normal file
27
.github/workflows/check-archs-consistency.yml
vendored
Normal file
@@ -0,0 +1,27 @@
|
|||||||
|
name: Test consistency between Paper & Fabric
|
||||||
|
on:
|
||||||
|
pull_request:
|
||||||
|
branches:
|
||||||
|
- main
|
||||||
|
paths:
|
||||||
|
- src/fabric/**
|
||||||
|
- android/src/paper/java/com/facebook/react/viewmanagers/**
|
||||||
|
- android/src/paper/java/com/horcrux/svg/**
|
||||||
|
jobs:
|
||||||
|
check:
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
concurrency:
|
||||||
|
group: check-archs-consistency-${{ github.ref }}
|
||||||
|
cancel-in-progress: true
|
||||||
|
steps:
|
||||||
|
- name: checkout
|
||||||
|
uses: actions/checkout@v4
|
||||||
|
- name: Use Node.js 18
|
||||||
|
uses: actions/setup-node@v4
|
||||||
|
with:
|
||||||
|
node-version: 18
|
||||||
|
cache: 'yarn'
|
||||||
|
- name: Install node dependencies
|
||||||
|
run: yarn
|
||||||
|
- name: Check Android Paper & Fabric generated interfaces consistency
|
||||||
|
run: yarn check-archs-consistency
|
||||||
@@ -3,7 +3,7 @@ apply plugin: 'com.diffplug.spotless'
|
|||||||
|
|
||||||
spotless {
|
spotless {
|
||||||
java {
|
java {
|
||||||
target 'src/main/java/**/*.java', 'src/paper/java/com/horcrux/svg/**/*.java'
|
target 'src/main/java/**/*.java'
|
||||||
googleJavaFormat()
|
googleJavaFormat()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -1,13 +1,15 @@
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* This code was generated by
|
* This code was generated by [react-native-codegen](https://www.npmjs.com/package/react-native-codegen).
|
||||||
* [react-native-codegen](https://www.npmjs.com/package/react-native-codegen).
|
|
||||||
*
|
*
|
||||||
* <p>Do not edit this file as changes may cause incorrect behavior and will be lost once the code
|
* Do not edit this file as changes may cause incorrect behavior and will be lost
|
||||||
* is regenerated.
|
* once the code is regenerated.
|
||||||
*
|
*
|
||||||
* @generated by codegen project: GenerateModuleJavaSpec.js
|
* @generated by codegen project: GenerateModuleJavaSpec.js
|
||||||
|
*
|
||||||
* @nolint
|
* @nolint
|
||||||
*/
|
*/
|
||||||
|
|
||||||
package com.horcrux.svg;
|
package com.horcrux.svg;
|
||||||
|
|
||||||
import com.facebook.proguard.annotations.DoNotStrip;
|
import com.facebook.proguard.annotations.DoNotStrip;
|
||||||
@@ -15,14 +17,14 @@ import com.facebook.react.bridge.Promise;
|
|||||||
import com.facebook.react.bridge.ReactApplicationContext;
|
import com.facebook.react.bridge.ReactApplicationContext;
|
||||||
import com.facebook.react.bridge.ReactContextBaseJavaModule;
|
import com.facebook.react.bridge.ReactContextBaseJavaModule;
|
||||||
import com.facebook.react.bridge.ReactMethod;
|
import com.facebook.react.bridge.ReactMethod;
|
||||||
|
import com.facebook.react.bridge.ReactModuleWithSpec;
|
||||||
import com.facebook.react.bridge.ReadableMap;
|
import com.facebook.react.bridge.ReadableMap;
|
||||||
import com.facebook.react.bridge.WritableMap;
|
import com.facebook.react.bridge.WritableMap;
|
||||||
import com.facebook.react.turbomodule.core.interfaces.TurboModule;
|
import com.facebook.react.turbomodule.core.interfaces.TurboModule;
|
||||||
import javax.annotation.Nonnull;
|
import javax.annotation.Nonnull;
|
||||||
import javax.annotation.Nullable;
|
import javax.annotation.Nullable;
|
||||||
|
|
||||||
public abstract class NativeSvgRenderableModuleSpec extends ReactContextBaseJavaModule
|
public abstract class NativeSvgRenderableModuleSpec extends ReactContextBaseJavaModule implements ReactModuleWithSpec, TurboModule {
|
||||||
implements TurboModule {
|
|
||||||
public static final String NAME = "RNSVGRenderableModule";
|
public static final String NAME = "RNSVGRenderableModule";
|
||||||
|
|
||||||
public NativeSvgRenderableModuleSpec(ReactApplicationContext reactContext) {
|
public NativeSvgRenderableModuleSpec(ReactApplicationContext reactContext) {
|
||||||
|
|||||||
@@ -1,13 +1,15 @@
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* This code was generated by
|
* This code was generated by [react-native-codegen](https://www.npmjs.com/package/react-native-codegen).
|
||||||
* [react-native-codegen](https://www.npmjs.com/package/react-native-codegen).
|
|
||||||
*
|
*
|
||||||
* <p>Do not edit this file as changes may cause incorrect behavior and will be lost once the code
|
* Do not edit this file as changes may cause incorrect behavior and will be lost
|
||||||
* is regenerated.
|
* once the code is regenerated.
|
||||||
*
|
*
|
||||||
* @generated by codegen project: GenerateModuleJavaSpec.js
|
* @generated by codegen project: GenerateModuleJavaSpec.js
|
||||||
|
*
|
||||||
* @nolint
|
* @nolint
|
||||||
*/
|
*/
|
||||||
|
|
||||||
package com.horcrux.svg;
|
package com.horcrux.svg;
|
||||||
|
|
||||||
import com.facebook.proguard.annotations.DoNotStrip;
|
import com.facebook.proguard.annotations.DoNotStrip;
|
||||||
@@ -15,13 +17,13 @@ import com.facebook.react.bridge.Callback;
|
|||||||
import com.facebook.react.bridge.ReactApplicationContext;
|
import com.facebook.react.bridge.ReactApplicationContext;
|
||||||
import com.facebook.react.bridge.ReactContextBaseJavaModule;
|
import com.facebook.react.bridge.ReactContextBaseJavaModule;
|
||||||
import com.facebook.react.bridge.ReactMethod;
|
import com.facebook.react.bridge.ReactMethod;
|
||||||
|
import com.facebook.react.bridge.ReactModuleWithSpec;
|
||||||
import com.facebook.react.bridge.ReadableMap;
|
import com.facebook.react.bridge.ReadableMap;
|
||||||
import com.facebook.react.turbomodule.core.interfaces.TurboModule;
|
import com.facebook.react.turbomodule.core.interfaces.TurboModule;
|
||||||
import javax.annotation.Nonnull;
|
import javax.annotation.Nonnull;
|
||||||
import javax.annotation.Nullable;
|
import javax.annotation.Nullable;
|
||||||
|
|
||||||
public abstract class NativeSvgViewModuleSpec extends ReactContextBaseJavaModule
|
public abstract class NativeSvgViewModuleSpec extends ReactContextBaseJavaModule implements ReactModuleWithSpec, TurboModule {
|
||||||
implements TurboModule {
|
|
||||||
public static final String NAME = "RNSVGSvgViewModule";
|
public static final String NAME = "RNSVGSvgViewModule";
|
||||||
|
|
||||||
public NativeSvgViewModuleSpec(ReactApplicationContext reactContext) {
|
public NativeSvgViewModuleSpec(ReactApplicationContext reactContext) {
|
||||||
@@ -35,6 +37,5 @@ public abstract class NativeSvgViewModuleSpec extends ReactContextBaseJavaModule
|
|||||||
|
|
||||||
@ReactMethod
|
@ReactMethod
|
||||||
@DoNotStrip
|
@DoNotStrip
|
||||||
public abstract void toDataURL(
|
public abstract void toDataURL(@Nullable Double tag, @Nullable ReadableMap options, @Nullable Callback callback);
|
||||||
@Nullable Double tag, @Nullable ReadableMap options, @Nullable Callback callback);
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -57,7 +57,9 @@
|
|||||||
"prepare": "npm run bob && husky install",
|
"prepare": "npm run bob && husky install",
|
||||||
"release": "npm login && release-it",
|
"release": "npm login && release-it",
|
||||||
"test": "npm run lint && npm run tsc",
|
"test": "npm run lint && npm run tsc",
|
||||||
"tsc": "tsc --noEmit"
|
"tsc": "tsc --noEmit",
|
||||||
|
"check-archs-consistency": "node ./scripts/codegen-check-consistency.js",
|
||||||
|
"sync-archs": "node ./scripts/codegen-sync-archs.js"
|
||||||
},
|
},
|
||||||
"peerDependencies": {
|
"peerDependencies": {
|
||||||
"react": "*",
|
"react": "*",
|
||||||
@@ -110,7 +112,8 @@
|
|||||||
"{src,Example}/**/*.{js,ts,tsx}": "yarn format-js",
|
"{src,Example}/**/*.{js,ts,tsx}": "yarn format-js",
|
||||||
"src/**/*.{js,ts,tsx}": "yarn lint",
|
"src/**/*.{js,ts,tsx}": "yarn lint",
|
||||||
"apple/**/*.{h,m,mm,cpp}": "yarn format-ios",
|
"apple/**/*.{h,m,mm,cpp}": "yarn format-ios",
|
||||||
"android/src/**/*.java": "yarn format-java"
|
"android/src/**/*.java": "yarn format-java",
|
||||||
|
"src/fabric/*.ts": "yarn sync-archs"
|
||||||
},
|
},
|
||||||
"nativePackage": true,
|
"nativePackage": true,
|
||||||
"codegenConfig": {
|
"codegenConfig": {
|
||||||
|
|||||||
3
scripts/codegen-check-consistency.js
Normal file
3
scripts/codegen-check-consistency.js
Normal file
@@ -0,0 +1,3 @@
|
|||||||
|
const { checkCodegenIntegrity } = require('./codegen-utils');
|
||||||
|
|
||||||
|
checkCodegenIntegrity();
|
||||||
3
scripts/codegen-sync-archs.js
Normal file
3
scripts/codegen-sync-archs.js
Normal file
@@ -0,0 +1,3 @@
|
|||||||
|
const { generateCodegenJavaOldArch } = require('./codegen-utils');
|
||||||
|
|
||||||
|
generateCodegenJavaOldArch();
|
||||||
131
scripts/codegen-utils.js
Normal file
131
scripts/codegen-utils.js
Normal file
@@ -0,0 +1,131 @@
|
|||||||
|
const fs = require('fs');
|
||||||
|
const path = require('path');
|
||||||
|
const { execSync } = require('child_process');
|
||||||
|
const packageJSON = require('../package.json');
|
||||||
|
|
||||||
|
const ERROR_PREFIX = 'RNSVG';
|
||||||
|
const ROOT_DIR = path.resolve(__dirname, '..');
|
||||||
|
const ANDROID_DIR = path.resolve(ROOT_DIR, 'android');
|
||||||
|
const GENERATED_DIR = path.resolve(ANDROID_DIR, 'build/generated');
|
||||||
|
const OLD_ARCH_DIR = path.resolve(ANDROID_DIR, 'src/paper');
|
||||||
|
const SPECS_DIR = path.resolve(ROOT_DIR, packageJSON.codegenConfig.jsSrcsDir);
|
||||||
|
const PACKAGE_NAME = packageJSON.codegenConfig.android.javaPackageName;
|
||||||
|
const RN_DIR = path.resolve(ROOT_DIR, 'node_modules/react-native');
|
||||||
|
const RN_CODEGEN_DIR = path.resolve(
|
||||||
|
ROOT_DIR,
|
||||||
|
'node_modules/@react-native/codegen'
|
||||||
|
);
|
||||||
|
|
||||||
|
const SOURCE_FOLDER = 'java/com/facebook/react/viewmanagers';
|
||||||
|
const SOURCE_FOLDER_HORCRUX = 'java/com/horcrux/svg';
|
||||||
|
|
||||||
|
const SOURCE_FOLDERS = [
|
||||||
|
{codegenPath: `${GENERATED_DIR}/source/codegen/${SOURCE_FOLDER}`, oldArchPath: `${OLD_ARCH_DIR}/${SOURCE_FOLDER}`},
|
||||||
|
{codegenPath: `${GENERATED_DIR}/source/codegen/${SOURCE_FOLDER_HORCRUX}`, oldArchPath: `${OLD_ARCH_DIR}/${SOURCE_FOLDER_HORCRUX}`},
|
||||||
|
|
||||||
|
]
|
||||||
|
|
||||||
|
function exec(command) {
|
||||||
|
console.log(`[${ERROR_PREFIX}]> ` + command);
|
||||||
|
execSync(command);
|
||||||
|
}
|
||||||
|
|
||||||
|
function fixOldArchJavaForRN72Compat(dir) {
|
||||||
|
// see https://github.com/rnmapbox/maps/issues/3193
|
||||||
|
const files = fs.readdirSync(dir);
|
||||||
|
files.forEach(file => {
|
||||||
|
const filePath = path.join(dir, file);
|
||||||
|
const fileExtension = path.extname(file);
|
||||||
|
if (fileExtension === '.java') {
|
||||||
|
let fileContent = fs.readFileSync(filePath, 'utf-8');
|
||||||
|
let newFileContent = fileContent.replace(
|
||||||
|
/extends ReactContextBaseJavaModule implements TurboModule/g,
|
||||||
|
'extends ReactContextBaseJavaModule implements ReactModuleWithSpec, TurboModule'
|
||||||
|
);
|
||||||
|
if (fileContent !== newFileContent) {
|
||||||
|
// also insert an import line with `import com.facebook.react.bridge.ReactModuleWithSpec;`
|
||||||
|
newFileContent = newFileContent.replace(
|
||||||
|
/import com.facebook.react.bridge.ReactMethod;/,
|
||||||
|
'import com.facebook.react.bridge.ReactMethod;\nimport com.facebook.react.bridge.ReactModuleWithSpec;'
|
||||||
|
);
|
||||||
|
|
||||||
|
console.log(' => fixOldArchJava applied to:', filePath);
|
||||||
|
fs.writeFileSync(filePath, newFileContent, 'utf-8');
|
||||||
|
}
|
||||||
|
} else if (fs.lstatSync(filePath).isDirectory()) {
|
||||||
|
fixOldArchJavaForRN72Compat(filePath);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
async function generateCodegen() {
|
||||||
|
exec(`rm -rf ${GENERATED_DIR}`);
|
||||||
|
exec(`mkdir -p ${GENERATED_DIR}/source/codegen/`);
|
||||||
|
|
||||||
|
exec(
|
||||||
|
`node ${RN_CODEGEN_DIR}/lib/cli/combine/combine-js-to-schema-cli.js --platform android ${GENERATED_DIR}/source/codegen/schema.json ${SPECS_DIR}`
|
||||||
|
);
|
||||||
|
exec(
|
||||||
|
`node ${RN_DIR}/scripts/generate-specs-cli.js --platform android --schemaPath ${GENERATED_DIR}/source/codegen/schema.json --outputDir ${GENERATED_DIR}/source/codegen --javaPackageName ${PACKAGE_NAME}`
|
||||||
|
);
|
||||||
|
|
||||||
|
fixOldArchJavaForRN72Compat(`${GENERATED_DIR}/source/codegen/java/`);
|
||||||
|
}
|
||||||
|
|
||||||
|
async function generateCodegenJavaOldArch() {
|
||||||
|
await generateCodegen();
|
||||||
|
|
||||||
|
SOURCE_FOLDERS.forEach(({codegenPath, oldArchPath}) => {
|
||||||
|
const generatedFiles = fs.readdirSync(codegenPath);
|
||||||
|
const oldArchFiles = fs.readdirSync(oldArchPath);
|
||||||
|
const existingFilesSet = new Set(oldArchFiles.map(fileName => fileName));
|
||||||
|
|
||||||
|
generatedFiles.forEach(generatedFile => {
|
||||||
|
if (!existingFilesSet.has(generatedFile)) {
|
||||||
|
console.warn(
|
||||||
|
`[${ERROR_PREFIX}] ${generatedFile} not found in paper dir, if it's used on Android you need to copy it manually and implement yourself before using auto-copy feature.`
|
||||||
|
);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
if (oldArchFiles.length === 0) {
|
||||||
|
console.warn(
|
||||||
|
`[${ERROR_PREFIX}] Paper destination with codegen interfaces is empty. This might be okay if you don't have any interfaces/delegates used on Android, otherwise please check if OLD_ARCH_DIR and SOURCE_FOLDERS are set properly.`
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
oldArchFiles.forEach(file => {
|
||||||
|
if (!fs.existsSync(`${codegenPath}/${file}`)) {
|
||||||
|
console.warn(
|
||||||
|
`[${ERROR_PREFIX}] ${file} file does not exist in codegen artifacts source destination. Please check if you still need this interface/delagete.`
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
exec(`cp -rf ${codegenPath}/${file} ${oldArchPath}/${file}`);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
function compareFileAtTwoPaths(filename, firstPath, secondPath) {
|
||||||
|
const fileA = fs.readFileSync(`${firstPath}/${filename}`, 'utf-8');
|
||||||
|
const fileB = fs.readFileSync(`${secondPath}/${filename}`, 'utf-8');
|
||||||
|
|
||||||
|
if (fileA !== fileB) {
|
||||||
|
throw new Error(
|
||||||
|
`[${ERROR_PREFIX}] File ${filename} is different at ${firstPath} and ${secondPath}. Make sure you commited codegen autogenerated files.`
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
async function checkCodegenIntegrity() {
|
||||||
|
await generateCodegen();
|
||||||
|
|
||||||
|
SOURCE_FOLDERS.forEach(({codegenPath, oldArchPath}) => {
|
||||||
|
const oldArchFiles = fs.readdirSync(oldArchPath);
|
||||||
|
oldArchFiles.forEach(file => {
|
||||||
|
compareFileAtTwoPaths(file, codegenPath, oldArchPath);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
module.exports = { generateCodegenJavaOldArch, checkCodegenIntegrity };
|
||||||
Reference in New Issue
Block a user