ES6 - Using babel/traceur with jQuery plugins - javascript

I want to be able to write ES6 with jQuery plugins and compile the code down to ES5 using Gulp Babel, without having to use Browserify to make them work.
For example, I may have a class like this:
import $ from 'jquery';
import somePlugin from 'somePlugin';
class myClass {
constructor(options) {
this.defaults = {
$body: $('body')
};
this.options = $.extend(this.defaults, options);
$body.somePlugin();
}
}
I can get this to work if I use Babelify but I'd prefer to find a way where I do not have to depend on another process. When I just use Babel, I get this error in the console: Uncaught ReferenceError: require is not defined.
I checked the code and it looks like it's turning the imports into requires.
Is there a way around this or will I have to stick with using Browserify (Babelify) to compile the JavaScript?
EDIT: I am currently using browserify-shim to make the jQuery plugins work too
EDIT2: No this is not about RequireJS - I'm trying to remove the use of Browserify with Babel

Answering my own question here. I did some digging and it looks like the best way of dealing with this issue at the moment is using jspm with the es6-module-loader.
Gulpfile:
var gulp = require('gulp');
var del = require('del');
var shell = require('gulp-shell');
gulp.task('clean', function(cb) {
del('dist', cb);
});
gulp.task('default', ['clean'], shell.task([
'jspm bundle-sfx app/main -o dist/app.js',
'./node_modules/.bin/uglifyjs dist/app.js -o dist/app.min.js',
'./node_modules/.bin/html-dist index.html --remove-all --minify --insert app.min.js -o dist/index.html'
]));
HTML:
<head>
<title>ES6</title>
<script src="jspm_packages/system.js"></script>
<script src="config.js"></script>
<script>
System.import('app/main');
</script>
</head>
The repo I created here will also show you how to do it

Related

Vue.js - Compile .vue file with gulp

I have a Vue component. This component is very basic and looks like this:
my-component.vue
<template>
<div class="foo">
</div>
</template>
<script>
export default {
data() {
return {};
},
props: {
message: {
type: String,
default: ''
}
},
methods: {
display: function() {
alert(this.message);
}
},
};
</script>
I want to import it into an HTML file using something like this:
<script type="text/javascript" src="/components/my-component.min.js"></script>
Then, I would like to be able to just use the component in my HTML file like this:
<my-component message="hello"></my-component>
Is this possible? If so, how? Everything that I see uses web pack and a bunch of other stuff. I have a working component. I just can't figure out how to make it easily deployable.
I created a gulpfile to help with this. This looks like this:
gulpfile.js
var gulp = require('gulp');
var concat = require('gulp-concat');
var uglify = require('gulp-uglify');
var webpack = require('webpack-stream');
gulp.task('default', ['build']);
gulp.task('build', [], function() {
return gulp.src('./src/my-component')
.pipe(webpack(require('./webpack.config.js')))
.pipe(concat('my-component.min.js'))
.pipe(gulp.dest('./deploy'))
;
});
When I run the build gulp task, I receive a message that says:
webpack-stream - No files given; aborting compilation
The confusing part is, I have some unit tests that successfully work. I'm running those tests via this command:
nyc mocha-webpack --webpack-config ./webpack.config.js test --recursive --require test/.setup
What am I doing wrong? How do I package up my component so that I can import it into other apps?
If you don't want to use any bundle - a full webpack bundle (please reconsider this decision if you are working on a big project) nor browserify (see vueify)... then you may use this package to compile single file component into single js file, with gulp:
https://www.npmjs.com/package/gulp-vueify2
I must mention that I am the author of this module.

Is there a way to allow browserify modules access to the global scope (window object)?

