es6ify.runtime makes my THREE js importing empty object - javascript

I am trying to browserify javascript es6 code with es6ify.
My code is using THREE js library (a webgl abstraction library), and everything works pretty well until I try to add the traceur compiler runtime at the top of the bundle.
Here is my gulp task (the problem mustn't be related to gulp):
gulp.task('build', function(){
browserify({debug: true})
.add(es6ify.runtime)
.transform(es6ify)
.require(require.resolve('./app/index.js'), {entry: true})
.bundle()
.pipe(source('bundle.js'))
.pipe(gulp.dest('./build/'));
});
somewhere in my application, I am trying to do something like:
import THREE from 'three';
var toto = new THREE.WebGLRenderer([...]);
and this fails because THREE is actualy an empty object, thus WebGLRenderer is undefined.
THREE js is in the dependencies of node package.json, and THREE JS is usually imported well. But when I add add(es6ify.runtime) in my build process, it causes require('three') to be an empty object...
is there something I missed?
thanks!

well, sorry for the inconvenience, I just found a solution.
If I simply exclude the node modules from traceur compilation, it works:
so instead of:
.transform(es6ify)
I have now:
.transform(es6ify.configure(/^(?!.*node_modules)+.+\.js$/))
(this is an es6ifyAPI documentation sample)
which is by the way faster to compile :-)

Related

does typescript with webpack tree shaking work with es5?

i have a module which is javascript ES5 module called phaser. It also contains type declarations.
In my main.ts file, i have
import * as phaser from 'phaser'
If i build it using webpack, (I am using ts-loader for module rules in webpack), What happens is the final output doesn't contain phaser at all. If i add console.log(phaser) to my file, then the build output contains phaser library.
Question: Seems like because of I don't use phaser , tree shaking works, but why? I am asking because, phaser is written in ES5,so even though i am importing it as ES6, tree shaking still shouldn't work. A good example of this is that if I change main.ts to main.js and have only importing statement as i have above, even though I don't console.log(phaser), the final build will contain phaser . Looks like it's something related to ts-loader or tsconfig.json? Any ideas? I have empty tsconfig.json not to complicate things.

Migrating node code base to TypeScript: global scope?

