Skip to content

Commit

Permalink
perf: add provideDelonMockConfig (#1695)
Browse files Browse the repository at this point in the history
  • Loading branch information
cipchk committed Nov 11, 2023
1 parent d6e75a0 commit 683ab23
Show file tree
Hide file tree
Showing 16 changed files with 113 additions and 60 deletions.
1 change: 0 additions & 1 deletion docs/module.en-US.md
Expand Up @@ -36,7 +36,6 @@ NG-ZORRO, @delon/abc, @delon/chart, etc. have changed from all import to on-dema

+ Angular Module: `BrowserModule`, `BrowserAnimationsModule`, `HttpClientModule`
+ `AlainThemeModule` Theme system
+ `DelonMockModule` Mock data
+ `AlainAuthModule` User authentication module
+ `AlainACLModule` Privilege module
+ Internationalization module
Expand Down
1 change: 0 additions & 1 deletion docs/module.zh-CN.md
Expand Up @@ -36,7 +36,6 @@ NG-ZORRO、@delon/abc、@delon/chart 等从版本11开始由一次性导入改

+ Angular 模块:`BrowserModule``BrowserAnimationsModule``HttpClientModule`
+ `AlainThemeModule` 主题系统
+ `DelonMockModule` Mock数据
+ `AlainAuthModule` 用户认证模块
+ `AlainACLModule` 权限模块
+ 国际化模块
Expand Down
14 changes: 13 additions & 1 deletion packages/mock/docs/getting-started.en-US.md
Expand Up @@ -38,4 +38,16 @@ Please refer to [global-config.module.ts](https://github.com/ng-alain/ng-alain/b
| `[executeOtherInterceptors]` | `boolean` | `true` | Whether continue to call other interceptor `intercept` method after mock rule hit ||
| `[copy]` | `boolean` | `true` | Whether to return copy data ||

> **Lazy modules** need to import `forChild`, You can import `forChild` in the `SharedModule`.
### Why is it only valid for development environment?

Mock is not real data, and most scenarios are for development local or test environments; therefore, Mock modules and rule data should not be included in the production environment.

Of course, you can also put the `provideDelonMockConfig` of `environment.ts` under `environment.prod.ts` so that the production environment also uses this rule, just like https://ng-alain.github.io/ng- Like alain/, some mock requests are needed to ensure the environment runs.

```ts
import { provideDelonMockConfig } from '@delon/mock';
import * as MOCKDATA from '../../_mock';
export const environment = {
providers: [provideDelonMockConfig({ data: MOCKDATA })]
} as Environment;
```
16 changes: 6 additions & 10 deletions packages/mock/docs/getting-started.zh-CN.md
Expand Up @@ -38,20 +38,16 @@ npm i --save-dev @delon/mock
| `[executeOtherInterceptors]` | `boolean` | `true` | 是否拦截命中后继续调用后续拦截器的 `intercept` 方法 ||
| `[copy]` | `boolean` | `true` | 是否返回副本数据 ||

> **懒模块**还需要导入 `forChild` 确保HTTP拦截器有效,一般可以直接在 SharedModule 直接使用 `forChild`
### 为什么只对开发环境有效?

Mock 并非是真实数据,大部分场景是针对开发本地或测试环境;所以在生产环境中不应该包括 Mock 模块以及规则数据。因此上述才会根据 `!environment.production` 依据环境来决定是否加载 `DelonMockModule`
Mock 并非是真实数据,大部分场景是针对开发本地或测试环境;所以在生产环境中不应该包括 Mock 模块以及规则数据。

当然,你依然可以在生产环境也使用这种规则,就像 //ng-alain.github.io/ng-alain/ 一样,需要一些模拟请求来保证环境的运行。
当然,也可以将 `environment.ts``provideDelonMockConfig` 放到 `environment.prod.ts` 下,使得生产环境也使用这种规则,就像 https://ng-alain.github.io/ng-alain/ 一样,需要一些模拟请求来保证环境的运行。

```ts
import { DelonMockModule } from '@delon/mock';
import { provideDelonMockConfig } from '@delon/mock';
import * as MOCKDATA from '../../_mock';
@NgModule({
imports: [
DelonMockModule.forRoot({ data: MOCKDATA })
]
})
export const environment = {
providers: [provideDelonMockConfig({ data: MOCKDATA })]
} as Environment;
```
2 changes: 1 addition & 1 deletion packages/mock/public_api.ts
Expand Up @@ -2,5 +2,5 @@ export * from './src/interface';
export * from './src/status.error';
export * from './src/mock.service';
export * from './src/mock.interceptor';
export * from './src/mock.module';
export * from './src/provide';
export * from './src/utils';
4 changes: 2 additions & 2 deletions packages/mock/src/interface.ts
@@ -1,10 +1,10 @@
/* eslint-disable @typescript-eslint/no-explicit-any */
import type { HttpRequest } from '@angular/common/http';
import { type HttpRequest } from '@angular/common/http';
import type { Observable } from 'rxjs';

export type MockCallback = any | Observable<any> | Promise<any>;

export class MockOptions {
export interface MockOptions {
data?: any;
}

Expand Down
12 changes: 7 additions & 5 deletions packages/mock/src/mock.interceptor.spec.ts
Expand Up @@ -21,7 +21,7 @@ import * as Mock from 'mockjs';
import { AlainMockConfig, provideAlainConfig } from '@delon/util/config';

import { MockRequest } from './interface';
import { DelonMockModule } from './mock.module';
import { provideDelonMockConfig } from './provide';
import { MockStatusError } from './status.error';
import { delay, r } from './utils';

Expand Down Expand Up @@ -78,10 +78,11 @@ describe('mock: interceptor', () => {
path: 'lazy',
loadChildren: jasmine.createSpy('expected')
}
]),
DelonMockModule.forRoot({ data })
])
].concat(imports),
providers: ([provideAlainConfig({ mock: options })] as any[]).concat(providers || [])
providers: ([provideAlainConfig({ mock: options }), provideDelonMockConfig({ data })] as any[]).concat(
providers || []
)
});
http = TestBed.inject<HttpClient>(HttpClient);
httpMock = TestBed.inject(HttpTestingController as Type<HttpTestingController>);
Expand Down Expand Up @@ -262,7 +263,8 @@ describe('mock: interceptor', () => {

@NgModule({
declarations: [LayoutComponent, ChildComponent],
imports: [DelonMockModule.forChild(), RouterModule.forChild([{ path: 'child', component: ChildComponent }])]
imports: [RouterModule.forChild([{ path: 'child', component: ChildComponent }])],
providers: [provideDelonMockConfig()]
})
class LazyModule {}

Expand Down
25 changes: 0 additions & 25 deletions packages/mock/src/mock.module.ts

This file was deleted.

5 changes: 2 additions & 3 deletions packages/mock/src/mock.service.spec.ts
Expand Up @@ -7,7 +7,7 @@ import { AlainMockConfig, provideAlainConfig } from '@delon/util/config';

import { MockOptions, MockRequest, MockRule } from './interface';
import { MockService } from './mock.service';
import { DelonMockModule } from '../index';
import { provideDelonMockConfig } from './provide';

const DATA = {
USERS: {
Expand All @@ -31,8 +31,7 @@ describe('mock: service', () => {

function genModule(options: AlainMockConfig, mockOptions?: MockOptions): void {
TestBed.configureTestingModule({
imports: [DelonMockModule.forRoot(mockOptions)],
providers: [provideAlainConfig({ mock: options })]
providers: [provideAlainConfig({ mock: options }), provideDelonMockConfig(mockOptions)]
});
srv = TestBed.inject<MockService>(MockService);
spyOn(console, 'log');
Expand Down
5 changes: 3 additions & 2 deletions packages/mock/src/mock.service.ts
@@ -1,17 +1,18 @@
/* eslint-disable @typescript-eslint/no-explicit-any */
import { Injectable, OnDestroy } from '@angular/core';
import { Inject, Injectable, OnDestroy, Optional } from '@angular/core';

import { AlainConfigService, AlainMockConfig } from '@delon/util/config';

import { MockCachedRule, MockOptions, MockRule } from './interface';
import { MOCK_DEFULAT_CONFIG } from './mock.config';
import { DELON_MOCK_CONFIG } from './provide';

@Injectable({ providedIn: 'root' })
export class MockService implements OnDestroy {
private cached: MockCachedRule[] = [];
readonly config: AlainMockConfig;

constructor(cogSrv: AlainConfigService, options: MockOptions) {
constructor(cogSrv: AlainConfigService, @Optional() @Inject(DELON_MOCK_CONFIG) options?: MockOptions) {
this.config = cogSrv.merge('mock', MOCK_DEFULAT_CONFIG)!;
this.setData(options?.data);
}
Expand Down
14 changes: 14 additions & 0 deletions packages/mock/src/provide.ts
@@ -0,0 +1,14 @@
import { HTTP_INTERCEPTORS } from '@angular/common/http';
import { EnvironmentProviders, InjectionToken, makeEnvironmentProviders } from '@angular/core';

import { MockOptions } from './interface';
import { MockInterceptor } from './mock.interceptor';

export const DELON_MOCK_CONFIG = new InjectionToken<MockOptions>('alain-mock-config');

export function provideDelonMockConfig(config?: MockOptions): EnvironmentProviders {
return makeEnvironmentProviders([
{ provide: DELON_MOCK_CONFIG, useValue: config },
{ provide: HTTP_INTERCEPTORS, useClass: MockInterceptor, multi: true }
]);
}
12 changes: 9 additions & 3 deletions packages/theme/src/config.ts
@@ -1,4 +1,4 @@
import { ModuleWithProviders, Type } from '@angular/core';
import type { EnvironmentProviders, ModuleWithProviders, Provider, Type } from '@angular/core';

import type { NzSafeAny } from 'ng-zorro-antd/core/types';

Expand All @@ -24,11 +24,17 @@ export interface Environment {
*/
api: ApiConfig;
/**
* Defined imported modules in `global-config.module.ts`
* Defined imported modules in `app-config.ts`
*
* 定义在 `global-config.module.ts` 导入的模块列表
* 定义在 `app-config.ts` 导入的模块列表
*/
modules?: Array<Type<NzSafeAny> | ModuleWithProviders<NzSafeAny> | NzSafeAny[]>;
/**
* Defined providers modules in `app-config.ts`
*
* 定义在 `app-config.ts` 导入的providers列表
*/
providers?: Array<Provider | EnvironmentProviders>;
}

export interface ApiConfig {
Expand Down
25 changes: 25 additions & 0 deletions schematics/ng-update/upgrade-rules/v17/index.spec.ts
Expand Up @@ -75,6 +75,31 @@ describe('Schematic: ng-update: v17Rule', () => {
expect(content).toContain(`provideAlainConfig(alainConfig)`);
});

it('should be use provideDelonMockConfig instead of DelonMockModule', async () => {
const globalConfigPath = '/projects/foo/src/environments/environment.ts';
tryAddFile(
tree,
globalConfigPath,
`import * as MOCKDATA from '@_mock';
import { DelonMockModule } from '@delon/mock';
import { Environment } from '@delon/theme';
export const environment = {
production: false,
useHash: true,
api: {
baseUrl: './',
refreshTokenEnabled: true,
refreshTokenType: 'auth-refresh'
},
modules: [DelonMockModule.forRoot({ data: MOCKDATA })]
} as Environment;`
);
await runMigration();
const content = tree.readContent(globalConfigPath);
expect(content).toContain(`provideDelonMockConfig`);
});

it('#preloader', async () => {
const appCompPath = '/projects/foo/src/app/app.component.ts';
tryAddFile(
Expand Down
27 changes: 27 additions & 0 deletions schematics/ng-update/upgrade-rules/v17/replaceProvideConfig.ts
Expand Up @@ -10,6 +10,7 @@ export function replaceProvideConfig(): Rule {
for (const name of projectNames) {
runAlain(tree, name, angularJson.projects[name].sourceRoot, context);
runZorro(tree, name, angularJson.projects[name].sourceRoot, context);
delonMock(tree, name, angularJson.projects[name].sourceRoot, context);
}
};
}
Expand All @@ -35,3 +36,29 @@ function runZorro(tree: Tree, name: string, sourceRoot: string, context: Schemat

logInfo(context, ` Use provideNzConfig instead of NzConfig in ${name} project`);
}

function delonMock(tree: Tree, name: string, sourceRoot: string, context: SchematicContext): void {
const filePath = `${sourceRoot}/environments/environment.ts`;
if (!tree.exists(filePath)) return;

const text = 'DelonMockModule.forRoot({ data: MOCKDATA })';
let content = tree.readText(filePath);
if (content.includes('text')) content = content.replace(text, '');

content = content
.replace('DelonMockModule.forRoot({ data: MOCKDATA })', '')
.replace('modules: [', 'providers: [provideDelonMockConfig({ data: MOCKDATA })],\nmodules: [')
.replace('DelonMockModule', 'provideDelonMockConfig');

tree.overwrite(filePath, content);

const globalFile = `${sourceRoot}/app/global-config.module.ts`;
if (tree.exists(globalFile)) {
const content = tree
.readText(globalFile)
.replace(', ...zorroProvides', ', ...zorroProvides, ...(environment.providers || [])');
tree.overwrite(globalFile, content);
}

logInfo(context, ` Use provideDelonMockConfig instead of DelonMockModule in ${name} project`);
}
6 changes: 2 additions & 4 deletions src/app/core/code/files/global-config.module.ts
@@ -1,5 +1,5 @@
export default `import { ModuleWithProviders, NgModule } from '@angular/core';
import { DelonMockModule } from '@delon/mock';
import { provideDelonMockConfig } from '@delon/mock';
import { AlainConfig, provideAlainConfig, AlainConfigService } from '@delon/util/config';
// Please refer to: https://ng-alain.com/docs/global-config
Expand All @@ -9,8 +9,7 @@ import * as MOCKDATA from '../../_mock';
const alainConfig: AlainConfig = { };
const alainModules = [DelonMockModule.forRoot({ data: MOCKDATA })];
const alainProvides = [{ provide: ALAIN_CONFIG, useValue: alainConfig }];
const alainProvides = [{ provide: ALAIN_CONFIG, useValue: alainConfig }, provideDelonMockConfig({ data: MOCKDATA })];
// #region reuse-tab
Expand Down Expand Up @@ -41,7 +40,6 @@ const zorroProvides = [provideNzConfig(ngZorroConfig)];
// #endregion
@NgModule({
imports: [...alainModules],
})
export class GlobalConfigModule {
static forRoot(): ModuleWithProviders<GlobalConfigModule> {
Expand Down
4 changes: 2 additions & 2 deletions src/app/global-config.module.ts
@@ -1,7 +1,6 @@
/* eslint-disable import/order */
import { ModuleWithProviders, NgModule } from '@angular/core';

import { DelonMockModule } from '@delon/mock';
import { AlainConfig, provideAlainConfig } from '@delon/util/config';

// Please refer to: https://ng-alain.com/docs/global-config
Expand Down Expand Up @@ -51,6 +50,7 @@ const alainProvides = [provideAlainConfig(alainConfig)];
// #region NG-ZORRO Config

import { NzConfig, provideNzConfig } from 'ng-zorro-antd/core/config';
import { provideDelonMockConfig } from '@delon/mock/src/provide';

const ngZorroConfig: NzConfig = {};

Expand All @@ -71,7 +71,7 @@ const zorroProvides = [provideNzConfig(ngZorroConfig)];
// #endregion

@NgModule({
imports: [DelonMockModule.forRoot({ data: MOCKDATA })]
providers: [provideDelonMockConfig({ data: MOCKDATA })]
})
export class GlobalConfigModule {
static forRoot(): ModuleWithProviders<GlobalConfigModule> {
Expand Down

0 comments on commit 683ab23

Please sign in to comment.