Jump to content

boilerplate setup


dmko
 Share

Recommended Posts

The structure there is all based around typescript (which is part of what made it so infuriating, as getting back to the original .ts files with correct line numbers can be tricky across scenarios)

Link to comment
Share on other sites

Truth is I didn't give too much thought to the code itself... other than getting the features I wanted to test working. I'm brand-new to typescript and not too seasoned with JS either (just got myself the "You don't know JS series..." already a few pages in and it's clearing up some misconceptions I had :P

The main thing about this boilerplate is dealing with all the configuration stuff... i.e. getting karma, webpack, mocha, tsc to all play nicely together - and also dealing with a sort of offline/online development scenario for media assets and things.

Link to comment
Share on other sites

1 hour ago, ivan.popelyshev said:

Good one !!!! :) But I prefer typescript namespaces. I'll take something from it ;)

That is what i meant, I prefer namespace or module instead of importing. otherwise it is very hard to manage especially for a plugin:)

Link to comment
Share on other sites

On 09.05.2017 at 5:56 PM, dmko said:

Truth is I didn't give too much thought to the code itself... other than getting the features I wanted to test working. I'm brand-new to typescript and not too seasoned with JS either (just got myself the "You don't know JS series..." already a few pages in and it's clearing up some misconceptions I had :P

The main thing about this boilerplate is dealing with all the configuration stuff... i.e. getting karma, webpack, mocha, tsc to all play nicely together - and also dealing with a sort of offline/online development scenario for media assets and things.

You're doing very good job. I recommend to try do it with namespaces. Its tricky, but result is much easier to handle than import/export.

I'm setting up something like that right now, but with all custom things, no webpack, karma, everything is special optimized for big games.

It'll be boilerplate optimized for production. When you have megabytes of code things become very diffirent ;) 

Link to comment
Share on other sites

1 hour ago, ivan.popelyshev said:

You're doing very good job. I recommend to try do it with namespaces. Its tricky, but result is much easier to handle than import/export.

I'm setting up something like that right now, but with all custom things, no webpack, karma, everything is special optimized for big games.

It'll be boilerplate optimized for production. When you have megabytes of code things become very diffirent ;) 

It is recommended way to do namespace same as Csharp. it becomes so easy to manage. it is same for unity as well, brilliant brilliant and super brilliant to use namespace

Link to comment
Share on other sites

In Unity - you MUST use namespaces to distinguish between classes of the same name, otherwise you get compile ("build") errors, even if you never actually use the conflicting classes

Consider this setup:

Main.cs

Vehicles/Jaguar.cs

Animals/Jaguar.cs

Immediately, unless you encapsulate those Jaguar classes in a namespace, you get errors- the Unity editor won't let you run it in play mode until you fix it.

With Typescript (and I assume ES6 modules) it's different

Because you must explicitly import the class, with a the same setup as above (cs=ts) this is fine:

import {Jaguar} from "./Vehicles/Jaguar"; 

No errors until you try to import BOTH Jaguars into the same module at the same time.

IF you actually need to use both Jaguars in the same class/module (which imho is not too often, but it can happen), then you still don't really need to encapsulate them with a namespace since can use aliases to distinguish:

import {Jaguar as JaguarCar} from "./Vehicles/Jaguar"
import {Jaguar as JaguarAnimal} from "./Animals/Jaguar"

This may be even more helpful since you can decide the best naming convention around how it differs for use in that module. I.e. calling it "JaguarCar" there may be more helpful while in another module it may be more helpful to call it than "JaguarMachine" (this comes at a cost of easy refactoring and search/replace, but it's a potential advantage).

From what I can see, the only real reason to use namespaces in TS projects is to allow for code-splitting across multiple files or where you're often importing classes of the same name into the same module (and therefore refactoring is a bigger concern).

In my tiny little demo there (which again is more about the configuration than the typescript code, but anyway...) - I don't see how introducing namespaces would actually improve the setup.

It's very possible I missed something though, and I'd love to learn - so please correct me if I'm missing something!

Link to comment
Share on other sites

I think you're talking about aliasing and they're talking about putting the class in namespace when you create the class. So then users don't need to import each class to give them better names(aliasing). Instead users could reference the classes like this for example: Boilerplate.Vehicles.Jaguar and Boilerplate.Animals.Jaguar

Maybe this can help:

// animals.ts
namespace Boilerplate {
    export namespace Animals {
        export class Jaguar { 
    
        }
        export class Fox {

        }
    }
}

// vehicles.ts
namespace Boilerplate {
    export namespace Vehicles {
        export class Jaguar { 
    
        }
        export class Mercedes {

        }
    }
}

// main.ts
let jaguarAnimal  = new Boilerplate.Animals.Jaguar();
let jaguarVehicle = new Boilerplate.Vehicles.Jaguar();

 

Link to comment
Share on other sites

Don't you still need to import it to use it though?

To me that's the big distinction - in Unity, if a .cs file exists in the project, it gets imported into the global namespace.

With ts, you need to explicitly import it - which means you can have conflicting classes in a "project" as long as you aren't importing them together at the same time.

I'm not anti-namespace, I'm just trying to figure out if it's really as necessary to start a project like that from the outset

Link to comment
Share on other sites

With a module system namespacing becomes irrelevant (namespacing causes way more problems than it solves), the only issue is with code that stores things on global (a shared namespace), which highlights how shared state leads to many problems (although a module system does not solve shared state problems at all).

By using a module system and aliasing you eliminate naming clashes, which is impossible to achieve with namespaces that tack on to global (I've no idea what TS does with namespaces, from the comments in this thread looks like its old-school JS namespacing i.e. tacking to global).

Another advantage of modules is that a great deal of tooling now available can work against the module specification and give you numerous advantages.

From looking through your `src` folder its immediately obvious what each file includes (via imports) and it doesn't mean I need knowledge of all the things contained in a namespace, just what you import.

Aside from Pixi's plugin system (which doesn't work with modules, I'm not an expert on it mind you, but sounds like plugins are included and tack on to a global pixi object) I'm also not clear why people dont like import/export, apart from saying its tricky no-one has offered a situation where it actually is tricky.

If someone could explain where import/export get tricky it might help clarify why a namespace system solves those problems, because it introduces a heap of problems (some of which I've stated already).

Link to comment
Share on other sites

@mattstyles namespaces are merged automatically, there are no problems with in-module local namespaces. Import/export is used because ".js" files are accessed remotely, and you cant just include whole subdirectory which has a module.

And we can just compile(concat) single modules and then apply import/exports.

I will never forget @rich's mathematics code where every function is an export in separate file, its my personal nightmare.

UPD. The problem is that pre-built modules are referenced wrongly in IDE. We need project file that specifies which modules are local, to jump between TS files and not TS.D that are build already. That's both JS/TS problem, I hope people will solve it.

Link to comment
Share on other sites

On 5/15/2017 at 7:06 AM, dmko said:

Don't you still need to import it to use it though?

No, you don't need to import. You can see in the main.ts file that I just start using the classes right away. In index.html file there would be a script tag for each of the above three files - or you could concat all of the files that add to the Boilerplate namespace for easier usability.

 

Edit: you don't need to import but you can if you want shorter names for example:

import Jaguar = Boilerplate.Vehicles.Jaguar;

let myCar = new Jaguar();
Link to comment
Share on other sites

If each library uses its own unique namespace then name conflicts between libraries are avoided. And for a large project spit into parts, if each part uses a unique namespace then name conflicts between these parts are avoided.

Like in my example the namespace Boilerplate is added to the global namespace and then more namespaces are added to the Boilerplate namespace (to organize the classes and keep their names from conflicting). Boilerplate is the only one added to the global namespace, so as long as no other code adds to the Boilerplate namespace, naming collisions with the Boilerplate library are avoided. Keeping the "top-level" namespaces (those that are added to the global namespace, like Boilerplate in my example) unique is relatively trivial, especially if each is named after the library or project/part.

Link to comment
Share on other sites

I think a big selling point is that the burden and hassle of avoiding name conflicts is removed from the user and instead handled by the library. So the library (or project part) is more self sufficient. Even if it only takes a tiny bit of thought and work to import and uniquely name (alias) each class, it still adds up and is one more thing to worry about/do over and over again. So instead of making each user do this for each usage, we can handle the naming/organizing once for all usages.

Link to comment
Share on other sites

"The top-level module here Shapes wraps up Triangle and Square for no reason. This is confusing and annoying for consumers of your module"

"Because the consumer of a module decides what name to assign it, there’s no need to proactively wrap up the exported symbols in a namespace."

https://www.typescriptlang.org/docs/handbook/namespaces-and-modules.html

Link to comment
Share on other sites

That's pretty much what I was trying to say... though it brings another point - along with the type of aliasing above, you can do this too:

import * as Animals from './animals/Jaguar'; 

let cat:Animals.Jaguar = new Animals.Jaguar();

It doesn't solve the issue of code-splitting though, which I think might just be contradictory to import style anyway. It seems namespaces in typescript are pretty arbitrary when everything is split into imported modules...

Link to comment
Share on other sites

15 hours ago, Taz said:

If each library uses its own unique namespace

That's a big 'if', and its unworkable with namespaces when you hit that problem. If you favour small modules (which is good for many many reasons) then the chance of conflicts increases. The es6/commonJS (even AMD) module system solves this.

15 hours ago, Taz said:

I think a big selling point is that the burden and hassle of avoiding name conflicts is removed from the user and instead handled by the library.

But namespaces don't do this, they merely enforce that the library must create a global object, meaning that as a consumer you have 0 options when conflicts occur. With a module system you name it what you like, normally the same as the library so you don't even need to deal with naming conflicts. Removing complexity from a consumer is a valid goal, removing choice is a poor one.

21 hours ago, ivan.popelyshev said:

And we can just compile(concat) single modules

Not sure why this is advantageous over using a bundler, apart from the fact that underneath its simpler, there are multiple bundlers to choose from with simple apis so that from the consumer end its no simpler to call, plus, bundlers will generally give you many many more advantages than just stitching files together, not least of which is encapsulation, nor circular reference problems.

Of course, if one of your compile targets is a commonJS/module compatible target (as browsers now/soon are) then concatting/bundler becomes unnecessary.

In fact, I'd go so far as to say concatting your files together, which requires globals to work, is an anti-pattern. It was previously necessary in JS, but with the new module spec it is not, and with newer bundling tools globals are either greatly reduced (to only bundling globals, such as AMD tooling) or removed completely (such as browserify's commonJS implementation).

21 hours ago, ivan.popelyshev said:

I will never forget @rich's mathematics code where every function is an export in separate file, its my personal nightmare.

I can understand this pain, but its not a module system problem, no JS module system enforces one export per file so if it makes sense to export many functions from a file then that should be done. With either a proper module system or a namespace system you should still ensure you aren't sharing state between these functions (or documenting where you are), something which is actually encouraged by a namespace system and discouraged by a module system (neither make this explicit, just one helps and one hinders, a good framework/library/module/tool should help developers fall into a pit of success).

21 hours ago, ivan.popelyshev said:

The problem is that pre-built modules are referenced wrongly in IDE.

Architectural decisions based on tooling support is generally a poor one (I'll take the case of writing testable code as an exception here where writing code that becomes inherently testable is a decision to improve your code, providing support for a specific IDE is not). JS (and any interpreted language) is inherently not-IDE friendly, but there are many many solutions out there that don't involve libraries/modules being written in a specific way. Totally agree its a JS problem, one that is a side-effect of being an interpreted language.

Link to comment
Share on other sites

On 5/16/2017 at 0:22 AM, dmko said:

"The top-level module here Shapes wraps up Triangle and Square for no reason. This is confusing and annoying for consumers of your module"

"Because the consumer of a module decides what name to assign it, there’s no need to proactively wrap up the exported symbols in a namespace."

https://www.typescriptlang.org/docs/handbook/namespaces-and-modules.html

"Because the consumer of a module decides what name to assign it, there’s no need to proactively wrap up the exported symbols in a namespace."

This could be rewritten as: "Because the exported symbols are proactively wrapped up in a namespace, there's no need for the consumer to decide what name to assign."

 

As for the example, it's an example of improper usage so of course it will be confusing and annoying;) Instead of importing as 'shapes' and using 'shapes.Shapes.Triangle' to refer to the the Triangle class, which is ridiculous, namespaces can be used to allow the consumers to refer to the class as 'Shapes.Triangle' without the need to import nor name:

// shapes.ts
namespace Shapes {
    export class Triangle { /* ... */ }
    export class Square { /* ... */ }
}

// consumer.ts
let t = new Shapes.Triangle();

 

Link to comment
Share on other sites

The goal I was suggesting is to remove the redundancy of importing and uniquely-naming-after-the-library the same way over and over again, not to reduce choice. Users still have the choice to alias and pick name they want, but also have the option to skip the imports and just use the names established by libraries.

As long as the per library namespaces are kept unique, avoiding name conflicts between them is a done deal and there's no need to import nor even think about naming. But using two libraries which happen to add to the same top-level namespace, yeah that should be easier to handle. There are options (like refactoring one library's top-level namespace), but yeah no good options I guess, at least not yet..

Link to comment
Share on other sites

Not sure I follow... afaik webpack needs an entry point and will bundle up everything through the dependency graph created from that point forward... if something isn't imported it isn't bundled/useable. Not sure how the IDE is even supposed to know about it... if I don't import something, VSCode doesn't know about it at least with my current setup.

Seems like you're proposing that everything in the folder should be automatically available to the IDE somehow, and also that it be included and injected into the global namespace (or some sort of global module that encapsulates everything).

Not saying it's necessarily a bad approach, but it seems inferior to the way webpack+ts/microsoft wants you to do things.

Do you have an example project on github where it's set up the way you describe?

Link to comment
Share on other sites

11 hours ago, Taz said:

Users still have the choice to alias and pick name they want, but also have the option to skip the imports and just use the names established by libraries.

This sounds absolutely dreadful, does this imply I can import an alias if desired but also just grab the namespace from global? Wow, why make something so simple more complex than it needs to be.

11 hours ago, Taz said:

The goal I was suggesting is to remove the redundancy of importing and uniquely-naming-after-the-library

Why is this even a worthwhile goal?

Is naming an import, usually as the name of the module, really so hard?

Is one additional line, often auto-completed, really a chore? If you're worried about code-bloat then some of it is recouped at the module end where exporting is shorter than namespacing + exporting, plus, as I'll go on about, there are other advantages es6 modules have when it comes to reducing the amount of code required.

As an example, what does this snippet do?

var triangle = new Shapes.Triangle(200)

You have no idea, and, to make matters worse, you have absolutely no idea where `Shapes` comes from. How does the global variable `Shapes` get put into the code? Into the page? Where does it come from? Where do I look to find out what the Triangle constructor does? What the param means?

The module system way:

import Shapes from 'shapes'

var triangle = new Shapes.Triangle(200)

You've still got no idea what that code does but you'll know exactly where to look, immediately, because you know how import works and where imports import from (these are standard and would only change if a developer has explicitly changed it in the build system).

Furthermore, I can actually do some other things with a proper module system, consider this:

import {Triangle} from 'shapes'

var triangle = new Triangle(200)

This follows object destructuring, if you've never encountered this before then it pulls `Triangle` from the `Shapes` object. This is nice, but its simple sugar, however, there is more.

Whilst the destructuring sugar is nice, its only sugar, but, with es6 modules it is not mere destructuring, certain tooling have taken this idiom to enable 'tree-shaking' (not that I'm not a fan of tree-shaking, I think the process should happen at a different stage, but thats a different story) or, smart module inclusion, it does this by statically analysing the dependency tree (which it knows about because you've used a defined syntax to do so) and deciding what is and isn't required for this code to run (this is new for JS but not new for programmers, many many languages do this).

The above paragraph is big, really big, so I'll elaborate. Imagine shapes exports 100 different shape constructors, but you only want Triangle, well, by analysing the dependency graph the tooling can ascertain whether you need more shapes and decide to include ONLY what you need, and it can do it deterministically because es6 modules are static, and it can do it down the dependency tree, wunderbar.

Outside of hoping that your minification process can remove redundant code (which it can not do effectively because it does not have as much information as module tooling does) this is impossible with namespaces. Compiled languages generally don't care (for the same reason, neither does node really), but as you're serving your stuff over the internet you should.

Just reread @dmkos reply and realised I've just rehashed what you already said!

By including imports explicitly at the top of a file its immediately clear what this code directly relies upon (although following the dependency tree further is difficult), easy for new developers to the code and easy for you when you return to that code after several weeks/months. It sets the scene for the code.

Link to comment
Share on other sites

At the risk of dirtying this back-and-forth, I just wanted to say I've been enjoying the hell out of this discussion. I know all modernity screams modules over namespaces these days, and I even think I agree at the end of the day. However, I only started really learning JS and Typescript recently, having come primarily from ActionScript since its inception. With that background, I have secretly been partial to a hybrid of current module handling and namespaces. For me at least, having the library's user determine the alias rather than defaulting to a developer's root package/namespace invites confusion for multiple developers sharing code that use the same libraries. JS modules currently look not-ready-for-my-suborn-ways, with so many flavors and build dependencies, but they're getting closer for sure. Maybe it's just muscle memory, but I was quite fond of reverse domain name notation being sort of a forced rule for packaging/namespacing imports in languages like AS3. Looking forward to seeing how the dust settles... both in this thread and in future ECMAScript.

As a side note, this article resonated with me: https://hackernoon.com/native-es-modules-ready-for-prime-time-87c64d294d3c

Link to comment
Share on other sites

Yes naming the same module the exact same way over and over is a chore I would rather do w/out. If it's usually named after the library to prevent collisions anyway, why not allow that to be the default naming behavior? And yes for projects with multiple developers default naming can help ensure that everyone's using the exact same names. Really I'm a proponent of name-once-ability, not of typescript namespace specifically. If could optionally omit the name when importing module and use default, that would be an improvement to take IMO. I don't see what's so dreadful about being able to use default names;)

Link to comment
Share on other sites

Join the conversation

You can post now and register later. If you have an account, sign in now to post with your account.
Note: Your post will require moderator approval before it will be visible.

Guest
Reply to this topic...

×   Pasted as rich text.   Paste as plain text instead

  Only 75 emoji are allowed.

×   Your link has been automatically embedded.   Display as a link instead

×   Your previous content has been restored.   Clear editor

×   You cannot paste images directly. Upload or insert images from URL.

Loading...
 Share

  • Recently Browsing   0 members

    • No registered users viewing this page.
×
×
  • Create New...