Skip to content

Commit

Permalink
feat(ui-devkit): Add experimental wrapper for shared ui providers
Browse files Browse the repository at this point in the history
  • Loading branch information
michaelbromley committed Aug 30, 2023
1 parent e2156cb commit daf6f8c
Show file tree
Hide file tree
Showing 6 changed files with 178 additions and 60 deletions.
127 changes: 75 additions & 52 deletions packages/ui-devkit/src/compiler/compile.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,6 @@ import { DEFAULT_BASE_HREF, MODULES_OUTPUT_DIR } from './constants';
import { copyGlobalStyleFile, setBaseHref, setupScaffold } from './scaffold';
import { getAllTranslationFiles, mergeExtensionTranslations } from './translations';
import {
Extension,
StaticAssetDefinition,
UiExtensionCompilerOptions,
UiExtensionCompilerProcessArgument,
Expand All @@ -36,53 +35,58 @@ import {
export function compileUiExtensions(
options: UiExtensionCompilerOptions,
): AdminUiAppConfig | AdminUiAppDevModeConfig {
const { outputPath, baseHref, devMode, watchPort, extensions, command, additionalProcessArguments } =
options;
const usingYarn = options.command && options.command === 'npm' ? false : shouldUseYarn();
const { devMode, watchPort, command } = options;
const usingYarn = command && command === 'npm' ? false : shouldUseYarn();
if (devMode) {
return runWatchMode(
outputPath,
baseHref || DEFAULT_BASE_HREF,
watchPort || 4200,
extensions,
return runWatchMode({
watchPort: watchPort || 4200,
usingYarn,
additionalProcessArguments,
);
...options,
});
} else {
return runCompileMode(
outputPath,
baseHref || DEFAULT_BASE_HREF,
extensions,
return runCompileMode({
usingYarn,
additionalProcessArguments,
);
...options,
});
}
}

function runCompileMode(
outputPath: string,
baseHref: string,
extensions: Extension[],
usingYarn: boolean,
args?: UiExtensionCompilerProcessArgument[],
): AdminUiAppConfig {
const cmd = usingYarn ? 'yarn' : 'npm';
function runCompileMode({
outputPath,
baseHref,
extensions,
usingYarn,
additionalProcessArguments,
ngCompilerPath,
}: UiExtensionCompilerOptions & { usingYarn: boolean }): AdminUiAppConfig {
const distPath = path.join(outputPath, 'dist');

const compile = () =>
new Promise<void>(async (resolve, reject) => {
await setupScaffold(outputPath, extensions);
await setBaseHref(outputPath, baseHref);
const commandArgs = ['run', 'build', ...buildProcessArguments(args)];
if (!usingYarn) {
// npm requires `--` before any command line args being passed to a script
commandArgs.splice(2, 0, '--');
await setBaseHref(outputPath, baseHref || DEFAULT_BASE_HREF);

let cmd = usingYarn ? 'yarn' : 'npm';
let commandArgs = ['run', 'build'];
if (ngCompilerPath) {
cmd = 'node';
commandArgs = [ngCompilerPath, 'build', '--configuration production'];
} else {
if (!usingYarn) {
// npm requires `--` before any command line args being passed to a script
commandArgs.splice(2, 0, '--');
}
}
const buildProcess = spawn(cmd, commandArgs, {
cwd: outputPath,
shell: true,
stdio: 'inherit',
});
console.log(`Running ${cmd} ${commandArgs.join(' ')}`);
const buildProcess = spawn(
cmd,
[...commandArgs, ...buildProcessArguments(additionalProcessArguments)],
{
cwd: outputPath,
shell: true,
stdio: 'inherit',
},
);

buildProcess.on('close', code => {
if (code !== 0) {
Expand All @@ -96,19 +100,19 @@ function runCompileMode(
return {
path: distPath,
compile,
route: baseHrefToRoute(baseHref),
route: baseHrefToRoute(baseHref || DEFAULT_BASE_HREF),
};
}

function runWatchMode(
outputPath: string,
baseHref: string,
port: number,
extensions: Extension[],
usingYarn: boolean,
args?: UiExtensionCompilerProcessArgument[],
): AdminUiAppDevModeConfig {
const cmd = usingYarn ? 'yarn' : 'npm';
function runWatchMode({
outputPath,
baseHref,
watchPort,
extensions,
usingYarn,
additionalProcessArguments,
ngCompilerPath,
}: UiExtensionCompilerOptions & { usingYarn: boolean }): AdminUiAppDevModeConfig {
const devkitPath = require.resolve('@vendure/ui-devkit');
let buildProcess: ChildProcess;
let watcher: FSWatcher | undefined;
Expand All @@ -118,17 +122,31 @@ function runWatchMode(
const compile = () =>
new Promise<void>(async (resolve, reject) => {
await setupScaffold(outputPath, extensions);
await setBaseHref(outputPath, baseHref);
await setBaseHref(outputPath, baseHref || DEFAULT_BASE_HREF);
const adminUiExtensions = extensions.filter(isAdminUiExtension);
const normalizedExtensions = normalizeExtensions(adminUiExtensions);
const globalStylesExtensions = extensions.filter(isGlobalStylesExtension);
const staticAssetExtensions = extensions.filter(isStaticAssetExtension);
const allTranslationFiles = getAllTranslationFiles(extensions.filter(isTranslationExtension));
buildProcess = spawn(cmd, ['run', 'start', `--port=${port}`, ...buildProcessArguments(args)], {
cwd: outputPath,
shell: true,
stdio: 'inherit',
});
let cmd = usingYarn ? 'yarn' : 'npm';
let commandArgs = ['run', 'start'];
if (ngCompilerPath) {
cmd = 'node';
commandArgs = [ngCompilerPath, 'serve'];
}
buildProcess = spawn(
cmd,
[
...commandArgs,
`--port=${watchPort || 4200}`,
...buildProcessArguments(additionalProcessArguments),
],
{
cwd: outputPath,
shell: true,
stdio: 'inherit',
},
);

buildProcess.on('close', code => {
if (code !== 0) {
Expand Down Expand Up @@ -252,7 +270,12 @@ function runWatchMode(
};

process.on('SIGINT', close);
return { sourcePath: outputPath, port, compile, route: baseHrefToRoute(baseHref) };
return {
sourcePath: outputPath,
port: watchPort || 4200,
compile,
route: baseHrefToRoute(baseHref || DEFAULT_BASE_HREF),
};
}

function buildProcessArguments(args?: UiExtensionCompilerProcessArgument[]): string[] {
Expand Down
1 change: 1 addition & 0 deletions packages/ui-devkit/src/compiler/index.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
export * from './compile';
export * from './helpers';
export * from './types';
export * from './wrappers';
38 changes: 31 additions & 7 deletions packages/ui-devkit/src/compiler/scaffold.ts
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ import {
Extension,
GlobalStylesExtension,
SassVariableOverridesExtension,
SharedUiProvidersExtension,
StaticAssetExtension,
} from './types';
import {
Expand All @@ -26,6 +27,7 @@ import {
isAdminUiExtension,
isGlobalStylesExtension,
isSassVariableOverridesExtension,
isSharedUiProvidersExtension,
isStaticAssetExtension,
isTranslationExtension,
logger,
Expand All @@ -37,12 +39,13 @@ export async function setupScaffold(outputPath: string, extensions: Extension[])
deleteExistingExtensionModules(outputPath);

const adminUiExtensions = extensions.filter(isAdminUiExtension);
const sharedUiProvidersExtensions = extensions.filter(isSharedUiProvidersExtension);
const normalizedExtensions = normalizeExtensions(adminUiExtensions);

const modulePathMapping = generateModulePathMapping(normalizedExtensions);
copyAdminUiSource(outputPath, modulePathMapping);

await copyExtensionModules(outputPath, normalizedExtensions);
await copyExtensionModules(outputPath, [...normalizedExtensions, ...sharedUiProvidersExtensions]);

const staticAssetExtensions = extensions.filter(isStaticAssetExtension);
await copyStaticAssets(outputPath, staticAssetExtensions);
Expand Down Expand Up @@ -88,13 +91,18 @@ function generateModulePathMapping(extensions: AdminUiExtensionWithId[]) {
* Copies all files from the extensionPaths of the configured extensions into the
* admin-ui source tree.
*/
async function copyExtensionModules(outputPath: string, extensions: AdminUiExtensionWithId[]) {
const extensionRoutesSource = generateLazyExtensionRoutes(extensions);
async function copyExtensionModules(
outputPath: string,
extensions: Array<AdminUiExtensionWithId | SharedUiProvidersExtension>,
) {
const adminUiExtensions = extensions.filter(isAdminUiExtension) as AdminUiExtensionWithId[];
const sharedUiProvidersExtensions = extensions.filter(isSharedUiProvidersExtension);
const extensionRoutesSource = generateLazyExtensionRoutes(adminUiExtensions);
fs.writeFileSync(path.join(outputPath, EXTENSION_ROUTES_FILE), extensionRoutesSource, 'utf8');
const sharedExtensionModulesSource = generateSharedExtensionModule(extensions);
fs.writeFileSync(path.join(outputPath, SHARED_EXTENSIONS_FILE), sharedExtensionModulesSource, 'utf8');

for (const extension of extensions) {
for (const extension of adminUiExtensions) {
const dest = path.join(outputPath, MODULES_OUTPUT_DIR, extension.id);
if (!extension.exclude) {
fs.copySync(extension.extensionPath, dest);
Expand All @@ -108,6 +116,10 @@ async function copyExtensionModules(outputPath: string, extensions: AdminUiExten
filter: name => name === extension.extensionPath || exclude.every(e => e !== name),
});
}
for (const extension of sharedUiProvidersExtensions) {
const dest = path.join(outputPath, MODULES_OUTPUT_DIR, `${extension.id}.ts`);
fs.copySync(extension.sharedProviders, dest);
}
}

async function copyStaticAssets(outputPath: string, extensions: Array<Partial<StaticAssetExtension>>) {
Expand Down Expand Up @@ -183,27 +195,39 @@ function generateLazyExtensionRoutes(extensions: AdminUiExtensionWithId[]): stri
return `export const extensionRoutes = [${routes.join(',\n')}];\n`;
}

function generateSharedExtensionModule(extensions: AdminUiExtensionWithId[]) {
function generateSharedExtensionModule(
extensions: Array<AdminUiExtensionWithId | SharedUiProvidersExtension>,
) {
const sharedProviderExtensions = extensions.filter((e): e is SharedUiProvidersExtension =>
e.hasOwnProperty('sharedProviders'),
);
const adminUiExtensions = extensions.filter((e): e is AdminUiExtensionWithId =>
e.hasOwnProperty('extensionPath'),
);
return `import { NgModule } from '@angular/core';
import { CommonModule } from '@angular/common';
${extensions
${adminUiExtensions
.map(e =>
e.ngModules
.filter(m => m.type === 'shared')
.map(m => `import { ${m.ngModuleName} } from '${getModuleFilePath(e.id, m)}';\n`)
.join(''),
)
.join('')}
${sharedProviderExtensions
.map((m, i) => `import SharedProviders${i} from './extensions/${m.id}';\n`)
.join('')}
@NgModule({
imports: [CommonModule, ${extensions
imports: [CommonModule, ${adminUiExtensions
.map(e =>
e.ngModules
.filter(m => m.type === 'shared')
.map(m => m.ngModuleName)
.join(', '),
)
.join(', ')}],
providers: [${sharedProviderExtensions.map((m, i) => `...SharedProviders${i}`).join(', ')}],
})
export class SharedExtensionsModule {}
`;
Expand Down
37 changes: 36 additions & 1 deletion packages/ui-devkit/src/compiler/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,8 @@ export type Extension =
| TranslationExtension
| StaticAssetExtension
| GlobalStylesExtension
| SassVariableOverridesExtension;
| SassVariableOverridesExtension
| SharedUiProvidersExtension;

/**
* @description
Expand Down Expand Up @@ -82,6 +83,19 @@ export interface SassVariableOverridesExtension {
sassVariableOverrides: string;
}

/**
* @description
* Defines an extension which contains only shared providers such as nav menu items, custom form inputs,
* custom detail components, action bar items, custom history entry components.
*
* @docsCategory UiDevkit
* @docsPage AdminUiExtension
*/
export interface SharedUiProvidersExtension {
id: string;
sharedProviders: string;
}

/**
* @description
* Defines extensions to the Admin UI application by specifying additional
Expand Down Expand Up @@ -316,6 +330,27 @@ export interface UiExtensionCompilerOptions {
* translations with which to extend the Admin UI.
*/
extensions: Extension[];
/**
* @description
* Allows you to manually specify the path to the Angular CLI compiler script. This can be useful in scenarios
* where for some reason the built-in start/build scripts are unable to locate the `ng` command.
*
* This option should not usually be required.
*
* @example
* ```ts
* compileUiExtensions({
* ngCompilerPath: path.join(__dirname, '../../node_modules/@angular/cli/bin/ng.js'),
* outputPath: path.join(__dirname, '../admin-ui'),
* extensions: [
* // ...
* ],
* })
* ```
*
* @since 2.1.0
*/
ngCompilerPath?: string | undefined;
/**
* @description
* Set to `true` in order to compile the Admin UI in development mode (using the Angular CLI
Expand Down
5 changes: 5 additions & 0 deletions packages/ui-devkit/src/compiler/utils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ import {
Extension,
GlobalStylesExtension,
SassVariableOverridesExtension,
SharedUiProvidersExtension,
StaticAssetDefinition,
StaticAssetExtension,
TranslationExtension,
Expand Down Expand Up @@ -96,6 +97,10 @@ export function isAdminUiExtension(input: Extension): input is AdminUiExtension
return input.hasOwnProperty('extensionPath');
}

export function isSharedUiProvidersExtension(input: Extension): input is SharedUiProvidersExtension {
return input.hasOwnProperty('sharedProviders');
}

export function isTranslationExtension(input: Extension): input is TranslationExtension {
return input.hasOwnProperty('translations');
}
Expand Down
Loading

0 comments on commit daf6f8c

Please sign in to comment.