If I use mithril (1.0) serverside (mithril-node-render) it complains about undefined variable window
create a file
// test.js
const m = require('mithril')
run it with
> node test.js
It should not throw any errors.
/Users/sh/Work/OS/mithril-node-render/node_modules/mithril/render.js:1
(function (exports, require, module, __filename, __dirname) { module.exports = require("./render/render")(window)
^
ReferenceError: window is not defined
at Object.<anonymous> (/Users/sh/Work/OS/mithril-node-render/node_modules/mithril/render.js:1:107)
at Module._compile (module.js:541:32)
at Object.Module._extensions..js (module.js:550:10)
at Module.load (module.js:458:32)
at tryModuleLoad (module.js:417:12)
at Function.Module._load (module.js:409:3)
at Module.require (module.js:468:17)
at require (internal/module.js:20:19)
at Object.<anonymous> (/Users/sh/Work/OS/mithril-node-render/node_modules/mithril/index.js:4:21)
at Module._compile (module.js:541:32)
currently, I'm doing this to use the public api in node scripts: https://github.com/lhorie/mithril.js/blob/rewrite/docs/lint.js#L91
@lhorie that is incredibly clever code. Great to know everything in the docs has to be true - nice work!
As part of my hacking about with the router API, I've been trying to use ospec to validate patch code, and I'm also struggling with the mocks. I had been using browserify & tape-run to get tape code to run in the browser, but at a certain point I had problems with resetting state and thought I may as well move over to using ospec (now Mithril is the only dependency, which makes a lot of sense).
I was hoping to bind the generated browser environment mock to global.window & global.document, then require the Mithril index. Most things seem to work this way - mount, render, and the location effects of route, but route endpoints don't. Of course, all the route API stuff is constructed differently, with special dependency injections etc in ways I can't follow through to the point where I can see what's missing.
Specifically, this won't pass:
const o = require( 'mithril/ospec/ospec' )
const callAsync = require( 'mithril/test-utils/callAsync' )
const mock = require( 'mithril/test-utils/browserMock' )()
global.window = mock
global.document = mock.document
const m = require( 'mithril' )
o( 'Route endpoint methods trigger', done => {
const onmatch = o.spy()
const view = o.spy()
m.route( document.body, '/', {
'/' : {
onmatch,
view
}
} )
callAsync( () => {
o( onmatch.callCount ).equals( 1 )
o( view.callCount ).equals( 1 )
done()
} )
} )
Is this a bug with the mock or dependency injection, or have I missed a trick?
o( onmatch.callCount ).equals( 1 ) wouldn't pass with the code that is in the rewrite branch. What error are you getting @barneycarroll ?
Right — that's the problem. I can't get _any_ methods on a route endpoint to trigger (whether it's a RouteResolver or a Component). But, FWIW I can set routes and verify that location is being updated. Why wouldn't onmatch trigger in this scenario?
Could be because RouteResolver is {onmatch, render}, not {onmatch, view}
@lhorie oops, my bad. To give you another example, none of this will work either:
o( 'Route endpoint methods trigger', done => {
const oninit = o.spy()
m.route( document.body, '/', {
'/' : {
oninit,
view : () =>
m( 'p' )
}
} )
callAsync( () => {
o( oninit.callCount ).equals( 1 )
o( document.body.firstChild.tagName ).equals( 'P' )
done()
} )
} )
...Which makes me think there is something special going on in how the router API test suite constructs the mocks which I don't understand.
@barneycarroll
try this
m.mount( document.body, '/', {
'/' : {
oninit,
view () {
return m( 'p' )
}
}
} )
@jonathanobino well spotted! I meant m.route, not m.mount! I'll edit the code snippets...
OK, _my_ problem was down to async test resolution. I found I needed to set a timeout of at least 9 milliseconds for the route to resolve. I'm not going to delve into the specifics of how the router API tests construct the mock differently, but it seems for convenience the standard frame budgeting doesn't apply in that suite.
So I can confirm the mocks work as expected with bundled Mithril routing as long as you make allowance for deferred execution.
@lhorie using global.window has some issues for me
global.window to determine if they are in browser or notOnly solution I would imagine is to allow to set window mock after initialisation (like in 0.2.x). Will try if that's possible somehow.
Other option would be to utilize browser-field. But I don't know if that's feasible with the custom bundler. It it would require to have 2 different /render.js one that uses window and one that uses the mock.
or something like...
(function (factory) {
if (typeof exports === 'object' && typeof module !== 'undefined') {
module.exports = factory(global);
} else if (typeof define === 'function' && define.amd) {
define(factory(window));
} else {
window.m = factory(window);
}
}(function (window) {
'use strict';
var document = window.document;
window is global in node and window in browser, you can then check
document ? or window.window === window ?
to know if you are in the node env or the browser
The browser field may be the better way to go, although I don't think the bundler understands that field.
Any progress on this? I need mithril to work in node, for my isomorphic framework...
Here's what I just did for our similar-to-node-but-not-exactly-the-same SSJS framework.
var m;
// Polyfill DOM env for mithril
global.window = require("mithril/test-utils/browserMock.js")();
global.document = window.document;
// Require the lib AFTER THE POLYFILL IS UP
m = require("mithril");
// Make available globally for client scripts running on the server
global.m = m;
// Export for normal server usage
module.exports = m;
Yes, it's a bit silly but I don't see an obvious alternative currently.
@tivac That's a nice hack, thanks for sharing!
I do before everything other. Odd, but works
require('mithril/test-utils/browserMock')(global);
Will close this for now.
Is that a good way to go though? Having a mock window can trip some libraries up; this method seems to work: https://www.npmjs.com/package/detect-is-node, but still...
@StephanHoyer How will your getting started example for mithril-node-render look then? Will it be this?
require('mithril/test-utils/browserMock')(global);
var m = require('mithril');
var render = require('mithril-node-render');
render(m('span', 'huhu')).then(function (html) {
// html === '<span>huhu</span>'
})
Ie: forced use of the mock?
Most helpful comment
Here's what I just did for our similar-to-node-but-not-exactly-the-same SSJS framework.
Yes, it's a bit silly but I don't see an obvious alternative currently.