Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: ship codegen-generated specs #566

Merged
merged 23 commits into from
Aug 16, 2024
Merged
Show file tree
Hide file tree
Changes from 22 commits
Commits
Show all changes
23 commits
Select commit Hold shift + click to select a range
e7b01a1
feat: add outputdir and includesGeneratedCode to package json
atlj Jun 12, 2024
7126ac9
feat: add codegen script to new arch templates
atlj Jun 12, 2024
ca8722c
feat: ignore codegen files
atlj Jun 12, 2024
7611db6
feat: register codegen native files
atlj Jun 12, 2024
ff6ba71
feat: add cmake path to react native config
atlj Jun 12, 2024
59a1b48
feat: add gradle script to invoke codegen
atlj Jun 12, 2024
4420147
feat: add codegen scripts to xcode
atlj Jun 12, 2024
d08abd0
refactor: extract the example app codegen patch function
atlj Jun 13, 2024
f95dc16
fix: patch the codegen java package issue
atlj Jun 13, 2024
a205cbb
chore: add util to patch codegen on bob
atlj Jul 5, 2024
4f8c2ef
feat: use bob codegen instead of scripts in templates
atlj Jul 5, 2024
03986b0
feat: codegen command for bob cli
atlj Jul 5, 2024
f5a5408
feat: move bob codegen to bob build --target codegen
atlj Jul 5, 2024
60d2ba1
chore: don't use rmDir
atlj Jul 5, 2024
e52f6b7
fix: android gradle task has whitespace
atlj Jul 5, 2024
c11042c
fix: don't add codegen target on legacy projects
atlj Jul 5, 2024
7c4ccf2
fix: json is officially the worst format ever
atlj Jul 5, 2024
d0b1ad2
fix: JSON is why we cannot have good things
atlj Jul 5, 2024
a202424
docs: add comments to example app codegen files
atlj Jul 5, 2024
ccb83d6
Merge branch 'main' into @atlj/ship-codegen-specs
atlj Jul 23, 2024
1fc2f60
fix: addres issues
atlj Aug 9, 2024
39d2a07
fix: use all as codegen type
atlj Aug 16, 2024
c75113a
feat: remove previous codegen specs
atlj Aug 16, 2024
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions packages/create-react-native-library/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ import generateExampleApp, {
} from './utils/generateExampleApp';
import { spawn } from './utils/spawn';
import { version } from '../package.json';
import { addCodegenBuildScript } from './utils/addCodegenBuildScript';

const FALLBACK_BOB_VERSION = '0.29.0';

Expand Down Expand Up @@ -788,6 +789,10 @@ async function create(_argv: yargs.Arguments<any>) {
rootPackageJson.devDependencies['react-native'] =
examplePackageJson.dependencies['react-native'];
}

if (arch !== 'legacy') {
addCodegenBuildScript(folder, options.project.name);
}
}

// Some of the passed args can already be derived from the generated package.json file.
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,98 @@
import path from 'path';
import fs from 'fs-extra';

// This is added to the example app's build.gradle file to invoke codegen before every build
const GRADLE_INVOKE_CODEGEN_TASK = `
def isNewArchitectureEnabled() {
return rootProject.hasProperty("newArchEnabled") && rootProject.getProperty("newArchEnabled") == "true"
}
if (isNewArchitectureEnabled()) {
// Since our library doesn't invoke codegen automatically we need to do it here.
tasks.register('invokeLibraryCodegen', Exec) {
workingDir "$rootDir/../../"
commandLine "npx", "bob", "build", "--target", "codegen"
}
preBuild.dependsOn invokeLibraryCodegen
}`;

// This is added to the example app's xcscheme file to invoke codegen before every build
const XCODE_INVOKE_CODEGEN_ACTION = `
<PreActions>
<ExecutionAction
ActionType = "Xcode.IDEStandardExecutionActionsCore.ExecutionActionType.ShellScriptAction">
<ActionContent
title = "Invoke Codegen"
scriptText = "cd &quot;$WORKSPACE_PATH/../../../&quot; &amp;&amp; npx bob build --target codegen&#10;">
</ActionContent>
</ExecutionAction>
</PreActions>`;

