webpacksingle-spa

How to avoid external setting of some projects under same scope with webpack (and single-spa)


The question:

How (is it possible) to configure webpack (with single-spa), to take make some packages (projects) under a scope (@orginazation) externals and other internals (not external) ?

Project information:

We are building a single-spa application using webpack an uses Github npm registrer (and github actions). All the micro front-ends are projects in Github, and some of them are util/helper/provider projects, that is compiled as externals with webpack.

But some of them are meant to be compiled with the micro-frontend (must not be external)

It seems that when webpack sees one of the project under the organization scope as external, it set all package under that scope to external!

As we can see from the log the "CompilingTool" will also be compiled as external, event thought it should not.

The packages are set in the files as:

import auth from "@ORGANIZATION/auth"  // <- Should be external
import compilingTool from "@ORGANIZATION/compilingTool" // <- Should NOT be external

It is possible to change the external within the single-spa configuration to something else like:

import compilingTool from "ORGANIZATION-Internal/compilingTool" // Not optimal!!

But this means that intelligence don't work anymore because its now not pointing at the actual package within the micro-frontend (which still is "@ORGANIZATION/compilingTool")

It would not be optimal the have to begin using prebuild tool like gulp, to make such a transformation. And we don't like to have to use different Github account to have different scopes.

So any help or ideas to make webpack understand that it should NOT make "@ORGANIZATION/compilingTool" package external is mush appriciated.

Already tried:

From the webpack documentation of externals it should be possible to use a validation function and/or subtract but both dont works for the scoped packages with the spa-setup.

Compile/dependency info:

The single-spa/webpack compiles to SystemJs

Webpack and single-spa packages:

webpack.config.js (Project specific has been replaced with dummy code!)

const { merge }  = require("webpack-merge");
const singleSpaDefaults = require("webpack-config-single-spa-react-ts");
const { readFileSync } = require('fs')
const path = require('path')
const dotenv = require('dotenv').config( {
  path: path.join(__dirname, '.env')
} );

module.exports = (webpackConfigEnv, argv) => {
  const defaultConfig = singleSpaDefaults({
    orgName: "ORGANIZATION",
    projectName: "SOME-MICRO-FRONTEND",
    webpackConfigEnv,
    argv,
  });

  return merge(defaultConfig, {
    // modify the webpack config however you'd like to by adding to this object
    externals: [
      "react", 
      "react-dom",
        "@ORGANIZATION/auth",  // <- This should be external
        //"@ORGANIZATION/compilingTool" // <- This should NOT be external
      ],
    devServer: {
      port: 5400,
      https: {
        key: readFileSync( path.resolve(__dirname, path.join(process.env.CERT_PATH, process.env.CERT_KEY_FILE_NAME))),
        cert: readFileSync(path.resolve(__dirname, path.join(process.env.CERT_PATH, process.env.CERT_FILE_NAME)))
      }
    },
  });
};

Run time console output from webpack build: (Project specific has been replaced with dummy code!)

webpack serve
<i> [webpack-dev-server] Project is running at:
<i> [webpack-dev-server] Loopback: https://localhost:XXXX/
<i> [webpack-dev-server] On Your Network (IPv4): https://XX.X.XXX.XXX:XXXX/
<i> [webpack-dev-server] Content not from webpack is served from 'C:\path-to-PROJECT\public' directory
<i> [webpack-dev-server] 404s will fallback to '/index.html'
asset PROJECT-NAME.js 436 KiB [emitted] (name: main) 1 related asset
asset index.html 3.1 KiB [emitted]
runtime modules 26.3 KiB 14 modules
modules by path ./node_modules/ 329 KiB 58 modules
modules by path external "@ORGANIZATION/ 210 bytes
  external "@ORGANIZATION/auth" 42 bytes [built] [code generated]
  external "@ORGANIZATION/compilingTool" 42 bytes [built] [code generated]
modules by path (...OTHER LOGS... ) 
external "react" 42 bytes [built] [code generated]
external "react-dom" 42 bytes [built] [code generated]
webpack 5.48.0 compiled successfully in 17732 ms
No issues found.

Solution

  • This is possible by passing in orgPackagesAsExternal: false to the single-spa webpack config, in your case named singleSpaDefaults. Per the create-single-spa documentation,

    This changes whether package names that start with @your-org-name are treated as webpack externals or not. Defaults to true.

    You will then need to either:

    and then merge that with single-spa's config as usual. Because this can become verbose and annoying to do for every microfrontend, consider doing this in a base config that you publish that is specific for your company and which each mfe will then need to consume.