ES6 module import is not defined during debugger - ecmascript-6

While playing around with Babel and Webpack I stumbled into some really weird behavior today.
I threw a debugger in my main.js to see if I was importing correctly, but Chrome's console kept yelling that the module I was trying to import was not defined. I try console logging the same module instead, and I see it printed to my console.
What gives? I've pasted the relevant code snippets below:
main.js
import Thing from './Thing.js';
debugger // if you type Thing into the console, it is not defined
console.log(new Thing()); // if you let the script finish running, this works
thing.js
class Thing {
}
export default Thing;
webpack.config.js
var path = require('path');
module.exports = {
entry: './js/main.js',
output: {
path: __dirname,
filename: 'bundle.js'
},
module: {
loaders: [
{ test: path.join(__dirname, 'js'), loader: 'babel-loader' }
]
}
};

tl;dr: Babel does not necessarily preserve variables names.
If we look at the code generated from
import Thing from './Thing.js';
debugger;
console.log(new Thing());
namely:
'use strict';
function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { 'default': obj }; }
var _ThingJs = require('./Thing.js');
var _ThingJs2 = _interopRequireDefault(_ThingJs);
debugger;
console.log(new _ThingJs2['default']());
We see that Things is not defined indeed. So Chrome is correct.

In some debugging scenarios, it may be sufficient to assign the imported variable to a new variable in local scope. For example:
import Thing from './Thing.js';
const TestThing = Thing;
debugger; // although Thing will not be defined, TestThing will be defined
console.log(new TestThing());
This doesn't fix the core issue at hand, but it can be a workaround for debugging in certain situations.

You can try my webpack plugin which defines debug vars for imports so that they are available in the debugger.
https://github.com/trias/debug-import-vars-webpack-plugin

Related

Web Component Tester - gulp task to run test with each build

I want to put inside gulpfile something like:
require('web-component-tester').gulp.init(gulp);
gulp.task('default', function() {
gulp.watch(['elements/**', 'test/**'], ['test:local']);
});
The purpose is to watch test folders or elements folders (with Polymer components). If some of them will change, run test with each build.
my wct.conf.js:
module.exports = {
root: '.tmp/elements/',
suites: ['**/test/'],
plugins: {
local: {browsers: ['chrome']},
}
};
I found the code above on some page but after I add some tests and then type gulp in my terminal I found error, because .tmp folder is not updated and strange errors like Polymer is not definedor ajax.generateRequest is not a function. I got also right errors when I intentionally made a mistake in a test to fail it, so it looks like something is ok, but not at all.
I add the tests to the existing project with lots of files. When I tried to do the same thing on empty project I also got the same error until I type bower install.
Is there any chance that this is the problem with bower dependencies?
Or have you any idea what is wrong? Is this part of code in gulpfile right to perform the desired effect?
Thanks a lot.
I am not answering your question directly, because its been a while since I've done it that way. But the following defines a sub task from among others to define a task called 'client' which then runs the tests in a frame buffer (so I don't have disturbing windows popping up all over the place when the tests run - they just run and output in a console window. Its effectively spawning a command line version of wct and I don't have a wct.conf file at all.
(function() {
'use strict';
const spawn = require('child_process').spawn;
module.exports = function(gulp) {
gulp.task('test:client',['lint:client'], () => {
var child = spawn('xvfb-run', ['-a', 'wct', '--color'], {cwd: process.cwd()});
child.stdout.setEncoding('utf8');
child.stdout.on('data', function(data) {
process.stdout.write(data);
});
child.stderr.setEncoding('utf8');
child.stderr.on('data', function(data) {
process.stderr.write(data);
});
});
gulp.task('client',function() {
gulp.watch([
'app/index.html',
'app/src/*.html',
'app/test/*.html',
'aoo/mocks/*.html',
'gulpfile.js',
'tasks/*.js'
],['test:client']);
});
};
})();
This file is one file within the tasks directory (which as you can see I am watching)
My gulpfile loads this, and other tasks like so (I copied this from the angular.js team who used it to load some of there tasks supporting angular)
(function() {
'use strict';
require('dotenv').config(); //load our environment
var gulp = require('gulp');
var includeAll = require('include-all');
/**
* Loads task modules from a relative path.
*/
function loadTasks(relPath) {
return includeAll({
dirname: require('path').resolve(__dirname, relPath),
filter: /(.+)\.js$/
}) || {};
}
// *
// * Invokes the function from a Gulp configuration module with
// * a single argument - the `gulp` object.
function addTasks(tasks) {
for (var taskName in tasks) {
if (tasks.hasOwnProperty(taskName)) {
tasks[taskName](gulp);
}
}
}
/**
* Add all Gulp tasks to the gulpfile.
* Tasks are in `tasks/`
*/
addTasks(loadTasks('tasks/'));
// require('gulp-load-tasks')(__dirname + '/tasks');
gulp.task('default', ['lint:gulp','client','server']);
})();

