Skip to content

Commit

Permalink
Merge branch 'master' into upgradeTS
Browse files Browse the repository at this point in the history
# Conflicts:
#	packages/documentation/docs/documentation/installation.mdx
#	packages/eslint-plugin-obsidian/package.json
#	packages/eslint-plugin-obsidian/src/dto/class.ts
#	packages/eslint-plugin-obsidian/src/rules/unresolvedProviderDependencies/dependencyResolver.ts
#	packages/eslint-plugin-obsidian/src/rules/unresolvedProviderDependencies/subgraphResolver.ts
#	packages/react-obsidian/package.json
#	yarn.lock
  • Loading branch information
guyca committed Jan 6, 2025
2 parents 680e678 + d64d66e commit 449dd19
Show file tree
Hide file tree
Showing 50 changed files with 7,035 additions and 174 deletions.
83 changes: 1 addition & 82 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -6,94 +6,21 @@ node_modules

# Lock files
package-lock.json
# This package-lock is used as cache key and needs to be comitted
!documentation/package-lock.json

############
# iOS
############

# Build generated
dist
ReactNativeExample/ios/build/
ReactNativeExample/ios/DerivedData/

# Various settings
ReactNativeExample/ios/xcuserdata/

# CocoaPods
#
# We recommend against adding the Pods directory to your .gitignore. However
# you should judge for yourself, the pros and cons are mentioned at:
# https://guides.cocoapods.org/using/using-cocoapods.html#should-i-check-the-pods-directory-into-source-control
#
ReactNativeExample/ios/Pods/

############
# Android
############
# Built application files
*.apk
*.ap_

# Files for the Dalvik VM
*.dex

# Java class files
*.class

# Generated files
ReactNativeExample/android/bin/
ReactNativeExample/android/gen/
ReactNativeExample/android/out/

# Gradle files
ReactNativeExample/android/.gradle/
ReactNativeExample/android/build/

# Local configuration file (sdk path, etc)
local.properties

# Proguard folder generated by Eclipse
ReactNativeExample/android/proguard/

# Log Files
*.log

# Android Studio Navigation editor temp files
ReactNativeExample/android/.navigation/

# Android Studio captures folder
ReactNativeExample/android/captures/

# Intellij
*.iml

##################
# React-Native
##################
# OSX
#
.DS_Store

# Xcode
#
*.pbxuser
!default.pbxuser
*.mode1v3
!default.mode1v3
*.mode2v3
!default.mode2v3
*.perspectivev3
!default.perspectivev3
xcuserdata
*.xccheckout
*.moved-aside
DerivedData
*.hmap
*.ipa
*.xcuserstate

# Android/IJ
#
build/
Expand All @@ -109,15 +36,6 @@ node_modules/
npm-debug.log
yarn-error.log

# BUCK
buck-out/
\.buckd/
*.keystore
!debug.keystore

main.jsbundle.map
index.android.bundle.map

