Application Structure

They could be, but if you’re then referencing routes.js and useraccounts-configuration.js in the same directory like your example shows, that means they’re in /client (outside of the imports directory), and will be eagerly loaded. If you wanted to update /client/main.js with something like:

import '/imports/startup/client/useraccounts-configuration.js';
import '/imports/startup/client/routes.js';

then no problem at all. Or any directory within imports really, like:

import '/imports/config/useraccounts-configuration.js';
import '/imports/config/routes.js';

The Guide/todos app are just suggesting a structure, and recommend keeping the contents of the default eagerly loaded files (like /client/main.js and /server/main.js) to a minimum, to help better control load order.

1 Like

Oh this is interesting, I didn’t know you can do

import '/imports/...'

That would’ve made my life a bit easier since I don’t have to count how far up I have to go to load the import.

2 Likes

I am having a hard time determining an approach for finding where to import from, currently referring to the todo app for most cases and just copy/pasting.

For example how does one determine that Accounts should be imported from meteor/accounts-base and that AccountsTemplate should be imported from meteor/useraccounts:core. How does one know that Template is in meteor/templating? Or… if Npm is used, where do we import Npm from?

How does one import packages from FS? For example, a module uses both FS.Collection and FS.FileSystem… what do those import statements look like? Is it one import? Two imports?

It would be very useful to outline this in the guide.

As a side note, I just started with meteor only shortly after the guide was released – kudos on bringing this to the community… I was eagerly awaiting the update for 1.3! It’s been an excellent resource, and it’s great to have a sample app to pair it with. Great job, and thank you! :slight_smile:

1 Like

We added that information to the docs for 1.3! Check them it again if you haven’t looked in a while.

1 Like

Yes, I now see the import statments in the docs, this is great – thanks. In the case of Npm.require I do not see this though…

What about atmosphere packages, like FS? If both FS.Collection and FS.FilesSystem are used, am I correct to assume that the import statement should be import { FS } from 'meteor/cfs:base-package'? I assume this since api.export('FS') is found in packages/base-package/package.js.

UPDATE:

  1. To prevent lint errors for Npm.require() I have set /* global Npm */
  2. import { FS } from 'cfs:base-package' seems to work without errors

Is there a reason why in the todos app an absolute path is used for import statements in client/main.js and server/main.js – but everwhere else it is a relative path? Can absolute paths be used anywhere?

Hey guys, nice work with 1.3, it’s a lot of little things that are going to make the overall developer experience a lot smoother. I’m reading through the guide now and had a couple of questions about this section, namely:

###1. Relative import paths

I found relative paths in the examples that have more than one ‘finding the parent level’, i.e. ../../ hard to follow. In past experience with React projects I’ve also found these tedious to maintain and at certain times difficult to reason about.

I can see a benefit in using relative paths because I’m assuming in future we will be able to move everything from imports/ into the project’s base directory (as one example). At that point we’d have to update a lot of individual files though if we’re using absolute paths, which would be a pain for version control.

I have used Broccoli in the past, which allows you to set a root directory for import statements in the brocfile (basically a project settings.js). So I’m wondering whether a similar option would be possible for Meteor: for now set the base import folder to /imports. Then from your routes.js you could just import '/ui/template/pages/xyz.html', and when the hypothetical Meteor 1.4 comes out and forces everyone to switch to the new imports pattern (making /imports redundant), we could just kill the that directory and change the import root in one line of code.

###2. Clarity about what to import

I found this sentence in particular unclear:

The imports/startup/client/routes.js configures all of the routes and then imports all other code that is required on the client, forming the main entry point for the rest of the client application

My intuition tells me that importing “all other code that is required on the client” is not actually what you’re suggesting. In any case I had to read the section three times to figure out what you really mean. I’m still not sure why importing anything close to ‘all’ code in the router would be advantageous, and what really needs to be imported.

My understanding is that after the (eagerly loaded) startup, the router – for all intents and purposes – forms the ‘root node’ of the app, from which other files are, at least in principle, lazily loaded. So it makes sense for the router to know about other layouts and pages that are directly accessible via routes. Is that the extent for files that should be imported here, or have I missed something?

1 Like

Your imports can be from /imports/... there’s no need for ../../../imports from what I can tell, I use

