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

Obsidian v3 #175

Draft
wants to merge 33 commits into
base: master
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
33 commits
Select commit Hold shift + click to select a range
c277488
Update to TypeScript 5.5 + ESLint 9.9
guyca Aug 19, 2024
fc8900f
Upgrade to ESLint 9 - part 1
guyca Aug 31, 2024
5fce66e
Update Babel + fix all lint issues + bump target to es2023
guyca Sep 5, 2024
0914014
Fix broken eslint rule test
guyca Sep 5, 2024
fdfe198
Remove unused eslint plugins
guyca Sep 5, 2024
5cd017c
Use Obsidian's ESLint rules
guyca Sep 5, 2024
cd3c2eb
Use prerelease tag
guyca Sep 5, 2024
61575a1
Fix isDev ts build error
guyca Sep 9, 2024
759ed02
Upgrade to TS 5.6.2
guyca Sep 10, 2024
a7b4453
Migrate to Stage 3 decorators + lower case all decorators
guyca Sep 14, 2024
e209bea
Bump major
guyca Sep 15, 2024
9234edd
Merge branch 'master' into upgradeTS
guyca Sep 15, 2024
380a89b
Explicitly reflect the fact that decorator comparison ignores casing
guyca Sep 15, 2024
7288dd6
Export both lower and upper case decorators
guyca Sep 15, 2024
14a7e5d
Deprecate Uppercase decorators
guyca Sep 15, 2024
2fc2915
Ignore some ESLint rules
guyca Sep 15, 2024
e764324
version bump
guyca Sep 18, 2024
d8f2ee3
Remove dependency on reflect-metadata
guyca Sep 19, 2024
0ea6b61
Merge branch 'master' into upgradeTS
guyca Sep 19, 2024
6599a65
Fix lint
guyca Sep 19, 2024
afc1218
version bump
guyca Sep 19, 2024
6fdf06f
Bump Babel deps in eslint plugin + remove default from memoize export…
guyca Sep 21, 2024
42e90b6
Stop using the Memoize decorator in the library
guyca Sep 21, 2024
7e778fb
Babel plugin transforms both lower case and upper case decorators
guyca Sep 21, 2024
874940a
version bump
guyca Sep 21, 2024
1df699b
Use the stage 3 decorators proposals in the babel plugin tests + fix …
guyca Sep 21, 2024
7508a4e
Update lock file
guyca Sep 21, 2024
7c0cbaf
Test both v2 and v3 syntax
guyca Sep 21, 2024
cfd53d2
Use Window as global when applicable
guyca Sep 21, 2024
f27b443
version bump
guyca Sep 21, 2024
71331ef
Update installation steps
guyca Sep 28, 2024
9355534
Use lower case decorators in the docs
guyca Sep 28, 2024
5c4837f
Add migration guide
guyca Sep 28, 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
2 changes: 1 addition & 1 deletion .nvmrc
Original file line number Diff line number Diff line change
@@ -1 +1 @@
18.12.0
20.16.0
4 changes: 4 additions & 0 deletions .vscode/settings.json
Original file line number Diff line number Diff line change
@@ -1,11 +1,15 @@
{
"cSpell.words": [
"Descriminator",
"esmodules",
"estree",
"memoizer",
"Middlewares",
"MVVM",
"plusplus",
"preconfigured",
"TSES",
"tseslint",
"unimported",
"unmagler",
"unsubscribers"
Expand Down
16 changes: 8 additions & 8 deletions packages/documentation/docs/documentation/documentation.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ Define a singleton graph that is instantiated once and is retained throughout th
import {Singleton, Graph, ObjectGraph, Provides} from 'react-obsidian';


@Singleton() @Graph()
@singleton() @graph()
export class ApplicationGraph extends ObjectGraph {

// fooService requires a barManager so it receives one as a parameter.
Expand Down Expand Up @@ -108,15 +108,15 @@ const SomeComponent = () => {
</TabItem>
<TabItem value="classComponent" label="Class component injection">

To inject a class, annotate it with the `@Injectable` decorator. The `@Injectable` decorator takes a single parameter - the graph that should be used to resolve the dependencies. Declare the dependencies as class members and annotate them with the `@Inject` decorator.
To inject a class, annotate it with the `@injectable` decorator. The `@injectable` decorator takes a single parameter - the graph that should be used to resolve the dependencies. Declare the dependencies as class members and annotate them with the `@inject` decorator.

```ts title="MyComponent.tsx"
import {Injectable, Inject} from 'react-obsidian';
import {ApplicationGraph} from './ApplicationGraph';

@Injectable(ApplicationGraph)
@injectable(ApplicationGraph)
export MyClassComponent extends React.Component {
@Inject() private fooService!: FooService;
@inject() private fooService!: FooService;

}
```
Expand All @@ -134,17 +134,17 @@ const SomeComponent = () => {
</TabItem>
<TabItem value="class" label="Class constructor injection">

To inject a class, annotate it with the `@Injectable` decorator. The `@Injectable` decorator takes a single parameter - the graph that should be used to resolve the dependencies.
Declare the dependencies as constructor parameters and annotate them with the `@Inject` decorator.
To inject a class, annotate it with the `@injectable` decorator. The `@injectable` decorator takes a single parameter - the graph that should be used to resolve the dependencies.
Declare the dependencies as constructor parameters and annotate them with the `@inject` decorator.

```ts title="MyClass.tsx"
import {Injectable, Inject} from 'react-obsidian';
import {ApplicationGraph} from './ApplicationGraph';

@Injectable(ApplicationGraph)
@injectable(ApplicationGraph)
export MyClass {
constructor (fooService?: FooService);
constructor(@Inject() private fooService: FooService) { }
constructor(@inject() private fooService: FooService) { }
}
```

Expand Down
115 changes: 59 additions & 56 deletions packages/documentation/docs/documentation/installation.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -7,73 +7,53 @@ 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 for code generation, so you'll need to have Babel configured in your project.
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.
:::

## 1. Install Obsidian
<Tabs>
<TabItem value="yarn" label="Yarn" default>

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

## 2. Install Reflect-metadata
First, install and enable the reflect-metadata polyfill.
```bash
npm install reflect-metadata
```

Then, add the following line to the top of your application's entry point (usually index.js or index.ts):
```js
import 'reflect-metadata';
```

## 3. Enable experimental decorators
Obsidian uses the Decorators feature whose proposal is at stage 3.

Add the following options to your tsconfig.json file.
```bash
yarn add react-obsidian
```
</TabItem>
<TabItem value="npm" label="NPM" default>

```js
{
"compilerOptions": {
"experimentalDecorators": true,
"emitDecoratorMetadata": true
}
}
```
```bash
npm install react-obsidian
```
</TabItem>
</Tabs>

## 4. Add the required Babel plugins
## 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.

### 4.1. Install the required Babel plugins
### 2.1. Install the required Babel plugins

You will need to install these plugins if you don't have them already:
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="yarn" label="Yarn" default>


```shell
yarn add @babel/plugin-proposal-decorators @babel/plugin-transform-class-properties babel-plugin-parameter-decorator @babel/core @babel/preset-env @babel/preset-typescript
yarn add @babel/plugin-proposal-decorators
```


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


```shell
npm install @babel/plugin-proposal-decorators @babel/plugin-transform-class-properties babel-plugin-parameter-decorator @babel/core @babel/preset-env
npm install @babel/plugin-proposal-decorators
```


</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>

Expand All @@ -92,26 +72,41 @@ If this is your first time using Babel, you will also need to install Babel's co

</Tabs>

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

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

```diff
```js title="babel.config.js"
module.exports = {
presets: [
'module:metro-react-native-babel-preset',
+ ['@babel/preset-typescript', {'onlyRemoveTypeImports': true}]
// Add this line
'@babel/preset-typescript'
],
plugins: [
+ react-obsidian/dist/transformers/babel-plugin-obsidian,
+ ['@babel/plugin-proposal-decorators', {legacy: true}],
+ '@babel/plugin-transform-class-properties',
+ 'babel-plugin-parameter-decorator'
// Added lines start
'react-obsidian/dist/transformers/babel-plugin-obsidian',
['@babel/plugin-proposal-decorators', {version: '2023-11'}],
// Added lines end
]
};
```

## 5. Optional - Add Obsidian's ESLint plugin
## 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
}
}
```


## 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.


Expand All @@ -131,8 +126,10 @@ export default defineConfig({
react({
babel: {
plugins: [
// Added lines start
'react-obsidian/dist/transformers/babel-plugin-obsidian',
['@babel/plugin-proposal-decorators', { legacy: true }],
// Added lines end
],
},
}),
Expand All @@ -151,16 +148,22 @@ When integrating Obsidian in a CRA application, follow steps **1-4 that are list
```diff title="package.json"
{
"devDependencies": {
+ "customize-cra": "1.0.0",
+ "react-app-rewired": "2.2.1"
// Added lines start
"customize-cra": "1.0.0",
"react-app-rewired": "2.2.1"
// Added lines end
},
"scripts": {
+ "start": "react-app-rewired start",
+ "build": "react-app-rewired build",
+ "test": "npx jest",
- "start": "react-scripts start",
- "build": "react-scripts build",
- "test": "react-scripts test --env=jsdom",
// Added lines start
"start": "react-app-rewired start",
"build": "react-app-rewired build",
"test": "npx jest",
// Added lines end
// Removed lines start
"start": "react-scripts start",
"build": "react-scripts build",
"test": "react-scripts test --env=jsdom",
// Removed lines end
}
}
```
Expand Down
4 changes: 2 additions & 2 deletions packages/documentation/docs/documentation/meta/eslint.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@ Ensure dependencies requested by providers can be resolved.
When a provider requests a dependency that is not provided by the graph or its subgraphs, this rule will trigger a lint error.

```ts
@Graph()
@graph()
class SomeGraph extends ObjectGraph {
@Provides()
someService(someDependency: SomeDependency) {
Expand All @@ -45,7 +45,7 @@ Prevent circular dependencies between providers.
When two providers depend on each other, this rule will trigger a lint error.

```ts
@Graph()
@graph()
class SomeGraph extends ObjectGraph {
@Provides()
foo(bar: Bar) {
Expand Down
39 changes: 39 additions & 0 deletions packages/documentation/docs/documentation/meta/migratingToV3.mdx
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
---
sidebar_position: 4
title: "Migrate to v3.x"
---

Migrating from v2.x to v3.x is a straightforward process. This guide will help you migrate your existing codebase to the latest version of Obsidian.

## Breaking changes
1. **Minimum supported version of TypeScript is now v5.0.0.** If you are using an older version of TypeScript, you will need to upgrade to v5.0.0 or later.
2. **Legacy decorators are no longer supported.** Starting from v3.x, Obsidian uses the stage 3 decorators proposal which is enabled by default in TypeScript v5.0.0. To use the new decorators proposal, you will need to remove the `experimentalDecorators` and `emitDecoratorMetadata` options from your `tsconfig.json` file:

```js title="tsconfig.json"
{
"compilerOptions": {
// Removed lines start
"experimentalDecorators": true,
"emitDecoratorMetadata": true
// Removed lines end
}
}
```

Additionally, update your Babel configurations file to use the new decorators proposal:

```js title="babel.config.js"
module.exports = {
plugins: [
// Add this line
['@babel/plugin-proposal-decorators', {version: '2023-11'}],
// Remove this line
['@babel/plugin-proposal-decorators', {legacy: true}],
]
};
```

## Deprecations
With the release of v3.x, we're aligning our API with the community standards. All decorators that were previously capitalized are now camel-cased. For example, the `@Graph` decorator is now `@graph` and the `@LifecycleBound` decorator is now `@lifecycleBound`.

The capitalized decorators will continue to work in v3.x, but they will be removed in the next major release. We recommend updating your codebase to use the camel-cased decorators to avoid any breaking changes in the future.
Original file line number Diff line number Diff line change
Expand Up @@ -4,15 +4,15 @@ title: "Class components"
---

## Injecting class components
Injecting class components is a two step process. First, annotate the class with the `@Injectable` annotation and pass the graph from which dependencies should be resolve. Then, declare the dependencies as class members and annotate them with the `@Inject` annotation.
Injecting class components is a two step process. First, annotate the class with the `@injectable` annotation and pass the graph from which dependencies should be resolve. Then, declare the dependencies as class members and annotate them with the `@inject` annotation.

```ts
import {Injectable, Inject} from 'react-obsidian';
import {ApplicationGraph} from './ApplicationGraph';

@Injectable(ApplicationGraph)
@injectable(ApplicationGraph)
export class ClassComponent extends React.Component {
@Inject() private httpClient!: HttpClient;
@inject() private httpClient!: HttpClient;

}
```
8 changes: 4 additions & 4 deletions packages/documentation/docs/documentation/usage/Classes.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -4,15 +4,15 @@ title: "Classes"
---

## Injecting classes
Injecting classes is a two step process. First, annotate the class with the `@Injectable` annotation and pass the graph from which dependencies should be resolve. Then, declare the dependencies as class members and annotate them with the `@Inject` annotation.
Injecting classes is a two step process. First, annotate the class with the `@Injectable` annotation and pass the graph from which dependencies should be resolve. Then, declare the dependencies as class members and annotate them with the `@inject` annotation.

```ts
import {Injectable, Inject} from 'react-obsidian';
import {ApplicationGraph} from './ApplicationGraph';

@Injectable(ApplicationGraph)
export class MyClass {
@Inject() private httpClient!: HttpClient;
@inject() private httpClient!: HttpClient;

}
```
Expand All @@ -22,15 +22,15 @@ Constructor injection is the preferred way to inject dependencies. It is more ex
:::

## Delayed injection
Dependencies annotated with the `@Inject` annotation are resolved immediately **after** the constructor is called. If you want to inject a class at a later point in time, you can use the `@LateInject` annotation instead, and inject the dependencies by manually with the `Obsidian.inject()` function.
Dependencies annotated with the `@inject` annotation are resolved immediately **after** the constructor is called. If you want to inject a class at a later point in time, you can use the `@lateInject` annotation instead, and inject the dependencies by manually with the `Obsidian.inject()` function.

```ts
import {Injectable, LateInject} from 'react-obsidian';
import {ApplicationGraph} from './ApplicationGraph';

@Injectable(ApplicationGraph)
export class MyClass {
@LateInject() private httpClient!: HttpClient;
@lateInject() private httpClient!: HttpClient;

public init() {
console.log(this.httpClient === undefined); // true
Expand Down
Loading