What is the best approach when creating an isomorphic resource with modules?
Here’s what I’m currently doing:
// /imports/foo.js
import { Meteor } from 'meteor/meteor';
export let foo;
if (Meteor.isClient) {
foo = require('./client/foo.js').foo;
} else {
foo = require('./server/foo.js').foo;
}
// /imports/client/foo.js
export const foo = new class Foo(/* client implementation */);
// /imports/server/foo.js
export const foo = new class Foo(/* server implementation */);
Pros:
Appears to work. It imports the correct implementation and users of the resource just import { foo } from '/imports/foo.js' without worrying about whether they’re running on the client or server.
Cons:
Uses require, side-stepping build tools that look for import statements.
Server code on the client (i.e., { foo } = require('./server/foo.js');) and client code on the server (i.e., { foo } = require('./client/foo.js');).
// imports/foo.js
/* declare shared code in shared directory */
// edit: ES6 exports are immutable, so you need to export an object
export let foo = {
foo: new class Foo(/* placeholder */);
}
// imports/client/foo.js
import {foo} from '/imports/foo';
foo.foo = new class Foo(/* client implementation */)
The Mantra spec actually recommends keeping the client & server separate, which, while it goes against Meteor’s benefits, sometimes makes sense for situations like these.
Hi @reoh, thanks for the reply. That was another approach that I looked at but I wasn’t sure about modifing a module’s exports. What’s the best practise there?
I guess you’ll probably want to make sure it’s only defined in one place and it can’t accidentally be re-defined later on by using Object.freeze to make the export immutable after defining it:
// imports/foo.js
foo.foo = new class Foo(/* client implementation */)
Object.freeze(foo);
export {foo};
// imports/whoops.js
foo.foo = new class Bar() // TypeError
and of course document the shared export so you know exactly where it’s client/server definitions are