nrwl / nx

Smart Monorepos · Fast CI
https://nx.dev
MIT License
23.28k stars 2.32k forks source link

Cannot add SSR in my Nx Angular monorepo with module federation #11732

Closed delairec closed 2 years ago

delairec commented 2 years ago

Current Behavior

I cannot start my SSR server in dev environment and I don't know how to solve errors that come up.

Expected Behavior

I want to be able to add SSR to my project and start dev server without error.

Steps to Reproduce

I can't push on public repos because of my company vpn, so I will detail the steps.

I tried to follow this tuto https://blog.nrwl.io/server-side-rendering-ssr-with-angular-for-nx-workspaces-14e2414ca532 But I need to adapt some things because I use module federation.

First, I generate a workspace with a host and a remote app. For this, I use these commands in a shell :

  1. npx create-nx-workspace nx-14-module-federation-with-ssr
  2. npm i @nrwl/angular
  3. nx g @nrwl/angular:host example-shell
  4. nx g @nrwl/angular:remote example-remote --host=example-shell --port=4201
  5. npm i @nguniversal/express-engine --legacy-peer-deps
  6. npm i @nguniversal/builders --save-dev

Here, I need to move bootstrap.ts code into main.ts code to avoid a Bootstrap module not found error in the next step.

  1. nx generate @schematics/angular:universal --project=example-shell

Then I move back the altered code in main.ts to bootstrap.ts file (in order to make the app work again).

Next, I update config files :

  1. Add "apps" in outputPath in project.json
  2. Add ssr.server.ts (copied from the tutorial with updated app name)
  3. Update tsconfig.server.ts like in the tutorial (add ssr.server.ts to files and angularCompilerOptions.entryModule prop)
  4. Update project.json to add serve-ssr target (again from tutorial).

This is were the tutorial ends. But to handle module federation, I need a custom webpack configuration (if not, I have module not found error because it can't resolve example-remote/Module.

  1. npm i --save-dev @angular-builders/custom-webpack
  2. Update project.json server target with @angular-builders/custom-webpack:server as executor and add customWebpackConfig.path in options
// webpack.config.js
const { withModuleFederation } = require('@nrwl/angular/module-federation');
const config = require('./module-federation.config');
module.exports = withModuleFederation(config);
  1. nx run example-shell:serve-ssr

This is were I'm stuck. I tried many things but in the end, even if I got the builds complete, then when I try to nx run example-shell:serve-ssr I have server errors mainly because of imports in the generated main.js file (it tries to import the remote).

Mainly, I played with these webpack options :

For this, I had to update webpack.config.js like this :

// updated webpack.config.js
const {withModuleFederation} = require('@nrwl/angular/module-federation');
const moduleFederationConfig = require('./module-federation.config');

module.exports = async (config/*, context*/) => {

  console.debug('\n[DEV MODE] Webpack development configuration\n');

  const federationModulesFn = await withModuleFederation(moduleFederationConfig);
  const federationModulesConfig = {...federationModulesFn(config)};

  return Object.assign({}, federationModulesConfig, {
    // custom options come here
    output: {
      ...federationModulesConfig.output,
      // output options come here
    }
  });
};

Failure Logs

With basic webpack.config.js, the build ends in error :

For the selected environment is no default ESM chunk format available:
ESM exports can be chosen when 'import()' is available.
JSONP Array push can be chosen when 'document' is available.
Select an appropriate 'target' to allow selecting one by default, or specify the 'output.chunkFormat' directly.

When builds complete and server try to start :

C:\projects\happynrwl\dist\apps\happynrwl-shell\server\main.js:1
import * as __WEBPACK_EXTERNAL_MODULE_http_localhost_4201_remoteEntry_mjs_ba8924b6__ from "http://localhost:4201/remoteEntry.mjs";
^^^^^^

SyntaxError: Cannot use import statement outside a module
    at Object.compileFunction (node:vm:352:18)
    at wrapSafe (node:internal/modules/cjs/loader:1033:15)
    at Module._compile (node:internal/modules/cjs/loader:1069:27)
    at Object.Module._extensions..js (node:internal/modules/cjs/loader:1159:10)
    at Module.load (node:internal/modules/cjs/loader:981:32)
    at Function.Module._load (node:internal/modules/cjs/loader:822:12)
    at Function.executeUserEntryPoint [as runMain] (node:internal/modules/run_main:77:12)
    at node:internal/main/run_main_module:17:47

A server error has occurred.
node exited with 1 code.
connect ECONNREFUSED 127.0.0.1:59895

When I try to add the type module in package.json :

require() of ES Module C:\projects\happynrwl\apps\happynrwl-shell\webpack.browser.config.js from C:\project
s\happynrwl\node_modules\@nrwl\angular\src\builders\utilities\webpack.js not supported.
webpack.browser.config.js is treated as an ES module file as it is a .js file whose nearest parent package.json contains "type": "module" which declares all .js files in that package scope as ES modules.
Instead rename webpack.browser.config.js to end in .cjs, change the requiring code to use dynamic import() which is available in all CommonJS modules, or change "type": "module" to "type": "commonjs" in C:\projects\happynr
wl\package.json to treat all .js files as CommonJS (using .mjs for all ES modules instead).

Environment

Node : 16.16.0
   OS   : win32 x64
   npm  : 8.3.1

   nx : 14.5.10
   @nrwl/angular : 14.5.10
   @nrwl/cypress : 14.5.10
   @nrwl/detox : Not Found
   @nrwl/devkit : 14.5.10
   @nrwl/eslint-plugin-nx : 14.5.10
   @nrwl/express : Not Found
   @nrwl/jest : 14.5.10
   @nrwl/js : 14.5.10
   @nrwl/linter : 14.5.10
   @nrwl/nest : Not Found
   @nrwl/next : Not Found
   @nrwl/node : Not Found
   @nrwl/nx-cloud : Not Found
   @nrwl/nx-plugin : Not Found
   @nrwl/react : Not Found
   @nrwl/react-native : Not Found
   @nrwl/schematics : Not Found
   @nrwl/storybook : 14.5.10
   @nrwl/web : 14.5.10
   @nrwl/workspace : 14.5.10
   typescript : 4.7.4
   ---------------------------------------
   Local workspace plugins:
   ---------------------------------------
   Community plugins:
         @nguniversal/express-engine: 14.1.0
         @nguniversal/builders: 14.1.0
delairec commented 2 years ago

I have to add that without module-federation (without custom webpack config) and using loadRemoteModule method, the server side rendering is working (but without remote apps as one can expect).

Has anyone had time to look into this ? Should I open an issue on webpack side ? Is there any known workaround ?

We are currently in a POC phase at my company and SSR is a must have for us. I really want to use Nx though. Any help will be strongly appreciated.

Coly010 commented 2 years ago

Related issue: https://github.com/nrwl/nx/issues/10887

We're aware SSR does not work with Module Federation and we're working with @ScriptedAlchemy to push Module Federation support to SSR via Node.

This is on our roadmap and we're actively working on it.

As this is a duplicate issue, I'm going to close this one to ensure that we keep all discussions in one place.

ScriptedAlchemy commented 2 years ago

Yep, I'm ready to publish module-Federation/node to npm.

Just waiting for the team to confirm it works for their use case.

Readme also needs serious love - but the tech should work. I lifted it out of my nextjs plugin which has been pretty stable for months in node Federation.

delairec commented 2 years ago

Good to hear, thank you !

github-actions[bot] commented 1 year ago

This issue has been closed for more than 30 days. If this issue is still occuring, please open a new issue with more recent context.