Organizing ES6 code


#1

Hi guys,

I need advice on how to organize the business logic of an app.

I’m using ECMA6 with Babel and it’s fantastic so far except that I know the code is going to grow and I would really love to have one class per file, like: ClassName.es6.js

so the code gets intuitively organized.

When I’ve tried I had 2 problems:

  1. Jasmine not being able to do ES6 and
  2. classes referenced from one file can’t be found from another

so the build fails.

So far I’m using one “big” ES6 file but this will be limited.

What would you suggest to do? What options do I have?

Thank you!


#2

You can export the classes like this:

class Foo {

}

this.Foo = Foo

I also test my ES6 files but my actual jasmine specs are in ES5. Works great for me.


#3

Fantastic. I can reach the classes as globals as expected in client and server.
I feel like a kid with new toy again :smile:

Regarding to Jasmine, it seems it can’t find my exported classes.
Sounds familiar? Did you do something about that?


#4

In what testing mode do you have this problem? Can you give me an example where the test code does not find the class? I think in the server integration and client integration mode should work with ES6.

Can you open an issue in GitHub please: https://github.com/Sanjo/meteor-jasmine/issues Thanks.


#5

Hi Jonas, thanks for taking a look.

I’ve added it as server unit test.

I’ve created a fresh repo showing the issue:

And here is the link to the opened issue:


#6

One possible solution will be to use rocket:module. It’ll let you use ES6 import/export (also AMD defines or CJS requires/exports)., so your’re code will be compatible outside of Meteor, and outside code compatible in Meteor. I’m aiming to finish it as soon as I can.

You’ll be able to do

/* Bar.js */

import Foo from 'path/to/Foo'

export default
class Bar extends Foo {
  /* ... */
}

#7

Hmmm they worked for me as long as the Jasmine specs were ES5. I don’t have enough time to clone down the repo yet but what if you do this instead? This is how I setup mine.

class Model {
  constructor ( plainJSONObject ) {
    // Initializes this instance loading it with plainJSONObject.
    this.data = plainJSONObject;
  }

  static fromData (plainJSONObject) {
    // Returns this instance of the model loaded with plainJSONObject.
    // Answers null if plainJSONObject is null
    if ( !plainJSONObject ) { return null; }
    return new this( plainJSONObject );
  }

  static findId ( anId ) {
    var found = this.getCollection().findOne( { _id: anId } );
    if(found) { return this.fromData( found ) }
    else { return null }
  }

  static getCollection () {
    // Returns the class of this instance.
    throw new Error( 'subclass responsibility' );
  }

  getData () {
    return this.data;
  }
}

this.Model = Model;

#8

Don’t you just love polluting your global scope?


#9

:laughing:

I hear that build in ES6 support is coming … with modules i’m hoping :smile:

Is rocket:module close to being ready? With either option, can’t wait!


#10

Classes as globals are probably the most justified case to use globals of the Computerverse.


#11

@SkinnyGeek1010 Soon I hope. I also hope my work is going to be worth it. I made an issue to try and figure it out, because if Meteor is going to do the same thing, I might as well work with them instead of on a separate package. I’m laboring away at it all day today until that becomes clear. Who knows, maybe my package will still bring extra functionality that their solution doesn’t.

@glasser @debergalis @awatson1978 @joshowens @arunoda ^ Any insights on this? Rumors/facts?


#12

It’s working for one class but now I’m having the issue of load order.

If a subclass of Model tries to get defined first (before Model) the build fails.

I don’t know what to do =(

Do we have something to work on that?


#13

It’s pretty normal in Meteor to run into loading-order issues, that’s why many people prefer package-only apps, where you can specify exactly how your code is loaded and cleanly split up your app into smaller chunks. I also like the pattern that each package exposes exactly one global namespace, so you immediately see where a class comes from.

If you really want to stick with the “normal” app approach i can recommend my Space framework for structuring your code, I created it exactly because of loading-order issues before I switched to a package-only structure :wink:


#14

Can you do something like this?:

Meteor.onReady(function(){
  class Model extends ModelDeluxe {
    constructor ( plainJSONObject ) {
    }
  }

  Model = Model;
})

Personally if I had to put a large app together with packages I would be packing up and going back to node/rails for an API with a SPA frontend. The friction and overhead of having tons of packages together is higher than dealing with data fetching with a rest api. (just my $0.02!)


#15

@SkinnyGeek1010 the onReady idea is doable but it’s not solving the original question.

I can already do code classes in one file and it will work. But the plan is to have one file per class so code becomes intuitively manageable.


#16

Right but couldn’t you use that with two files? This way the one that depends on another external file is loaded after Meteor is ready. For ex:

// file 1
  class Automobile {
    constructor ( plainJSONObject ) {
    }
  }

  Automobile = Automobile;
})
// file 2  (edited, now is .startup!)
Meteor.startup(function(){
  class Truck extends Automobile {
    constructor ( plainJSONObject ) {
    }
  }

  Truck = Truck;
})

#17

I really like this pattern. I’ve been working on a dozen carefully designed classes the past couple months, and this looks like it may be exactly the right way to piece them together. Looking forward to giving this pattern a test run.


#18

Thanks for describing that @SkinnyGeek1010
The Meteor.onReady(function(){..}) way seems to be good for the client side classes which isn’t bad but is not helping us to take the full advantage of isomorphism.

I want to define the classes behavior in one place for both sides. For some this sounds like the mixing server and client methods in the classes definition might create trouble but I consider that a feature and the objects using those models should be responsible on what API they use (client should use client only methods and server, should use server only methods, all defined in one file per class).

Wonder if that’s currently possible? any onReady equivalent for he server side?


#19

Meteor.startup() works on both server and client. Seems like a reasonable place to link classes.


#20

True! Thank you for the idea @awatson1978

When I’ve tried that, what I saw was the build failing due to pre-requisites order issues, so we’re back on the import/export issue.

The hackish workaround I’ve found was to prefix the classes filenames with numbers and get them to be shown by the OS in an order that allows it to build successfully.

Is not the most elegant thing but the important part is done right and it might be enough for some time.

I’m really looking forward to try @joe 's module import/export solution