.jest_cache
.eslintcache
**/.docusaurus
Expand All @@ -128,3 +46,4 @@ packages/react-obsidian/coverage/
documentation/.yarn/cache
documentation/.yarn/install-state.gz
.vscode/yw_helper_history.txt
packages/swc-plugin-obsidian/target
8 changes: 7 additions & 1 deletion .vscode/settings.json
Original file line number Diff line number Diff line change
Expand Up @@ -12,9 +12,15 @@
"tseslint",
"unimported",
"unmagler",
"unplugin",
"unsubscribers"
],
"eslint.ignoreUntitled": true,
"eslint.format.enable": true,
"eslint.workingDirectories": [ {"pattern": "packages/*"} ]
"eslint.workingDirectories": [
{
"pattern": "packages/*"
}
],
"typescript.preferences.importModuleSpecifier": "relative"
}
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
{
"private": true,
"scripts": {
"test": "yarn workspaces foreach run test --colors",
"test": "yarn workspaces foreach --exclude swc-plugin-obsidian run test --colors && yarn workspace swc-plugin-obsidian test",
"lint": "yarn workspaces foreach run lint",
"build": "yarn workspace react-obsidian build && yarn workspace eslint-plugin-obsidian build"
},
Expand Down
135 changes: 61 additions & 74 deletions packages/documentation/docs/documentation/installation.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -6,35 +6,43 @@ import Tabs from '@theme/Tabs';
import TabItem from '@theme/TabItem';

# Installation

Like most Dependency Injection frameworks, Obsidian uses automatic code generation to create the bindings necessary for resolving dependencies. This approach helps reduce the amount of boilerplate code required by developers. Obsidian relies on [Babel](https://babeljs.io/) for code generation, so you'll need to have Babel configured in your project.

:::important
If your project uses either **Vite** or **Create React App**, please refer to the [Integration with third-party front-end environments](#integration-with-third-party-front-end-environments) section before following the steps below.
:::
Installing Obsidian is a straightforward process that involves adding the package to your project, enabling TypeScript decorators, and adding Obsidian's transformer to your build system, either Babel or SWC. Let's go through the steps.

## 1. Install Obsidian
Install the package using your preferred package manager:

<Tabs>
<TabItem value="yarn" label="Yarn" default>

```bash
```shell
yarn add react-obsidian
```

</TabItem>
<TabItem value="npm" label="NPM" default>

```bash
```shell
npm install react-obsidian
```

</TabItem>

</Tabs>

## 2. Add the required Babel plugins
[BabelJS](https://babeljs.io/) is a JavaScript compiler that is used to transpile modern JavaScript code into code that is compatible with older browsers. Obsidian uses a Babel transformer to generate code that is used to resolve dependencies.
## 2. Add Obsidian's transformer
Like most Dependency Injection frameworks, Obsidian uses automatic code generation to create the bindings necessary for resolving dependencies. This approach helps reduce the amount of boilerplate code required by developers.

### 2.1. Install the required Babel plugins
You'll need to add either the Babel or SWC plugins depending on your project's configuration.
* **React Native:** React Native projects only support Babel, so you'll need to use the Babel plugin.
* **Vite:** Vite supports both Babel and SWC. Choose the one according to your project's build system.
* **NextJS:** NextJS supports both Babel and SWC. If your project uses the `next/font` package you'll have to use the SWC plugin as `next/font` doesn't support Babel.

Install the [@babel/plugin-proposal-decorators](https://babeljs.io/docs/babel-plugin-proposal-decorators) plugin if you don't have it already:
<Tabs>
<TabItem value="babel" label="Babel (React Native or Vite + Babel)" default>

### Install the required Babel plugins

You will need to install the `plugin-proposal-decorators` plugin if it's not installed already:
<Tabs>
<TabItem value="yarn" label="Yarn" default>

Expand All @@ -50,29 +58,25 @@ Install the [@babel/plugin-proposal-decorators](https://babeljs.io/docs/babel-pl
```

</TabItem>

</Tabs>

If this is your first time using Babel, you will also need to install Babel's core packages:
<Tabs>
<TabItem value="yarn" label="Yarn" default>


```shell
yarn add @babel/core @babel/preset-env @babel/preset-typescript
```
</TabItem>
<TabItem value="npm" label="NPM" default>


```shell
npm install @babel/core @babel/preset-env @babel/preset-typescript
```
</TabItem>

</Tabs>

### 2.2. Update your Babel configuration
### Update your Babel configuration

Add the transformer and the required plugin to the list of plugins in your `babel.config.js` file or `.babelrc` file:

Expand All @@ -91,87 +95,70 @@ module.exports = {
};
```

## 3. Configure TypeScript (Optional)
Obsidian supports the latest version of the decorators proposal available in TypeScript 5.0 and later. You might need to disable the legacy decorators in your tsconfig.json file. To do this, remove both `experimentalDecorators` and `emitDecoratorMetadata` from the `compilerOptions` section.

```js title="tsconfig.json"
{
"compilerOptions": {
// Removed lines start
"experimentalDecorators": true,
"emitDecoratorMetadata": true
// Removed lines end
}
}
```
</TabItem>
<TabItem value="swc" label="SWC (Vite + SWC)">

### Install the required dependencies
Currently, Obsidian can be used via [unplugin-swc](https://github.com/unplugin/unplugin-swc). [vite-plugin-react-swc](https://github.com/vitejs/vite-plugin-react-swc/) is not yet supported.

## 4. Add Obsidian's ESLint plugin (Recommended)
Obsidian provides an ESLint plugin that can help you find errors in your code related to dependency injection. See the [ESLint plugin](/docs/documentation/meta/eslint) documentation for more information.
<Tabs>
<TabItem value="yarn" label="Yarn" default>

```shell
yarn add -D unplugin-swc @swc/core
```
</TabItem>
<TabItem value="npm" label="NPM" default>

## Integration with third-party front-end environments
### Vite
[Vite](https://vitejs.dev/) provides a modern development environment for React application. It boasts an extremely fast development server that uses Hot Module Replacement (HMR) to enable near-instant startup time.
```shell
npm install -D unplugin-swc @swc/core
```
</TabItem>
</Tabs>

When integrating Obsidian in a Vite application, follow steps **1-3 and step 4.1 that are listed above**. Instead of step 4.2, we'll configure Obsidian't Babel transformer in Vite's `vite.config.js` file:
### Update your Vite configuration
Add the transformer to the list of plugins in your `vite.config.js` file:

```js title="vite.config.js"
import { defineConfig } from 'vite';
import react from '@vitejs/plugin-react';
import swc from 'unplugin-swc';
import obsidian from 'swc-plugin-obsidian';

// https://vitejs.dev/config/
export default defineConfig({
plugins: [
react({
babel: {
plugins: [
// Added lines start
'react-obsidian/dist/transformers/babel-plugin-obsidian',
['@babel/plugin-proposal-decorators', { legacy: true }],
// Added lines end
],
swc.vite({
jsc: {
parser: {
syntax: "typescript",
decorators: true,
},
experimental: {
runPluginFirst: true,
plugins: [obsidian()],
},
},
}),
],
});
```

### Create React App
Create React App (CRA) is a popular tool for getting started with React. It provides a pre-configured development environment that is ready to use out of the box.
</TabItem>
</Tabs>

When integrating Obsidian in a CRA application, follow steps **1-4 that are listed above**. Since CRA doesn't allow you to configure Babel directly, we'll need to install two additional packages:
1. [react-app-rewired](https://github.com/timarney/react-app-rewired). This package allows you to customize CRA scripts without ejecting.
2. [customize-cra](https://github.com/arackaf/customize-cra). This package provides a set of utilities that can be used to customize CRA configurations. It has to be used instead of the default `react-scripts` package.
## 3. Configure TypeScript (Optional)
Obsidian supports the latest version of the decorators proposal available in TypeScript 5.0 and later. You might need to disable the legacy decorators in your tsconfig.json file. To do this, remove both `experimentalDecorators` and `emitDecoratorMetadata` from the `compilerOptions` section.

#### Install the required packages and modify the default scripts
```diff title="package.json"
```js title="tsconfig.json"
{
"devDependencies": {
// Added lines start
"customize-cra": "1.0.0",
"react-app-rewired": "2.2.1"
// Added lines end
},
"scripts": {
// Added lines start
"start": "react-app-rewired start",
"build": "react-app-rewired build",
"test": "npx jest",
// Added lines end
"compilerOptions": {
// Removed lines start
"start": "react-scripts start",
"build": "react-scripts build",
"test": "react-scripts test --env=jsdom",
"experimentalDecorators": true,
"emitDecoratorMetadata": true
// Removed lines end
}
}
```

#### Configure Babel
Create a `config-overrides.js` file in the root of your project and add the following code to it:

```js title="config-overrides.js"
const { useBabelRc, override } = require('customize-cra');
module.exports = override(useBabelRc());
```
## 4. Add Obsidian's ESLint plugin (Recommended)
Obsidian provides an ESLint plugin that can help you find errors in your code related to dependency injection. See the [ESLint plugin](/docs/documentation/meta/eslint) documentation for more information.
29 changes: 29 additions & 0 deletions packages/documentation/docs/documentation/usage/Graphs.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -117,6 +117,35 @@ class FormGraph extends ObjectGraph {
Lifecycle-bound graphs are feature-scoped by default.
:::

3. **Custom scope**: A custom scope is a special case of a feature scope. When multiple `@LifecycleBound` graphs share the same custom scope, they are considered to be part of the same UI scope. When a custom scoped graph is requested, Obsidian will create all the subgraphs in the same UI scope and destroy them when the last component or hook that requested them is unmounted.

```ts title="A custom-scoped lifecycle-bound graph"
import {LifecycleBound, Graph, ObjectGraph, Provides} from 'react-obsidian';

@LifecycleBound({scope: 'AppScope'}) @Graph({subgraphs: [ScreenGraph]})
class HomeScreenGraph extends ObjectGraph {
constructor(private props: HomeScreenProps & BaseProps) {
super(props);
}
}
```

```ts title="A custom-scoped lifecycle-bound subgraph"
@LifecycleBound({scope: 'AppScope'}) @Graph()
class ScreenGraph extends ObjectGraph {
constructor(private props: BaseProps) {
super(props);
}
}
```

:::info
The differences between a feature-scoped graph and a custom-scoped graph:
1. By default, subgraphs are instantiated lazily. Custom-scoped subgraphs are instantiated immediately when a parent graph with the same scope is instantiated.
2. When instantiated, custom-scoped subgraphs receive the props of the custom-scoped graph that triggered their instantiation.
3. Custom-scoped subgraphs can only be instantiated from a lifecycle bound graph with the same scope.
:::

#### Passing props to a lifecycle-bound graph
When a graph is created, it receives the props of the component or hook that requested it. This means that the graph can use the props to construct the dependencies it provides. The `@lifecycleBound` in the example below graph provides a `userService` which requires a `userId`. The `userId` is obtained from props.

Expand Down
Loading

0 comments on commit 449dd19

Please sign in to comment.