Skip to content

Commit

Permalink
feat(admin-ui): Allow shared & lazy UI plugins to be specified
Browse files Browse the repository at this point in the history
Relates to #55
  • Loading branch information
michaelbromley committed Sep 25, 2019
1 parent 1e41b92 commit 5daf756
Show file tree
Hide file tree
Showing 10 changed files with 101 additions and 77 deletions.
2 changes: 2 additions & 0 deletions packages/admin-ui/src/app/app.module.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ import { CoreModule } from './core/core.module';
import { CustomHttpTranslationLoader } from './core/providers/i18n/custom-http-loader';
import { I18nService } from './core/providers/i18n/i18n.service';
import { DataService } from './data/providers/data.service';
import { SharedExtensionsModule } from './extensions/shared-extensions.module';

@Injectable()
export class BaseHrefHolder {
Expand All @@ -39,6 +40,7 @@ export function HttpLoaderFactory(http: HttpClient, location: PlatformLocation)
compiler: { provide: TranslateCompiler, useClass: TranslateMessageFormatCompiler },
}),
CoreModule,
SharedExtensionsModule,
],
providers: [BaseHrefHolder],
bootstrap: [AppComponent],
Expand Down
2 changes: 1 addition & 1 deletion packages/admin-ui/src/app/app.routes.ts
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,7 @@ export const routes: Route[] = [
},
{
path: 'extensions',
loadChildren: `./extensions/extensions.module#ExtensionsModule`,
loadChildren: `./extensions/lazy-extensions.module#LazyExtensionsModule`,
},
],
},
Expand Down
Original file line number Diff line number Diff line change
@@ -1,12 +1,13 @@
/** This file is generated by the createExtensionsModules() function in devkit/common.ts. Do not edit. */
import { CommonModule } from '@angular/common';
import { NgModule } from '@angular/core';

/**
* This is a placeholder module for UI extensions provided by the AdminUiPlugin `extensions` option.
* When the {@link compileUiExtensions} function is executed, this module gets temporarily replaced
* When the {@link createExtensionsModules} function is executed, this module gets temporarily replaced
* by a generated module which includes all of the configured extension modules.
*/
@NgModule({
imports: [CommonModule],
})
export class ExtensionsModule {}
export class LazyExtensionsModule {}
13 changes: 13 additions & 0 deletions packages/admin-ui/src/app/extensions/shared-extensions.module.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
/** This file is generated by the createExtensionsModules() function in devkit/common.ts. Do not edit. */
import { CommonModule } from '@angular/common';
import { NgModule } from '@angular/core';

/**
* This is a placeholder module for UI extensions provided by the AdminUiPlugin `extensions` option.
* When the {@link createExtensionsModules} function is executed, this module gets temporarily replaced
* by a generated module which includes all of the configured extension modules.
*/
@NgModule({
imports: [CommonModule],
})
export class SharedExtensionsModule {}
80 changes: 41 additions & 39 deletions packages/admin-ui/src/app/shared/shared.module.ts
Original file line number Diff line number Diff line change
Expand Up @@ -19,45 +19,47 @@ import { IfPermissionsDirective } from './directives/if-permissions.directive';
import { HasPermissionPipe } from './pipes/has-permission.pipe';
import { ModalService } from './providers/modal/modal.service';
import { CanDeactivateDetailGuard } from './providers/routing/can-deactivate-detail-guard';
import { ChipComponent } from './shared-declarations';
import { ConfigurableInputComponent } from './shared-declarations';
import { CurrencyInputComponent } from './shared-declarations';
import { CustomFieldControlComponent } from './shared-declarations';
import { CustomerLabelComponent } from './shared-declarations';
import { DataTableColumnComponent } from './shared-declarations';
import { DataTableComponent } from './shared-declarations';
import { DropdownItemDirective } from './shared-declarations';
import { DropdownMenuComponent } from './shared-declarations';
import { DropdownTriggerDirective } from './shared-declarations';
import { DropdownComponent } from './shared-declarations';
import { FacetValueChipComponent } from './shared-declarations';
import { FacetValueSelectorComponent } from './shared-declarations';
import { FormFieldControlDirective } from './shared-declarations';
import { FormFieldComponent } from './shared-declarations';
import { FormItemComponent } from './shared-declarations';
import { AffixedInputComponent } from './shared-declarations';
import { PercentageSuffixInputComponent } from './shared-declarations';
import { LabeledDataComponent } from './shared-declarations';
import { LanguageSelectorComponent } from './shared-declarations';
import { DialogButtonsDirective } from './shared-declarations';
import { DialogComponentOutletComponent } from './shared-declarations';
import { DialogTitleDirective } from './shared-declarations';
import { ModalDialogComponent } from './shared-declarations';
import { ObjectTreeComponent } from './shared-declarations';
import { OrderStateLabelComponent } from './shared-declarations';
import { PaginationControlsComponent } from './shared-declarations';
import { RichTextEditorComponent } from './shared-declarations';
import { SelectToggleComponent } from './shared-declarations';
import { SimpleDialogComponent } from './shared-declarations';
import { TableRowActionComponent } from './shared-declarations';
import { TitleInputComponent } from './shared-declarations';
import { CurrencyNamePipe } from './shared-declarations';
import { FileSizePipe } from './shared-declarations';
import { SentenceCasePipe } from './shared-declarations';
import { SortPipe } from './shared-declarations';
import { StringToColorPipe } from './shared-declarations';
import { FormattedAddressComponent } from './shared-declarations';
import { ItemsPerPageControlsComponent } from './shared-declarations';
import {
AffixedInputComponent,
ChipComponent,
ConfigurableInputComponent,
CurrencyInputComponent,
CurrencyNamePipe,
CustomerLabelComponent,
CustomFieldControlComponent,
DataTableColumnComponent,
DataTableComponent,
DialogButtonsDirective,
DialogComponentOutletComponent,
DialogTitleDirective,
DropdownComponent,
DropdownItemDirective,
DropdownMenuComponent,
DropdownTriggerDirective,
FacetValueChipComponent,
FacetValueSelectorComponent,
FileSizePipe,
FormattedAddressComponent,
FormFieldComponent,
FormFieldControlDirective,
FormItemComponent,
ItemsPerPageControlsComponent,
LabeledDataComponent,
LanguageSelectorComponent,
ModalDialogComponent,
ObjectTreeComponent,
OrderStateLabelComponent,
PaginationControlsComponent,
PercentageSuffixInputComponent,
RichTextEditorComponent,
SelectToggleComponent,
SentenceCasePipe,
SimpleDialogComponent,
SortPipe,
StringToColorPipe,
TableRowActionComponent,
TitleInputComponent,
} from './shared-declarations';

