Using Browserify to Import Smooth Scroll - javascript

I'm not sure if I'm understanding Browserify correctly, but am I able to essentially require() this smooth-scroll plugin (installed via npm) in my app.js file and Browserify will bundle it all together in my final app.js file?
app.js
var ss = require('./smooth-scroll');
$(document).ready(function(){
ss.init();
});
gulpfile.js
gulp.task('js', function () {
var browserified = transform(function(filename) {
var b = browserify(filename);
return b.bundle();
});
return gulp.src('./src/js/app.js')
.pipe(browserified)
.pipe(sourcemaps.init({loadMaps: true}))
.pipe(sourcemaps.write('./'))
.pipe(gulp.dest('./_site/public/js'))
});
Update
So, I'm trying to create my own module using the answer below but I'm having a problem in getting it to work with jquery.adaptive-backgrounds.js.
adaptive-backgrounds.js
var $ = require('jquery/dist/jquery');
module.exports = function(){
/* jshint debug: true, expr: true */
;(function($){
/* Constants & defaults. */
var DATA_COLOR = 'data-ab-color';
var DATA_PARENT = 'data-ab-parent';
var DATA_CSS_BG = 'data-ab-css-background';
var EVENT_CF = 'ab-color-found';
...
};
app.js
var $ = require('jquery/dist/jquery');
var ab = require('./vendors/adaptive-backgrounds');
$(function(){
$.ab.run();
});
For some reason the adaptive backgrounds plugin doesn't seem to be running on the page.

That is correct, if Smooth Scroll is available as a npm module. If it´s not you can create your own module with the script. Then, browserify would bundle everything into one js file.
Or you could just add it to your web page before your bundle, as you would normally do, and then it would be accessible from the bundle since it would be declared in global scope. But in this case, keep in mind you should concatenate your scripts for production (in the right order) to reduce the page loading time by minimizing the number of http requests.
Creating your own module is really simple. Let's say Smooth Scroll wasn't available as npm module, you could simply wrap the plugin in an anonymous function and assign that function to module.exports and then call the related variable in your bundle when you require the modified plugin.
Your plugin would then look like this:
/*!
* jQuery Smooth Scroll - v1.5.2 - 2014-10-01
* https://github.com/kswedberg/jquery-smooth-scroll
* Copyright (c) 2014 Karl Swedberg
* Licensed MIT (https://github.com/kswedberg/jquery-smooth-scroll/blob/master/LICENSE-MIT)
*/
//WE ADD THIS, BECAUSE ITS A DEPENDENCY (downloaded with npm)
var jQuery = require('jquery');
//AND THIS
module.exports = function(){
//THIS IS THE ORIGINAL CODE
(function($) {
var version = '1.5.2',
optionOverrides = {},
defaults = {
exclude: [],
excludeWithin:[],
offset: 0,
//....
//AND WE CLOSE OUR FUNCTION
};
And in the bundle you would do something like this
var SmoothScroll = require('../jquery.smooth-scroll');
var $ = require('jquery');
SmoothScroll();
// From now on Smooth Scroll is available
$('#mydiv').smoothScroll();
Something to note: you could have simply assigned the existing immediate anonymous function wrapping the plugin code to module.exports and then simply calling require('./jquery.smooth-scroll') would have make it available in the current scope. But imo it's better practice to wrap it with another anonymous function to do a call to make the plugin available to current scope explicitly.
Update
For your plugin, you should first do
npm install jquery. Because jquery as a regular script isn't exported as a module. So you need to install jquery module (or you could do the export yourself but why reinventing the wheel?)
Then...
adaptive-backgrounds.js
//Here you have to call your variable jQuery since its named this way when
//it's passed as an argument to the plugin immediate function
var jQuery = require('jquery');
module.exports = function(){
/* jshint debug: true, expr: true */
(function($){
/* Constants & defaults. */
var DATA_COLOR = 'data-ab-color';
var DATA_PARENT = 'data-ab-parent';
var DATA_CSS_BG = 'data-ab-css-background';
var EVENT_CF = 'ab-color-found';
...
// This is what I meant
})(jQuery);
};
app.js
//Now you can use $
var $ = require('jquery');
var ab = require('./vendors/adaptive-backgrounds');
//add this to call the immediate function used to set up the plugin
ab();
$(function(){
$.ab.run();
});

Related

Load a javascript file into a module pattern on Client-side

I have a simple javascript module pattern that executes client-side.
var module = (function() {
var _privateVariable = 10;
function publicMethod () {
console.log("public method; private variable: " + _privateVariable);
}
return {
publicMethod: publicMethod
};
})();
Let's say I want to load in another module (which also uses a module pattern) from a separate javascript file. How do I do that, i.e. something like:
?? Load other module here ??
var _other_module = ??
var module = (function() {
var _privateVariable = 10;
function publicMethod () {
console.log("public method; private variable: " + _privateVariable);
console.log("public method; other module: " + other_module.publicMethod());
}
return {
publicMethod: publicMethod
};
})();
You can't. To load another module form another file you need to use a Module formats.
It's a long story, i will try to shorten.
Let's talk first about the old way. earlier the developer used to load alle the JS-Files in a certain order in the HTML-Page. If we have 2 JS-Files index.js and variables.js and we want to get a variable from varible.js in index.js, we had load them like that
<script src="variables.js"></script>
<script src="index.js"></script>
But this is not a good way and has many negatives.
The right way is to use a Module formats.
There are many Module formats,
Asynchronous Module Definition (AMD)
CommonJS
Universal Module Definition (UMD)
System.register
ES6 module format
and each format has own Syntax.
For example CommonJS:
var dep1 = require('./dep1');
module.exports = function(){
// ...
}
but the Browsers dont't understand that. so we need a Module bundlers or Module loaders
to convert our code to code which browsers can understand.
Module bundlers: Bundle your inter-dependent Javascript files in the correct order
Browserify
Webpack
Module loaders: a module loader interprets and loads a module written in a certain module format.
RequireJS
SystemJS
This article will help you so much to understand exactly how modules work.
Not sure which context you are doing this.
But. In node.JS this would be typically done by
module1.js
module.exports.publicMethod = function() {}
index.js
const module1 = require('./module1.js');
module1.publicMethod();
or
const {publicMethod} = require('./module1.js');
publicMethod();

DataTables API undefined with Symfony Webpack Encore

I'm using Webpack Encore with Symfony 3.4 (part of migration to Symfony 4).
I have Datatables (installed via NPM to node_modules) working with jQuery but the api functions such as .columns are returning: .column is not a function at
Package Versions:
jQuery 2.14.4
Datatables 1.10.19
Webpack Encore 0.27.0
Webpack app.js:
global.$ = global.jQuery = require('jquery');
require('bootstrap');
global.moment = require('moment');
require('datatables.net-dt');
$.fn.dataTable = $.fn.DataTable = global.DataTable = require('datatables.net');
webpack.config.js:
var Encore = require('#symfony/webpack-encore');
Encore
// directory where compiled assets will be stored
.setOutputPath('code/web/build/')
// public path used by the web server to access the output path
.setPublicPath('/build')
// only needed for CDN's or sub-directory deploy
//.setManifestKeyPrefix('build/')
.addEntry('site', './assets/js/site/app.js')
// will require an extra script tag for runtime.js
// but, you probably want this, unless you're building a single-page app
.enableSingleRuntimeChunk()
.cleanupOutputBeforeBuild()
.enableSourceMaps(!Encore.isProduction())
// enables hashed filenames (e.g. app.abc123.css)
.enableVersioning(Encore.isProduction())
// uncomment if you use TypeScript
//.enableTypeScriptLoader()
// uncomment if you use Sass/SCSS files
//.enableSassLoader()
// uncomment if you're having problems with a jQuery plugin
//.autoProvidejQuery()
;
module.exports = Encore.getWebpackConfig();
Javascript example in template.html.twig (extends base html file):
{{ encore_entry_script_tags('site') }}
<script type="text/javascript">
$(document).ready(function() {
var $dtable;
$dtable = $('#simpleTable')
.DataTable({
data: data,
deferRender: true,
scrollX: false,
searching: true,
paging: true,
pageLength: 25});
console.log($dtable);
// Error occurs here
var column = $dtable.column(index);
});
</script>
A console log of $dtable immediately after instantiation outputs the following which seems to confirm an Api instance isn't created?
Is it possible this is related to the DataTable loader that uses the AMD method due to Webpack?
jquery.dataTables.js:
(function( factory ) {
"use strict";
if ( typeof define === 'function' && define.amd ) {
define( ['jquery'], function ( $ ) {
return factory( $, window, document );
} );
}
else if ( typeof exports === 'object' ) {
module.exports = function (root, $) {
if ( ! root ) {
// CommonJS environments without a window global must pass a
// root. This will give an error otherwise
root = window;
}
if ( ! $ ) {
$ = typeof window !== 'undefined' ? // jQuery's factory checks for a global window
require('jquery') :
require('jquery')( root );
}
return factory( $, root, root.document );
};
}
else {
factory( jQuery, window, document );
}
}
The AMD loader may be a problem here,
Can you try to disable AMD Loader it to see if it work :
var config = Encore.getWebpackConfig();
config.module.rules.unshift({
parser: {
amd: false,
}
});
module.exports = config;
Otherwise you could try to make it work with AMD Loader :
First install DataTables :
npm install datatables.net
Then DataTables style (bootstrap) :
npm install datatables.net-bs
Then the imports-loader plugin
Modify your webpack.config.js to make an exception for datatables:
module: {
loaders: [
{
test: /datatables\.net.*/,
loader: 'imports?define=>false'
}
]
}
Now you should be able to use DataTables :
import 'datatables.net';
import dt from 'datatables.net-bs';
dt(window, $);
//And now use it
$('#exampleDatatable').DataTable();
The issue I see is with line:
$.fn.dataTable = $.fn.DataTable = global.DataTable = require('datatables.net');
dataTable() and DataTable() are not equivalent.
The correct functions will be added to Jquery by the factory, no need to set these. Actually, if I can guess, the above code will overwrite DataTable() with dataTable().
Change to:
require('datatables.net');
It should work.
Maybe it's a stupid idea ... but what is the data value in your template-defined call $('#simpleTable').DataTable({data:data, ...}) call?
According to docs (3rd paragraph) setting the data option (even with an empty value) will override the data from the table. If your value data is undefined, this might turn your data-rich table into a vegetable in the eyes of DataTable.
I assume that your table is fine, the module is loaded fine, ... so the data might be the problem ...
If, however, it does contain useful data, would you mind posting some example of it? ;o)
The DataTables documentation for using with NPM show that the export is a function that returns the DataTable API.
var dt = require( 'datatables.net' )(); // N.b. the extra parenthesis.
So it's likely you'd need to alter your global assignment; something like this might work:
$.fn.dataTable = $.fn.DataTable = global.DataTable = require('datatables.net')(); // N.b. the extra parentheses.

Setting a path for require in node.js

I have a folder structure like this.
include/
index.js
plugin/
plugin.js
helper.js
Where:-
include/index.js
//Function for mapping the path of "require" statement in the plugin.js file.
var mapRequirePath = function(){
var plugins = require('./plugin/plugin');
return plugins;
}
//Now call the plugins..
var plugin = mapRequirePath();
include/plugin/plugin.js
/*
I want all four require statements to point to the same file location '/include/plugin/helper.js'
i.e search in the same folder location for module irrespective of the '../' or '../../' present in the require statement
*/
var helper1 = require('./helper');
var helper2 = require('helper');
var helper3 = require('../../helper');
var helper4 = require('../helper');
I want to map the path of require in plugin.js file so that all require call should search for its module in the same directory only.
You might be able to dynamically change the NODE_PATH environment variable:
// First take a backup:
var _NODE_PATH = process.env.NODE_PATH;
// Add /includes/plugin to the path, also note that we need to support
// `require('../hello.js')`. We can do that by adding /includes/plugin/a,
// /includes/plugin/a/b, etc.. to the list
process.env.NODE_PATH+=':/includes/plugin:/includes/plugin/a';
// Do your think...
require('./plugins/plugin');
// Restore NODE_PATH
process.env.NODE_PATH = _NODE_PATH;
Try changing the NODE_PATH variable via the command line:
exports NODE_PATH=directoryYouWant
If you don't want to have to change it for every other project, you could try just dynamically changing it in you .js file:
var currentNodePath = process.env.NODE_PATH;
process.env.NODE_PATH = directoryYouWant;
//do stuff then change it back
process.env.NODE_PATH = currentNodePath;
If your wanna add /foo/bar to require:
by editting process.env.NODE_PATH
then your js files should reside inside /foo/bar/node_modules/
process.env.NODE_PATH = '/foo/bar' + ':' + process.env.NODE_PATH;
by editting module.paths
then your js files should reside inside /foo/bar/
module.paths.push('/foo/bar');

Using require with relative paths

We have a rather big set of end-to-end tests on Protractor. We are following the Page Object pattern which helps us to keep our tests clean and modular. We also have a set of helper functions which help us to follow the DRY principle.
The Problem:
A single spec may require multiple page objects and helper modules. For instance:
"use strict";
var helpers = require("./../../helpers/helpers.js");
var localStoragePage = require("./../../helpers/localStorage.js");
var sessionStoragePage = require("./../../helpers/sessionStorage.js");
var loginPage = require("./../../po/login.po.js");
var headerPage = require("./../../po/header.po.js");
var queuePage = require("./../../po/queue.po.js");
describe("Login functionality", function () {
beforeEach(function () {
browser.get("/#login");
localStoragePage.clear();
});
// ...
});
You can see that we have that directory traversal in every require statement: ./../... This is because we have a specs directory where we keep the specs and multiple directories inside grouped by application functionality under test.
The Question:
What is the canonical way to approach the relative path problem in Protractor?
In other words, we'd like to avoid traversing the tree, going up to import modules. It would be much cleaner to go down from the base application directory instead.
Attempts and thoughts:
There is a great article about approaching this problem: Better local require() paths for Node.js, but I'm not sure which of the options is a recommended one when developing tests with Protractor.
We've also tried to use require.main to construct the path, but it points to the node_modules/protractor directory instead of our application directory.
I had the same problem and I ended up with the following solution.
In my Protractor config file I have a variable which stores a path to a base folder of my e2e tests. Also, Protractor config provides the onPrepare callback, where you can use a variable called global to create global variables for your tests. You define them as a properties of that global variable and use the same way you use globals browser or element in tests. I've used it to create custom global require functions to load different types of entities:
// __dirname retuns a path of this particular config file
// assuming that protractor.conf.js is in the root of the project
var basePath = __dirname + '/test/e2e/';
// /path/to/project/test/e2e/
exports.config = {
onPrepare: function () {
// "relativePath" - path, relative to "basePath" variable
// If your entity files have suffixes - you can also keep them here
// not to mention them in test files every time
global.requirePO = function (relativePath) {
return require(basePath + 'po/' + relativePath + '.po.js');
};
global.requireHelper = function (relativePath) {
return require(basePath + 'helpers/' + relativePath + '.js');
};
}
};
And then you can use these global utility methods in your test files right away:
"use strict";
var localStorageHelper = requireHelper('localStorage');
// /path/to/project/test/e2e/helpers/localStorage.js
var loginPage = requirePO('login');
// /path/to/project/test/e2e/po/login.po.js
var productShowPage = requirePO('product/show');
// /path/to/project/test/e2e/po/product/show.po.js
describe("Login functionality", function () {
beforeEach(function () {
browser.get("/#login");
localStorageHelper.clear();
});
// ...
});
We've been facing the same issue and decided to turn all page object and helper files into node packages. Requiring them in tests is now as easy as var Header = require('header-po'). Another benefit of converting to packages is that you can use proper versioning.
Here is a simple example:
./page-objects/header-po/index.js
//page-objects/header-po/index.js
'use strict';
var Header = function () {
this.goHome = function () {
$('#logo a').click();
};
};
module.exports = Header;
./page-objects/header-po/package.json
{
"name": "header-po",
"version": "0.1.1",
"description": "Header page object",
"main": "index.js",
"dependencies": {}
}
./package.json
{
"name": "e2e-test-framework",
"version": "0.1.0",
"description": "Test framework",
"dependencies": {
"jasmine": "^2.1.1",
"header-po": "./page-objects/header-po/",
}
}
./tests/header-test.js
'use strict';
var Header = require('header-po');
var header = new Header();
describe('Header Test', function () {
it('clicking logo in header bar should open homepage', function () {
browser.get(browser.baseUrl + '/testpage');
header.goHome();
expect(browser.getCurrentUrl()).toBe(browser.baseUrl);
});
});
I have had the same issue. Did similar solution to Michael Radionov's, but not setting a global function, but setting a property to protractor itself.
protractor.conf.js
onPrepare: function() {
protractor.basePath = __dirname;
}
test-e2e.js
require(protractor.basePath+'/helpers.js');
describe('test', function() {
.......
});
I think the method we use where I work might be a good solution for you. I have posted a brief example of how we handle everything. It's pretty nice b/c you can just call the page object functions in any spec file and you don't need to use require in the spec.
Call a node module from another module without using require() everywhere
All answers seem to be more of workarounds
The actual working solution would be this:
install module alias
add this to your package.json
"_moduleAliases": {
"#protractor": "protractor/_protractor",
"#tmp": "protractor/.tmp_files",
"#test_data": "protractor/.tmp_files/test_data",
"#custom_implementation": "protractor/custom_implementation",
},
add this as very first line of your protractor config
require('module-alias/register');
use it anywhere in the project like so
const params = require('#test_data/parameters');
const customImplementation require('#custom_implementation')
// etc

Is there a Java classpath-like feature for server-side Javascript?

When using JUnit and Maven in Java, one can have separate property files for src/main and src/test. This allows different configuration for code and tests, having Maven to manage the resources by using Java classpath.
Is there a similar way in Javascript code run by Node.js? I use Mocha for unit-testing and Grunt for task management.
Code example for script.js:
var config = require('./config/dev/app.js');
exports.getFileName = function() {
return config.fileName; // returns 'code.txt'
}
What I need is to make the script.js use different config file when being required in a test.js unit test like this:
var assert = require('assert');
var s = require('./script.js');
describe('Test', function () {
it('should use different config file', function() {
assert.equal('test.txt', s.getFileName());
});
});
Is there a way to use different configuration ./config/test/app.js in the script.js without having to alter the code of script.js? What I really try to avoid is to adjust the code to support unit tests. Instead, I want to achieve similar functionality such as mentioned Java classpath.
Please try this code.
Script.js
var config;
if(process.env.dev===true){
config = require('./config/dev/config.js');
}
if(process.env.prod===true){
config = require('./config/prod/config.js');
}
exports.getFileName = function() {
return config.fileName; // returns 'code.txt'
}
test.js
//set the environment here
process.env.dev = true;
var assert = require('assert');
var s = require('./script.js');
describe('Test', function () {
it('should use different config file', function() {
assert.equal('test.txt', s.getFileName());
});
});
I have not found any elegant solution out there on the web so I have implemented and published my own.
Check it out here: https://npmjs.org/package/app-config
Using the app-config plugin, only the script.js needs to get changed this way:
var config = require('app-config').app;
exports.getFileName = function() {
return config.fileName; // returns 'code.txt'
}
The app needs to be run this way for example
NODE_ENV=dev node script.js
NODE_ENV=unitTest mocha test.js
Depending on the NODE_ENV environmental variable, the right set of configuration files will be loaded by the app-config plugin.

Categories

Resources