react-nativescript: HMR broken (issue is actually with Webpack configuration, not RNS itself)

Why is HMR broken?

HMR has been broken since the migration to NativeScript 7 because @nativescript/webpack depends on copy-webpack-plugin@~6.0.2, which – to the understanding of @rigor789 – has broken HMR most likely due to this outstanding issue.

The last known working version was copy-webpack-plugin@4.6.0. It’s possible that the latest major in-between version, 5.1.1, or one of the 6.x.x series may lack this bug, but I don’t have time to dig into it. At the very least, I know how to correctly invoke the plugin for version 4.6.0. So it’s clear that we can roll back to that version.

How can we roll back to copy-webpack-plugin@4.6.0?

Annoyingly, we cannot achieve this by a change to the RNS-owned webpack.config.js; the moment we evaluate const baseConfig = webpackConfig(env); it evaluates the array of plugins, and unfortunately, that API has changed so much since version 4.6.0 that the build throws an error on that line.

So instead, our easiest option is to patch webpack.typescript.js. This is incovenient because it’s not technically a file owned by the RNS project; it’s really supposed to remain common to all NativeScript flavours, unchanged by users and library maintainers, and not kept under version control (if I recall, it’s generated by a hook). Hence why I’m hesitant about patching it in the RNS template, as they might get blown away anyway.

Regardless, here’s how to do it, and it’s how I’ve set up sample in this repo on master.

Steps to patch webpack.typescript.js

  1. Install the last-known working version of CopyWebpackPlugin:
npm install -D "copy-webpack-plugin@4.6.0"
  1. Replace the following in the NativeScript Core common webpack config, webpack.typescript.ts:
      new CopyWebpackPlugin({
        patterns: [
          { from: 'assets/**', noErrorOnMissing: true, globOptions: { dot: false, ...copyIgnore } },
          { from: 'fonts/**', noErrorOnMissing: true, globOptions: { dot: false, ...copyIgnore } },
          { from: '**/*.jpg', noErrorOnMissing: true, globOptions: { dot: false, ...copyIgnore } },
          { from: '**/*.png', noErrorOnMissing: true, globOptions: { dot: false, ...copyIgnore } },
        ],
      }),

… with the necessary construction arguments for this older version of CopyWebpackPlugin (v6 takes an object instead of an array):

      new CopyWebpackPlugin([
        { from: { glob: 'assets/**', dot: false } },
        { from: { glob: 'fonts/**', dot: false } },
        { from: { glob: '**/*.jpg', dot: false } },
        { from: { glob: '**/*.png', dot: false } },
      ], copyIgnore),

See CopyWebpackPlugin’s Changelog for version differences.

Hopefully they fix whatever issue is causing the regression, and we can eventually use the latest CopyWebpackPlugin without obstacle. It’s required for adopting Webpack 5, for example.

About this issue

  • Original URL
  • State: closed
  • Created 4 years ago
  • Reactions: 4
  • Comments: 20 (2 by maintainers)

Most upvoted comments

So with a little help from your suggestion @ChrisSchneiider, I basically add the following code which is the function in that file. 😃 Man, you rock…

global.__onLiveSyncCore = function () {
  var frame = require('@nativescript/core').Frame.topmost()
  if (frame) {
    if (frame.currentPage && frame.currentPage.modal) {
      frame.currentPage.modal.closeModal()
    }

    if (frame.currentPage) {
      frame.currentPage.addCssFile(
        // Causing crash for HMR changes with the CopyWebpackPlugin bug
        // See: https://github.com/shirakaba/react-nativescript/issues/65
        // require('@nativescript/core').getCssFileName()
      )
    }
  }
}

@mykone for NativeScript Vue, the corresponding section is instead found in webpack.config.js:

            new CopyWebpackPlugin({
              patterns: [
                { from: 'assets/**', noErrorOnMissing: true, globOptions: { dot: false, ...copyIgnore } },
                { from: 'fonts/**', noErrorOnMissing: true, globOptions: { dot: false, ...copyIgnore } },
                { from: '**/*.+(jpg|png)', noErrorOnMissing: true, globOptions: { dot: false, ...copyIgnore } }
              ],
            }),

So again, just replace it with this:

      new CopyWebpackPlugin([
        { from: { glob: 'assets/**', dot: false } },
        { from: { glob: 'fonts/**', dot: false } },
        { from: { glob: '**/*.jpg', dot: false } },
        { from: { glob: '**/*.png', dot: false } },
      ], copyIgnore),

… and run:

npm install -D "copy-webpack-plugin@4.6.0"

It’s Working thank you …

Thanks for the suggestion @ChrisSchneiider, but it’s not an ideal solution. For developing locally and testing, probably yes. But if I put my app through a CI/CD it’s a clean build directory and when npm install get run it’s the new files. Maybe, I can override that function somehow.

I also came across this problem. I spent the whole day hunting for a solution and that was the only one that worked! Good job.

@shirakaba what version of the {NS} Core did you try your regression on? After I run the tns debug android command, the app runs fine. But when I make a changes in the <style/> block of the App.vue, I get an exception. What stands out is this line:

System.err: TypeError: webpack_require(…).getCssFileName is not a function

Console Out with error

File change detected. Starting incremental webpack compilation...
Hash: 94eef7d0fc58d950bc02
Version: webpack 4.44.2
Time: 156ms
Built at: 11/01/2020 8:45:09 AM
                                    Asset      Size   Chunks                               Chunk Names
     9b7c51df04d0bbf0fb8d.hot-update.json  48 bytes           [emitted] [immutable] [hmr]  
bundle.9b7c51df04d0bbf0fb8d.hot-update.js  8.19 KiB   bundle  [emitted] [immutable] [hmr]  bundle
                                bundle.js   356 KiB   bundle  [emitted]                    bundle
                               runtime.js  77.1 KiB  runtime  [emitted]                    runtime
 + 1 hidden asset
Entrypoint bundle = runtime.js vendor.js bundle.js bundle.9b7c51df04d0bbf0fb8d.hot-update.js
[./ sync ^\.\/app\.(css|scss|less|sass)$] . sync nonrecursive ^\.\/app\.(css|scss|less|sass)$ 175 bytes {bundle} [built]
[./ sync recursive (?<!\bApp_Resources\b.*)(?<!\.\/\btests\b\/.*?)\.(xml|css|js|(?<!\.d\.)ts|(?<!\b_[\w-]*\.)scss)$] . sync (?<!\bApp_Resources\b.*)(?<!\.\/\btests\b\/.*?)\.(xml|css|js|(?<!\.d\.)ts|(?<!\b_[\w-]*\.)scss)$ 204 bytes {bundle} [built]
    + 353 hidden modules
Webpack compilation complete. Watching for file changes.
Webpack build done!
Successfully transferred bundle.9b7c51df04d0bbf0fb8d.hot-update.js on device emulator-5554.
Successfully transferred 9b7c51df04d0bbf0fb8d.hot-update.json on device emulator-5554.
JS: HMR: Checking for updates to the bundle with hmr hash 9b7c51df04d0bbf0fb8d.
Refreshing application on device emulator-5554...
JS: HMR: The following modules were updated:
JS: HMR:          ↻ ../node_modules/@nativescript/webpack/helpers/style-hot-loader.js!../node_modules/@nativescript/webpack/helpers/apply-css-loader.js!../node_modules/css-loader/dist/cjs.js?!../node_modules/vue-loader/lib/loaders/stylePostLoader.js!../node_modules/vue-loader/lib/index.js?!./components/App.vue?vue&type=style&index=0&id=45ba5ed4&scoped=true&lang=css&
JS: HMR:          ↻ ../node_modules/babel-loader/lib/index.js!../node_modules/vue-loader/lib/index.js?!./components/App.vue?vue&type=script&lang=js&
JS: HMR:          ↻ ./components/App.vue?vue&type=script&lang=js&
JS: HMR:          ↻ ./components/App.vue
JS: HMR: Successfully applied update with hmr hash 9b7c51df04d0bbf0fb8d. App is up to date.
System.err: An uncaught Exception occurred on "main" thread.
System.err: Calling js method run failed
System.err: TypeError: __webpack_require__(...).getCssFileName is not a function
System.err: 
System.err: StackTrace:
System.err: push.../node_modules/nativescript-vue/dist/index.js.global.__onLiveSyncCore(file: node_modules/nativescript-vue/dist/index.js:12810:38)
System.err:     at livesync(file: src/packages/core/application/application-common.ts:79:2)
System.err:     at __onLiveSync(file: src/packages/core/application/index.android.ts:264:11)
System.err:     at (file:///data/data/org.nativescript.application/files/app/bundle.js:301:28)
System.err:     at invoke(file: src/packages/core/timer/index.android.ts:23:22)
System.err:     at run(file: src/packages/core/timer/index.android.ts:28:3)
System.err:     at com.tns.Runtime.callJSMethodNative(Native Method)
System.err:     at com.tns.Runtime.dispatchCallJSMethodNative(Runtime.java:1302)
System.err:     at com.tns.Runtime.callJSMethodImpl(Runtime.java:1188)
System.err:     at com.tns.Runtime.callJSMethod(Runtime.java:1175)
System.err:     at com.tns.Runtime.callJSMethod(Runtime.java:1153)
System.err:     at com.tns.Runtime.callJSMethod(Runtime.java:1149)
System.err:     at com.tns.gen.java.lang.Runnable.run(Runnable.java:17)
System.err:     at android.os.Handler.handleCallback(Handler.java:873)
System.err:     at android.os.Handler.dispatchMessage(Handler.java:99)
System.err:     at android.os.Looper.loop(Looper.java:193)
System.err:     at android.app.ActivityThread.main(ActivityThread.java:6669)
System.err:     at java.lang.reflect.Method.invoke(Native Method)
System.err:     at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:493)
System.err:     at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:858)
Successfully transferred bundle.9b7c51df04d0bbf0fb8d.hot-update.js on device emulator-5554.
Successfully transferred 9b7c51df04d0bbf0fb8d.hot-update.json on device emulator-5554.
Restarting application on device emulator-5554...
JS: HMR: Hot Module Replacement Enabled. Waiting for signal.
device: emulator-5554 debug port: 40000

