Import best practices

With respect to imports in 1.3, I’m a bit confused by the Todos example.
I’m not against imports, but what is the point of all the daisy-chained imports?

For example: server/main.js imports the startup/server folder which imports index.js which imports register-api.js which imports publications.js.

Why not just import publication.js from the server/main.js? Why add index.js?
Also there seems to be a mix of relative and absolute paths.

8 Likes

I agree, the Todo example is quite difficult to decipher.
It seems over engineered.

3 Likes

Yeah I’ve been totally thrown off with those imports.
I haven’t changed how I structure my apps yet.
I only import external NPM packages and components from other files.

2 Likes

Jumping in here as well. I’m currently using Meteor 1.3 with Polymer, dependency management is mostly straightforward for frontend. Our backend developer wants to go back to 1.2 because he says it’s too complicated to prototype in 1.3 due to the fact that you have to import/export stuff whenever you want to use something in another file from another file.

Is there a simpler way to make it easier to prototype and do things more fine-grained/modular later?

2 Likes

Use the import/export stuff only if you want! If you decide not to use it, you can create prototypes in exactly the same way than Meteor 1.2.

3 Likes

The reason for creating an index.js is that you can import just the module instead of the specific file. If you define all your exports in an index.js in the root directory of module than you just need to import the module only for e.g.

import { xyz, abc } from '/client/module';

In index.js we can export xyz and abc.

import xyz from './components/test/xyz';
import abc from './components/test/abc';

export {
 xyz,
 abc,
};

@suhaila You can also export directly from those modules in index.js:

export { default as xyz } from './components/test/xyz';
export { default as abc } from './components/test/abc';

Like mentioned by @nullpo, the module system is optional. The todos example has been developed according to the new official Meteor Guide. The Guide’s goal is to provide developers a common way to structure Meteor apps in a way that can keep a project manageable at scale. It helps a ton to have a single entry point into certain modules if your project gets bigger!

Perhaps, if you’re just prototyping, it’d be in your favor to not use the module system. I’d recommend to get used to it, as it helps a ton with seeing where everything is coming from. Even if you’re just prototyping. :slight_smile: You don’t have to chain the imports as much as in the todos example if you don’t feel like it. At least use it to avoid globals and have more clarity as to where your modules are coming from.

1 Like

The todos app has to walk a fine line between:

  • Demonstrating best practices that can help as your application grows in size and complexity (even though they don’t make sense in the context of such a small app).
  • Showing how your application can still work with pre 1.3 eager loading and with post 1.3 lazy loading.

A lot of the application design choices seem like overkill (and they are), but they’re intended to demonstrate various pieces of the 1.3 pie. By all means feel free to follow your own approach and simplify things as needed. And keep those todos app issues/PRs coming! MDG (well, @sashko aka “The Machine”) is reviewing and accepting PR’s amazingly quickly, given everything they have on their plate right now.

3 Likes

Yeah, I’ve already raised the confusing bits relating to the container components part of the React section and Sasha has jumped on that really quickly.

Regarding the imports, Aurelia and Ember use this methodology. Lazy loading is a nice idea but probably comes with an overhead - either load everything or calculate what’s needed by looking through all the files and loading those dependencies. I would imagine that defining these imports is part of helping the largeer applications.

The logic of loading files in alphabetical order (or at least depending on it) seems like a very strange concept to begin with and that using imports help remove this fuzzy logic down the line, so we should get used to it. I tend to rely on three points of execution, 1) what needs to happen on the client at startup, 2) what needs to happen on the server at startup, 3) what heppens when the navigation occurs. This is possible a naive opinion and it would be good to hear more from those that have built larger applications and the types of gotcha’s they’ve faced relating to app architecture. I hope that Meteor gets a little more opinionated and importing looks like a step in that direction.

I appreciate the replies and I’m not complaining about the import system. I’m just having trouble understanding best practices.

None of the files I mentioned in the Todos example even use export.

todos/imports/api/todos/server/publications.js // (no export)
todos/imports/startup/server/register-api.js – import ‘…/…/api/todos/server/publications.js’; // (no export)
todos/imports/startup/server/index.js – import ‘./register-api.js’; // (no export)
todos/server/main.js – import ‘/imports/startup/server’; // (no export)

Here are my issues with respect to Todos:

  1. Why not just import publications.js in main.js and skip the middle steps?
  2. Why use index.js? Register-api.js is already being imported by main.js which imports startup/server folder.
  3. Why is export not needed? Is it because main.js is outside of imports?
3 Likes

Here’s my understanding, hopefully it helps with some confusion:

Why not just import publications.js in main.js and skip the middle steps?