The Scenario
I'm using Browserify (or NodeJS/CommonJS like imports) to require some JavaScript libraries. However, I'm having trouble getting them to play nice since apparently Browserify by default denies global scope access to all modules. For example, doing this doesn't work;
file1.js
require('moment');
file2.js
moment(new Date()); // throws moment is undefined
But this works by changing the contents of file1.js to the following;
window.moment = require('moment');
The Problem
This has worked well enough so far, however now I'm having trouble loading the moment timezone library (an extension of MomentJS). Moment Timezone's docs imply both scripts should run on the window global scope by adding them as script tags like so;
<script src="moment.js"></script>
<script src="moment-timezone-with-data.js"></script>
So they can be used like this;
moment().tz("America/Los_Angeles").format();
This however, seems very hard to achieve, since if I try the following;
window.moment = require('moment');
require('./../../node_modules/moment-timezone/builds/moment-timezone-with-data-2010-2020'); // the location of my moment-timezone library
I get a runtime error saying that moment.tz is undefined (meaning that the second library wasn't run with the global scope). And if I try to add the window.moment to the second line of code, I'll be rewriting the first instance of the full library.
So, in short;
Is there a way to allow certain Browserify imports to have global scope access, or to run with the window object as their scope?
I'm aware of the security implications this has, but selectively used, this would be immensely useful while requiring things like JavaScript libraries that need global access to set up themselves correctly. Any help would be greatly appreciated.
Put tiny libraries into vendor.js, include that on your page with <script>, make them known to browserify with browserify-shim and use them with require('libname') in your modules.
Larger libraries may be included from, say, vendor CDNs, and also be made known to browserify with browserify-shim.
index.html
<html>
<head>
<script src="vendor.js"></script>
<script src="bundle.js"></script>
</head>
<body>
Open devtools and inspect the output
</body>
</html>
package.json
{
"scripts": {
"build": "cat vendor1.js vendor2.js > dist/vendor.js && cp index.html dist/index.html && browserify index.js -o dist/bundle.js"
},
"browserify-shim" : {
"vendor1" : "global:vendor1",
"vendor2" : "global:vendor2"
},
"browserify" : {
"transform" : [ "browserify-shim" ]
},
"devDependencies": {
"browserify": "^14.1.0",
"browserify-shim": "^3.8.14"
},
"dependencies": {
"moment": "^2.18.1",
"moment-timezone": "^0.5.11"
}
}
A couple of things to note about the above package.json:
build: it builds 'application' in dist folder, creating the following structure:
dist
index.html
bundle.js
vendor.js
"browserify" entry: it just adds browserify-shim
"browserify-shim" entry: it tells browserify that vendor1 and vendor2 are available as properties on window (global) object. Browserify won't attempt to bundle them, and require() will just return those properties. You need to make sure they are actually available (so index.html above includes them with <script src="vendor.js">
index.js
var momentfromnpm = require('moment-timezone');
var vendor1 = require('vendor1');
var vendor2 = require('vendor2');
console.log("Hello");
console.log("Using module from npm", momentfromnpm().tz("Europe/London").format());
console.log("using a function from vendor1.js (bundled into vendor.js)", vendor1());
console.log("using a 'module' from vendor2.js (bundled to vendor.js)", vendor2.doWork());
vendor1.js
window.vendor1 = function() { // could also be just `function vendor1()...`
return "I'm a simple function, defined on a window";
};
vendor2.js
var vendor2 = { // could also be window.vendor2
doWork: function() {
console.log("vendor2 doing work");
}
};
Install the moment-timezone package as instructed on the website.
npm install moment-timezone --save
In file1.js, define window.moment as so:
window.moment = require('moment-timezone/builds/moment-timezone-with-data-2010-2020');
This should allow you to use moment().tz() in file2.js

ES6 imports and require not working properly with gulp/browserify/babelify

I have a simple Gulp configuration to transpile my javascript with babel :
gulp.task('js_dev', function () {
var bundler = browserify({entries: ['js/index.js'], debug: true});
bundler.external('jquery');
return bundler
.transform("babelify", {presets: ["es2015"]})
.bundle()
.on('error', function (err) {
console.error(err);
this.emit('end');
})
.pipe(source('dev.js'))
.pipe(gulp.dest(jsdest));
});
With this, I can import my development files.
But I can't import some package installed with npm (isotope, textfit, babel-polyfill...).
For example, if I import babel-polyfill in my index.js file :
import "babel-polyfill";
No errors with gulp, and the code seems to be added in dev.js, but it's not working, and I can't require it either : it gives an empty Object.
Same things for other npm modules (isotope for example). When I require them, it just gives an empty object. And Isotope is supposed to work with require.
Any idea what's happening ?
Thanks.
Alright, I found it.
My JavaScript files were added to a CMS which already used AMD/Require.js. This conflicts with browserify.
The solution was here.
What about doing:
import BabelPolyfill from "babel-polyfill"

Require another file in gulpfile (which isn't in node_modules)

I've been using gulp for a while now and know how to import another node module, e.g.
var sass = require('gulp-sass');
That's fine, but my gulpfile is filling up with code that I'd like to move into a separate file and "require". Specifically I am writing a postcss plugin, which I already have working when declared as a function inside of the gulpfile. My question is how to put my function in an external file and require it like I do a node module. Do I need to "export" the function in the file being required? Do I need to use ES6 modules or something like that?
As an aside, I realise that if i was doing this probably I would either (A) turn this into a proper node module and put it on a private NPM repository, but that seems unnecessary, or (B) turn it into a proper gulp plugin, but that would require learning how to author a gulp plugin and learning about streams and stuff. Both of these are probably better but would take more time so I've decided to just keep the function simple and local for now.
First create a new js file (here ./lib/myModule.js):
//./lib/myModule.js
module.exports = {
fn1: function() { /**/ },
fn2: function() { /**/ },
}
You could also pass some arguments to your module:
// ./lib/myAwesomeModule.js
var fn1 = function() {
}
module.exports = function(args) {
fn1: fn1,
fn2: function() {
// do something with the args variable
},
}
Then require it in your gulpfile:
//gulpfile.js
var myModule = require('./lib/myModule')
// Note: here you required and call the function with some parameters
var myAwesomeModule = require('./lib/myAwesomeModule')({
super: "duper",
env: "development"
});
// you could also have done
/*
var myAwesomeModuleRequire = require('./lib/myAwesomeModule')
var myAwesomeModule = myAwesomeModuleRequire({
super: "duper",
env: "development"
});
*/
gulp.task('test', function() {
gulp.src()
.pipe(myModule.fn1)
.pipe(myAwesomeModule.fn1)
.gulp.dest()
}
First, you have to add export default <nameOfYourFile> at the end of your file
Then to use it, write import gulp from 'gulp'
If you have an error message, install babel-core and babel-preset-es2015 with NPM, and add a preset "presets": ["es2015"] in your .babelrc config file.
I fix my problem by install:
npm i babel-plugin-add-module-exports
Then i add "plugins": [["add-module-exports"]] to the .babelrc

NodeJS/Browser Cross-Development

I'm working on a library that targets both browsers and NodeJS applications. Modules use AMD convention which is theorically flexible enough to map pretty much any situation today. Source files are then to be converted with tools to be distributed for different platforms - again browsers and NodeJS.
By the way, there's a wonderful tool called uRequire to help with that but I'm still not sure what my best option is, so I'm asking here for relevant experience.
Here are the files hierarchy I have:
- bower_components/
- eventemitter2/ ...
- lodash/ ...
- source/
- library/
- lodash.js -> ../../bower_components/lodash/dist/lodash.js
- EventEmitter.js -> ../../bower_components/eventemitter2/lib/eventemitter2.js
- Observable.js:
define(["lodash", "EventEmitter"], function(Utility, EventEmitter) {
function Observable(options) { ... };
return Observable;
});
At the end, the big difference between browser and NodeJS sides is:
Browser-side: EventEmitter implementation simply is the eventemitter2 browser module that is configured to be "library/EventEmitter";
NodeJS-side: EventEmitter is gotten from require("events").EventEmitter, with events being a native package, not a local file or module;
So, my question is: how can I have that Observable object work with NodeJS without massive tinkering? What I'm not sure about how I can make the EventEmitter implementation available to my module since it is not a local module (as such I cannot write any paths mapping) and moreover it is not directly the module itself we'll use but the "EventEmitter" property of it...
Any help/thinking would be appreciated. I believe that many have run in similar situations and I'd be curious to know what they have to say!
uRequire makes it trivial to use runtimeInfo and selectivelly load alternative dependencies at runtime (you can always choose to have alternative builds and replace deps with alternative/mocks at build time, if you dont want to write selective code like this).
Runtime info works the same in all templates, including UMD and combined, so either if executing on:
nodejs
browser with an AMD loader like requirejs or
browser with the plain </script> tag,
you can choose what each module dependency means in each case dynamically, using __isAMD, __isNode & __isWeb runtime variables.
What you need is :
- bower_components/
- eventemitter2/ ...
- lodash/ ...
- requirejs/ ...
- source/
- library/
- EventEmitter.js
- Observable.js:
where Observable.js is for example
define(["lodash", "EventEmitter"], function(_, EventEmitter) {
function Observable(options) { this.myOptions = options };
Observable.EventEmitter = EventEmitter;
Observable._ = _;
return Observable;
});
and EventEmitter.js is :
define(function(){
var EventEmitter2;
if (__isNode) {
return require("events").EventEmitter;
} else {
if (__isAMD) {
return EventEmitter2 = require("eventemitter2");
} else if (__isWeb) {
return window.EventEmitter2;
}
}
});
** notes** :
You don't need to worry about "events" trying to load on the AMD side, cause its a known node dep (otherwise you would need to list it).
EventEmitter2 = require(...) is needed to establish the inference of exported dependency identifier EventEmitter2 to window. The last case, Web/Script uses window.EventEmitter2 thanks to this! Alternatively you can list it in depsVars.
Then with the following grunt-urequire config (in coffeescript):
module.exports = gruntFunction = (grunt) ->
grunt.initConfig gruntConfig =
urequire:
library:
path: "source/library"
dstPath: "build/UMD"
runtimeInfo: ['EventEmitter'] # dont need it in other files
template: 'UMDplain'
combined:
derive: 'library'
main: 'Observable'
dependencies: exports: root: {'Observable': 'Obs'}
dstPath: "build/almond/Observable.js"
template: 'combined'
grunt.loadNpmTasks "grunt-urequire"
you have two builds:
A) library : with separate UMD files, where you can run eg from source\test\load_node.js :
var Observable = require("../../build/UMD/Observable");
console.log(Observable.EventEmitter);
or from the browser (source/test/Loader_unoptimized_AMD.html):
<!DOCTYPE html>
<html>
<head><title>test crossdev: RequireJs, UMD</title></head>
<body>Check console!</body>
<script src="../../bower_components/requirejs/require.js"></script>
<script>
require.config ({
baseUrl: '../../build/almond',
paths: {
lodash: "../../bower_components/lodash/dist/lodash.min",
eventemitter2: "../../bower_components/eventemitter2/lib/eventemitter2"
}
});
require(["Observable" ], function(Observable){
console.log(Observable);
console.log(Observable.EventEmitter);
});
</script>
</html>
and
B) combined with all files inlined & having its own mini-loader (almond) that works on nodejs, Web/AMD and Web/Script. Running from source/test/Loader_almondJs_plainScript.html :
<!DOCTYPE html>
<html>
<head><title>test crossdev: plain script, combined/almond</title></head>
<body>Check console!</body>
<script src="../../bower_components/lodash/dist/lodash.min.js"></script>
<script src="../../bower_components/eventemitter2/lib/eventemitter2.js"></script>
<script src="../../build/almond/Observable.js"></script>
<script>
console.log(window.Obs);
console.log(window.Obs.EventEmitter);
</script>
</html>
or using RequireJs as AMD loader (source/test/Loader_almondJs_AMD.html):
<!DOCTYPE html>
<html>
<head><title>test crossdev: RequireJs, combined/almond</title></head>
<body>Check console!</body>
<script src="../../bower_components/requirejs/require.js"></script>
<script>
require.config ({
baseUrl: '../../build/almond',
paths: {
lodash: "../../bower_components/lodash/dist/lodash.min",
eventemitter2: "../../bower_components/eventemitter2/lib/eventemitter2"
}
});
require(["Observable" ], function(Observable){
console.log(Observable);
console.log(Observable.EventEmitter);
});
</script>
</html>
You can see the test project in https://github.com/anodynos/nodejs-browser-cross-development

Categories

Resources