Skip to content

feat: add fabric option #273

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

Merged
merged 41 commits into from
Oct 27, 2022
Merged
Show file tree
Hide file tree
Changes from 36 commits
Commits
Show all changes
41 commits
Select commit Hold shift + click to select a range
8e8e114
feat: initial fabric cli
MateWW Aug 25, 2022
d5aec16
feat: fabric root directory
MateWW Aug 25, 2022
dbde595
feat: fabric src dir
MateWW Aug 25, 2022
677b08b
feat: fabric ios dir
MateWW Aug 25, 2022
9f1febf
feat: generate example app via `react-native init`
atlj Sep 6, 2022
a06282b
chore(example template): remove android files
atlj Sep 6, 2022
3d2482f
chore(example template): remove unused root files
atlj Sep 6, 2022
98f8e40
chore(example template): remove ios files
atlj Sep 6, 2022
795d980
chore(example template): remove android buildscripts
atlj Sep 6, 2022
93223d1
refactor: move rn example app generator to utils
atlj Sep 6, 2022
d7fa53f
feat: tbc
MateWW Sep 6, 2022
25bb21b
chore(example template): rename index.tsx to index.ts
atlj Sep 6, 2022
8bd1521
feat: enable new architecture on turbo module libraries
atlj Sep 6, 2022
1e3c167
feat: rename to new_arch
Sep 6, 2022
e1aacb6
refactor: rename app exampleAppGenerators to generateRNApp
atlj Sep 6, 2022
910769d
refactor: don't specify RN version for example apps
atlj Sep 6, 2022
8d3e444
refactor(example generator): extract files to delete to constant
atlj Sep 6, 2022
3e1548b
refactor(rn example generator): extract packages to add and remove
atlj Sep 6, 2022
2f01afb
fix: native example code is generated on expo projects
atlj Sep 6, 2022
a112c16
fix: example
Sep 6, 2022
a0390ea
fix: MainComponentsRegistry headers
Sep 6, 2022
f7fb47d
refactor(example generator): change isTurbomodule flag as isNewArch
atlj Sep 6, 2022
950881a
docs(example generator): remove version param from init command
atlj Sep 6, 2022
e5a5e8f
chore: revert naming
Sep 7, 2022
0a27b16
feat: implement fabric native view
Sep 7, 2022
6fd32f3
feat: set root package's react and react native versions from example…
atlj Sep 7, 2022
813862f
fix: lint
Sep 7, 2022
9e3a313
chore: add a patch for react-native 0.70.0 for codegen on ios
atlj Sep 7, 2022
72097b4
fix(example template): tests cant import `App.tsx`
atlj Sep 7, 2022
5a23737
fix(example generator): react-test-renderer and react-native types ar…
atlj Sep 8, 2022
bac2efa
chore: unify filenames
Sep 8, 2022
881aaf4
Merge branch '@atlj/integrate-react-native-init' into MateWW/fabric-t…
Sep 8, 2022
6d19ea5
fix: java view mixed path
Sep 13, 2022
bc926bc
fix: App.tsx template
Sep 13, 2022
dbfaf38
chore: bump react native version
Sep 13, 2022
bdb6dce
fix: App.tsx new arch
Sep 14, 2022
8ac7b5d
chore: bump @types/react-native to 0.70.0
Sep 14, 2022
fc40661
Merge branch 'main' into MateWW/fabric-template
Oct 24, 2022
a066c22
remove patch-package
Oct 24, 2022
1daacea
add fix flag
Oct 24, 2022
59d628c
- Remove commands
Oct 25, 2022
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
109 changes: 78 additions & 31 deletions packages/create-react-native-library/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ import spawn from 'cross-spawn';
import validateNpmPackage from 'validate-npm-package-name';
import githubUsername from 'github-username';
import prompts, { PromptObject } from './utils/prompts';
import generateRNApp from './utils/generateRNApp';

const FALLBACK_BOB_VERSION = '0.18.3';