const IMPORTS = [
ClarityModule,
Expand Down
59 changes: 31 additions & 28 deletions packages/admin-ui/src/devkit/common.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,8 @@ import * as path from 'path';

const EXTENSIONS_DIR = path.join(__dirname, '../src/app/extensions');
const EXTENSIONS_MODULES_DIR = 'modules';
const originalExtensionsModuleFile = path.join(EXTENSIONS_DIR, 'extensions.module.ts');
const tempExtensionsModuleFile = path.join(EXTENSIONS_DIR, 'extensions.module.ts.temp');
const lazyExtensionsModuleFile = path.join(EXTENSIONS_DIR, 'lazy-extensions.module.ts');
const sharedExtensionsModuleFile = path.join(EXTENSIONS_DIR, 'shared-extensions.module.ts');

/**
* Returns true if currently being executed from inside the Vendure monorepo.
Expand Down Expand Up @@ -38,56 +38,59 @@ export function getModuleOutputDir(extension: Required<AdminUiExtension>): strin
return path.join(EXTENSIONS_DIR, EXTENSIONS_MODULES_DIR, extension.id);
}

export function createExtensionsModule(extensions: Array<Required<AdminUiExtension>>) {
export function createExtensionsModules(extensions: Array<Required<AdminUiExtension>>) {
const removeTsExtension = (filename: string): string => filename.replace(/\.ts$/, '');
const importPath = (e: Required<AdminUiExtension>): string =>
`./${EXTENSIONS_MODULES_DIR}/${e.id}/${removeTsExtension(e.ngModuleFileName)}`;
fs.renameSync(originalExtensionsModuleFile, tempExtensionsModuleFile);

const source = generateExtensionModuleTsSource(
extensions.map(e => ({ className: e.ngModuleName, path: importPath(e) })),
);
fs.writeFileSync(path.join(EXTENSIONS_DIR, 'extensions.module.ts'), source, 'utf-8');
for (const type of ['lazy', 'shared'] as const) {
const source = generateExtensionModuleTsSource(
type,
extensions
.filter(e => e.type === type)
.map(e => ({ className: e.ngModuleName, path: importPath(e) })),
);
const filePath = type === 'lazy' ? lazyExtensionsModuleFile : sharedExtensionsModuleFile;
fs.writeFileSync(filePath, source, 'utf-8');
}
}

export function restoreOriginalExtensionsModule() {
fs.renameSync(originalExtensionsModuleFile, path.join(EXTENSIONS_DIR, 'extensions.module.ts.generated'));
restoreExtensionsModule();
restoreExtensionsModules();
}

/**
* Restores the placeholder ExtensionsModule file from a template.
*/
export function restoreExtensionsModule() {
const source = `import { CommonModule } from '@angular/common';
import { NgModule } from '@angular/core';
/**
* This is a placeholder module for UI extensions provided by the AdminUiPlugin \`extensions\` option.
* When the {@link compileUiExtensions} function is executed, this module gets temporarily replaced
* by a generated module which includes all of the configured extension modules.
*/
@NgModule({
imports: [CommonModule],
})
export class ExtensionsModule {}
`;
fs.writeFileSync(originalExtensionsModuleFile, source, 'utf-8');
export function restoreExtensionsModules() {
for (const type of ['lazy', 'shared'] as const) {
const source = generateExtensionModuleTsSource(type, []);
const filePath = type === 'lazy' ? lazyExtensionsModuleFile : sharedExtensionsModuleFile;
fs.writeFileSync(filePath, source, 'utf-8');
}
}

function generateExtensionModuleTsSource(modules: Array<{ className: string; path: string }>): string {
return `/** This file is generated by the build() function. Do not edit. */
function generateExtensionModuleTsSource(
type: 'shared' | 'lazy',
modules: Array<{ className: string; path: string }>,
): string {
return `/** This file is generated by the createExtensionsModules() function in devkit/common.ts. Do not edit. */
import { CommonModule } from '@angular/common';
import { NgModule } from '@angular/core';
${modules.map(e => `import { ${e.className} } from '${e.path}';`).join('\n')}
/**
* This is a placeholder module for UI extensions provided by the AdminUiPlugin \`extensions\` option.
* When the {@link createExtensionsModules} function is executed, this module gets temporarily replaced
* by a generated module which includes all of the configured extension modules.
*/
@NgModule({
imports: [
CommonModule,
${modules.map(e => e.className + ',').join('\n')}
],
})
export class ExtensionsModule {}
export class ${type === 'lazy' ? 'Lazy' : 'Shared'}ExtensionsModule {}
`;
}
4 changes: 2 additions & 2 deletions packages/admin-ui/src/devkit/compile.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ import * as path from 'path';

import {
copyExtensionModules,
createExtensionsModule,
createExtensionsModules,
deleteExistingExtensionModules,
isInVendureMonorepo,
restoreOriginalExtensionsModule,
Expand All @@ -20,7 +20,7 @@ export function compileAdminUiApp(outputPath: string, extensions: Array<Required
restoreOriginalExtensionsModule();
deleteExistingExtensionModules();
copyExtensionModules(extensions);
createExtensionsModule(extensions);
createExtensionsModules(extensions);

const config = isInVendureMonorepo() ? 'plugin-dev' : 'plugin';
const buildProcess = spawn(
Expand Down
8 changes: 4 additions & 4 deletions packages/admin-ui/src/devkit/watch.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,11 +6,11 @@ import * as path from 'path';

import {
copyExtensionModules,
createExtensionsModule,
createExtensionsModules,
deleteExistingExtensionModules,
getModuleOutputDir,
isInVendureMonorepo,
restoreExtensionsModule,
restoreExtensionsModules,
restoreOriginalExtensionsModule,
} from './common';

Expand All @@ -25,10 +25,10 @@ export type Watcher = {
*/
export function watchAdminUiApp(extensions: Array<Required<AdminUiExtension>>, port: number): Watcher {
const cwd = path.join(__dirname, '..');
restoreExtensionsModule();
restoreExtensionsModules();
deleteExistingExtensionModules();
copyExtensionModules(extensions);
createExtensionsModule(extensions);
createExtensionsModules(extensions);

const config = isInVendureMonorepo() ? 'plugin-dev' : 'plugin';
const buildProcess = spawn('yarn', ['ng', 'serve', `-c=${config}`, `--port=${port}`], {
Expand Down
2 changes: 2 additions & 0 deletions packages/admin-ui/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,4 +9,6 @@ export { DataService } from './app/data/providers/data.service';
export { ServerConfigService } from './app/data/server-config';
export { ModalService } from './app/shared/providers/modal/modal.service';
export { SharedModule } from './app/shared/shared.module';
export { NavBuilderService } from './app/core/providers/nav-builder/nav-builder.service';
export * from './app/core/providers/nav-builder/nav-builder-types';
export * from './app/shared/shared-declarations';
3 changes: 2 additions & 1 deletion packages/common/src/shared-types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ export type DeepRequired<T, U extends object | undefined = undefined> = T extend
? {
[P in keyof T]-?: NonNullable<T[P]> extends NonNullable<U | Function | Type<any>>
? NonNullable<T[P]>
: DeepRequired<NonNullable<T[P]>, U>
: DeepRequired<NonNullable<T[P]>, U>;
}
: T;
// tslint:enable:ban-types
Expand Down Expand Up @@ -92,6 +92,7 @@ export interface AdminUiConfig {

export interface AdminUiExtension {
id?: string;
type: 'shared' | 'lazy';
ngModulePath: string;
ngModuleFileName: string;
ngModuleName: string;
Expand Down

0 comments on commit 5daf756

Please sign in to comment.