From 187bd5a2abf5e242aabd5c38738a2a450d40a7ce Mon Sep 17 00:00:00 2001 From: Colum Ferry Date: Fri, 30 Jun 2023 10:03:36 +0100 Subject: [PATCH] feat(angular): add support for TS Module Federation Config #15739 --- packages/angular/package.json | 2 - .../builders/utilities/module-federation.ts | 55 +++++++++++---- .../angular/src/builders/utilities/webpack.ts | 23 +----- packages/js/package.json | 2 + .../src/utils/typescript/tsnode-register.ts | 21 ++++++ packages/next/package.json | 2 - .../executors/server/lib/tsnode-register.ts | 20 ------ .../module-federation-dev-server.impl.ts | 70 +++++++++++++++---- packages/webpack/package.json | 2 - .../src/utils/webpack/custom-webpack.ts | 25 +------ 10 files changed, 125 insertions(+), 97 deletions(-) create mode 100644 packages/js/src/utils/typescript/tsnode-register.ts delete mode 100644 packages/next/src/executors/server/lib/tsnode-register.ts diff --git a/packages/angular/package.json b/packages/angular/package.json index 4c424489995fe..ebe3b14cd1288 100644 --- a/packages/angular/package.json +++ b/packages/angular/package.json @@ -54,8 +54,6 @@ "magic-string": "~0.26.2", "minimatch": "3.0.5", "semver": "7.5.3", - "ts-node": "10.9.1", - "tsconfig-paths": "^4.1.2", "webpack": "^5.80.0", "webpack-merge": "5.7.3", "enquirer": "^2.3.6", diff --git a/packages/angular/src/builders/utilities/module-federation.ts b/packages/angular/src/builders/utilities/module-federation.ts index eebc91bc660c1..73a118753053f 100644 --- a/packages/angular/src/builders/utilities/module-federation.ts +++ b/packages/angular/src/builders/utilities/module-federation.ts @@ -1,7 +1,8 @@ import { ProjectConfiguration } from 'nx/src/config/workspace-json-project-json'; import { join } from 'path'; import { existsSync, readFileSync } from 'fs'; -import { logger, Remotes } from '@nx/devkit'; +import { logger } from '@nx/devkit'; +import { tsNodeRegister } from '@nx/js/src/utils/typescript/tsnode-register'; export function getDynamicRemotes( project: ProjectConfiguration, @@ -71,26 +72,54 @@ export function getDynamicRemotes( return dynamicRemotes; } -export function getStaticRemotes( - project: ProjectConfiguration, - context: import('@angular-devkit/architect').BuilderContext, - workspaceProjects: Record, - remotesToSkip: Set -): string[] { - const mfConfigPath = join( - context.workspaceRoot, - project.root, +function getModuleFederationConfig( + tsconfigPath: string, + workspaceRoot: string, + projectRoot: string +) { + const moduleFederationConfigPathJS = join( + workspaceRoot, + projectRoot, 'module-federation.config.js' ); - let mfeConfig: { remotes: Remotes }; + const moduleFederationConfigPathTS = join( + workspaceRoot, + projectRoot, + 'module-federation.config.ts' + ); + + let moduleFederationConfigPath = moduleFederationConfigPathJS; + + if (existsSync(moduleFederationConfigPathTS)) { + tsNodeRegister(moduleFederationConfigPathTS, tsconfigPath); + moduleFederationConfigPath = moduleFederationConfigPathTS; + } + try { - mfeConfig = require(mfConfigPath); + const config = require(moduleFederationConfigPath); + return { + mfeConfig: config.default || config, + mfConfigPath: moduleFederationConfigPath, + }; } catch { throw new Error( - `Could not load ${mfConfigPath}. Was this project generated with "@nx/angular:host"?` + `Could not load ${moduleFederationConfigPath}. Was this project generated with "@nx/angular:host"?` ); } +} + +export function getStaticRemotes( + project: ProjectConfiguration, + context: import('@angular-devkit/architect').BuilderContext, + workspaceProjects: Record, + remotesToSkip: Set +): string[] { + const { mfeConfig, mfConfigPath } = getModuleFederationConfig( + project.targets.build.options.tsConfig, + context.workspaceRoot, + project.root + ); const remotesConfig = Array.isArray(mfeConfig.remotes) && mfeConfig.remotes.length > 0 diff --git a/packages/angular/src/builders/utilities/webpack.ts b/packages/angular/src/builders/utilities/webpack.ts index 0392d70b0f873..3e213d43939fa 100644 --- a/packages/angular/src/builders/utilities/webpack.ts +++ b/packages/angular/src/builders/utilities/webpack.ts @@ -1,4 +1,5 @@ import { merge } from 'webpack-merge'; +import { tsNodeRegister } from '@nx/js/src/utils/typescript/tsnode-register'; export async function mergeCustomWebpackConfig( baseWebpackConfig: any, @@ -48,25 +49,3 @@ export function resolveIndexHtmlTransformer( return (indexHtml) => transform(target, indexHtml); } - -function tsNodeRegister(file: string, tsConfig?: string) { - if (!file?.endsWith('.ts')) return; - // Register TS compiler lazily - require('ts-node').register({ - project: tsConfig, - compilerOptions: { - module: 'CommonJS', - types: ['node'], - }, - }); - - if (!tsConfig) return; - - // Register paths in tsConfig - const tsconfigPaths = require('tsconfig-paths'); - const { absoluteBaseUrl: baseUrl, paths } = - tsconfigPaths.loadConfig(tsConfig); - if (baseUrl && paths) { - tsconfigPaths.register({ baseUrl, paths }); - } -} diff --git a/packages/js/package.json b/packages/js/package.json index 2ec87c187a4a6..90d922000c143 100644 --- a/packages/js/package.json +++ b/packages/js/package.json @@ -47,6 +47,8 @@ "detect-port": "^1.5.1", "fast-glob": "3.2.7", "fs-extra": "^11.1.0", + "ts-node": "10.9.1", + "tsconfig-paths": "^4.1.2", "ignore": "^5.0.4", "js-tokens": "^4.0.0", "minimatch": "3.0.5", diff --git a/packages/js/src/utils/typescript/tsnode-register.ts b/packages/js/src/utils/typescript/tsnode-register.ts new file mode 100644 index 0000000000000..a1d2ef6eb8af7 --- /dev/null +++ b/packages/js/src/utils/typescript/tsnode-register.ts @@ -0,0 +1,21 @@ +export function tsNodeRegister(file: string, tsConfig?: string) { + if (!file?.endsWith('.ts')) return; + // Register TS compiler lazily + require('ts-node').register({ + project: tsConfig, + compilerOptions: { + module: 'CommonJS', + types: ['node'], + }, + }); + + if (!tsConfig) return; + + // Register paths in tsConfig + const tsconfigPaths = require('tsconfig-paths'); + const { absoluteBaseUrl: baseUrl, paths } = + tsconfigPaths.loadConfig(tsConfig); + if (baseUrl && paths) { + tsconfigPaths.register({ baseUrl, paths }); + } +} diff --git a/packages/next/package.json b/packages/next/package.json index 6bbb9db10d3eb..d9a7fefffd736 100644 --- a/packages/next/package.json +++ b/packages/next/package.json @@ -44,8 +44,6 @@ "http-proxy-middleware": "^2.0.6", "ignore": "^5.0.4", "semver": "7.5.3", - "ts-node": "10.9.1", - "tsconfig-paths": "^4.1.2", "url-loader": "^4.1.1", "webpack-merge": "^5.8.0", "@nx/devkit": "file:../devkit", diff --git a/packages/next/src/executors/server/lib/tsnode-register.ts b/packages/next/src/executors/server/lib/tsnode-register.ts deleted file mode 100644 index 6cfd836fc4385..0000000000000 --- a/packages/next/src/executors/server/lib/tsnode-register.ts +++ /dev/null @@ -1,20 +0,0 @@ -export function tsNodeRegister(file: string = '', tsConfig?: string) { - if (file && file.endsWith('.ts')) { - // Register TS compiler lazily - require('ts-node').register({ - project: tsConfig, - compilerOptions: { - module: 'CommonJS', - types: ['node'], - }, - }); - - // Register paths in tsConfig - const tsconfigPaths = require('tsconfig-paths'); - const { absoluteBaseUrl: baseUrl, paths } = - tsconfigPaths.loadConfig(tsConfig); - if (baseUrl && paths) { - tsconfigPaths.register({ baseUrl, paths }); - } - } -} diff --git a/packages/react/src/executors/module-federation-dev-server/module-federation-dev-server.impl.ts b/packages/react/src/executors/module-federation-dev-server/module-federation-dev-server.impl.ts index 262ac3d878dd2..68e0e5dd39191 100644 --- a/packages/react/src/executors/module-federation-dev-server/module-federation-dev-server.impl.ts +++ b/packages/react/src/executors/module-federation-dev-server/module-federation-dev-server.impl.ts @@ -1,4 +1,10 @@ -import { ExecutorContext, logger, runExecutor } from '@nx/devkit'; +import { + ExecutorContext, + logger, + parseTargetString, + readTargetOptions, + runExecutor, +} from '@nx/devkit'; import devServerExecutor from '@nx/webpack/src/executors/dev-server/dev-server.impl'; import { WebDevServerOptions } from '@nx/webpack/src/executors/dev-server/schema'; import { join } from 'path'; @@ -10,33 +16,72 @@ import * as chalk from 'chalk'; import { waitForPortOpen } from '@nx/web/src/utils/wait-for-port-open'; import { findMatchingProjects } from 'nx/src/utils/find-matching-projects'; import { fork } from 'child_process'; +import { existsSync } from 'fs'; +import { tsNodeRegister } from '@nx/js/src/utils/typescript/tsnode-register'; type ModuleFederationDevServerOptions = WebDevServerOptions & { devRemotes?: string | string[]; skipRemotes?: string[]; }; -export default async function* moduleFederationDevServer( - options: ModuleFederationDevServerOptions, - context: ExecutorContext -): AsyncIterableIterator<{ success: boolean; baseUrl?: string }> { - const currIter = devServerExecutor(options, context); - const p = context.projectsConfigurations.projects[context.projectName]; +function getBuildOptions(buildTarget: string, context: ExecutorContext) { + const target = parseTargetString(buildTarget, context.projectGraph); - const moduleFederationConfigPath = join( - context.root, - p.root, + const buildOptions = readTargetOptions(target, context); + + return { + ...buildOptions, + }; +} + +function getModuleFederationConfig( + tsconfigPath: string, + workspaceRoot: string, + projectRoot: string +) { + const moduleFederationConfigPathJS = join( + workspaceRoot, + projectRoot, 'module-federation.config.js' ); - let moduleFederationConfig: any; + const moduleFederationConfigPathTS = join( + workspaceRoot, + projectRoot, + 'module-federation.config.ts' + ); + + let moduleFederationConfigPath = moduleFederationConfigPathJS; + + if (existsSync(moduleFederationConfigPathTS)) { + tsNodeRegister(moduleFederationConfigPathTS, tsconfigPath); + moduleFederationConfigPath = moduleFederationConfigPathTS; + } + try { - moduleFederationConfig = require(moduleFederationConfigPath); + const config = require(moduleFederationConfigPath); + return config.default || config; } catch { throw new Error( `Could not load ${moduleFederationConfigPath}. Was this project generated with "@nx/react:host"?\nSee: https://nx.dev/recipes/module-federation/faster-builds` ); } +} + +export default async function* moduleFederationDevServer( + options: ModuleFederationDevServerOptions, + context: ExecutorContext +): AsyncIterableIterator<{ success: boolean; baseUrl?: string }> { + const nxBin = require.resolve('nx'); + const currIter = devServerExecutor(options, context); + const p = context.projectsConfigurations.projects[context.projectName]; + const buildOptions = getBuildOptions(options.buildTarget, context); + + const moduleFederationConfig = getModuleFederationConfig( + buildOptions.tsConfig, + context.root, + p.root + ); const remotesToSkip = new Set( findMatchingProjects(options.skipRemotes, context.projectGraph.nodes) ?? [] @@ -88,7 +133,6 @@ export default async function* moduleFederationDevServer( )} with ${knownRemotes.length} remotes` ); - const nxBin = require.resolve('nx'); const devRemoteIters: AsyncIterable<{ success: boolean }>[] = []; let isCollectingStaticRemoteOutput = true; diff --git a/packages/webpack/package.json b/packages/webpack/package.json index ec9cb46f34fe8..c74d0397865e9 100644 --- a/packages/webpack/package.json +++ b/packages/webpack/package.json @@ -59,8 +59,6 @@ "stylus-loader": "^7.1.0", "terser-webpack-plugin": "^5.3.3", "ts-loader": "^9.3.1", - "ts-node": "10.9.1", - "tsconfig-paths": "^4.1.2", "tsconfig-paths-webpack-plugin": "4.0.0", "tslib": "^2.3.0", "webpack": "^5.80.0", diff --git a/packages/webpack/src/utils/webpack/custom-webpack.ts b/packages/webpack/src/utils/webpack/custom-webpack.ts index dc963026e246d..7e4d00bc39f5e 100644 --- a/packages/webpack/src/utils/webpack/custom-webpack.ts +++ b/packages/webpack/src/utils/webpack/custom-webpack.ts @@ -1,26 +1,4 @@ -export function tsNodeRegister(file: string = '', tsConfig?: string) { - if (!file?.endsWith('.ts')) return; - - // Avoid double-registering which can lead to issues type-checking already transformed files. - if (isRegistered()) return; - - // Register TS compiler lazily - require('ts-node').register({ - project: tsConfig, - compilerOptions: { - module: 'CommonJS', - types: ['node'], - }, - }); - - // Register paths in tsConfig - const tsconfigPaths = require('tsconfig-paths'); - const { absoluteBaseUrl: baseUrl, paths } = - tsconfigPaths.loadConfig(tsConfig); - if (baseUrl && paths) { - tsconfigPaths.register({ baseUrl, paths }); - } -} +import { tsNodeRegister } from '@nx/js/src/utils/typescript/tsnode-register'; export function resolveCustomWebpackConfig(path: string, tsConfig: string) { tsNodeRegister(path, tsConfig); @@ -33,6 +11,7 @@ export function resolveCustomWebpackConfig(path: string, tsConfig: string) { // `{ default: { ... } }` return customWebpackConfig.default || customWebpackConfig; } + export function isRegistered() { return ( require.extensions['.ts'] != undefined ||