How to load and use environment-related values in config phase

I would like to deploy my web application to several environments. Using Continuous Integration I can run a task to generate a config.json for a particular environment. This file will contain, among others, the particular URLs to use for it.
{
"baseUrl": "http://www.myapp.es/",
"baseApiUrl": "http://api.myapp.es/",
"baseAuthUrl": "http://api.myapp.es/auth/"
}
The issue comes up when I try to set my different services through providers in the config phase. Of course, services are not available yet in the phase so I cannot use $http to load that json file and set my providers correctly.
Basically I would like to do something like:
function config($authProvider) {
$authProvider.baseUrl = config.baseAuthUrl;
}
Is there a way to load those values on runtime from a file? The only thing I can think about is having that mentioned task altering this file straight away. However I have several modules and therefore, that would have to do in all of them which doesn´t seem right.
You can create constants in the config of your main module:
Add $provide as a dependency in your config method
use the provider method to add all constants like this
$provide.provider('BASE_API_URL', {
$get: function () {
return 'https://myexample.net/api/';
}
});
You can use BASE_API_URL as a dependency in your services.
I hope this helps
Optionally you can set the url depending of your environment:
$provide.provider('BASE_API_URL', {
$get: function () {
if(window.location.hostname.toLowerCase() == 'myapp.myexample.net')
{
return 'https://myexample.net/api/' //pre-production
}else
{
return 'http://localhost:61132/'; //local
}
}
});
Regards!
Finally, the solution was generating an angular constants file using templating (gulp-template) through a gulp task. At the end, I am using a yaml file instead a json one (which is the one generated my CI engine with the proper values for the environment I want to deploy to).
Basically:
config.yml
baseUrl: 'http://www.myapp.es/'
baseApiUrl: 'http://api.myapp.es/'
auth:
url: 'auth/'
config.module.constants.template
(function () {
'use strict';
angular
.module('app.config')
.constant('env_variables', {
baseUrl: '<%=baseUrl%>',
baseApiUrl: '<%=baseApiUrl%>',
authUrl: '<%=auth.url%>'
});
}());
gulpfile.js
gulp.task('splicing', function(done) {
var yml = path.join(conf.paths.src, '../config/config.yml');
var json = yaml.safeLoad(fs.readFileSync(yml, 'utf8'));
var template = path.join(conf.paths.src, '../config/config.module.constants.template');
var targetFile = path.join(conf.paths.src, '/app/config');
return gulp.src(template)
.pipe($.template(json))
.pipe($.rename("config.module.constants.js"))
.pipe(gulp.dest(targetFile), done);
});
Then you just inject it in the config phase you need:
function config($authProvider, env_variables) {
$authProvider.baseUrl = env_variables.baseApiUrl + env_variables.authUrl;
}
One more benefit about using gulp for this need is that you can integrate the generation of these constants with your build, serve or watch tasks and literally, forget about doing any change from now on. Hope it helps!

Gulp - using ES6 modules in a combined js file?

