diff --git a/docs/module.en-US.md b/docs/module.en-US.md index 5faa7bbe9d..0e8c327795 100644 --- a/docs/module.en-US.md +++ b/docs/module.en-US.md @@ -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 diff --git a/docs/module.zh-CN.md b/docs/module.zh-CN.md index aa3a3562b8..9c2f01dbcb 100644 --- a/docs/module.zh-CN.md +++ b/docs/module.zh-CN.md @@ -36,7 +36,6 @@ NG-ZORRO、@delon/abc、@delon/chart 等从版本11开始由一次性导入改 + Angular 模块:`BrowserModule`、`BrowserAnimationsModule`、`HttpClientModule` + `AlainThemeModule` 主题系统 -+ `DelonMockModule` Mock数据 + `AlainAuthModule` 用户认证模块 + `AlainACLModule` 权限模块 + 国际化模块 diff --git a/packages/mock/docs/getting-started.en-US.md b/packages/mock/docs/getting-started.en-US.md index a4af7026e4..6aac22ddd9 100644 --- a/packages/mock/docs/getting-started.en-US.md +++ b/packages/mock/docs/getting-started.en-US.md @@ -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; +``` diff --git a/packages/mock/docs/getting-started.zh-CN.md b/packages/mock/docs/getting-started.zh-CN.md index 2c8859293b..3cb6948470 100644 --- a/packages/mock/docs/getting-started.zh-CN.md +++ b/packages/mock/docs/getting-started.zh-CN.md @@ -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; ``` diff --git a/packages/mock/public_api.ts b/packages/mock/public_api.ts index 4362ed085d..cf1e52f61d 100644 --- a/packages/mock/public_api.ts +++ b/packages/mock/public_api.ts @@ -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'; diff --git a/packages/mock/src/interface.ts b/packages/mock/src/interface.ts index a9bd601adb..0fe80031c0 100644 --- a/packages/mock/src/interface.ts +++ b/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 | Promise; -export class MockOptions { +export interface MockOptions { data?: any; } diff --git a/packages/mock/src/mock.interceptor.spec.ts b/packages/mock/src/mock.interceptor.spec.ts index d2b37b5dfc..8fa5597da6 100644 --- a/packages/mock/src/mock.interceptor.spec.ts +++ b/packages/mock/src/mock.interceptor.spec.ts @@ -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'; @@ -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); httpMock = TestBed.inject(HttpTestingController as Type); @@ -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 {} diff --git a/packages/mock/src/mock.module.ts b/packages/mock/src/mock.module.ts deleted file mode 100644 index 8d53b84976..0000000000 --- a/packages/mock/src/mock.module.ts +++ /dev/null @@ -1,25 +0,0 @@ -import { HTTP_INTERCEPTORS } from '@angular/common/http'; -import { ModuleWithProviders, NgModule } from '@angular/core'; - -import { MockOptions } from './interface'; -import { MockInterceptor } from './mock.interceptor'; - -@NgModule({}) -export class DelonMockModule { - static forRoot(options?: MockOptions): ModuleWithProviders { - return { - ngModule: DelonMockModule, - providers: [ - { provide: MockOptions, useValue: options }, - { provide: HTTP_INTERCEPTORS, useClass: MockInterceptor, multi: true } - ] - }; - } - - static forChild(): ModuleWithProviders { - return { - ngModule: DelonMockModule, - providers: [{ provide: HTTP_INTERCEPTORS, useClass: MockInterceptor, multi: true }] - }; - } -} diff --git a/packages/mock/src/mock.service.spec.ts b/packages/mock/src/mock.service.spec.ts index e52fb1dd88..ed624b4d01 100644 --- a/packages/mock/src/mock.service.spec.ts +++ b/packages/mock/src/mock.service.spec.ts @@ -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: { @@ -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); spyOn(console, 'log'); diff --git a/packages/mock/src/mock.service.ts b/packages/mock/src/mock.service.ts index 7f4e1402c9..2188653a6d 100644 --- a/packages/mock/src/mock.service.ts +++ b/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); } diff --git a/packages/mock/src/provide.ts b/packages/mock/src/provide.ts new file mode 100644 index 0000000000..96a1892717 --- /dev/null +++ b/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('alain-mock-config'); + +export function provideDelonMockConfig(config?: MockOptions): EnvironmentProviders { + return makeEnvironmentProviders([ + { provide: DELON_MOCK_CONFIG, useValue: config }, + { provide: HTTP_INTERCEPTORS, useClass: MockInterceptor, multi: true } + ]); +} diff --git a/packages/theme/src/config.ts b/packages/theme/src/config.ts index adf2de9b74..90ae5e271b 100644 --- a/packages/theme/src/config.ts +++ b/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'; @@ -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 | ModuleWithProviders | NzSafeAny[]>; + /** + * Defined providers modules in `app-config.ts` + * + * 定义在 `app-config.ts` 导入的providers列表 + */ + providers?: Array; } export interface ApiConfig { diff --git a/schematics/ng-update/upgrade-rules/v17/index.spec.ts b/schematics/ng-update/upgrade-rules/v17/index.spec.ts index 0ade8f9f6f..6e58cefe3e 100644 --- a/schematics/ng-update/upgrade-rules/v17/index.spec.ts +++ b/schematics/ng-update/upgrade-rules/v17/index.spec.ts @@ -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( diff --git a/schematics/ng-update/upgrade-rules/v17/replaceProvideConfig.ts b/schematics/ng-update/upgrade-rules/v17/replaceProvideConfig.ts index 3b597bb8b7..9524e29e10 100644 --- a/schematics/ng-update/upgrade-rules/v17/replaceProvideConfig.ts +++ b/schematics/ng-update/upgrade-rules/v17/replaceProvideConfig.ts @@ -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); } }; } @@ -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`); +} diff --git a/src/app/core/code/files/global-config.module.ts b/src/app/core/code/files/global-config.module.ts index f8cdcb409f..8a6e5c77e9 100644 --- a/src/app/core/code/files/global-config.module.ts +++ b/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 @@ -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 @@ -41,7 +40,6 @@ const zorroProvides = [provideNzConfig(ngZorroConfig)]; // #endregion @NgModule({ - imports: [...alainModules], }) export class GlobalConfigModule { static forRoot(): ModuleWithProviders { diff --git a/src/app/global-config.module.ts b/src/app/global-config.module.ts index de8147eafd..20c42e8115 100644 --- a/src/app/global-config.module.ts +++ b/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 @@ -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 = {}; @@ -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 {