Closed benrbray closed 4 years ago
I think that is a good guess. If we are just losing reactivity only in the views it is probably one of two things. Either they must be doing some sort of default hyperscript transformation. Although that would usually lead to a runtime error since it wouldn't be able to find h
or React.createElement
etc..
The other possibility is there are 2 copies of Solid in your node modules for some reason solid-js/dom
that babel plugin writes into your compiled files is a different Solid. Solid's reactive system depends on a singleton reactive scope tracking. So 2 copies of modules might be tracking separately (ie.. the signals are coming from one runtime and the DOM effects from another). Why it would do that is a bit lost on me but check for duplicate copies of Solid in your node modules.
I hope that sets you on the right path. Other than that seeing the compiled output could help. Let me know if that reveals anything. Otherwise when I get a chance I can see if I can download the repo locally and try to help you debug.
Thanks for the advice! I verified that there is only one copy of solid-js
in my node_modules
folder, so that doesn't seem to be the issue.
To pinpoint the issue, I made a new branch in the Electron MWE that uses plain JS/JSX rather than TypeScript. Unfortunately the issue still persists, so I took a look at the compiled code. (it should appear in dist/renderer/renderer.js
after running npm run compile
).
Here's the original JSX:
jsx:
const [count, setCount] = createSignal(0),
timer = setInterval(() => setCount(count() + 1), 1000);
onCleanup(() => clearInterval(timer));
// ...
const AppContent = () => {
return (<div id="content">Counter: {count()}</div>);
}
Compiled versions are shown below, with and without electron. As you can see, they are compiled in slightly different ways, but as far as I can tell, the electron version should still work, as it's calling createEffect
as part of the insert()
function.
I'll keep investigating, but since it seems that babel-preset-solid
is running as expected, I'm more or less out of ideas. Thanks in advance for any help you can provide!
compiled (from jsx, no electron):
/* harmony import */ var solid_js_dom__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! solid-js/dom */ "./node_modules/solid-js/dist/dom/index.js");
//...
const [count, setCount] = Object(solid_js__WEBPACK_IMPORTED_MODULE_1__["createSignal"])(0),
timer = setInterval(() => setCount(count() + 1), 1000);
Object(solid_js__WEBPACK_IMPORTED_MODULE_1__["onCleanup"])(() => clearInterval(timer)); // components
//...
const AppContent = () => {
return (() => {
const _el$2 = _tmpl$2.cloneNode(true),
_el$3 = _el$2.firstChild;
Object(solid_js_dom__WEBPACK_IMPORTED_MODULE_0__["insert"])(_el$2, () => count(), null);
return _el$2;
})();
};
compiled (from jsx, with electron-webpack): (view full source)
Object(external_solid_js_["createEffect"])(() => console.log("\n\nfilePath:", state.filePath, "\n\n\n"));
const [count, setCount] = Object(external_solid_js_["createSignal"])(0),
timer = setInterval(() => setCount(count() + 1), 1000);
Object(external_solid_js_["onCleanup"])(() => clearInterval(timer)); // components
//...
const AppContent = () => {
return (() => {
const _el$2 = _tmpl$2.cloneNode(true),
_el$3 = _el$2.firstChild;
insert(_el$2, () => count(), null);
return _el$2;
})();
};
// where `insert` is defined as...
function insert(parent, accessor, marker, initial) {
if (marker !== undefined && !initial) initial = [];
if (typeof accessor !== "function") return insertExpression(parent, accessor, initial, marker);
createEffect(current => insertExpression(parent, accessor(), current, marker), initial);
}
Thanks I see the problem but I'm trying to figure out how to fix it. It looks like solid-js is marked as external which is what is being used in by your user code, but a version is also being bundled in your build probably coming in from solid-js/dom
. So you have the 2 copies of Solid problem. I'm gathering the intention is not to bundle solid-js/dom. So it should be added to externals in the same way solid-js
is. Or we need to remove solid-js from the externals and have it all use the bundled versions. I just didn't see that in your custom config.
This sort of tipped me off https://github.com/benrbray/solidjs-electron-webpack-mwe/blob/jsonly/dist/renderer/renderer.js#L2218 And: https://github.com/benrbray/solidjs-electron-webpack-mwe/blob/jsonly/dist/renderer/renderer.js#L92
webpack require(0) is external solid-js webpack require(2) appears to be solid-js and solid-js/dom bundled right into the build
EDIT:
I see nothing for externals in your config so if I had to guess this has to do with node module resolution. I wonder if this is something webpack target electron-renderer
does. I wonder if it is requiring the CJS version of the packages instead of the ES versions in different scenarios.
Thanks for the tip about externals! I am not manually defining any externals, but when I print the config.externals
defined by electron-webpack
, solid-js
does indeed appear in the list:
externals : ["solid-js", "source-map-support", "electron", "webpack", "electron-devtools-installer"]
So, this is definitely unexpected behavior arising from either electron-webpack
or webpack
itself. After some digging, I found an option for whitelisting modules in electron-webpack:
Since webpack is set to target the electron environment, all modules are treated as externals. Unfortunately, there can be a few situations where this behavior may not be expected by some modules. For the case of some Vue UI libraries that provide raw *.vue components, they will needed to be white-listed. This ensures that vue-loader is able to compile them as the UI library originally expected.
Adding the following to my package.json
did the trick:
"electronWebpack": {
"whiteListedModules": ["solid-js"]
}
Some relevant links about the electron-renderer
webpack target for future visitors:
Thanks very much for all your help, this issue has been quite the headache! I appreciate the quick response time, on a Saturday no less! I'll push my solution to the mwe repo for any future visitors.
That's great. Although I suspect if the default is to external that the preferred solution might be add solid-js/dom
to the externals rather than whitelist solid-js
. I wonder if they construct the list from package.json. In either case I'm glad it works.
Followup to my previous issue. I was able to modify the
electron-webpack
config to run tsx files through babel withbabel-preset-solid
active, but unfortunately I'm getting no reactivity in my application.I've created two minimal working examples:
electron-webpack-quick-start
.Even though I'm running the same code and nearly the same webpack config (merged with the
electron-webpack
defaults), the Electron version has no reactivity (the counter remains at zero). There are no errors in the console. However, callingcreateState
andcreateEffect
manually still works as expected.The following files are most important:
src/webpack.base.js
is where the webpack config is defined to usebabel-loader
for.tsx
files, along withbabel-preset-solid
src/renderer/render.tsx
defines the state / JSX needed for a basic counterI'm new to solid, webpack, and Electron, so it's been quite difficult to pinpoint the issue. My best guess is that I have a problem with my webpack configuration, or that there is an extra transpilation happening which transpiles the JSX before
babel-preset-solid
has a chance to do its thing.I will update here as I learn more on my own, but any additional help resolving the issue would be appreciated! Thanks!