Skip to content

Commit

Permalink
feat(react): add ability to serve remote apps in dev or static mode (#…
Browse files Browse the repository at this point in the history
  • Loading branch information
jaysoo authored Apr 20, 2022
1 parent 2e907af commit e32932d
Show file tree
Hide file tree
Showing 12 changed files with 119 additions and 68 deletions.
10 changes: 5 additions & 5 deletions docs/generated/packages/react.json
Original file line number Diff line number Diff line change
Expand Up @@ -1162,6 +1162,11 @@
"cli": "nx",
"type": "object",
"properties": {
"devRemotes": {
"type": "array",
"items": { "type": "string" },
"description": "List of remote applications to run in development mode (i.e. using serve target)."
},
"buildTarget": {
"type": "string",
"description": "Target which builds the application."
Expand Down Expand Up @@ -1228,11 +1233,6 @@
"baseHref": {
"type": "string",
"description": "Base url for the application being built."
},
"apps": {
"type": "array",
"items": { "type": "string" },
"description": "List of remote applications to serve in addition to the host application."
}
},
"presets": []
Expand Down
11 changes: 5 additions & 6 deletions docs/generated/packages/web.json
Original file line number Diff line number Diff line change
Expand Up @@ -876,12 +876,6 @@
"type": "string",
"description": "Target which builds the application."
},
"withDeps": {
"type": "boolean",
"description": "Build the target and all its deps",
"default": false,
"x-deprecated": "\"withDeps\" is deprecated and it will be removed in `v14`. Configure target dependencies instead: https://nx.dev/configuration/projectjson."
},
"parallel": {
"type": "boolean",
"description": "Build the target in parallel.",
Expand Down Expand Up @@ -924,6 +918,11 @@
"default": {},
"properties": { "secure": { "type": "boolean", "default": false } },
"additionalProperties": true
},
"watch": {
"type": "boolean",
"description": "Watch for file changes.",
"default": true
}
},
"additionalProperties": false,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ import devServerExecutor, {
import { join } from 'path';

type ModuleFederationDevServerOptions = WebDevServerOptions & {
apps?: string[];
devRemotes?: string | string[];
};

export default async function* moduleFederationDevServer(
Expand All @@ -31,25 +31,27 @@ export default async function* moduleFederationDevServer(
);
}

// Remotes can be specified with a custom location
// e.g.
// ```
// remotes: ['app1', 'http://example.com']
// ```
// This shouldn't happen for local dev, but we support it regardless.
let apps = options.apps ?? moduleFederationConfig.remotes ?? [];
apps = apps.map((a) => (Array.isArray(a) ? a[0] : a));
const knownRemotes = moduleFederationConfig.remotes ?? [];

for (const app of apps) {
const devServeApps = !options.devRemotes
? []
: Array.isArray(options.devRemotes)
? options.devRemotes
: [options.devRemotes];

for (const app of knownRemotes) {
const isDev = devServeApps.includes(app);
iter = combineAsyncIterators(
iter,
await runExecutor(
{
project: app,
target: 'serve',
target: isDev ? 'serve' : 'serve-static',
configuration: context.configurationName,
},
{},
{
watch: isDev,
},
context
)
);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,13 @@
"cli": "nx",
"type": "object",
"properties": {
"devRemotes": {
"type": "array",
"items": {
"type": "string"
},
"description": "List of remote applications to run in development mode (i.e. using serve target)."
},
"buildTarget": {
"type": "string",
"description": "Target which builds the application."
Expand Down Expand Up @@ -70,13 +77,6 @@
"baseHref": {
"type": "string",
"description": "Base url for the application being built."
},
"apps": {
"type": "array",
"items": {
"type": "string"
},
"description": "List of remote applications to serve in addition to the host application."
}
}
}
23 changes: 23 additions & 0 deletions packages/react/src/module-federation/load-config.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
import { ExecutorContext } from '@nrwl/devkit';
import { join } from 'path';
import { ModuleFederationConfig } from './models';

export function loadModuleFederationConfigFromContext(
context: ExecutorContext
): ModuleFederationConfig {
const p = context.workspace.projects[context.projectName];
const moduleFederationConfigPath = join(
context.root,
p.root,
'module-federation.config.js'
);

try {
return require(moduleFederationConfigPath) as ModuleFederationConfig;
} catch {
// TODO(jack): Add a link to guide
throw new Error(
`Could not load ${moduleFederationConfigPath}. Was this project generated with "@nrwl/react:host"?`
);
}
}
21 changes: 21 additions & 0 deletions packages/react/src/module-federation/models.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
export interface SharedLibraryConfig {
singleton: boolean;
strictVersion: boolean;
requiredVersion: string;
eager: boolean;
}

export type ModuleFederationLibrary = { type: string; name: string };

export type Remotes = string[] | [remoteName: string, remoteUrl: string][];

export interface ModuleFederationConfig {
name: string;
remotes?: string[];
library?: ModuleFederationLibrary;
exposes?: Record<string, string>;
shared?: (
libraryName: string,
library: SharedLibraryConfig
) => undefined | false | SharedLibraryConfig;
}
8 changes: 1 addition & 7 deletions packages/react/src/module-federation/webpack-utils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,13 +7,7 @@ import {
getRootTsConfigPath,
readTsConfig,
} from '@nrwl/workspace/src/utilities/typescript';

export interface SharedLibraryConfig {
singleton: boolean;
strictVersion: boolean;
requiredVersion: string;
eager: boolean;
}
import { SharedLibraryConfig } from './models';

export function shareWorkspaceLibraries(
libraries: string[],
Expand Down
22 changes: 2 additions & 20 deletions packages/react/src/module-federation/with-module-federation.ts
Original file line number Diff line number Diff line change
@@ -1,8 +1,4 @@
import {
SharedLibraryConfig,
sharePackages,
shareWorkspaceLibraries,
} from './webpack-utils';
import { sharePackages, shareWorkspaceLibraries } from './webpack-utils';
import {
createProjectGraphAsync,
ProjectGraph,
Expand All @@ -17,21 +13,7 @@ import {
import { ParsedCommandLine } from 'typescript';
import { readWorkspaceJson } from 'nx/src/project-graph/file-utils';
import ModuleFederationPlugin = require('webpack/lib/container/ModuleFederationPlugin');

export type ModuleFederationLibrary = { type: string; name: string };

export type Remotes = string[] | [remoteName: string, remoteUrl: string][];

export interface ModuleFederationConfig {
name: string;
remotes?: string[];
library?: ModuleFederationLibrary;
exposes?: Record<string, string>;
shared?: (
libraryName: string,
library: SharedLibraryConfig
) => undefined | false | SharedLibraryConfig;
}
import { ModuleFederationConfig, Remotes } from './models';

function recursivelyResolveWorkspaceDependents(
projectGraph: ProjectGraph<any>,
Expand Down
24 changes: 23 additions & 1 deletion packages/react/src/rules/update-module-federation-project.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,18 +8,40 @@ export function updateModuleFederationProject(
host: Tree,
options: { name: string; appProjectRoot: string; devServerPort?: number }
) {
let projectConfig = readProjectConfiguration(host, options.name);
const projectConfig = readProjectConfiguration(host, options.name);

projectConfig.targets.build.options = {
...projectConfig.targets.build.options,
main: `${options.appProjectRoot}/src/main.ts`,
webpackConfig: `${options.appProjectRoot}/webpack.config.js`,
};

projectConfig.targets.build.configurations.production = {
...projectConfig.targets.build.configurations.production,
webpackConfig: `${options.appProjectRoot}/webpack.config.prod.js`,
};

projectConfig.targets.serve.executor =
'@nrwl/react:module-federation-dev-server';
projectConfig.targets.serve.options.port = options.devServerPort;

// `serve-static` for remotes that don't need to be in development mode
projectConfig.targets['serve-static'] = {
executor: '@nrwl/web:file-server',
defaultConfiguration: 'development',
options: {
buildTarget: `${options.name}:build`,
port: options.devServerPort,
},
configurations: {
development: {
buildTarget: `${options.name}:build:development`,
},
production: {
buildTarget: `${options.name}:build:production`,
},
},
};

updateProjectConfiguration(host, options.name, projectConfig);
}
16 changes: 12 additions & 4 deletions packages/web/src/executors/file-server/file-server.impl.ts
Original file line number Diff line number Diff line change
Expand Up @@ -91,7 +91,10 @@ function getIgnoredGlobs(root: string) {
return ig;
}

function createFileWatcher(root: string, changeHandler: () => void) {
function createFileWatcher(
root: string,
changeHandler: () => void
): () => void {
const ignoredGlobs = getIgnoredGlobs(root);
const layout = workspaceLayout();

Expand All @@ -109,7 +112,7 @@ function createFileWatcher(root: string, changeHandler: () => void) {
if (ignoredGlobs.ignores(path)) return;
changeHandler();
});
return { close: () => watcher.close() };
return () => watcher.close();
}

export default async function* fileServerExecutor(
Expand All @@ -131,7 +134,10 @@ export default async function* fileServerExecutor(
}
};

const watcher = createFileWatcher(context.root, run);
let disposeWatch: () => void;
if (options.watch) {
disposeWatch = createFileWatcher(context.root, run);
}

// perform initial run
run();
Expand All @@ -148,7 +154,9 @@ export default async function* fileServerExecutor(
});
const processExitListener = () => {
serve.kill();
watcher.close();
if (disposeWatch) {
disposeWatch();
}
};
process.on('exit', processExitListener);
process.on('SIGTERM', processExitListener);
Expand Down
1 change: 1 addition & 0 deletions packages/web/src/executors/file-server/schema.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,4 +10,5 @@ export interface Schema {
maxParallel?: number;
withDeps: boolean;
proxyOptions?: object;
watch?: boolean;
}
11 changes: 5 additions & 6 deletions packages/web/src/executors/file-server/schema.json
Original file line number Diff line number Diff line change
Expand Up @@ -8,12 +8,6 @@
"type": "string",
"description": "Target which builds the application."
},
"withDeps": {
"type": "boolean",
"description": "Build the target and all its deps",
"default": false,
"x-deprecated": "\"withDeps\" is deprecated and it will be removed in `v14`. Configure target dependencies instead: https://nx.dev/configuration/projectjson."
},
"parallel": {
"type": "boolean",
"description": "Build the target in parallel.",
Expand Down Expand Up @@ -61,6 +55,11 @@
}
},
"additionalProperties": true
},
"watch": {
"type": "boolean",
"description": "Watch for file changes.",
"default": true
}
},
"additionalProperties": false,
Expand Down

1 comment on commit e32932d

@vercel
Copy link

@vercel vercel bot commented on e32932d Apr 20, 2022

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Successfully deployed to the following URLs:

nx-dev – ./

nx-dev-git-master-nrwl.vercel.app
nx-five.vercel.app
nx.dev
nx-dev-nrwl.vercel.app

Please sign in to comment.