// You need to have the files before calling pod install otherwise they won't be registered in your pod.
// So we add a pre_install hook to the podfile that invokes codegen
const PODSPEC_INVOKE_CODEGEN_SCRIPT = `
pre_install do |installer|
system("cd ../../ && npx bob build --target codegen")
end
`;

/**
* Codegen isn't invoked for libraries with `includesGeneratedCode` set to `true`.
* This patches the example app to invoke library codegen on every app build.
*/
export async function addCodegenBuildScript(
libraryPath: string,
projectName: string
) {
const appBuildGradlePath = path.join(
libraryPath,
'example',
'android',
'app',
'build.gradle'
);
const exampleAppBuildSchemePath = path.join(
libraryPath,
'example',
'ios',
`${projectName}Example.xcodeproj`,
'xcshareddata',
'xcschemes',
`${projectName}Example.xcscheme`
);
const podfilePath = path.join(libraryPath, 'example', 'ios', 'Podfile');

// Add a gradle task that runs before every build
let appBuildGradle = (await fs.readFile(appBuildGradlePath)).toString();
appBuildGradle += GRADLE_INVOKE_CODEGEN_TASK;

await fs.writeFile(appBuildGradlePath, appBuildGradle);

// Add an XCode prebuild action.
const exampleAppBuildScheme = (await fs.readFile(exampleAppBuildSchemePath))
.toString()
.split('\n');
// Used XCode and inspected the result to determine where it inserts the actions
const actionTargetLineIndex = exampleAppBuildScheme.findIndex((line) =>
line.includes('<BuildActionEntries>')
);
exampleAppBuildScheme.splice(
actionTargetLineIndex,
0,
XCODE_INVOKE_CODEGEN_ACTION
);

await fs.writeFile(
exampleAppBuildSchemePath,
exampleAppBuildScheme.join('\n')
);

// Add a preinstall action to the podfile that invokes codegen
const podfile = (await fs.readFile(podfilePath)).toString().split('\n');
const podfilePostInstallIndex = podfile.findIndex((line) =>
line.includes('post_install do |installer|')
);
podfile.splice(podfilePostInstallIndex, 0, PODSPEC_INVOKE_CODEGEN_SCRIPT);

await fs.writeFile(podfilePath, podfile.join('\n'));
}
Original file line number Diff line number Diff line change
Expand Up @@ -76,3 +76,7 @@ android/keystores/debug.keystore

# generated by bob
lib/

