[Bug] Webpack externals based config not exporting

This issue has been created since 2020-09-02.

We use webpack externals powered config file handling to hold stuff like API URLs etc. While testing react-static, I moved this logic into a plugin. Including here for clarity:

export default () => ({
  webpack: (config) => {
    let externalConfig = {
      // getAppConfig simply loads json file and returns object, aka config
      config: JSON.stringify(getAppConfig(currentEnv)),
    };
    if (config.externals != null && !Array.isArray(config.externals)) {
      config.externals = [config.externals, externalConfig];
    } else {
      config.externals = (config.externals ?? []).concat(externalConfig);
    }
    return config;
  },
});

Environment


System:
    OS: Windows 10 10.0.18362
    CPU: (12) x64 Intel(R) Core(TM) i7-8700K CPU @ 3.70GHz
    Memory: 5.46 GB / 15.95 GB
  Binaries:
    Node: 12.14.1 - C:\Program Files\nodejs\node.EXE
    Yarn: 1.22.4 - C:\Program Files (x86)\Yarn\bin\yarn.CMD
    npm: 6.13.4 - C:\Program Files\nodejs\npm.CMD
  Browsers:
    Chrome: 85.0.4183.83
    Edge: Spartan (44.18362.449.0)
    Internet Explorer: 11.0.18362.1
  npmPackages:
    react: ^16.9.0 => 16.13.1
    react-dom: ^16.9.0 => 16.13.1
    react-hot-loader: ^4.12.11 => 4.12.21
    react-static: ^7.2.0 => 7.4.2
    react-static-plugin-reach-router: ^7.2.0 => 7.4.2
    react-static-plugin-sitemap: ^7.2.0 => 7.4.2
    react-static-plugin-source-filesystem: ^7.2.0 => 7.4.2
    react-static-plugin-typescript: ^7.2.0 => 7.4.2
  npmGlobalPackages:
    react-native-cli: 2.0.1

Steps to Reproduce the problem

All works as expected when running the app using npm start (no errors or warnings, the app works and reads the config). However, npm export returns:

Cannot find module '{"marketingUrl":"https://guestbell.com", /*rest of the config file continues here*/}'      
  Require stack:
  - C:\Coding\GuestBell\delete-me\artifacts\static-app.js
  - C:\Coding\GuestBell\delete-me\node_modules\react-static\lib\static\exportRoutes.threaded.js

  - loader.js:793 Function.Module._resolveFilename
    internal/modules/cjs/loader.js:793:17

  - loader.js:686 Function.Module._load
    internal/modules/cjs/loader.js:686:27

  - loader.js:848 Module.call
    internal/modules/cjs/loader.js:848:19

  - binHelper.js:62 Module.require
    [delete-me]/[react-static]/src/utils/binHelper.js:62:26

  - helpers.js:74 require
    internal/modules/cjs/helpers.js:74:18

  - static-app.js:3 webpackUniversalModuleDefinition
    C:/Coding/GuestBell/delete-me/artifacts/static-app.js:3:28

  - static-app.js:10 Object.<anonymous>
    C:/Coding/GuestBell/delete-me/artifacts/static-app.js:10:3

  - loader.js:955 Module._compile
    internal/modules/cjs/loader.js:955:30

  - index.js:99 Module._compile
    [delete-me]/[pirates]/lib/index.js:99:24

  - loader.js:991 Module._extensions..js
    internal/modules/cjs/loader.js:991:10

  - index.js:104 Object.newLoader [as .js]
    [delete-me]/[pirates]/lib/index.js:104:7

  - loader.js:811 Module.load
    internal/modules/cjs/loader.js:811:32

  - loader.js:723 Function.Module._load
    internal/modules/cjs/loader.js:723:14

  - loader.js:848 Module.call
    internal/modules/cjs/loader.js:848:19

  - binHelper.js:62 Module.require
    [delete-me]/[react-static]/src/utils/binHelper.js:62:26

  - helpers.js:74 require
    internal/modules/cjs/helpers.js:74:18

  - exportRoutes.threaded.js:20 _callee2$
    [delete-me]/[react-static]/src/static/exportRoutes.threaded.js:20:18

  - runtime.js:63 tryCatch
    [delete-me]/[regenerator-runtime]/runtime.js:63:40

  - runtime.js:293 Generator.invoke [as _invoke]
    [delete-me]/[regenerator-runtime]/runtime.js:293:22

  - runtime.js:118 Generator.next
    [delete-me]/[regenerator-runtime]/runtime.js:118:21

  - runtime.js:63 tryCatch
    [delete-me]/[regenerator-runtime]/runtime.js:63:40

  - runtime.js:356 maybeInvokeDelegate
    [delete-me]/[regenerator-runtime]/runtime.js:356:18

  - runtime.js:267 Generator.invoke [as _invoke]
    [delete-me]/[regenerator-runtime]/runtime.js:267:32

  - runtime.js:118 Generator.next
    [delete-me]/[regenerator-runtime]/runtime.js:118:21

  - asyncToGenerator.js:3 asyncGeneratorStep
    [delete-me]/[@babel]/runtime/helpers/asyncToGenerator.js:3:24

  - asyncToGenerator.js:25 _next
    [delete-me]/[@babel]/runtime/helpers/asyncToGenerator.js:25:9

  - task_queues.js:94 processTicksAndRejections
    internal/process/task_queues.js:94:5


  Error: Trace: {
    code: 'MODULE_NOT_FOUND',
    requireStack: [
      'C:\\Coding\\GuestBell\\delete-me\\artifacts\\static-app.js',
      'C:\\Coding\\GuestBell\\delete-me\\node_modules\\react-static\\lib\\static\\exportRoutes.threaded.js'
    ]
  }
      at C:\Coding\GuestBell\delete-me\node_modules\react-static\bin\react-static:6:11
      at processTicksAndRejections (internal/process/task_queues.js:94:5)

