Meteor 1.8.1 client and server directories in packages

For a long time, Meteor has been offering the possibility to build very large, truly modular applications with its unique packaging system. With ES6 modules, dynamic imports, and separated server and client code inside packages, there is almost no limit to the modularity and pluggability which can be achieved in a Meteor app.

The architecture envisaged and proposed by @sacha in Vulcan, and which depends on the packaging system, is a truly unique way of building apps. In our view, it trumps micro-services when it comes to large applications. For a very simple visual of what one can do with this architecture, have a look at micro-frontends, under Organisation in verticals. Just think of the possibilities!

It was simply too good.

Meteor 1.8.1 does not treat the client and server folders as special anymore, which makes development of client code in packages more difficult. A lot more difficult if you have many packages. :frowning:

It would be a real shame if this ability was taken away for good. We looked a while back at using NPM packages as a replacement for Meteor packages in this architecture and it’s just not the same. For complex Meteor apps (I would say complex apps generally), the package based architecture is one really neat solution.

1 Like

It’s true we use a package-based architecture, but we don’t rely on the client and server folders being treated as special. We just do:

  api.mainModule('lib/server/main.js', 'server');
  api.mainModule('lib/client/main.js', 'client');

That is exactly what we do also, but after the pull request mentioned in my original post, a client-only code change triggers a server rebuild. Hence my complaint that it makes development harder.

The behaviour is described in detail in this issue.

Oh ok, got it. Although in our case we (almost) don’t have any client-only code since Vulcan supports SSR and all code is shared.

That makes the rebuild time worth it, I guess. In all other situations, it’s a bummer

Here’s how I would prefer to fix/restore this behavior: https://github.com/meteor/meteor/pull/10414#issuecomment-481293530

That actually doesn’t seem to work, as described here: https://github.com/meteor/meteor/pull/10414#issuecomment-481313792

@illustreets I wasn’t saying it worked, just that it should work, and I will make it work in Meteor 1.8.2: https://github.com/meteor/meteor/pull/10414#issuecomment-481320587

For example, if we get this right, and you change a file in your application’s imports/ directory that we know is only used on the client, even though it’s not in the client/ directory, Meteor should be able to do a client-only refresh (without restarting the server). The same logic would apply to packages.

In general, I want to move away from magic directories and use the information that’s already embedded in the module dependency graph.

7 Likes

@benjamn Thank you for clarifying this! Now it all makes sense. Good to know it is a known issue.

This is very good news. It will allow, among other things, a much easier transition from packages to standard ES6 modules and back.

2 Likes

After this is all sorted out, it would be nice for a comprehensive explanation of how to structure our applications again. I’m on Meteor 1.8 and still have client/server directories (and make little use of the imports directory).

The application is on Blaze, but I’m migrating to React now. Amazing Meteor 1.8 still works with my code from years ago by the way.

I’d like to jump to a situation where I don’t have to use an imports directory. I don’t see what’s so wrong with having a server/client/both directory and inside each making everything modular with imports like is the case using a standard bundler like Webpack for example.

I don’t use a “Packaging” system as some rave about, I don’t see what the big deal is and why some are adding another level of abstraction. Just to split the codebase up for farming out code chuncks for development? Are you guys making a bunch of small projects and just linking them into a “greater” meteor project for bundling? If so, seems overkill.

If you move to micro services, usually they’re totall separate, could stand alone, chunck of functionality that “talk” via a web API or the like. I’d like someone to explain what micro services mean in the Meteor context, are you building separate micro services “Packages” that communicate via Meteor Methods?

In my case, i am using packaging system because my client code base is web and cordova in the same time, so i separate each logic in a different package to avoid having web code in cordova ( mobile ) app and vice versa.

I have an app that still uses Blaze, and it still uses the magic folders, with eager loading, and all that.

My more modern react apps all mostly use the imports directory, though I still find it useful to place my entry points and other configuration in the client and server folders. I had played with moving everything to root, but that got messy, and decided to simply leave everything in classic folders, and it’s pretty clean that way. Inside the imports directory things are organized like many react apps - api, ui, and utils folders at the base, and then appropriate things where they go, etc.

Occasionally, I do create a local package. This is useful for example if you want to have a single import location, which works for both client rendering and SSR, but want to provide different API compatible libraries for client and server. I do this in my data tier, which is offline first, and built on top of Meteor methods, to bypass the overhead of pub/sub (which I only use sparingly for cases where I do need realtime). But most stuff lives cleanly in the imports directory.

1 Like

Hey. I’ve just realized that inside a package I can import files from the server folder on the client with no issues. Do I understand correctly that there is no way to prevent such accidental imports of server-only code now?

That is correct; besides you can name those directories any way you like. At this point, I think it’s mostly just a convention (and good practice) to name them like that, because Meteor uses modules nowadays as opposed to relying on folder naming. You could also have a common folder from where you import in both, should you need to do so (e.g. when defining collections).

The most elegant way is to use imports, and a modular package structure, which prevents this type of accidental error. Also good for maintainability.

Thank you for the quick reply. I do use a modular package structure but it doesn’t seem to solve the issue. Specified entry points still don’t prevent doing something like this:

// server.js
export const a = 'a'

// client.js
import { a } from './server'

Or more real-life example:

// client.js
import { serverOnlyFn } from './common/api/{module}/server'

It’s really easy to overlook some nested import of the server code and load it to the client.

Am I missing something?

These recommendations becomes totally useless. It’s now impossible to import secret server code with require() in method body as all require statements are proceed on the client.

The recommendations still work for main app code, so it’s by no means totally useless

I get around this change by sticking to the folders as a convention, which makes it easier to see that a file should be server or client only. You could also make an eslint rule for it if you wanted

Thanks! I meant totally useless for packages of course. I’ve also discovered that @meteorjs/eslint-config-meteor can help but only in case of direct imports of files with server in the path by files with client in the path and vice versa.

Yeah I don’t think it exists yet, but working off of eslint-import-resolver-meteor could work

Simplest would be to show an error if client or server is on the path and you’re importing from the other