To start debugging, open the following URL in Chrome:
devtools://devtools/bundled/inspector.html?ws=localhost:40000

JS: NativeScript-Vue has "Vue.config.silent" set to true, to see output logs set it to false.
Successfully synced application org.nativescript.application on device emulator-5554.
JS: HMR: Checking for updates to the bundle with hmr hash f33a17de87652374589a.
JS: HMR: The following modules were updated:
JS: HMR:          ↻ ../node_modules/@nativescript/webpack/helpers/style-hot-loader.js!../node_modules/@nativescript/webpack/helpers/apply-css-loader.js!../node_modules/css-loader/dist/cjs.js?!../node_modules/vue-loader/lib/loaders/stylePostLoader.js!../node_modules/vue-loader/lib/index.js?!./components/App.vue?vue&type=style&index=0&id=45ba5ed4&scoped=true&lang=css&
JS: HMR:          ↻ ../node_modules/vue-loader/lib/loaders/templateLoader.js?!../node_modules/vue-loader/lib/index.js?!./components/App.vue?vue&type=template&id=45ba5ed4&scoped=true&
JS: HMR:          ↻ ./components/App.vue?vue&type=template&id=45ba5ed4&scoped=true&
JS: HMR: The following modules were updated:
JS: HMR:          ↻ ../node_modules/@nativescript/webpack/helpers/style-hot-loader.js!../node_modules/@nativescript/webpack/helpers/apply-css-loader.js!../node_modules/css-loader/dist/cjs.js?!../node_modules/vue-loader/lib/loaders/stylePostLoader.js!../node_modules/vue-loader/lib/index.js?!./components/App.vue?vue&type=style&index=0&id=45ba5ed4&scoped=true&lang=css&
JS: HMR:          ↻ ../node_modules/vue-loader/lib/loaders/templateLoader.js?!../node_modules/vue-loader/lib/index.js?!./components/App.vue?vue&type=template&id=45ba5ed4&scoped=true&
JS: HMR:          ↻ ./components/App.vue?vue&type=template&id=45ba5ed4&scoped=true&
JS: HMR: The following modules were updated:
JS: HMR:          ↻ ../node_modules/@nativescript/webpack/helpers/style-hot-loader.js!../node_modules/@nativescript/webpack/helpers/apply-css-loader.js!../node_modules/css-loader/dist/cjs.js?!../node_modules/vue-loader/lib/loaders/stylePostLoader.js!../node_modules/vue-loader/lib/index.js?!./components/App.vue?vue&type=style&index=0&id=45ba5ed4&scoped=true&lang=css&
JS: HMR:          ↻ ../node_modules/babel-loader/lib/index.js!../node_modules/vue-loader/lib/index.js?!./components/App.vue?vue&type=script&lang=js&
JS: HMR:          ↻ ./components/App.vue?vue&type=script&lang=js&
JS: HMR:          ↻ ./components/App.vue
JS: HMR: Successfully applied update with hmr hash f33a17de87652374589a. App is up to date.
System.err: An uncaught Exception occurred on "main" thread.
System.err: Calling js method run failed
System.err: TypeError: __webpack_require__(...).getCssFileName is not a function
System.err: 
System.err: StackTrace:
System.err: push.../node_modules/nativescript-vue/dist/index.js.global.__onLiveSyncCore(file: node_modules/nativescript-vue/dist/index.js:12810:38)
System.err:     at livesync(file: src/packages/core/application/application-common.ts:79:2)
System.err:     at __onLiveSync(file: src/packages/core/application/index.android.ts:264:11)
System.err:     at (file:///data/data/org.nativescript.application/files/app/bundle.js:301:28)
System.err:     at invoke(file: src/packages/core/timer/index.android.ts:23:22)
System.err:     at run(file: src/packages/core/timer/index.android.ts:28:3)
System.err:     at com.tns.Runtime.callJSMethodNative(Native Method)
System.err:     at com.tns.Runtime.dispatchCallJSMethodNative(Runtime.java:1302)
System.err:     at com.tns.Runtime.callJSMethodImpl(Runtime.java:1188)
System.err:     at com.tns.Runtime.callJSMethod(Runtime.java:1175)
System.err:     at com.tns.Runtime.callJSMethod(Runtime.java:1153)
System.err:     at com.tns.Runtime.callJSMethod(Runtime.java:1149)
System.err:     at com.tns.gen.java.lang.Runnable.run(Runnable.java:17)
System.err:     at android.os.Handler.handleCallback(Handler.java:873)
System.err:     at android.os.Handler.dispatchMessage(Handler.java:99)
System.err:     at android.os.Looper.loop(Looper.java:193)
System.err:     at android.app.ActivityThread.main(ActivityThread.java:6669)
System.err:     at java.lang.reflect.Method.invoke(Native Method)
System.err:     at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:493)
System.err:     at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:858)