Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
1 change: 0 additions & 1 deletion docs/pages/build.md
Original file line number Diff line number Diff line change
Expand Up @@ -78,7 +78,6 @@ To configure your project manually, follow these steps:
"types": "./lib/typescript/src/index.d.ts",
"exports": {
".": {
"source": "./src/index.tsx",
"types": "./lib/typescript/src/index.d.ts",
"default": "./lib/module/index.js"
},
Expand Down
55 changes: 50 additions & 5 deletions docs/pages/esm.md
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,7 @@ To make use of the output files, ensure that your `package.json` file contains t
"types": "./lib/typescript/src/index.d.ts",
"exports": {
".": {
"source": "./src/index.tsx",
"my-library-source": "./src/index.tsx",
"types": "./lib/typescript/src/index.d.ts",
"default": "./lib/module/index.js"
},
Expand All @@ -56,10 +56,57 @@ The `exports` field is used by Node.js 12+, modern browsers and tools to determi

Here, we specify 3 conditions:

- `source`: A custom condition used by `react-native-builder-bob` to determine the source file for the library.
- `my-library-source`: A custom condition used to resolve the source file for the library in development.
- `types`: Used for the TypeScript definitions.
- `default`: Used for the actual JS code when the library is imported or required.

When creating a project, a custom condition name pointing to the source code is automatically set. You can also change it to something else if you want by updating any occurrences in your project.

TypeScript can resolve the source condition by adding it to [`customConditions`](https://www.typescriptlang.org/tsconfig/#customConditions):

```json
{
"compilerOptions": {
"customConditions": ["my-library-source"]
}
}
```

When using [`react-native-monorepo-config`](https://github.com/satya164/react-native-monorepo-config), pass the same condition to Metro so the example app resolves the library source:

```js
const config = withMetroConfig(getDefaultConfig(__dirname), {
root,
dirname: __dirname,
conditions: ['my-library-source'],
});
```

If you use [Jest](https://jestjs.io), add the source condition to [`testEnvironmentOptions.customExportConditions`](https://jestjs.io/docs/configuration#testenvironmentoptions-object). With the React Native Jest preset, keep React Native's default conditions as well:

```json
{
"jest": {
"preset": "@react-native/jest-preset",
"testEnvironmentOptions": {
"customExportConditions": ["require", "react-native", "my-library-source"]
}
}
}
```

If you use [Vite](https://vitejs.dev), add the source condition to [`resolve.conditions`](https://vitejs.dev/config/#resolve-conditions):

```ts
import { defineConfig } from 'vite';

export default defineConfig({
resolve: {
conditions: ['my-library-source'],
},
});
```

You can also specify additional conditions for different scenarios, such as `react-native`, `browser`, `production`, `development` etc. Note that support for these conditions depends on the tooling you're using.

The `./package.json` field is used to point to the library's `package.json` file. It's necessary for tools that may need to read the `package.json` file directly (e.g. [React Native Codegen](https://reactnative.dev/docs/the-new-architecture/what-is-codegen)).
Expand All @@ -74,7 +121,7 @@ Using the `exports` field has a few benefits, such as:
```diff
"exports": {
".": {
"source": "./src/index.tsx",
"my-library-source": "./src/index.tsx",
"types": "./lib/typescript/src/index.d.ts",
"default": "./lib/module/index.js"
},
Expand Down Expand Up @@ -135,7 +182,6 @@ To configure a dual package setup, you can follow these steps:
```

Here, we specify 2 conditions:

- `import`: Used when the library is imported with an `import` statement or a dynamic `import()`. It will point to the ESM build.
- `require`: Used when the library is required with a `require` call. It will point to the CommonJS build.

Expand Down Expand Up @@ -292,7 +338,6 @@ There are still a few things to keep in mind if you want your library to be ESM-
```

Alternatively, if you want to be able to use the library in Node.js with `import` syntax, there are a few options:

- Use `Platform.select` instead of platform-specific extensions:

```js
Expand Down
7 changes: 2 additions & 5 deletions docs/pages/faq.md
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,6 @@ If your library depends on another react-native library containing native code,
- **Add the native library to `peerDependencies`**

This means that the consumer of the library will need to install the native library and add it to the `dependencies` section of their `package.json`. It makes sure that:

- There are no version conflicts if another package also happens to use the same library, or if the user wants to use the library in their app. While there can be multiple versions of a JavaScript-only library, there can only be one version of a native library - so avoiding version conflicts is important.
- The package manager installs it in correct location so that autolinking can work properly.

Expand All @@ -51,7 +50,6 @@ If your library depends on another react-native library containing native code,
Since this is a library, the `react-native` version specified in the `package.json` is not relevant for the consumers. It's only used for developing and testing the library. If you'd like to upgrade the `react-native` version to test with it, you'd need to:

1. **Bump versions of the following packages under `devDependencies` in the `package.json`:**

- `react-native`
- `react`
- `@types/react`
Expand All @@ -74,9 +72,8 @@ There are 2 parts to this process.
1. **Aliasing the JavaScript code**

The JavaScript (or TypeScript) source code is aliased to be used by the example app. This makes it so that when you import from `'your-library-name'`, it imports the source code directly and avoids having to rebuild the library for JavaScript only changes. We configure several tools to make this work:

- [Metro](https://facebook.github.io/metro/) is configured to allow importing from outside of the `example` directory by configuring `watchFolders`, to use the appropriate peer dependencies, and to import source code of the library in the example. This configuration exists in the `example/metro.config.js` file.
- [TypeScript](https://www.typescriptlang.org/) is configured to use the source code for type checking by using the `paths` property under `compilerOptions`. This configuration exists in the `tsconfig.json` file at the root.
- [Metro](https://facebook.github.io/metro/) is configured to allow importing from outside of the `example` directory by configuring `watchFolders`, to use the appropriate peer dependencies, and to resolve the library's custom source condition. This configuration exists in the `example/metro.config.js` file.
- [TypeScript](https://www.typescriptlang.org/) is configured to use the source code for type checking with the custom source condition and the `paths` property under `compilerOptions`. This configuration exists in the `tsconfig.json` file at the root.

2. **Linking the native code**

Expand Down
2 changes: 1 addition & 1 deletion packages/create-react-native-library/src/constants.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
export const FALLBACK_BOB_VERSION = '0.41.0';
export const FALLBACK_NITRO_MODULES_VERSION = '0.35.3';
export const SUPPORTED_MONOREPO_CONFIG_VERSION = '0.3.3';
export const SUPPORTED_MONOREPO_CONFIG_VERSION = '0.4.0';
export const SUPPORTED_REACT_NATIVE_VERSION = '0.85.0';
export const SUPPORTED_EXPO_SDK_VERSION = '55';
4 changes: 4 additions & 0 deletions packages/create-react-native-library/src/template.ts
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@ export type TemplateConfiguration = {
swift: boolean;
viewConfig: ViewConfig;
moduleConfig: ModuleConfig;
sourceCondition: string;
};
author: {
name: string;
Expand Down Expand Up @@ -177,6 +178,8 @@ export function generateTemplateConfiguration({
}

const project = slug.replace(/^(react-native-|@[^/]+\/)/, '');
const sourceCondition = `${slug.replace(/^@/, '').replace(/\//g, '-')}-source`;

let namespace: string | undefined;

if (slug.startsWith('@') && slug.includes('/')) {
Expand Down Expand Up @@ -213,6 +216,7 @@ export function generateTemplateConfiguration({
swift: languages === 'kotlin-swift',
viewConfig: getViewConfig(type),
moduleConfig: getModuleConfig(type),
sourceCondition,
},
author: {
name: authorName,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@
"types": "./lib/typescript/src/index.d.ts",
"exports": {
".": {
"source": "./src/index.tsx",
"<%- project.sourceCondition -%>": "./src/index.tsx",
"types": "./lib/typescript/src/index.d.ts",
"default": "./lib/module/index.js"
},
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,10 @@
},
"allowUnreachableCode": false,
"allowUnusedLabels": false,
"customConditions": ["react-native-strict-api"],
"customConditions": [
"<%- project.sourceCondition -%>",
"react-native-strict-api"
],
"esModuleInterop": true,
"forceConsistentCasingInFileNames": true,
"jsx": "react-jsx",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ const root = path.resolve(__dirname, '..');
const config = withMetroConfig(getDefaultConfig(__dirname), {
root,
dirname: __dirname,
conditions: ['<%- project.sourceCondition -%>'],
});

module.exports = config;
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,13 @@
},
"jest": {
"preset": "@react-native/jest-preset",
"testEnvironmentOptions": {
"customExportConditions": [
"require",
"react-native",
"<%- project.sourceCondition -%>"
]
},
"modulePathIgnorePatterns": [
"<rootDir>/example/node_modules",
"<rootDir>/lib/"
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ export default defineConfig((env) =>
alias: {
[pack.name]: new URL('..', import.meta.url),
},
conditions: ['<%- project.sourceCondition -%>'],
dedupe: Object.keys(pack.peerDependencies),
},
})
Expand Down
3 changes: 0 additions & 3 deletions packages/react-native-builder-bob/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,6 @@
".": "./lib/src/index.js",
"./babel-config": "./lib/src/configs/babel-config.cjs",
"./babel-preset": "./lib/src/configs/babel-preset.cjs",
"./metro-config": "./lib/src/configs/metro-config.mjs",
"./vite-config": "./lib/src/configs/vite-config.mjs",
"./package.json": "./package.json"
},
Expand Down Expand Up @@ -64,7 +63,6 @@
"json5": "^2.2.3",
"kleur": "^4.1.5",
"prompts": "^2.4.2",
"react-native-monorepo-config": "^0.3.3",
"typescript": "^6.0.3",
"which": "^6.0.1",
"yargs": "^18.0.0"
Expand All @@ -84,7 +82,6 @@
"@types/yargs": "^17.0.35",
"concurrently": "^9.2.1",
"del-cli": "^7.0.0",
"metro-config": "^0.84.2",
"mock-fs": "^5.5.0",
"mock-stdin": "^1.0.0",
"vitest": "^4.1.2"
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,6 @@ exports[`initializes the configuration 1`] = `
},
"exports": {
".": {
"source": "./src/index.ts",
"types": "./lib/typescript/src/index.d.ts",
"default": "./lib/module/index.js"
},
Expand Down
17 changes: 0 additions & 17 deletions packages/react-native-builder-bob/src/configs/metro-config.mjs

This file was deleted.

Original file line number Diff line number Diff line change
Expand Up @@ -44,7 +44,7 @@ export default defineConfig(({ mode }) => ({
},
resolve: {
extensions,
conditions: ['source', 'module', 'browser', mode],
conditions: ['module', 'browser', mode],
alias: {
'react-native': 'react-native-web',
},
Expand Down
17 changes: 9 additions & 8 deletions packages/react-native-builder-bob/src/init.ts
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,12 @@ export async function init() {
const pkg = JSON.parse(await fs.readFile(projectPackagePath, 'utf-8'));
const result = loadConfig(root);

const name = pkg.name;

if (typeof name !== 'string' || name.length === 0) {
throw new Error(`Couldn't find a 'name' field in '${projectPackagePath}'.`);
}

if (
result?.config &&
pkg.devDependencies &&
Expand Down Expand Up @@ -144,10 +150,8 @@ export async function init() {
: undefined;

const entries: {
[key in 'source' | 'commonjs' | 'module']?: string;
} = {
source: `./${path.join(source, entryFile)}`,
};
[key in 'commonjs' | 'module']?: string;
} = {};

let esm = false;

Expand Down Expand Up @@ -263,18 +267,15 @@ export async function init() {

if (targets.includes('commonjs') && targets.includes('module')) {
exportsField['.'] = {
source: entries.source,
import: importField,
require: requireField,
};
} else if (targets.includes('commonjs')) {
exportsField['.'] = {
source: entries.source,
...requireField,
};
} else if (targets.includes('module')) {
exportsField['.'] = {
source: entries.source,
...importField,
};
}
Expand Down Expand Up @@ -464,7 +465,7 @@ export async function init() {

process.stdout.write(
dedent(`
Project ${kleur.yellow(pkg.name)} configured successfully!
Project ${kleur.yellow(name)} configured successfully!

${kleur.magenta(
`${kleur.bold('Perform last steps')} by running`
Expand Down
Loading
Loading