Expand All @@ -17,43 +18,46 @@ const COMMON_FILES = path.resolve(__dirname, '../templates/common');
const JS_FILES = path.resolve(__dirname, '../templates/js-library');
const EXPO_FILES = path.resolve(__dirname, '../templates/expo-library');
const CPP_FILES = path.resolve(__dirname, '../templates/cpp-library');
const EXAMPLE_FILES = path.resolve(__dirname, '../templates/example');
const EXAMPLE_TURBO_FILES = path.resolve(
__dirname,
'../templates/example-turbo'
);
const EXAMPLE_FILES = path.resolve(__dirname, '../templates/example-legacy');
const EXAMPLE_NEW_FILES = path.resolve(__dirname, '../templates/example-new');
const NATIVE_COMMON_FILES = path.resolve(
__dirname,
'../templates/native-common'
);

const NATIVE_FILES = {
module_legacy: path.resolve(__dirname, '../templates/native-library-legacy'),
module_turbo: path.resolve(__dirname, '../templates/native-library-turbo'),
module_new: path.resolve(__dirname, '../templates/native-library-new'),
module_mixed: path.resolve(__dirname, '../templates/native-library-mixed'),
view: path.resolve(__dirname, '../templates/native-view-library'),
view_legacy: path.resolve(__dirname, '../templates/native-view-legacy'),
view_mixed: path.resolve(__dirname, '../templates/native-view-mixed'),
view_new: path.resolve(__dirname, '../templates/native-view-new'),
};

const JAVA_FILES = {
module_legacy: path.resolve(__dirname, '../templates/java-library-legacy'),
module_turbo: path.resolve(__dirname, '../templates/java-library-turbo'),
module_new: path.resolve(__dirname, '../templates/java-library-new'),
module_mixed: path.resolve(__dirname, '../templates/java-library-mixed'),
view: path.resolve(__dirname, '../templates/java-view-library'),
view_legacy: path.resolve(__dirname, '../templates/java-view-legacy'),
view_mixed: path.resolve(__dirname, '../templates/java-view-mixed'),
view_new: path.resolve(__dirname, '../templates/java-view-new'),
};

const OBJC_FILES = {
module: path.resolve(__dirname, '../templates/objc-library'),
view: path.resolve(__dirname, '../templates/objc-view-library'),
view_legacy: path.resolve(__dirname, '../templates/objc-view-legacy'),
view_mixed: path.resolve(__dirname, '../templates/objc-view-mixed'),
view_new: path.resolve(__dirname, '../templates/objc-view-new'),
};

const KOTLIN_FILES = {
module: path.resolve(__dirname, '../templates/kotlin-library'),
view: path.resolve(__dirname, '../templates/kotlin-view-library'),
view: path.resolve(__dirname, '../templates/kotlin-view-legacy'),
};

const SWIFT_FILES = {
module: path.resolve(__dirname, '../templates/swift-library'),
view: path.resolve(__dirname, '../templates/swift-view-library'),
module: path.resolve(__dirname, '../templates/swift-library-legacy'),
view: path.resolve(__dirname, '../templates/swift-view-legacy'),
};

type ArgName =
Expand All @@ -80,7 +84,14 @@ type Answers = {
| 'kotlin-objc'
| 'kotlin-swift'
| 'cpp';
type?: 'module-legacy' | 'module-turbo' | 'module-mixed' | 'view' | 'library';
type?:
| 'module-legacy'
| 'module-new'
| 'module-mixed'
| 'view'
| 'view-mixed'
| 'view-new'
| 'library';
example?: 'expo' | 'native';
};