import { Foo } from '/imports/bar'

Right now, you can already import using absolute paths, so your path becomes '/imports/ui/template/pages/xyz.html', which I think is not so bad because it’s just one more /imports in there.

There’s an open issue about this, I think it definitely could be a loooot clearer.

Hi Sashko, thanks for your response. What you’re saying here is true. As I wrote in my previous answer though, this is a poor solution for version control, especially assuming the /import directory might well disappear in future once lazy-loading becomes the norm (maybe that’s a false assumption, please correct me if I’m wrong):

Our project currently needs to be maintained across ios and web branches (to name a couple), each with varying levels of feature modernity, even different meteor versions, etc. In any case, it would be ideal if we could just import /ui/layout/etc... and then if the base import directory changes, version control only has to deal with a one line change in one file, not thousands across multiple files - does it make sense that the potential for merge conflicts (and difficulty sharing files across versions) is much greater in the second case?

In any case it seems like writing import '/imports/...' with every absolute path import goes against D.R.Y. and is basically unnecessary noise - just for my understanding, is there ever a time where an absolute path wouldn’t start with that?

I couldn’t see discussion of ‘importing all client code’ on that link, which was the main thing I found problematic.

Looking over the section again, there are a few other references to client/main.js // imports all client code etc., I think that wording is confusing. I’m assuming the intention is that main.js should import the starting points / features that in turn import all other code, but the way it’s written makes it seem like you should import every piece of code in your app directly in main.js.

Referring to the import paths, I also noticed there are a bunch of import '../imports/etc'. I don’t have a lot of experience in this realm, but I’m wondering why that’d be considered better practice than import '/imports/etc'. To me the second one is a lot clearer and conciser (as I said above, without ‘/imports’ would be even better, in my opinion)

Hi!

Having trouble understanding how to use modules with ‘package-only’ type application structure.

I’m working on an app with the following structure:

/app
  /packages
    /foo
    /bar

I was following this structure to create flexibility, because I want to isolate features into packages (allows me to plug and play features by importing a package)

The problem I’m having is when trying to import/export between the packages foo and bar. Let’s say I have an exported function called baz() that is implemented in the ‘bar’ package, and I wanted to import that in the ‘foo’ package - I’m not entirely sure where to do this, or if I should abandon the package-structure totally and switch to modules.

I would consider it, I’m not sure there’s much benefit in packages for their own sake any more.

Did you try import { baz } from 'meteor/bar'?

Thanks for the reply!

Yeah, I tried that. I’m not sure there is a case which we can import a package from Atmosphere vs a local package.

Leaning towards switching to module structure completely, since that’s going to be the new convention.

I’m not sure what you mean here, but perhaps you forgot to api.use('bar') in foo?

Sorry,

I tried that before. Wasn’t working either. Foo threw a ‘bar not defined’ error. It’s all good, currently switching to modules. Thanks so much!

1 Like

Another question, I’m having some trouble understanding importing from Atmosphere packages.

let’s say I want to use SimpleSchema from aldeed:collection2 - how do I know what to import from aldeed:collection2?

import {SimpleSchema} from 'meteor/aldeed:collection2'

is this correct? what if I wanted to import other things from the aldeed:collection2 package?

In the same way as you’d know what symbols it auto-exported in the old world (in truth, no good way apart from documentation or inspecting the package source or futzing around on the console).

You can import multiple things with that syntax:

import { A, B, C } from 'meteor/foo';
1 Like

Okay great, thanks so much for you help. Time to start reading through the source :grinning:

There is one other way to be able to quickly tell what you can import from an Atmosphere package. After you’ve added the package to your project, open that packages source file from within your local app. For example, to see your import options for jquery, open the following file:

[app_root]/.meteor/local/build/programs/web.browser/packages/jquery.js

Scroll right to bottom and you’ll see your available import options in the exports section:

/* Exports */
if (typeof Package === 'undefined') Package = {}; 
(function (pkg, symbols) {
  for (var s in symbols)
    (s in pkg) || (pkg[s] = symbols[s]);
})(Package.jquery = exports, {
  $: $, 
  jQuery: jQuery
});

So from this we know we can use one of:

import { $ } from 'meteor/jquery';
import { jQuery } from 'meteor/jquery';
1 Like