# React Native Codegen
ios/generated
android/generated
Original file line number Diff line number Diff line change
Expand Up @@ -168,6 +168,9 @@
"source": "src",
"output": "lib",
"targets": [
<% if (project.arch !== 'legacy') { -%>
"codegen",
<% } -%>
[
"commonjs",
{
Expand All @@ -191,8 +194,16 @@
},
"codegenConfig": {
"name": "RN<%- project.name -%><%- project.view ? 'View': '' -%>Spec",
"type": <%- project.view ? '"components"': '"modules"' %>,
"jsSrcsDir": "src"
"type": "all",
"jsSrcsDir": "src",
"outputDir": {
"ios": "ios/generated",
"android": "android/generated"
},
"android": {
"javaPackageName": "com.<%- project.package %>"
},
"includesGeneratedCode": true
<% } -%>
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -114,8 +114,8 @@ android {
main {
if (isNewArchitectureEnabled()) {
java.srcDirs += [
// This is needed to build Kotlin project with NewArch enabled
"${project.buildDir}/generated/source/codegen/java"
"generated/java",
"generated/jni"
]
}
}
Expand All @@ -127,8 +127,9 @@ android {
if (isNewArchitectureEnabled()) {
java.srcDirs += [
"src/newarch",
// This is needed to build Kotlin project with NewArch enabled
"${project.buildDir}/generated/source/codegen/java"
// Codegen specs
"generated/java",
"generated/jni"
]
} else {
java.srcDirs += ["src/oldarch"]
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,8 @@ Pod::Spec.new do |s|
s.source_files = "ios/**/*.{h,m,mm}", "cpp/**/*.{hpp,cpp,c,h}"
<% } else if (project.swift) { -%>
s.source_files = "ios/**/*.{h,m,mm,swift}"
<% } else if (project.arch !== "legacy") { -%>
s.source_files = "ios/**/*.{h,m,mm,cpp}"
<% } else { -%>
s.source_files = "ios/**/*.{h,m,mm}"
<% } -%>
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
/**
* @type {import('@react-native-community/cli-types').UserDependencyConfig}
*/
module.exports = {
dependency: {
platforms: {
android: {
cmakeListsPath: 'generated/jni/CMakeLists.txt',
},
},
},
};
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
/**
* @type {import('@react-native-community/cli-types').UserDependencyConfig}
*/
module.exports = {
dependency: {
platforms: {
android: {
cmakeListsPath: 'generated/jni/CMakeLists.txt',
},
},
},
};
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
/**
* @type {import('@react-native-community/cli-types').UserDependencyConfig}
*/
module.exports = {
dependency: {
platforms: {
android: {
cmakeListsPath: 'generated/jni/CMakeLists.txt',
},
},
},
};
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
/**
* @type {import('@react-native-community/cli-types').UserDependencyConfig}
*/
module.exports = {
dependency: {
platforms: {
android: {
cmakeListsPath: 'generated/jni/CMakeLists.txt',
},
},
},
};
4 changes: 3 additions & 1 deletion packages/react-native-builder-bob/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -79,10 +79,12 @@
"@types/fs-extra": "^9.0.13",
"@types/glob": "^7.2.0",
"@types/json5": "^2.2.0",
"@types/mock-fs": "^4.13.4",
"@types/prompts": "^2.0.14",
"@types/which": "^2.0.1",
"@types/yargs": "^17.0.10",
"concurrently": "^7.2.2",
"jest": "^29.7.0"
"jest": "^29.7.0",
"mock-fs": "^5.2.0"
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,86 @@
import { expect, it, describe, beforeEach, afterEach } from '@jest/globals';
import fs from 'fs-extra';
import path from 'node:path';
import { patchCodegen } from '../utils/patchCodegen';
import mockfs from 'mock-fs';

const mockPackageJson = {
codegenConfig: {
outputDir: {
android: 'android/generated',
},
android: {
javaPackageName: 'com.bobtest',
},
},
};

const mockJavaSpec = `
/**
* Some comment
*/

package com.bobtest;

import com.example.exampleimport;

class SomeClass {
public void someMethod() {
// some code
}
}`;

const mockProjectPath = path.resolve(__dirname, 'mockProject');
const mockCodegenSpecsPath = path.resolve(
mockProjectPath,
'android/generated/java/com/facebook/fbreact/specs'
);

describe('patchCodegen', () => {
beforeEach(() => {
mockfs({
[mockProjectPath]: {
'package.json': JSON.stringify(mockPackageJson),
},
[mockCodegenSpecsPath]: {
'NativeBobtestSpec.java': mockJavaSpec,
},
});
});

afterEach(() => {
mockfs.restore();
});

it('moves the files to correct dir', async () => {
await patchCodegen(mockProjectPath);

const expectedDir = path.resolve(
mockProjectPath,
'android/generated/java/com/bobtest'
);

expect(await fs.pathExists(expectedDir)).toBe(true);
});

it('replaces the package name in the files', async () => {
await patchCodegen(mockProjectPath);

const expectedDir = path.resolve(
mockProjectPath,
'android/generated/java/com/bobtest'
);

const expectedFile = path.resolve(expectedDir, 'NativeBobtestSpec.java');

const fileContent = await fs.readFile(expectedFile, 'utf8');

expect(fileContent).toContain('package com.bobtest;');
});

it('removes the old package dir', async () => {
await patchCodegen(mockProjectPath);

expect(await fs.pathExists(mockCodegenSpecsPath)).toBe(false);
});
});
Loading
Loading