Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

HtmlWebpackPlugin inject html/variables/etc #22

Open
mstruensee opened this issue May 18, 2022 · 10 comments
Open

HtmlWebpackPlugin inject html/variables/etc #22

mstruensee opened this issue May 18, 2022 · 10 comments

Comments

@mstruensee
Copy link

mstruensee commented May 18, 2022

is there a way currently to inject something like ... html or templateParameters into the HtmlWebpackPlugin ...
my use case is ... the root-config is setting API_ENDPOINTS on the window for all other micro services to use, works great, they have access to it and everything ... problem is standalone development ... they can't get the endpoint as its not on the window

or even a feature to set window variables?

https://github.com/single-spa/create-single-spa/blob/main/packages/webpack-config-single-spa/lib/webpack-config-single-spa.js#L126

might have to change the design to pass it as a customProp as that is supported in the standalone-single-spa-webpack-plugin

    customProps: {
        authToken: "sadf7889fds8u70df9s8fsd"
      },

EDIT: attempted this, there is no way for me to send customProps from webpack.config inside the root-config ... can do that from a non root-config, as it uses the standalone-single-spa-webpack-plugin, but in root-config, that is disabled.

https://github.com/single-spa/standalone-single-spa-webpack-plugin#customizing-the-html-file
^^ can i use teplateParamters and put it in the index file and pass to standalone-single-spa-webpack-plugin?

@mstruensee mstruensee reopened this May 18, 2022
@filoxo filoxo transferred this issue from single-spa/create-single-spa May 18, 2022
@mstruensee
Copy link
Author

mstruensee commented May 18, 2022

so far this is a solution that works ... but hackkkkkky ...

in webpack.config in root-config, set window.endpoints inside html plugin ...
index.ejs

  <script> window.endpoints = <%= endpoints %> </script> 

webpack.config.js

new HtmlWebpackPlugin({
          inject: false,
          template: "src/index.ejs",
          templateParameters: {
            endpoints: JSON.stringify(endpoints),
          },
        }),

in webpack.config in app1, pass endpoints as customProps

webpack.config.js

    const defaultConfig = singleSpaDefaults({
      orgName,
      projectName,
      webpackConfigEnv,
      argv,
      standaloneOptions: {
        customProps: {
          endpoints
        }
      }
    });

grab endpoints inside of the mount lifecycle and set window.endpoints (if not set, as all front end services will be running this logic)

org-app1.tsx

export const mount = (props) => {
  if(!window.endpoints) {
    console.log("setting endpoints!", props.endpoints)
    window.endpoints = props.endpoints
  } else {
    console.log("endpoints already set")
  }
  return lifecycles.mount(props);
}

this works for standalone and for deployed to environment ... not ideal but works, as all apps can get host endpoints from window.endpoints

let me know if there is or can or will be an easier way :D

@mstruensee
Copy link
Author

mstruensee commented May 20, 2022

update: this has 1 flaw so far, when trying to use redux toolkit query, when trying to setup createApi, fetchBaseQuery seems to be compiled and generated at import time, so grabbing the baseUrl from window.endpoints throws an error as window.endpoints is undefined as it is not set until mount when doing development in standalone mode. I think the best solution would be to have it put on via HtmlWebpackPlugin in the webpack config, just like the root-config, this way it is there during start/load/import.

If HtmlWebpackPlugin options can be passed down just like standaloneOptions then when adding a src/index.ejs, then templateParameters should be able to be referenced and then swapped/replaced prior to standalone app loading. This would be a good approach as it is also how the root config does it.

@mstruensee
Copy link
Author

mstruensee commented May 20, 2022

update:
modifying webpack-config-single-spa and adding options to the HtmlWebpackPlugin, worked successfully.

const defaultConfig = singleSpaDefaults({
      orgName,
      projectName,
      webpackConfigEnv,
      argv,
      htmlWebpackPluginOptions: {
        templateParameters: {
          endpoints: JSON.stringify(endpoints),
        },
      }
    });

