MarvinB Posted September 8, 2016 Share Posted September 8, 2016 Hi Guys, I have been using ES6 for my game development and I really enjoy the functionality it supports. I started of using this example (Boilerplate) and kept the gulp compiling/traversing script, as it suits my needs well. I ran into a little challenge, that I could not figure out yet, but maybe somebody else has a good idea on how to achieve the following: After the gulp script ran through it creates a build.js file, which starts like this (function e(t,n,r){function s(o,u){if(!n[o]){if(!t[o]){var a=typeof require=="function"&&require;if(!u&&a)return a(o,!0);if(i)return i(o,!0);var f=new Error("Cannot find module '"+o+"'");throw f.code="MODULE_NOT_FOUND",f}var l=n[o]={exports:{}};t[o][0].call(l.exports,function(e){var n=t[o][1][e];return s(n?n:e)},l,l.exports,e,t,n,r)}return n[o].exports}var i=typeof require=="function"&&require;for(var o=0;o<r.length;o++)s(r[o]);return s})({1:[function(require,module,exports){ 'use strict'; and ends like this: },{}]},{},[44]) So it creates this nice little sandbox around my code. The sandbox is unfortunately also what is giving me a little headache, as I would like to consume some off the classes/methods outside of sandbox. For instance to simple call new Game(); from another script, that is linked to my index file. Important to mention here is that I would like to keep the sandbox and only expose certain functionality to the outside world. Hope somebody has a clever idea to help me on this issue! Thanks guys! Link to comment Share on other sites More sharing options...
tips4design Posted September 8, 2016 Share Posted September 8, 2016 That's what exports are for https://developer.mozilla.org/en/docs/web/javascript/reference/statements/export Otherwise you could return a JavaScript facade object containing the functions/object you want to be visible outside that closure. Link to comment Share on other sites More sharing options...
mattstyles Posted September 8, 2016 Share Posted September 8, 2016 Don't bother mucking with build, just expose what you need for dev to the window so you have access to it for debugging. e.g. // super-awesome.js import Something from './something' const Game = class .... window.Game = Game export default Game An even nicer solution is to also use a transform that shims environment variables into your build (e.g. envify) then you can wrap that dirty global leaking dev code in an `if (process.env.DEBUG)` and call your build script with environment variables to control it, the super good thing here is that any decent minifier can perform dead-code removal and delete that entire block from your prod builds. I've no idea if the build process you're using supports this easily, I've got a boilerplate for your reference https://github.com/mattstyles/phaser-es2015, although I've not added the dev switching behaviour, I can probably provide other examples if you need to see something to clarify though. Link to comment Share on other sites More sharing options...
MarvinB Posted September 8, 2016 Author Share Posted September 8, 2016 Hi Guys, I tried the window.game approach, but calling new window.Game(); did not create my game unfortunately. The game appears in the window object though. Are these exports registered somewhere globally? (function e(t,n,r){function s(o,u){if(!n[o]){if(!t[o]){var a=typeof require=="function"&&require;if(!u&&a)return a(o,!0);if(i)return i(o,!0);var f=new Error("Cannot find module '"+o+"'");throw f.code="MODULE_NOT_FOUND",f}var l=n[o]={exports:{}};t[o][0].call(l.exports,function(e){var n=t[o][1][e];return s(n?n:e)},l,l.exports,e,t,n,r)}return n[o].exports}var i=typeof require=="function"&&require;for(var o=0;o<r.length;o++)s(r[o]);return s})({1:[function(require,module,exports){ 'use strict'; Object.defineProperty(exports, '__esModule', { value: true }); exports.setupChat = setupChat; Or do they only live within this function e(t,n,r) .... scope? Could I call them with regular ES5 outside of this function scope to start up my game and basically channel into this function scope? Apologies for all these follow up question. This kind of scoping is new to me. Link to comment Share on other sites More sharing options...
mattstyles Posted September 8, 2016 Share Posted September 8, 2016 Have a read up on commonjs modules and how browserify brings that to the browser, I'll summarise briefly, apologies if I'm rehashing stuff you know. Commonjs (most popularly used by node) module spec defines 2 methods to allow you to expose and consumes modules, namely `require` and `exports` (there are 2 ways to export things but ignore that for now, just know that you expose a module to another one via an export). ES2015 promised the same thing but with slightly different syntax, `import` and `export`, however, delivering this to the browser is very complex and isn't quite ready yet so we have to use something else to provide that functionality. The boilerplate uses babel to turn the proposed module spec into the commonjs format that browserify understands and then uses browserify to turn that into something the browser can use. What all this means is that browserify is primarily a tool for consuming and then packaging modules and the easiest (and usually best) way to use it is to create a single bundle that contains your application code (this includes all your modules that you create and any dependencies you pull in). A module system should be self-contained with a clean and clear api for inclusion and exposure of modules, this makes interoperability possible and provides encapsulation, hence the global environment (in the browser case, window) is not muddied. Browserify can export globals but its not usually a good idea, unless you have very specific reasons for doing so (I dont think you do) which is usually to do with packaging library code and usually isn't an issue for application code. The wrapping function just creates a closure and you don't really want to muck with it, browserify creates it as an automated build step. If you want to explicitly create globals then you can, nothing in the browser or JS can stop you (without freezing window and you can't do that). window.foo explicitly creates a global and whilst this can be useful sometimes for dev/debug it isn't a great idea and its very easy to get into the situation where you punt your app into prod mode on a staging box only to find something is broken because part of your app was relying on a global when you thought you wired it up right and everything explodes, often without a clear explanation of why and where its exploded. I'm not clear whether you want to just use the console to poke at some of your structures during development or you just want to consume one module with another, the following stuff should solve both. game.js - this contains your main game module (I'm pulling in lodash here just as an example of consuming a 3rd-party module) import _ from 'lodash' class Game { run (data) { _.map(data, d => console.log(d)) } } window.Game = Game export default Game index.js - this is the main entry point of your application and consumes the game module you just created import Game from './game' const game = new Game() window.game = game game.run([1, 2, 3]) To use browserify to package this code for the browser you'll have to use additionally transpile new-spec JS into something the browser can use, babelify is used by your boilerplate, the following command line instruction can do this browserify -t [ babelify --presets [ es2015 ] ] index.js > bundle.js All this does is tell browserify to use the babelify transform module with a preset that understands the proposed module spec, it tells it to start at index.js and punt its output to bundle.js. Bundle.js contains all of the transpiled code and those imports and exports get transformed into references to functions which execute the code those files contain (browserify uses filepaths to determine where modules live, hence ./game is a local import but browserify will look inside the node_modules directory, and elsewhere if you like, for the lodash dependency). If you punt bundle.js into a browser then it starts executing the code inside index.js, which immediately executes game.js which defines a Game class, punts that Game constructor to window (global) and passes that reference back to index.js, which then instantiates the class and calls a method on it, the output of this being that you should be 1, 2, 3 output in the console, although with 2 shiny globals sitting accessible to you, the Game constructor and the game instantiated object. spencerTL, drhayes and MarvinB 3 Link to comment Share on other sites More sharing options...
MarvinB Posted September 8, 2016 Author Share Posted September 8, 2016 Hi Matt, First of all, thank you for taking the time and writing up all of this! Sorry, if I will mention something you already explained, but I still need to understand everything fully. So lets say I load my game-bundle.js script transformed earlier by babelify and an angular controller within my index.html <script src="scripts/phaser.js"></script> <script src="scripts/angular-controller.js"></script> <script src="scripts/game-bundle.js"></script> To mention here is that my angular code is not tranformed with babelify. So an ES6 import within the directive that would grab my game class to init my game within the directive is not possible. (I feel like I am making it more complicated actually, by not compiling angular from ES6 to ES5 aswell and get one large bundled file, but maybe there is way to keep it as is). You mentioned that: Quote The wrapping function just creates a closure and you don't really want to muck with it, browserify creates it as an automated build step. Is there a simple way to deactivate this automated step, such that maybe all my classes become exposed? Link to comment Share on other sites More sharing options...
MarvinB Posted September 9, 2016 Author Share Posted September 9, 2016 I wrote a function that calls new game inside the function sandbox and appended this function to the global window object and it works just perfectly from outside of the script to call my function. Thanks a lot, Matt! Didn't even need to call any import of some kind. Link to comment Share on other sites More sharing options...
mattstyles Posted September 9, 2016 Share Posted September 9, 2016 1 hour ago, MarvinB said: I wrote a function that calls new game inside the function sandbox and appended this function to the global window object and it works just perfectly from outside of the script to call my function. Thanks a lot, Matt! Didn't even need to call any import of some kind. Thats the only way you can do it, unless you tie your angular build in with your game build (which is perhaps a good idea, but, for now, you have it working so I wouldn't worry too much about your game bundle manually placing a global, if you used browserify with its export flags that is all it would do anyway, its all it can do—also, depending on how you've structured your angular code you'll have a bitch of a time getting it to play nice with anything else, its an ugly beast in many many respects, often best to leave it be and separate things, unfortunately ng2 doesn't solve this problem either but I shouldnt be too hard, its a choice they made to be opinionated and it wasnt made lightly). That little wrapping closure also defines what the require function does so its absolutely necessary for importing all your files, inside the bundle. The way scripts work is that their only means of communication is via a global object of some sort, they don't share state (you could also communicate via an intermediary, requiring server hops, but that wouldn't really be of help here!). This is exactly how namespacing works in JS, you tack a global on to the window and all your subsequent script tags can then have access to it, although you have to be a little careful about the order you place your scripts in (this can get awkward with many many files and concatenation, its quite easy to slip up and fall into a circular dependency trap or a deferred execution situation). When the module spec hits the browsers then this potentially becomes easier but there will still be some very interesting choices for you to make as a developer. You can try using the systemjs polyfill to get a taste of this behaviour right now, but, it can get pretty hairy. Whilst I said that polluting the global namespace is dangerous (it is) there are lots of situations where it makes more sense than worrying about creating some complex build step. For a start, in this case, you presumably have total control over the page your stuff is headed for so you can be reasonably assured that you aren't going to hit major problems, particularly if you limit those globals you are polluting with, I'd say polluting with Phaser, Angular and Game objects is fine. If you go ahead and add loads of 3rd party stuff later you'll have to be a little mindful but you almost always do need to be and you often have little control how these 3rd parties deliver their services anyway. tl;dr I guess the short of it, if you want to really understand it, is to remember that scripts in the browser can only share state via the window object, there are lots of other smart systems around but this is a platform constraint so by all means take steps where necessary to mitigate the risk but remember that if you want to share stuff between scripts then eventually you'll be booting something on to the window object. Link to comment Share on other sites More sharing options...
Recommended Posts