I am trying to use ES6 modules in my current GULP setup. I've read that this is yet to be supported by browsers or Babel, so there is a need some elaborate setup to make this work, using things such Browserify, babelify, vinyl-source-stream. (Seems extremely complex setup).
What I want is different from examples I had found online. All the examples are with external files being imported, and I really don't want that. I want all the files to be bundled into a single file, with all the modules there already. Here's what I have:
My current GULP setup is like this:
gulp.task('buildJS', function() {
var src = [
'./js/dist/app.js',
'./js/dist/templates.js',
'./js/dist/connect.js',
'./js/dist/config.js',
'./js/dist/utilities.js',
'./js/dist/components/*.js',
'./js/dist/pages/**/*.js',
'./js/dist/modals/*.js',
'./js/dist/init.js' // must be last
];
gulp.src(src)
.pipe(concat('app.js'))
.pipe(babel({modules:"common"})) // I have no idea what "modules" actually does
.pipe(gulp.dest('../js/'))
});
And this is an example of a component file in /js/dist/components/.
There are many files like this, and they are all combined to a single file.
module "components/foo" {
export function render(settings) {
return ...
}
}
So later in some page controller I would use it:
import { render } from "components/foo";
Question:
Now that I have a single file, (been transformed using Babel), how can I use the modules via Import?
No, don't naively concatenate the files. Use browserify to bundle them, with babelify to compile them (via babel). A basic example would look something like this:
browserify('./entry')
.transform(babelify)
.bundle()
// ...
It's hard to give more specific advice because your use case is so unclear. Do you have a dependency graph that begins at one file, or are you trying to bundle together a bunch of indepdendent modules? Are you trying to run a script to kick off an application, or do you just want to be able to access modules individually?
Based on the example you linked to in your comment you should have something like this:
components/doughnut.js
export default function Doughnut (settings = {}) {
// ...
};
Doughnut.prototype = {}
routes/home.js
import Doughnut from './components/doughnut';
export default function () {
var component = new Doughnut();
$('body').html(component.render());
};
Have each module export what you want to be available from any other module. Have each module import whatever it needs from any other module(s). Whatever uses the controller from this example should then do import home from './routes/home'; These modules aren't tied to a global variable App and can be reused in other applications (as long as you otherwise make them reusable).
.pipe(babel({modules:"common"})) // I have no idea what "modules"
modules is a babel option that determines what module format it compiles ES6 module syntax to. In this case, CommonJS.
module "components/foo" {
Thanks to your comments I now understand why you have this. You need to eliminate that. Your component file should look something like:
export function render (settings) {
return ...
}
Paired with:
import { render } from "components/foo";
Or if you want a default export / import:
export default function render (settings) {
return ...
}
import render from "components/foo";
import { render } from "components/foo";
If you're browserifying your modules, you're probably going to need to use relative paths like ./components/foo or use something else to deal with the paths, like babel's resolveModuleSource option.
Since the end of 2015 I have been using rollupjs in order to create a bundle of ES2015 (ES6) modules, so I could use import/export freely in my code.
I've found Rollupjs to be very good and easy to use. The people behind
it are great people which devote themselves to the project. I've had
many questions which I had posted on the project's Github issues page
and I always got answered pretty quickly.
Setup includes these rollupjs plugins:
rollup (basic rollupjs bundler)
rollup-plugin-babel (converts ES2015 code to ES5 or earlier, for legacy browsers support)
rollup-plugin-eslint (verify the javascript code is valid)
rollup-plugin-uglify (minify the code, to make it smaller)
rollup-plugin-progress (shows bundle progress in terminal. shows which file being "worked on")
beepbeep (Make a console beep sound. I use this to inform me of compilaction errors)
Simplified GULP setup I'm using:
var gulp = require('gulp'),
gutil = require('gulp-util'),
rollup = require('rollup').rollup,
babelRollup = require('rollup-plugin-babel'),
eslintRollup = require('rollup-plugin-eslint'),
uglifyRollup = require('rollup-plugin-uglify'),
rollupProgress = require('rollup-plugin-progress'),
beep = require('beepbeep');
// ESlint
var eslint_settings = {
rulePaths: [],
rules: {
"no-mixed-spaces-and-tabs" : [2, "smart-tabs"],
"block-spacing" : [2, "always"],
"comma-style" : [2, "last"],
"no-debugger" : [1],
"no-alert" : [2],
"indent-legacy" : [1, 4, {"SwitchCase":1}],
'strict' : 0,
'no-undef' : 1
},
ecmaFeatures : {
modules: true,
sourceType: "module"
},
"parserOptions": {
"ecmaVersion" : 6,
"sourceType": "module",
"ecmaFeatures": {
"jsx": false,
"experimentalObjectRestSpread": true
}
},
globals : ['$', '_', 'afterEach', 'assert', 'beforeEach', 'Cookies', 'd3', 'dataLayer', 'describe', 'done', 'expect', 'ga', 'it', 'jQuery', 'sinon'], baseConfig: {
//parser: 'babel-eslint',
},
envs: [
'browser', 'es6'
]
};
// Rollup plugins configuration
function getRollupPlugins( settings = {} ){
var rollupPlugins = [];
rollupPlugins.push({
presets : [['es2015', {"modules": false}]], //['es2015-rollup'],
runtimeHelpers : true,
exclude : 'node_modules/**',
plugins : ["external-helpers"]
});
rollupPlugins.push(eslintRollup( Object.assign({throwOnError:true}, eslint_settings) ))
rollupPlugins.push(rollupProgress({
clearLine:true // default: true
}))
// I would advise Babel to only be used for production output since it greatly slower bundle creation
if( settings.ENV == 'production' ){
rollupPlugins.push(uglifyRollup())
rollupPlugins.push(babelRollup(rollupPlugins__babel));
}
return rollupPlugins;
}
var rollupPlugins = getRollupPlugins();
/**
* a generic Rollup bundle creator
* #param {String} outputPath [where to save the bundle to (must end with /)]
* #param {String} outputFileName [bundle file name]
* #param {String} entryFile [rollup entry file to start scanning from]
* #return {Object} [Promise]
*/
function rollupBundle(outputPath, outputFileName, entryFile, bundleOptions){
bundleOptions = bundleOptions || {};
bundleOptions.plugins = bundleOptions.plugins || rollupPlugins;
return new Promise(function(resolve, reject) {
outputFileName += '.js';
var cache;
// fs.truncate(outputPath + outputFileName, 0, function() {
// gutil.log( gutil.colors.dim.gray('Emptied: '+ outputPath + outputFileName) );
// });
rollup({
entry : entryFile,
plugins : bundleOptions.plugins,
cache : cache
})
.then(function (bundle) {
var bundleSettings = {
format : bundleOptions.format || 'umd',
sourceMap : false,
banner : config.banner
},
result = bundle.generate(bundleSettings),
mapFileName = outputFileName + '.map',
sourceMappingURL = '\n//# sourceMappingURL='+ mapFileName;
cache = bundle;
// if folder does not exists, create it
if( !fs.existsSync(outputPath) ){
gutil.log( gutil.colors.black.bgWhite('Creating directory ' + outputPath) );
fs.mkdirSync(outputPath);
}
// save bundle file to disk
fs.writeFile( outputPath + outputFileName, result.code + (bundleSettings.sourceMap ? sourceMappingURL : ''), function(){
resolve();
});
// save map file to disk
if( bundleSettings.sourceMap )
fs.writeFile( outputPath + mapFileName, result.map.toString());
})
.catch(function(err){
beep(1);
gutil.log( gutil.colors.white.bgRed('Rollup [catch]: ', err.stack) );
resolve();
})
});
}
// This task bundles the main application, using an entry file which itself has many imports,
// and those imports also has imports.. like a tree branching
gulp.task('bundle-app', ()=>{
return rollupBundle('../dist/js/', 'app', 'js/dist/app.js', {format:'cjs'});
});

Importing node modules from root directory using es6 and babel-node

I'm writing a node app with es6 using babel transpiler.
I have 2 files index.js & my-module.js on my root directory
- index.js
- my-module.js
my-module.js
export let myFunc = () => {
console.log('myFunc was called!!!');
}
index.js
import {myFunc} from './my-module';
myFunc();
if I run the following line from the command line everything works as expected.
$ babel-node index.js >> myFunc was called!!!
but if I remove the dot when importing my-module:
import {myFunc} from '/my-module';
myFunc();
I'm getting an error:
Error: Cannot find module '/my-module'
Any reason why I can't import modules using an absolute path? anyway to change .babelrc config to support it?
Thanks
Like (almost) any tool '/x' means 'x' in the root of your filesystem. Babel doesn't actually look at the paths, it just compiles
import {myFunc} from '/my-module';
into
var _myModule = require('/my-module');
And node actually looks up the module.
If you really want to import relative to the root of the project, you could use a plugin. I recommend using something that isn't very ambiguous, and make sure you document this for the next person reading your code!
Here's an example where we use a leading ~ to mean project relative. You could use anything you like e.g. ^ would also be good.
Example Input:
import {a} from '~my-module';
import {b} from '/my-module';
import {c} from './my-module';
scripts/babel-plugin-project-relative-require.js
module.exports = function (babel) {
// get the working directory
var cwd = process.cwd();
return new babel.Transformer("babel-plugin-project-relative-require", {
ImportDeclaration: function(node, parent) {
// probably always true, but let's be safe
if (!babel.types.isLiteral(node.source)) {
return node;
}
var ref = node.source.value;
// ensure a value, make sure it's not home relative e.g. ~/foo
if (!ref || ref[0] !== '~' || ref[1] === '/') {
return node;
}
node.source.value = cwd + '/' + node.source.value.slice(1);
return node;
}
});
};
.babelrc
{
"plugins": [
"./scripts/babel-plugin-project-relative-require.js"
]
}
Output (if run in /tmp):
'use strict';
var _tmpMyModule = require('/tmp/my-module');
var _myModule = require('/my-module');
var _myModule2 = require('./my-module');
The solution from FakeRainBrigand/Gavriguy is good and works well.
So i decided to develop a plugin which you can easily install with npm in and use a simple Babel-Plugin.
https://github.com/michaelzoidl/babel-root-import
Hope this helps...
First of all, Babel is just a transpiler from ES2015 to ES5 syntax. Its job is to transpile this:
import {myFunc} from '/my-module'
into this:
var _myModule = require('/my-module');
Actual module requiring performed by Node, and how Node does it you can read in details here: https://nodejs.org/api/modules.html#modules_file_modules
To summary, ./module means path to module relative to local directory, /module is an absolute path to module, module triggers Node to look for module in local node_modules directory and all ascending.

Building durandaljs with gulp fails for external modules

I'm using gulp-durandal to build our durandal app. It fails on our first module which has a depeendecy to knockout through:
define(['knockout',....
[09:35:27] Durandal Error: ENOENT, no such file or directory 'C:\xxxxx\app\knockout.js'
In module tree:
company/viewmodels/edit
at Object.fs.openSync (fs.js:438:18)
I have knockout defined as a patch in config.js (standard requirejs way) but it seems gulp-durandal does not resolve paths from config.js ?
'knockout': '../Scripts/lib/knockout/knockout-2.3.0',
How do you get gulp-durandal to use our config paths instead of trying to resolve the modules directly under the app folder ? I tried using options.extraModules but that only allows you to add paths to modules, not symbolic names for the module so that doesn't seem to be the correct way.
The basic structure of my durandaljs app follows the standard guidelines I believe, I have a config.js and main.js under the App folder.
My config.js:
define([], function() {
return {
paths: {
'text': '../Scripts/lib/require/text',
'durandal': '../Scripts/durandal',
'plugins': '../Scripts/durandal/plugins',
My main.js
require(['config'], function(config) {
require.config(config);
require(['durandal/system', 'durandal/app', 'durandal/viewLocator', 'plugins/widget', 'custombindings'],
function(system, app, viewLocator, widget) {
..... app code here.....
}
gulpfile.js:
var gulp = require('gulp');
var durandal = require('gulp-durandal');
require(['App/config'], function(config){
console.log('loaded config');
});
gulp.task('durandal', function(){
durandal({
baseDir: 'app', //same as default, so not really required.
main: 'main.js', //same as default, so not really required.
output: 'main.js', //same as default, so not really required.
almond: true,
minify: true,
require:true
})
.pipe(gulp.dest('dir/to/save/the/output'));
});
I guess the question is how do I load my config.js paths into gulp so the paths are resolved correctly ? I tried:
var gulp = require('gulp');
var durandal = require('gulp-durandal');
require(['App/config'], function(config){
console.log('loaded config');
});
But it seems require only wants a string as input (I guess require function in gulp != require from require.js)
I believe the issue is that your gulp-durandal task needs configuration to mimic the config.js file. If you need further assistance please provide more code from your gulp-durandal task.