https://github.com/single-spa/create-single-spa/blob/main/packages/webpack-config-single-spa/lib/webpack-config-single-spa.js#L126
!isProduction && !opts.disableHtmlGeneration && new HtmlWebpackPlugin(opts.htmlWebpackPluginOptions? opts.htmlWebpackPluginOptions : null),

can there be a feature request for htmlWebpackPluginOptions?

@mstruensee
Copy link
Author

any update on this?

@mstruensee
Copy link
Author

mstruensee commented Jun 14, 2022

bump @filoxo

@mstruensee
Copy link
Author

mstruensee commented Jun 15, 2022

a temporary solution that seems to be working

const {merge} = require("webpack-merge")
const singleSpaDefaults = require("webpack-config-single-spa-react-ts")
const packageJson = require("./package.json")
const {fetchServiceEndpoints} = require("./util")
const HtmlWebpackPlugin = require("html-webpack-plugin")

const { groups: {orgName, projectName} } = /@(?<orgName>.*)\/(?<projectName>.*)/.exec(packageJson.name)

module.exports = fetchServiceEndpoints().then((endpoints) => {
  class ExtendHtmlWebpackPlugin extends HtmlWebpackPlugin {
    constructor() {
      super({
        templateParameters: {
          title: projectName,
          serviceEndpoints: JSON.stringify(endpoints),
        },
      })
    }
  }

  return (webpackConfigEnv, argv) => {
    const defaultConfig = singleSpaDefaults({
      orgName,
      projectName,
      webpackConfigEnv,
      argv,
      HtmlWebpackPlugin: ExtendHtmlWebpackPlugin,
    })

    return merge(defaultConfig, {
      output: {
        clean: true,
      },
      devServer: {
        server: "https",
      },
    })
  }
})

this now unlocks the capability to have a custom html file used for the generation of the standalone component, other stuff can be added, like pattern library classes, themes, etc. Currently all documentation only allows all this stuff to work via root-config, and not in standalone mode for individual components.

@filoxo
Copy link
Contributor

filoxo commented Jul 18, 2022

Sorry about the lack of response, I hadn't seen this ping.

I wonder if webpack-merge's mergeWithCustomize can be used to clean this up a little bit:

const { mergeWithCustomize, unique } = require('webpack-merge');
const singleSpaDefaults = require('webpack-config-single-spa');
const HtmlWebpackPlugin = require('html-webpack-plugin');

const merge = mergeWithCustomize({
  customizeArray: unique(
    'plugins',
    ['HtmlWebpackPlugin'],
    (plugin) => plugin.constructor && plugin.constructor.name
  ),
});

which I've used in an example repo that I maintain here: https://github.com/filoxo/single-spa-example-rxjs-shared-state/blob/main/packages/root-config/webpack.config.js#L5-L11 except that that example doesn't make use of standalone mode. Maybe that could work for this case still though?

I think your solution should be taken and made into an org-specific single-spa-webpack-config package to share across your applications. Its basically just taking our config and wrapping with your org's needs/opinions/rules.

@mstruensee
Copy link
Author

Thanks for the reply! I will give that approach a try ...
Yeah it is hard to be 100% standalone, in my case it is just the theme, so you can do the development without it, it just looks different :D

@stephenwil
Copy link

stephenwil commented Aug 2, 2022

I'm wanting to use standalone mode in a similar fashion, and I can't get it working with either mergeWithCustomize or instantiating an extended HtmlWebpackPlugin class.

I'd vote for going with the options param for HtmlWebpackPlugin, similar to standaloneOptions.

And/or the ability to expand to be not limited to 1 html file that gets generated with the various import-map etc

@filoxo
Copy link
Contributor

filoxo commented Aug 16, 2022

I disagree with expanding the configuration API... mostly because where does it end? The base single-spa-webpack-config includes other plugins too so if we allow passing through all options for one plugin, we'd probably have to allow all options for all plugins. This is why I prefer webpack-merge: you can extend the inner parts of the config without it having to create a top-level API for overrides.

Alternatively, using webpack-chain could provides a different way of extending plugin options that you might prefer.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

3 participants