MODULE_NOT_FOUND is obviously a typescript thing, yet I don't understand what could be causing it. Moreover, why it works with the dev server but doesn't when exporting.

When looking at C:\Coding\GuestBell\delete-me\artifacts\static-app.js, it literally contains stuff like require('{/serialized JSON config/}'). I don't think this is wrong, but since this is a JS file, I have no idea where the typescript error is suddenly coming from.

Repo

Clone this https://github.com/PeterKottas/react-static-webpack-externals

  1. install modules
  2. run start - notice it works
  3. run build - notice it fails

Expected Behavior

I don't think we are doing something too 'crazy' so this should just work.

For bonus points, if you can suggest a different way of working with front-end config (compatible with typescript e.g. typesafe) which works with react-static, I'd be more than happy to adopt it while the situation around this is resolved. Cheers!

PeterKottas wrote this answer on 2020-09-03

With regards to the front-end configuration "files", believe this could be handled in pure js based on process.env.NAME provided by webpack define plugin.

I generally prefer *.json config files as it's easier to transform via build steps but at least there's a relatively straight forward way. That being said, the bug, or whatever this is, stays.

SleeplessByte wrote this answer on 2020-09-03

MODULE_NOT_FOUND is not a TypeScript thing. It's both an EcmaScript and Webpack thing. In react-static it can occur because of two things:

  1. webpack can't find the module (referring to an incorrect path). It seems that the config object is being required. I think that's the real issue here. I think config is supposed to be a path to react-static.config.js, not an object.
  2. something somewhere goes wrong which causes some file (like the artifacts) not to be generated, which results in a module load failure (because the file wasn't generated).

What is it exactly that you're trying to do? Are you trying to have an external file with "env" values?

PeterKottas wrote this answer on 2020-09-03

Thanks for the feedback mate.

What is it exactly that you're trying to do? Are you trying to have an external file with "env" values?

I am not sure if I can explain better than what I've written in the first message. To simplify as best as possible:
I load an object from a json file (during build) and include it in the build via webpack externals. The attached repo should answer any and all questions.

webpack can't find the module (referring to an incorrect path). It seems that the config object is being required. I think that's the real issue here. I think config is supposed to be a path to react-static.config.js, not an object.

I believe this is not how externals work. Externals are meant to avoid the whole resolving thing a simply provide a value as stated in config. More info: https://webpack.js.org/configuration/externals/

where it says:

Prevent bundling of certain imported packages and instead retrieve these external dependencies at runtime.

something somewhere goes wrong which causes some file (like the artifacts) not to be generated, which results in a module load failure (because the file wasn't generated).

Even this seems unlikely because the files are generated and I can even see the config in it, included as a serialized json which is what I expect.

MODULE_NOT_FOUND is not a TypeScript thing. It's both an EcmaScript and Webpack thing.

Yep, you're totally right there! My bad

SleeplessByte wrote this answer on 2020-09-03

I believe this is not how externals work. Externals are meant to avoid the whole resolving thing a simply provide a value as stated in config.

That's not what it does, to my understanding, and I think I now know what's breaking.

Webpack changes require to be a "special" webpack require (so it can run loaders, remap names, and bundle things). .externals allows you to opt-out of that behaviour. You can use it to define mappings outside of the bundle system. So instead of trying to bundle jquery, it's going to say "jQuery" should be available at runtime, as this name/global.

However, you seem to load some arbitrary configuration object as externals. The only shape objects inside externals may have is described here.

You probably want to use siteData from react-static config to load your "external configuration". This can be async!.

PeterKottas wrote this answer on 2020-09-03

It certainly does more than that (while that doesn't mean that you are not right). Look at this for instance:

https://stackoverflow.com/questions/30568796/how-to-store-configuration-file-and-read-it-using-react/30602665#30602665

If you do something like:

config.externals = {
  test: `{'key':'value'}`
}

It definitely works with webpack and it's also automatically parsed. So you can do:

import test from 'test';
console.log(test.key);
// Prints 'value'

This issue is not about figuring out if this approach works, it's more about figuring out why it doesn't work in react-static.

Or to be more precise, why it doesn't work with the export command when it works with start.

As mentioned in the docs, it supports string as a value and that's exactly what this is.

I definitely don't want to use siteData because that data will then be available only with a hook. I need to access this data outside of react lifecycle by simply importing a module like with my previous example.

Hope this makes things a little clearer.

SleeplessByte wrote this answer on 2020-09-03

It certainly does more than that

That example is similar. It replaces a module (Config) with some static data (the json). I missed that you also call JSON.stringify(, which is correct. In my mind this "should" work.

Or to be more precise, why it doesn't work with the export command when it works with start.

I agree; we might be trying to require it or whatever; MAYBE #1383 is related, even though you don't use a monorepo. The only other thing I can give right now is that start does something different than export, mainly that the first is a dev build without prerendering, and the second one generates all the files for all the routes. We do some externals stuff here to inline any dependencies required.

I can point you to the cli files if you want to explore this yourself?

(again, I think you're right that this shouldn't fail, and the solution might be a single line change).

PeterKottas wrote this answer on 2020-09-03

Awesome! Glad we are progressing. It might not be widely used but we have a great experience with this "front-end config" solution. Moreover, the fact that this doesn't work might be a symptom of a deeper problem that can manifest somewhere along the line.

I started digging around, it first seemed like the stuff you linked to (the context function that you pass into externals) might be causing this. But then I tried replacing the externals altogether via a plugin:

export default () => ({
  webpack: (config) => {
    let externalConfig = {
      config: JSON.stringify(getAppConfig(currentEnv)),
    };
    config.externals = externalConfig;
    return config;
  },
});

Removing all the stuff that was there but the problem remains.

I can point you to the cli files if you want to explore this yourself?

Please do.

(again, I think you're right that this shouldn't fail, and the solution might be a single line change).

Agreed. Just finding that one line might be a pain. Especially for me since I don't have much experience with how the code is structured in react-static. I'd certainly welcome some help from people who wrote this :)

PeterKottas wrote this answer on 2020-09-04

Ok, I gave it a fair shot to no avail. Also encountered other issues with react-static with regards to importing images at build time (this appears to be abandoned and there's no real progress here) and a few others. As a result, we chose to go forward with next.js where most of these things work out of the box. Happy to leave this open but I won't be participating any more. Thanks for your assistance!

SleeplessByte wrote this answer on 2020-09-04

Understood; thanks for trying anyway :)

PeterKottas wrote this answer on 2020-09-04

Np, if anybody comes here looking for answers, I think this can be achieved using webpack Define plugin. It might need a little bit of work, especially when using with typescript. But it should be doable! Glhf.

More Details About Repo
Owner Name react-static
Repo Name react-static
Full Name react-static/react-static
Language JavaScript
Created Date 2017-09-09
Updated Date 2022-09-28
Star Count 10148
Watcher Count 133
Fork Count 811
Issue Count 5

YOU MAY BE INTERESTED

Issue Title Created Date Comment Count Updated Date
Email with AWS SES throws "Invalid Mail From address" error 1 2020-10-22 2022-09-18
One Player Crashes upon interacting with the voice chat 2 2022-08-19 2022-09-19
Airdrop Option Crashes Iridium 12 2022-01-21 2022-09-15
zstd: command not found 1 2021-03-16 2022-08-10
from-to date and time filters 14 2016-08-03 2022-08-26
请问如何关闭显示无意义的英文字符串? 2 2021-08-04 2022-01-11
小狼毫手动安装easy-en无提示 3 2021-07-02 2022-01-11
UNITY_PASSWORD is readable in the log 2 2022-06-17 2022-09-09
TabPy: Deploying endpoints in heroku 4 2021-02-18 2022-09-25
在Docker中执行时,出现这情况,如何修复?? 1 2021-08-26 2022-07-26
Handle sharing of options and arguments between subcommands 2 2019-02-11 2022-09-28
[pt_BR] Translator access request 3 2021-04-07 2022-08-13
[nb_NO] Translator access request 3 2021-04-08 2022-08-12
[zh_CN] Translator access request 3 2021-04-06 2022-08-13
TypeORM and Parcel 2 5 2022-02-22 2022-09-26
Keras2onnx support dynamic input? 2 2021-01-21 2022-09-03
The prompt version is too old. Obviously, mine is the latest version in February 1 2022-06-23 2022-09-12
Question: Is there a built in way to deal with different screen sizes? 3 2021-12-21 2022-08-01
Support Scala Native and Scala JS 0 2022-04-03 2022-09-10
NetworkPolicy PodSelector needs array instead of empty 2 2021-07-15 2022-09-28
Error: Cannot run with sound null safety 0 2022-01-21 2022-09-15
Add ability to disable haveibeenpwned 1 2021-10-04 2022-07-15
Kafkasource is not responding for every events received ,had to recreate for each trigger 0 2022-07-04 2022-09-22
Allow for multiple JSON providers 3 2022-01-25 2022-09-06
Question : What should I use 2 2021-09-25 2022-09-09
Magnetic Bed Identification 2 2020-05-04 2022-09-17
Add informations about multi-tenant scenario 1 2022-05-12 2022-08-27
Upload release signatures to the downloads page 3 2021-06-15 2022-09-28
CSP Issue with new Function("return this") 1 2022-09-01 2022-09-26
[BUG] Compilation error 3 2021-08-28 2022-09-28
[question] About the gamma distribution noise used in the vector mechanism 2 2021-10-02 2022-09-28
Job Service: Async Task Execution, Time Execution, and handling of Job Failures 15 2022-01-18 2022-07-15
Confusing use of Task Name: BPMN vs endpoints 4 2022-01-18 2022-02-02
[Feature] vertex或edge查询时允许部分过滤属性没有建立索引的查询 4 2021-06-03 2022-09-28
GPT2 training example 1 2022-06-08 2022-09-17
[mmwatch] TypeError: cannot use a string pattern on a bytes-like object 0 2020-07-17 2022-09-27
Provide query results when cache is restored 11 2019-02-20 2022-09-09
Strange nested exception during wrong call of load_state_dict 7 2021-10-29 2022-09-18
aws_kms: You can create only one replica of each primary key in each AWS Region. 4 2022-07-18 2022-08-29
Could Kucoin 42900 be applied to `retrier`? 1 2022-03-23 2022-07-26
Remove deprecated DataPrepperPlugin type property 0 2022-08-11 2022-09-22
[BUG] Trace event processor does not work 3 2022-08-10 2022-09-02
请问paddlex gui中训练模型成功后对指定文件夹的图片进行预测的步骤是使用文件夹中的python文件实现的呢? 1 2022-01-28 2022-09-16
MMdnn 0.3.1 numpy and mxnet required version? 0 2021-04-19 2022-09-29
Updating Cargo.toml file doesn't change the lint 0 2022-03-07 2022-08-03
Support Python 3.10 0 2021-10-06 2022-01-16
Allow to select default IAM role for Redshift cluster 2 2022-01-28 2022-09-10
Page's width is not right on mobile screen with long words 2 2022-07-25 2022-09-28
Fix typos in code and documentation 0 2022-07-26 2022-09-08
Uploading firmware with DFU fails to start occasionally. 1 2021-02-11 2022-09-29