Skip to content

Commit

Permalink
feat: added lazy root component
Browse files Browse the repository at this point in the history
  • Loading branch information
KernelPanic92 committed Oct 14, 2024
1 parent b546203 commit 37a7caf
Show file tree
Hide file tree
Showing 8 changed files with 275 additions and 39 deletions.
168 changes: 162 additions & 6 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -90,22 +90,166 @@ fast(bootstrapApplication, AppComponent, {
});
```

#### Provider example
### Providers

In the following example, we demonstrate how to define and export an array of providers that include both Provider and EnvironmentProviders. This array can be used with ngx-fastboot to dynamically load and resolve these providers during application bootstrap, optimizing the initial load performance.
`ngx-fastboot` provides great flexibility in how providers are loaded. Each element of the `providers` field can be either a **single provider** or an **array of providers**, depending on the needs of your application.

You can specify these providers in three main ways:

- **Static import**: The traditional method used during the application's bootstrap, where providers are imported and directly included at configuration time. This approach is simple but can increase the initial bundle size.

- **Dynamic Named Import**: To reduce the bundle size, you can load providers dynamically using a named import. In this way, providers are only loaded when they are actually needed.

- **Dynamic Default Import**: Similar to named import, but loads the provider or a group of providers using the default export of the module. This is useful when a module exports a single provider or an array of providers as a default value.

#### Static Import

`main.ts`
```typescript
import { AppComponent } from './app.component';
import { bootstrapApplication } from '@angular/platform-browser';
import { MyProvider } from 'providers/my-provider';
import { OtherProvider } from 'providers/other-provider';
import { fast } from 'ngx-fastboot';

fast(bootstrapApplication, AppComponent, {
providers: [
MyProvider,
OtherProvider,
]
}).then(appRef => {
console.log('App is bootstrapped');
}).catch(error => {
console.error('Error bootstrapping the app', error);
});
```

#### Dynamic Named Import

`providers/my-provider.ts`
```typescript
export const MyProvider: Provider = ....;
```

`providers/other-provider.ts`
```typescript
export const OtherProvider = [
provideHttpClient(),
ReactiveFormsModule,
provideZoneChangeDetection({ eventCoalescing: true }),
] satisfies Array<Provider | EnvironmentProviders>;
```

`main.ts`
```typescript
import { AppComponent } from './app.component';
import { bootstrapApplication } from '@angular/platform-browser';
import { fast } from 'ngx-fastboot';

fast(bootstrapApplication, AppComponent, {
providers: [
() => import('providers/my-provider').then((m) => m.MyProvider), // single
() => import('providers/other-provider').then((m) => m.OtherProvider), // array
]
}).then(appRef => {
console.log('App is bootstrapped');
}).catch(error => {
console.error('Error bootstrapping the app', error);
});
```

#### Dynamic Default Import
`providers/my-provider.ts`
```typescript
import { provideHttpClient } from '@angular/common/http';
import { EnvironmentProviders, Provider, provideZoneChangeDetection } from '@angular/core';
import { ReactiveFormsModule } from '@angular/forms';
const MyProvider: Provider = ....;
export default MyProvider;
```

`providers/other-provider.ts`
```typescript
export default [
provideHttpClient(),
ReactiveFormsModule,
provideZoneChangeDetection({ eventCoalescing: true }),
] satisfies Array<Provider | EnvironmentProviders>;
```

`main.ts`
```typescript
import { AppComponent } from './app.component';
import { bootstrapApplication } from '@angular/platform-browser';
import { fast } from 'ngx-fastboot';

fast(bootstrapApplication, AppComponent, {
providers: [
() => import('providers/my-provider'), // single
() => import('providers/other-provider'), // array
]
}).then(appRef => {
console.log('App is bootstrapped');
}).catch(error => {
console.error('Error bootstrapping the app', error);
});
```

#### Full example

`main.ts`
```typescript
import { AppComponent } from './app.component';
import { bootstrapApplication } from '@angular/platform-browser';
import { MyStaticImportedProvider } from './providers/my-static-imported-provider';
import { fast } from 'ngx-fastboot';

