diff --git a/angular/app-types/angular-app-type/application.bundler.ts b/angular/app-types/angular-app-type/application.bundler.ts index b4da7a3..bce6370 100644 --- a/angular/app-types/angular-app-type/application.bundler.ts +++ b/angular/app-types/angular-app-type/application.bundler.ts @@ -5,7 +5,6 @@ import { ApplicationOptions, dedupPaths, getLoggerApi, - loadEsmModule, normalizePath } from '@bitdev/angular.dev-services.common'; import { @@ -19,7 +18,7 @@ import { outputFileSync, removeSync } from 'fs-extra'; import type { NitroConfig } from 'nitropack'; import { basename, extname, join, posix, relative, resolve } from 'path'; import definePlugin from './plugins/define.plugin'; -import { getIndexInputFile } from './utils'; +import { getIndexInputFile, loadEsmModule } from './utils'; export type BuildApplicationOptions = { angularOptions: Partial; @@ -238,7 +237,6 @@ async function buildNitro(options: BuildApplicationOptions): Promise { prerender } = await loadEsmModule('nitropack'); - const nitro = await createNitro({ dev: false, ...nitroConfig diff --git a/angular/app-types/angular-app-type/application.dev-server.ts b/angular/app-types/angular-app-type/application.dev-server.ts index bc7aa07..beec70b 100644 --- a/angular/app-types/angular-app-type/application.dev-server.ts +++ b/angular/app-types/angular-app-type/application.dev-server.ts @@ -1,12 +1,7 @@ /* eslint-disable no-param-reassign */ import { executeDevServerBuilder, OutputHashing } from '@angular-devkit/build-angular'; import { VERSION } from '@angular/cli'; -import { - dedupPaths, - getLoggerApi, - loadEsmModule, - normalizePath -} from '@bitdev/angular.dev-services.common'; +import { dedupPaths, getLoggerApi, normalizePath } from '@bitdev/angular.dev-services.common'; import { type ApplicationBuilderOptions, type DevServerBuilderOptions @@ -20,6 +15,7 @@ import { join, posix, relative, resolve } from 'path'; // @ts-ignore import type { Connect } from 'vite'; import definePlugin from './plugins/define.plugin'; +import { loadEsmModule } from './utils'; export type ServeApplicationOptions = { angularOptions: Partial; diff --git a/angular/app-types/angular-app-type/utils.ts b/angular/app-types/angular-app-type/utils.ts index d6c2b1f..e43765d 100644 --- a/angular/app-types/angular-app-type/utils.ts +++ b/angular/app-types/angular-app-type/utils.ts @@ -63,3 +63,24 @@ export function getIndexInputFile(index: ApplicationOptions['index']): string { } return (index as any).input; } + +/** + * This uses a dynamic import to load a module which may be ESM. + * CommonJS code can load ESM code via a dynamic import. Unfortunately, TypeScript + * will currently, unconditionally downlevel dynamic import into a require call. + * require calls cannot load ESM code and will result in a runtime error. To work around + * this, a Function constructor is used to prevent TypeScript from changing the dynamic import. + * Once TypeScript provides support for keeping the dynamic import, this workaround can + * be dropped. + * + * @param modulePath The path of the module to load. + * @returns A Promise that resolves to the dynamically imported module. + */ +export async function loadEsmModule(modulePath: string): Promise { + try { + return await import(modulePath); + } catch (e) { + // eslint-disable-next-line @typescript-eslint/no-implied-eval + return new Function('modulePath', `return import(modulePath)`)(modulePath) as Promise; + } +} diff --git a/angular/devkit/compiler/ng-packagr/ng-packagr.compiler.ts b/angular/devkit/compiler/ng-packagr/ng-packagr.compiler.ts index b0b736e..66d2166 100644 --- a/angular/devkit/compiler/ng-packagr/ng-packagr.compiler.ts +++ b/angular/devkit/compiler/ng-packagr/ng-packagr.compiler.ts @@ -3,8 +3,7 @@ import { AngularEnvOptions, componentIsApp, getNodeModulesPaths, - getWorkspace, - loadEsmModule + getWorkspace } from '@bitdev/angular.dev-services.common'; import { ApplicationAspect, ApplicationMain } from '@teambit/application'; import { @@ -44,6 +43,27 @@ export function isFatalDiagnosticError(err: any): err is FatalDiagnosticError { return err._isFatalDiagnosticError === true; } +/** + * This uses a dynamic import to load a module which may be ESM. + * CommonJS code can load ESM code via a dynamic import. Unfortunately, TypeScript + * will currently, unconditionally downlevel dynamic import into a require call. + * require calls cannot load ESM code and will result in a runtime error. To work around + * this, a Function constructor is used to prevent TypeScript from changing the dynamic import. + * Once TypeScript provides support for keeping the dynamic import, this workaround can + * be dropped. + * + * @param modulePath The path of the module to load. + * @returns A Promise that resolves to the dynamically imported module. + */ +export async function loadEsmModule(modulePath: string): Promise { + try { + return await import(modulePath); + } catch (e) { + // eslint-disable-next-line @typescript-eslint/no-implied-eval + return new Function('modulePath', `return import(modulePath)`)(modulePath) as Promise; + } +} + export async function createDiagnosticsReporter(logger: Logger): Promise { const { formatDiagnostics } = await loadEsmModule(`@angular/compiler-cli`); const formatter = (diagnostic: Diagnostic) => formatDiagnostics([diagnostic]);