Expand Down Expand Up @@ -120,6 +131,7 @@ const args: Record<ArgName, yargs.Options> = {
'js',
],
},
// TODO: update those types
'type': {
description: 'Type of library you want to develop',
choices: ['module', 'view'],
Expand Down Expand Up @@ -246,21 +258,31 @@ async function create(argv: yargs.Arguments<any>) {
},
{
title: 'Turbo module (experimental)',
value: 'module-turbo',
value: 'module-new',
},
{
title: 'Native module',
value: 'module-legacy',
},
{ title: 'Native view', value: 'view' },
{
title: 'Native fabric view with backward compat (experimental)',
value: 'view-mixed',
},
{
title: 'Native fabric view (experimental)',
value: 'view-new',
},
{ title: 'JavaScript library', value: 'library' },
],
},
'languages': {
type: (_, values) =>
values.type === 'library' ||
values.type === 'module-turbo' ||
values.type === 'module-mixed'
values.type === 'module-new' ||
values.type === 'module-mixed' ||
values.type === 'view-new' ||
values.type === 'view-mixed'
? null
: 'select',
name: 'languages',
Expand Down Expand Up @@ -339,17 +361,14 @@ async function create(argv: yargs.Arguments<any>) {
version = FALLBACK_BOB_VERSION;
}

const moduleType = type === 'view' ? 'view' : 'module';

const moduleType = type.startsWith('view') ? 'view' : 'module';
const architecture =
type === 'module-turbo'
? 'turbo'
: type === 'module-mixed'
type === 'module-new' || type === 'view-new'
? 'new'
: type === 'module-mixed' || type === 'view-mixed'
? 'mixed'
: 'legacy';

const turbomodule = architecture === 'turbo' || architecture === 'mixed';

const options = {
bob: {
version: version || FALLBACK_BOB_VERSION,
Expand All @@ -364,11 +383,11 @@ async function create(argv: yargs.Arguments<any>) {
identifier: slug.replace(/[^a-z0-9]+/g, '-').replace(/^-/, ''),
native: languages !== 'js',
architecture,
turbomodule,
cpp: languages === 'cpp',
kotlin: languages === 'kotlin-objc' || languages === 'kotlin-swift',
swift: languages === 'java-swift' || languages === 'kotlin-swift',
view: type === 'view',
view: moduleType === 'view',
module: moduleType === 'module',
},
author: {
name: authorName,
Expand Down Expand Up @@ -407,6 +426,15 @@ async function create(argv: yargs.Arguments<any>) {
}
};

await fs.mkdirp(folder);
if (example === 'native') {
generateRNApp({
dest: folder,
projectName: options.project.name,
isNewArch: options.project.architecture === 'new',
});
}

await copyDir(COMMON_FILES, folder);

if (languages === 'js') {
Expand All @@ -426,9 +454,9 @@ async function create(argv: yargs.Arguments<any>) {
path.join(folder, 'example')
);

if (turbomodule) {
if (architecture === 'new') {
await copyDir(
path.join(EXAMPLE_TURBO_FILES, 'example'),
path.join(EXAMPLE_NEW_FILES, 'example'),
path.join(folder, 'example')
);
}
Expand All @@ -438,13 +466,17 @@ async function create(argv: yargs.Arguments<any>) {
if (moduleType === 'module') {
await copyDir(NATIVE_FILES[`${moduleType}_${architecture}`], folder);
} else {
await copyDir(NATIVE_FILES[moduleType], folder);
await copyDir(NATIVE_FILES[`${moduleType}_${architecture}`], folder);
}

if (options.project.swift) {
await copyDir(SWIFT_FILES[moduleType], folder);
} else {
await copyDir(OBJC_FILES[moduleType], folder);
if (moduleType === 'module') {
await copyDir(OBJC_FILES[moduleType], folder);
} else {
await copyDir(OBJC_FILES[`view_${architecture}`], folder);
}
}

if (options.project.kotlin) {
Expand All @@ -453,7 +485,7 @@ async function create(argv: yargs.Arguments<any>) {
if (moduleType === 'module') {
await copyDir(JAVA_FILES[`${moduleType}_${architecture}`], folder);
} else {
await copyDir(JAVA_FILES[moduleType], folder);
await copyDir(JAVA_FILES[`${moduleType}_${architecture}`], folder);
}
}

Expand All @@ -463,6 +495,21 @@ async function create(argv: yargs.Arguments<any>) {
}
}

if (example === 'native') {
// Set `react` and `react-native` versions of root `package.json` from example `package.json`
const examplePackageJson = fs.readJSONSync(
path.join(folder, 'example', 'package.json')
);
const rootPackageJson = fs.readJSONSync(path.join(folder, 'package.json'));
rootPackageJson.devDependencies.react =
examplePackageJson.dependencies.react;
rootPackageJson.devDependencies['react-native'] =
examplePackageJson.dependencies['react-native'];
fs.writeJSONSync(path.join(folder, 'package.json'), rootPackageJson, {
spaces: 2,
});
}

try {
spawn.sync('git', ['init'], { cwd: folder });
spawn.sync('git', ['add', '.'], { cwd: folder });
Expand Down
138 changes: 138 additions & 0 deletions packages/create-react-native-library/src/utils/generateRNApp.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,138 @@
import fs from 'fs';
import spawn from 'cross-spawn';
import path from 'path';

const FILES_TO_DELETE = [
'.eslintrc.js',
'tsconfig.json',
'.gitignore',
'.git',
'.prettierrc.js',
'index.js',
'App.tsx',
];

const PACKAGES_TO_REMOVE = [
'@react-native-community/eslint-config',
'@tsconfig/react-native',
'@types/jest',
'@typescript-eslint/eslint-plugin',
'@typescript-eslint/parser',
'babel-jest',
'eslint',
'jest',
'typescript',
];

const PACKAGES_TO_ADD = {
'babel-plugin-module-resolver': '^4.1.0',
'metro-react-native-babel-preset': '^0.72.1',
'patch-package': '^6.4.7',
'postinstall-postinstall': '^2.1.0',
};

export default function generateRNApp({
dest,
projectName,
isNewArch,
}: {
dest: string;
projectName: string;
isNewArch: boolean;
}) {
// Generate the example app's base using `npx react-native init <projectName>Example --template react-native-template-typescript --directory example --skip-install`
const createRNAppProcess = spawn.sync(
'npx',
[
'react-native',
'init',
`${projectName}Example`,
'--template',
'react-native-template-typescript',
'--directory',
path.join(dest, 'example'),
'--skip-install',
],
{
cwd: dest,
}
);
if (createRNAppProcess.error) {
throw createRNAppProcess.error;
}

// Remove unnecessary files
FILES_TO_DELETE.forEach((file) => {
try {
fs.unlinkSync(path.join(dest, 'example', file));
} catch (e) {
// ignore
}
});

// Patch the example app's package.json
const examplePackageJson = JSON.parse(
fs.readFileSync(path.join(dest, 'example', 'package.json'), 'utf8')
);
examplePackageJson.scripts = {
...examplePackageJson.scripts,
test: undefined,
lint: undefined,

pods: 'pod-install --quiet',
postinstall: 'patch-package',
};
PACKAGES_TO_REMOVE.forEach((pkg) => {
examplePackageJson.devDependencies[pkg] = undefined;
});
examplePackageJson.devDependencies = {
...examplePackageJson.devDependencies,
...PACKAGES_TO_ADD,
};
examplePackageJson.jest = undefined;
fs.writeFileSync(
path.join(dest, 'example', 'package.json'),
JSON.stringify(examplePackageJson, null, 2)
);

// Patch the integration tests to correct imports
// Instead of importing App from `../App`, use `../src/App`
const integrationTest = fs.readFileSync(
path.join(dest, 'example', '__tests__', 'App-test.tsx'),
'utf8'
);
fs.writeFileSync(
path.join(dest, 'example', '__tests__', 'App-test.tsx'),
integrationTest.replace(
"import App from '../App';",
"import App from '../src/App';"
)
);

// If the library is on new architecture, enable new arch for IOS and Android
if (isNewArch) {
// Android
// Change newArchEnabled=false to newArchEnabled=true in example/android/gradle.properties
const gradleProperties = fs
.readFileSync(
path.join(dest, 'example', 'android', 'gradle.properties'),
'utf8'
)
.replace('newArchEnabled=false', 'newArchEnabled=true');
fs.writeFileSync(
path.join(dest, 'example', 'android', 'gradle.properties'),
gradleProperties
);

// IOS
// Add ENV['RCT_NEW_ARCH_ENABLED'] = 1 on top of example/ios/Podfile
const podfile = fs.readFileSync(
path.join(dest, 'example', 'ios', 'Podfile'),
'utf8'
);
fs.writeFileSync(
path.join(dest, 'example', 'ios', 'Podfile'),
"ENV['RCT_NEW_ARCH_ENABLED'] = '1'\n" + podfile
);
}
}
Loading