fast(bootstrapApplication, AppComponent, {
providers: [
MyStaticImportedProvider,
() => import('providers/my-provider').then((m) => m.MyProvider),
() => import('providers/other-provider'),
]
}).then(appRef => {
console.log('App is bootstrapped');
}).catch(error => {
console.error('Error bootstrapping the app', error);
});
```

### RootComponent

Similar to providers, you can manage the root component of the application both **statically** and **dynamically**. Dynamically loading the root component can help reduce the initial bundle size.

#### Static Import

The classic method to bootstrap the root component involves a static import:

```typescript
fast(bootstrapApplication, AppComponent, {
providers: [...]
});
```

#### Dynamic Named Import
To optimize bundle size, the root component can be loaded dynamically with a named import:

```typescript
fast(
bootstrapApplication,
() => import('./app-component').then((m) => m.AppComponent), {
providers: [...]
});
```

#### Dynamic Default Import
Alternatively, you can use a dynamic default import if the root component is exported as the default from the module:

```typescript
fast(
bootstrapApplication,
() => import('./app-component'), {
providers: [...]
});
```

### Sentry Integration Example
This example shows how to integrate Sentry with ngx-fastboot for error monitoring and performance tracing in your Angular application.

Expand Down Expand Up @@ -186,7 +330,7 @@ Dynamically loads the specified providers in the configuration and bootstraps an
#### Parameters

- **`bootstrap`**: The Angular application's bootstrap function (typically `bootstrapApplication`).
- **`rootComponent`**: The root component of the application, which should be of type `Type<unknown>`.
- **`rootComponent`**: The root component of the application, which should be of type `FastComponent`.
- **`options`**: (Optional) The application configuration, including the providers to be loaded. It should conform to the `FastApplicationConfig` type. Providers can be `Provider`, `EnvironmentProviders`, or lazy modules that return these providers.

#### Returns
Expand Down Expand Up @@ -221,6 +365,18 @@ export type LazyModule<T> = () => Promise<T | { default: T }>;
export type AngularProvider = Provider | EnvironmentProviders;
```

### `FastComponent`

```typescript
export type FastComponent = Type<unknown> | LazyComponent;
```

### `LazyComponent`

```typescript
export type LazyComponent = LazyModule<Type<unknown>>;
```

## Contributing

We welcome contributions! Please read our [Contributing Guide](CONTRIBUTING.md) to learn how you can help improve **ngx-fastboot**.
Expand Down
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -70,7 +70,7 @@
"@semantic-release/github": "github:semantic-release/git",
"@semantic-release/npm": "^12.0.1",
"@semantic-release/release-notes-generator": "^14.0.0",
"@types/jest": "^29.5.12",
"@types/jest": "^29.5.13",
"@typescript-eslint/eslint-plugin": "8.8.1",
"@typescript-eslint/parser": "8.8.1",
"commitlint": "19.5.0",
Expand Down
46 changes: 23 additions & 23 deletions pnpm-lock.yaml

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

18 changes: 10 additions & 8 deletions src/lib/fast.ts
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
import { ApplicationConfig, ApplicationRef, Type } from '@angular/core';
import { ApplicationConfig, ApplicationRef } from '@angular/core';
import type { bootstrapApplication } from '@angular/platform-browser';

import { resolveProviders } from './resolve-providers';
import { AngularProvider, FastApplicationConfig } from './types';
import { resolveDependencies } from './resolve-dependencies';
import { FastApplicationConfig, FastComponent } from './types';

/**
* Dynamically loads the specified providers in the configuration and bootstraps an Angular application.
Expand All @@ -12,7 +12,8 @@ import { AngularProvider, FastApplicationConfig } from './types';
* be loaded asynchronously. This function handles resolving these providers and passing them to the bootstrap method.
*
* @param bootstrap - The Angular application's bootstrap function (typically `bootstrapApplication`).
* @param rootComponent - The root component of the application, which should be of type `Type<unknown>`.
* @param rootComponent - The root component of the application, which should be of type `FastComponent`
* (ie. Type<unknown> or lazy module that return this component).
* @param options - (Optional) The application configuration, including the providers to be loaded. It should conform
* to the `FastApplicationConfig` type. Providers can be `Provider`, `EnvironmentProviders`,
* or lazy modules that return these providers.
Expand All @@ -39,16 +40,17 @@ import { AngularProvider, FastApplicationConfig } from './types';
*/
export const fast = async (
bootstrap: typeof bootstrapApplication,
rootComponent: Type<unknown>,
rootComponent: FastComponent,
options?: FastApplicationConfig,
): Promise<ApplicationRef> => {
const resolvedProviders: Array<AngularProvider> = await resolveProviders(
const { component, providers } = await resolveDependencies(
rootComponent,
options?.providers ?? [],
);
const nextOptions: ApplicationConfig = {
...options,
providers: resolvedProviders,
providers,
};

return bootstrap(rootComponent, nextOptions);
return bootstrap(component, nextOptions);
};
Loading

0 comments on commit 37a7caf

Please sign in to comment.