I think they’re trying to push the single entry point idea, so /startup/server is a ‘module’ where index.js is it’s entry point that imports everything that module needs. Currently that’s only register-api.js, but it could be much more in a larger app. You would still only need to import '/imports/startup/server' to get that entire module.

Why is export not needed? Is it because main.js is outside of imports?

import '/imports/startup/server' merely evaluates that module so the code is then accessible, where before the import it didn’t exist. If you’re not explicitly importing something from a module, such as import {XYZ} from '/imports/startup/server' then there’s no need to export anything. In other words, you’re importing/evaluating that module, which index.js imports what it needs, register-api.js imports what it needs, etc. while main.js doesn’t need to know about any of that.

Please correct me if I’m wrong (I’m still grasping all these ideas as well).

5 Likes

(@zachdixon - just typed this up as you answered, so I’ll post anyways; there is some overlap!)

You definitely could - the todos app is just showing you an approach that aims to help keep things cleanly separated, easy to maintain, and easy to grow with.

Not quite - /server/main.js just calls import '/imports/startup/server', which in turn is translated into import '/imports/startup/server/index.js'. index.js then calls register-api.js. index.js is used as a catch all to keep /server/main.js nice and clean, and still give a way to control load order (if you want certain files to be loaded before register-api.js you can import them earlier in the index.js).

Imports are used to either get access to exported items from the imported files, or to leverage the side effects of importing a file. In the case of the files you mentioned that don’t have exports, that’s because we’re only interested in the side effects of importing those files. For example importing a publications file - we don’t need access to anything imported directly, so we don’t need any exports in the file. We’re just interested in the side effects of importing the publications file, which is to have the publications registered via Meteor.publish.

8 Likes

Thanks for the fantastic explanation. You should write software books.

I think I understand now. Importing from a file with no export only runs the code in the target file.

3 Likes

Exactly (even if there is an export or multiple exports, the code is still evaluated). As a quick test to verify this, try:

  1. meteor create some-app; cd some-app
  2. mkdir imports
  3. Create a file called hello.js that just contains console.log('Hello!');
  4. Edit /client/main.js to add the following line somewhere:
import '/imports/test.js';
  1. Run the app and watch your browser console.
2 Likes

Here is an example of what I’m doing:

Client

/client/main.js
import '/imports/startup/client';

/imports/startup/client/index.js
import '/imports/startup/client/routes;

/imports/startup/client/routes.js
This file containes my FlowRouter configs...

/imports/tools/index.js

import { Meteor } from 'meteor/meteor';
import { Accounts } from 'meteor/accounts-base';
import React from 'react';
import ReactDOM from 'react-dom';
...
export {
  React,
  ReactDOM,
  Meteor,
  Accounts,
  ...
};

/client/imports/ui/components/index.js

import CompA from './CompA';
import CompB from './CompB';
export { CompA, CompB };

/client/imports/ui/components/CompA.jsx

import { React, Meteor } from '/imports/tools';
... use React and Meteor as you wish

/client/imports/ui/components/CompB.jsx

import { React, Meteor } from '/imports/tools';
import { CompA } from '/client/imports/ui/components';
import { Icon, Button } from '/client/imports/ui/semantic'; (i have my semantic-ui comps there)
... use React and Meteor as you wish

I think it simplifies the way to load your stuff. As you add components, you add them to your index.js file inside the directory. When you have to move stuff, it is really easy.

In /imports/tools I also have underscore, moment, and a few other libs that I use almost everywhere.

I also have /imports/tools/server.js for server-side aggregation or Meteor, underscore, HTTP, etc.

I believe that imports translate to nodejs requires… So the modules should be loaded only once.

Any comments or suggestions would be appreciated!

5 Likes

Continuing on the imports, how would we import meteor packages, like underscore, alanning:roles, jQuery, etc ?

import { Meteor } from 'meteor/meteor'
import { lodash } from 'meteor/stevezhu:lodash'
import { Roles } from 'meteor/alanning:roles'

I generally prefer lodash over underscore because I need a few extra things that underscore does not provide. From what I can tell there’s no need to explicitly import underscore.

However, if I want to add lodash functions I do this in server/main.js

Meteor.startup(() => {
  // Add missing functions from lodash to underscore
  _.xor = lodash.xor
 _.escapeRegExp = lodash.escapeRegExp
})
1 Like

No reason to use a Meteor wrapper for lodash anymore. Just install with meteor npm install --save lodash and then from your app call import _ from 'lodash';

2 Likes

Have you tried doing that? I recall when I did it earlier it broke my app badly, so I just pick the parts I wanted. It could be something else that broke it, but the approach I had above worked when I tested on my migrated app.