I'm trying to migrate large node codebase to TypeScript. To make things easier, I just want to start by renaming .js files to .ts files, fix any semantic issues, without much refactoring, and then gradually improve the code base.
Let's consider the following example:
logger.js:
exports = function(string) {
console.log(string);
}
moduleA.js:
var logger = require('./logger')
exports.someAction = function() {
logger.log('i'm in module a')
}
moduleB.js:
//var logger = require('./logger')
exports.someOtherAction = function() {
logger.log('i'm in module B')
}
moduleC.js:
var moduleA = require('./moduleA');
var moduleB = require('./moduleB');
exports.run = function(string) {
moduleA.someAction();
moduleB.someOtherAction(); //exception here - logger not defined
}
So if i execute moduleB.someOtherAction() i'll get an exception, because logger is not defined in scope in moduleB.js.
But typescript compiles just fine, because logger is declared in moduleA, and this is because (if i understand correctly) typescript treats all of those files as a single compilation unit.
So is there anyway to avoid this without much refactoring?
Update
I've created a sample project which can be found here
If i run typescript compiler, i get no errors, though logger is commented out in moduleB.ts:
g#w (master) ~/projects/ts-demo: gulp generate
[10:39:46] Using gulpfile ~/projects/ts-demo/gulpfile.js
[10:39:46] Starting 'generate'...
[10:39:46] Starting 'clean'...
[10:39:46] Finished 'clean' after 11 ms
[10:39:46] Starting '<anonymous>'...
[10:39:47] Finished '<anonymous>' after 1.4 s
[10:39:47] Finished 'generate' after 1.41 s
g#w (master) ~/projects/ts-demo:
Update 2
Ok, so this is expected behaviour as stated in TypeScript deep dive book:
If you now create a new file bar.ts in the same project, you will be allowed by the TypeScript type system to use the variable foo from foo.ts as if it was available globally
Louy is right. Every file (when compiling for node with CommonJS) is created like its own module. Just like the normal case in node.
So to get this to work you could do something like this:
logger.ts
export default (str: string) => console.log(str);
moduleA.ts
import logger from './logger';
export var someAction = () => {
logger("i'm in module a");
}
moduleB.ts
import logger from './logger';
export var someOtherAction = () => {
logger("i'm in module B");
}
moduleC.ts
import { someAction } from './moduleA';
import { someOtherAction } from './moduleB';
someAction();
someOtherAction();
I also used a tsconfig.json:
{
"compilerOptions": {
"module": "commonjs",
"noImplicitAny": true
},
"files": [
"logger.ts",
"moduleA.ts",
"moduleB.ts",
"moduleC.ts"
]
}
Compiling this (just type tsc in the folder containing the tsconfig.json) results in one .js file per .ts file. It should compile and run just fine. The main thing you need to take from this is that each file is its own self contained module when compiling for node/CommonJS.
EDIT Due to edits in question:
Ok after looking at your code again there is a couple of reasons that this compiles but fail to run properly:
If you send a group of files to the compiler all references in all of those files, and all of the variables in the global scope will be available in all .ts files sent to the compiler. This is confusing and sometimes regrettable, but at the moment it is how things work. This in your case results in that the require function defined in the global scope in node.d.ts is available in all .ts files, not only in moduleC.ts; if the reference is removed from moduleC.ts you will get an error in logger.ts because the require function is not defined for example. It also results in var logger = .. defined in logger.ts will be seen as available in the other .ts files. Typescript assumes that it will be available at run time.. not ideal when compiling for node, but not a problem when you are writing actual typescript not trying to compile pure javascript. In fact, because it is pure javascript the compiler does not recognise it as a node module, and treats it as a generic javascript file.
When compiling with commonjs each file is compiled on its own by the typescript compiler. If any references or imports are found those will be followed and used to type and verify the code of course, but the compilation is still done on a per file basis (var something = require('./whatever'); is not an import, it is interpreted by the compiler as a variable being assigned by a function taking a string as an argument.)
So thats why it compiles, lets go on to why its not working then.
Each file outputted by the compiler in commonjs mode is its own "node-module". If you want to have access to one module from within another in node (this has nothing to do with typescript really, it is just how node works) you will need to require that file. in moduleB.ts (and in moduleB.js for that matter) there is no indication to node to say what logger is. So you simply need to require logger.
In the example you are referring to in your second update of the question commonjs is not being used, and further more it is in connection with typescripts internal modules - not node modules which is a completely different thing.
So the simple answer is, that if you want to use one file from within a another in node you have to require it. Simply uncommenting the commented reference in moduleB.ts should get everything up and running.
In the current state of your code the typescript compiler is not helping you much, because it can't really. The code is close to pure javascript, to get decent help from the compiler you will need to start converting it to typescript. The first step might be to replace var asdf = require("") with import asdf = require("");, this will make it possible for the compiler to check the references and help you a lot in return.
I realise this is a wordy answer, but you managed to create yourself a confusing and difficult to explain behaviour.
There's something called internal/external modules in typescript.
Internal modules can declare stuff that can be referenced everywhere in the project. Think about typings/lib files, you should be able to reference things like Error and String everywhere in your project.
External modules on the other hand have to be imported to be referenced. This is what you want to do.
The difference between internal and external modules is that external ones use import and export.
Internal ones use declare module/namespace/var.
Just add "compilerOptions": {"module": "commonjs"} to your tsconfig.json file to enable external modules.

Require JS files dynamically on runtime using webpack

I am trying to port a library from grunt/requirejs to webpack and stumbled upon a problem, that might be a game-breaker for this endeavor.
The library I try to port has a function, that loads and evaluates multiple modules - based on their filenames that we get from a config file - into our app. The code looks like this (coffee):
loadModules = (arrayOfFilePaths) ->
new Promise (resolve) ->
require arrayOfFilePaths, (ms...) ->
for module in ms
module ModuleAPI
resolve()
The require here needs to be called on runtime and behave like it did with requireJS. Webpack seems to only care about what happens in the "build-process".
Is this something that webpack fundamentally doesn't care about? If so, can I still use requireJS with it? What is a good solution to load assets dynamically during runtime?
edit: loadModule can load modules, that are not present on the build-time of this library. They will be provided by the app, that implements my library.
So I found that my requirement to have some files loaded on runtime, that are only available on "app-compile-time" and not on "library-compile-time" is not easily possible with webpack.
I will change the mechanism, so that my library doesn't require the files anymore, but needs to be passed the required modules. Somewhat tells me, this is gonna be the better API anyways.
edit to clarify:
Basically, instead of:
// in my library
load = (path_to_file) ->
(require path_to_file).do_something()
// in my app (using the 'compiled' libary)
cool_library.load("file_that_exists_in_my_app")
I do this:
// in my library
load = (module) ->
module.do_something()
// in my app (using the 'compiled' libary)
module = require("file_that_exists_in_my_app")
cool_library.load(module)
The first code worked in require.js but not in webpack.
In hindsight i feel its pretty wrong to have a 3rd-party-library load files at runtime anyway.
There is concept named context (http://webpack.github.io/docs/context.html), it allows to make dynamic requires.
Also there is a possibility to define code split points: http://webpack.github.io/docs/code-splitting.html
function loadInContext(filename) {
return new Promise(function(resolve){
require(['./'+filename], resolve);
})
}
function loadModules(namesInContext){
return Promise.all(namesInContext.map(loadInContext));
}
And use it like following:
loadModules(arrayOfFiles).then(function(){
modules.forEach(function(module){
module(moduleAPI);
})
});
But likely it is not what you need - you will have a lot of chunks instead of one bundle with all required modules, and likely it would not be optimal..
It is better to define module requires in you config file, and include it to your build:
// modulesConfig.js
module.exports = [
require(...),
....
]
// run.js
require('modulesConfig').forEach(function(module){
module(moduleAPI);
})
You can also try using a library such as this: https://github.com/Venryx/webpack-runtime-require
Disclaimer: I'm its developer. I wrote it because I was also frustrated with the inability to freely access module contents at runtime. (in my case, for testing from the console)

Gulpfile Javascript Concat Issue

I am trying to get my gulp file working right and I am not sure whats wrong. Here is the file. https://gist.github.com/digilord/9265640 The section I am having issues with is the js:dev. When a coffeescript file is changed it drops the resulting js file into the app/js directory. The js:dev rule isn't picking up that change. Can anyone tell me what I am missing?
Immediate solution: gulp-watch needs the glob information to find new files. You can pass your glob to its glob option. Something like:
return gulp.src('app/js/*.coffee')
.pipe(watch({glob: 'app/js/*.coffee'}))
Meanwhile if you were to use gulp.watch() it wouldn't pick up new files either but only because of a bug in its dependency, which a fix is coming soon for.
I have to also mention that your gulpfile is needlessly complex. Your coffee and js operations could be in the same stream instead of creating intermediate js files. I also don't really know why some of your streams are repeated twice. Stay dry:
var coffeeGlob = 'app/js/*.coffee';
gulp.task('js:dev', ['clean:dev'], function() {
return gulp.src(coffeeGlob)
.pipe(watch({glob: coffeeGlob}))
.pipe(coffee({bare: true}).on('error', gutil.log))
.pipe(concat('app.js'))
.pipe(gulp.dest('build/js'))
.pipe(reload());
});
As I mentioned on IRC the boilerplate you based it on might not be up to date with best practices, and I recommend basing on something newer like Yeoman's gulp generator's gulpfile.

Client Side Dependency Management in CoffeeScript

What is the best way to do Dependency Management amongst CoffeeScript files if the resulting javascript files eventually need to be concatenated together for use on the client side?
For server side CoffeeScript I can just use the 'require' keyword to require other pieces of javascript. When this is compiled for client side apps to have the dependency tree, based on the requires, calculated and a concatenated stand alone javascript file produced. Is there anyway to do this in a generic way?
Another option is to use CoffeeToaster, which uses another approach than implementing AMD / CJS module patterns.
Take a look:
http://github.com/serpentem/coffee-toaster
Usually, for client-side packaging of JavaScript (and CSS), you want some sort of asset-packaging plugin. We use Jammit, but there are many other options: Sprockets, Django-Compress ... and more.
Villain (a CoffeeScript game engine for browsers) defines functions that do just that (dependency management and js concatenation).
The code for that is in these two files:
cake.coffee, see , determineDependencies(), wrapModule(), and bundleSources()
brequire.coffee, a require replacement for the browser, to use with wrapModule()
I use it here (see the 'bundle' Cake task).
Note: I just declare the 'main' module's build directory, and Villain scans my compiled JS files to build the dependency tree (starting with index.js), then creates a JavaScript bundle file containing Villain's require replacement and all my relevant code sorted and correctly wrapped.
Villain's author uses it in orona, a CoffeeScript game made with Villain.
For dependency management on client side, I use requirejs for javascript and coffeescript source. It'possible to use a requirejs plugin to load natively coffee files, but I prefer to "compile" into js.
requirejs also provide / work with r.js optimizer. It can be used to aggregate a set of js file into one and minified it. you don't have to specify the file to aggregate it is the dependency definition of each module require by your "main.js". (feature that match your request)
Something I like a lot with requirejs, it "promots" creating module and declare explicit dependencies.
# A.coffee
define(() ->
class A
constructor: (#c1, #c2) ->
#c2 ?= 1
m1 : () ->
"hello"
toString : () -> "#{#c1}#{#c2}"
)
# B.coffee
define(['A'], (A) ->
a = new A(33)
console.log(a, a.m1())
)
I've used (and I guess am still using) requirejs but I've started to find it to be fairly clumsy. A lot of my files end up having ~10-12 imports at the top which just take up a lot of space and don't look great.
For a new project I tried browserify. It's great! And if you use grunt (you should), you can make a watch task to browserify your code on change. grunt-browserify also provides the ability to do a coffeescript transform.
https://github.com/jmreidy/grunt-browserify
So your watch task in your Gruntfile.coffee would look something like:
watch:
files: [
"app/**/*.coffee"
]
tasks: "browserify"
browserify:
'build/app.js': ['app/**/*.coffee']
options:
transform: ['coffeeify']

Categories

Resources