rixo/svelte-loader-hot

Fails with webpack

Closed this issue · 13 comments

With webpack (following the template), attempt to update even the simplest component fails with Failed to init component. More specifically: TypeError: "cmp.$capture_state is not a function".
This happens regardless of the value of optimistic.

rixo commented

$capture_state was introduced in Svelte 3.19.1 or so. Since then, Svelte HMR requires at least this version of Svelte, and dev mode enabled to work.

I have a Webpack HMR example somewhere, let me check if it is up to date.

rixo commented

Yes, it still works. See for yourself: https://github.com/rixo/svelte-template-webpack-hot

You're absolutely right. Mind boggles, I'm on latest version of every package and yet it doesn't seem to like me. No matter, I'll start removing things from webpack.config.js until I find the culprit.

Closing.

rixo commented

Sorry, I didn't mean to scare you off... Double check the dev option of the Svelte loader, it is required for HMR (and would produce the $capture_state error you've had if off).

I have this issue too. I'll investigate and find out what can be done.

rixo commented

@rendall Can you share you Webpack config?

I can and will.

I noticed something not clear and came back to ask about it.

In https://github.com/rixo/svelte-template-webpack-hot webpack config the svelte-loader-hot rule options has dev on line 31 (as below), which seems to be just a boolean that's true in development mode.

I don't see in svelte-loader-hot documentation for that? Am I missing something?

const mode = process.env.NODE_ENV || 'development'
const prod = mode === 'production'
const dev = !prod

...

{
	test: /\.svelte$/,
	use: {
		loader: 'svelte-loader-hot',
		options: {
			dev, // <-- what does this do?
			hotReload: true,
			hotOptions: {
				// whether to preserve local state (i.e. any `let` variable) or
				// only public props (i.e. `export let ...`)
				noPreserveState: false,
				// optimistic will try to recover from runtime errors happening
				// during component init. This goes funky when your components are
				// not pure enough.
				optimistic: true,

				// See docs of svelte-loader-hot for all available options:
				//
				// https://github.com/rixo/svelte-loader-hot#usage
			},
		},
	},
}

Here's a link to the webpack.config.js that errors out

Here's the specfic ruleset I'm using

{
	test: /\.svelte$/,
	use: {
		loader: "svelte-loader-hot",
		options: {
			preprocess,
			// NOTE emitCss: true is currently not supported with HMR
			emitCss: isProductionMode,
			hotReload: !isProductionMode,
			hotOptions: {
				noPreserveState: false,
				noPreserveStateKey: "@!hmr",
				noReload: false,
				optimistic: false,
				acceptAccessors: true,
				acceptNamedExports: true,
			},
		},
	},
}

Where preprocess is:

const sveltePreprocess = require("svelte-preprocess")
const ExtractCssChunks = require("extract-css-chunks-webpack-plugin")
const preprocess = sveltePreprocess({ typescript: true })

I should point out, if it's helpful, that hot loading works just perfectly with SASS => CSS => server pipeline 💯

The template uses this setup for module options:

hotOptions: {
              noPreserveState: false,
              optimistic: true,
            },

I tried using those options alone and still no bueno

The overlay error is:

Failed to init component
<App>
TypeError: cmp.$capture_state is not a function
    at captureState (http://localhost:8080/index.js:10486:21)
    at App.targetCmp.$replace (http://localhost:8080/index.js:10615:23)
    at refreshComponent (http://localhost:8080/index.js:10238:21)
    at ProxyAdapterDom.rerender (http://localhost:8080/index.js:10079:5)
    at http://localhost:8080/index.js:10433:9
    at Array.forEach (<anonymous>)
    at Object.reload (http://localhost:8080/index.js:10431:15)
    at http://localhost:8080/index.js:9819:31
    at next (http://localhost:8080/index.js:10770:11)
    at runAcceptHandlers (http://localhost:8080/index.js:10775:9)

From the console it's:

proxy.js:14 [HMR][Svelte] Error during component init: <App>
logError @ proxy.js:14
refreshComponent @ proxy.js:140
rerender @ proxy-adapter-dom.js:62
(anonymous) @ proxy.js:318
reload @ proxy.js:316
(anonymous) @ hot-api.js:155
next @ hot-api.js:52
runAcceptHandlers @ hot-api.js:57
check @ hot-api.js:84
hotSetStatus @ bootstrap:241
hotApplyInternal @ bootstrap:748
hotApply @ bootstrap:361
(anonymous) @ bootstrap:336
Promise.then (async)
hotUpdateDownloaded @ bootstrap:335
hotAddUpdateChunk @ bootstrap:311
webpackHotUpdateCallback @ bootstrap:7
(anonymous) @ index.6f0ce478032676e350e8.hot-update.js:1
proxy.js:18 TypeError: cmp.$capture_state is not a function
    at captureState (svelte-hooks.js:25)
    at App.targetCmp.$replace (svelte-hooks.js:154)
    at refreshComponent (proxy.js:123)
    at ProxyAdapterDom.rerender (proxy-adapter-dom.js:62)
    at proxy.js:318
    at Array.forEach (<anonymous>)
    at Object.reload (proxy.js:316)
    at hot-api.js:155
    at next (hot-api.js:52)
    at runAcceptHandlers (hot-api.js:57)
logError @ proxy.js:18
refreshComponent @ proxy.js:140
rerender @ proxy-adapter-dom.js:62
(anonymous) @ proxy.js:318
reload @ proxy.js:316
(anonymous) @ hot-api.js:155
next @ hot-api.js:52
runAcceptHandlers @ hot-api.js:57
check @ hot-api.js:84
hotSetStatus @ bootstrap:241
hotApplyInternal @ bootstrap:748
hotApply @ bootstrap:361
(anonymous) @ bootstrap:336
Promise.then (async)
hotUpdateDownloaded @ bootstrap:335
hotAddUpdateChunk @ bootstrap:311
webpackHotUpdateCallback @ bootstrap:7
(anonymous) @ index.6f0ce478032676e350e8.hot-update.js:1

HEY! I got it working! 🥳

It's that dev line!

I added that, and it works as expected: rendall/svelte-minimal@990c938

Now I just have to figure out why vscode is saving my files with tabs. 📑

What does that boolean do?

I think just documenting that boolean could close this issue.

rixo commented

@rendall Nice to hear, and thanks for taking the time to report back. I've added a note in the README, do you think that's helpful enough?

Yes, that is definitely helpful! This can be closed now. Thanks very much for your help!