I'm using RequireJS (version 2.1.14) and would like to concatenate my JavaScript files into one single app-built.js.
I've created a little node module which reads my app.js, extracts the project paths and gets executed once I run node build in the js directory of my application.
The node module (build.js):
var fs = require('fs'),
path = require('path'),
directory = __dirname + path.sep,
requirejs = require(directory + 'vendor/r.js');
fs.readFile(directory + 'app.js', 'utf8', function(err, data) {
if (err) {
console.log('Error: ' + err);
return
} else {
data = data.replace(/'/g, '"').replace(/\s+/g, '');
var paths = data.substr(data.indexOf('{'), data.indexOf('}')),
paths = paths.substr(0, paths.indexOf('}') + 1),
paths = JSON.parse(paths);
createAppBuilt(paths);
}
});
function createAppBuilt(paths) {
var config = {
baseUrl: __dirname,
paths: paths,
name: 'app',
out: 'app-built.js',
preserveLicenseComments: false,
findNestedDependencies: true,
removeCombined: true
};
requirejs.optimize(config, function(buildResponse) {
var contents = fs.readFileSync(config.out, 'utf8');
console.log('Created app-built.js');
}, function(err) {
console.log('Error: ' + err);
return;
});
}
app.js:
var paths = {
'jquery': 'vendor/jquery-1.11.0.min',
// other paths
};
// Set language, necessary for validtaion plugin -> validation.js
if (Modernizr.localstorage) {
localStorage.getItem('language') || localStorage.setItem('language', navigator.language || navigator.userLanguage);
}
requirejs.config({
paths: paths,
shim: {
touchswipe: {
deps: ['jquery']
},
icheck: {
deps: ['jquery']
},
validate: {
deps: ['jquery']
},
mask: {
deps: ['jquery']
},
chosenImage: {
deps: ['jquery', 'chosen']
},
cookie: {
deps: ['jquery']
}
}
});
require(['globals', 'jquery', 'underscore'], function() {
var initial = ['main'];
if (!Modernizr.localstorage) {
initial.push('cookie');
}
require(initial, function(Main) {
$(function() {
if (!Modernizr.localstorage) {
$.cookie.json = true;
}
Main.init();
});
});
});
The app-built.js gets generated but when I include it in my index.php all the other modules get loaded as well. How can I prevent the loading of all modules and only load the app-built.js?
I recommend you look into http://webpack.github.io/
or http://browserify.org/ as these solve this problem for you.
They allow you to use require much as before, yet the code is compiled/concatenated into a single file.
Webpack allows for a bit more flexibility in loading different chunks of code for different parts of your site, but Browserify is the most well-known so far.
There may be a cost in switching over to these, as I don't think that they're 100% compatible requirejs, however they bring great advantages.
Here's someone's journey from RequireJS to Browserify with some Pros and Cons.
Separate modules into different files, e.g. app-built.js, user-built.js. Then load script when it's needed.
Here's a demo: http://plnkr.co/edit/s6hUOEHjRbDhtGxaagdR?p=preview .
When page loaded, requirejs only loads global.js. After clicking the Change Color button, requirejs starts to load colorfy.js and random-color.js, which required by colorfy.js.
I am not sure about the exact details, but, yet if you don't have an exports option, r.js doesn't define a named module for you, that causes to actually load the script.
I assume you have jquery plugins there so add this extra exports option:
shim: {
touchswipe: {
deps: ['jquery'],
exports: 'jQuery.fn.touchswipe'
},
This should force r.js to build a named module for touchswipe:
define("touchswipe", (function (global) {
return function () {
var ret, fn;
return ret || global.jQuery.fn.touchswipe;
};
}(this)));
Note that, exports option might not build this named module, in that case your best bet is to include this manually.
Again I am not sure about why and how this happens, It must be a bug in requirejs, it's unlikely there is a tweak for this.
Changing the r.js optimizer (to uglify2) solved the problem for me:
var config = {
baseUrl: __dirname,
paths: paths,
name: 'app',
out: 'app-built.js',
findNestedDependencies: true,
preserveLicenseComments: false,
removeCombined: true,
optimize: 'uglify2'
};
Related
I'm probably doing something wrong, so feel free to question all things. I'm using an npm package xrm-mock for a MS CRM mocking framework. I've setup my config as such
steal.config({
meta: {
"dependencyModule": {
deps: [
/***********************************
* List of Spec Files goes here! *
***********************************/
"spec/po_/commonSpec"
,"spec/xrmMockGeneratorSpec"
]
},
"jasmine": {
"exports": "jasmineRequire"
},
"jasmine-html": {
deps: ["jasmine"]
},
"jasmine-boot": {
deps: ["jasmine", "jasmine-html"]
},
"xrm-mock-generator": {
deps: ["xrm-mock"]
}
},
bundlesPath: "../WebResources",
loadBundles: true,
paths: {
"jasmine": "../node_modules/jasmine-core/lib/jasmine-core/jasmine.js",
"jasmine-html": "../node_modules/jasmine-core/lib/jasmine-core/jasmine-html.js",
"jasmine-boot": "../node_modules/jasmine-core/lib/jasmine-core/boot.js",
"sourcemapped-stacktrace": "../node_modules/sourcemapped-stacktrace/dist/sourcemapped-stacktrace.js",
"xrm-mock": "../node_modules/xrm-mock/index.js",
"xrm-mock-generator": "../node_modules/xrm-mock-generator/dist/xrm-mock-generator.js"
},
map: {},
main: "./testRunner"
});
but xrm-mock/index.js looks like this:
"use strict";
exports.__esModule = true;
var formselector_mock_1 = require("./dist/page/formselector/formselector.mock");
exports.FormSelectorMock = formselector_mock_1.FormSelectorMock;
var formitem_mock_1 = require("./dist/page/formitem/formitem.mock");
exports.FormItemMock = formitem_mock_1.FormItemMock;
... 80 more lines...
and I get 404s for each require: "http://localhost:62576/test/dist/page/formselector/formselector.mock.js" which should be "http://localhost:62576/node_modules/xrm-mock/dist/page/formselector/formselector.mock.js"
I'm guessing I could add each and every module file as a module with a path, but that's 40 some modules I'd have to define. Is there an easier way?
Most devs use the npm plugin nowadays, do you know about it? Removes the need for this manual (and difficult) configuration
To answer your question though, I think what you want to do is remove the xrm-mock path and instead have something like
"xrm-mock/*": "../node_modules/xrm-mock/*.js"
and then a map for the main module:
"map": {
"xrm-mock": "xrm-mock/index"
}
I created a simple app using knockout/bootstrap/gulp that downloads a pdf using pdfMake.js. It works fine in debug mode using VS2017. After publishing and using gulp it gives this error when run: File 'Roboto-Regular.ttf' not found in virtual file system
Note: After gulp, all JS files are in one script.js file.
I tried many things, it always works when debugging, as soon as I run gulp, it gives the error.
I tried joepal1976's solution from here (what I did with the dependencies in require.config.js)
Someone suggested .pipe(uglify({
compress: {
hoist_funs: false
}
})) which doesn't appear to help.
Included in require.config like so:
var require = {
baseUrl: ".",
paths: {
"jquery": "js-libs/jquery.min",
"bootstrap": "js-libs/bootstrap.min",
"crossroads": "js-libs/crossroads.min",
"hasher": "js-libs/hasher.min",
"knockout": "js-libs/knockout",
"knockout-projections": "js-libs/knockout-projections.min",
"signals": "js-libs/signals.min",
"text": "js-libs/text",
"vfs_fonts": "js-libs/vfs_fonts",
"pdfMake": "js-libs/pdfmake.min"
},
shim: {
"bootstrap": { deps: ["jquery"] },
'pdfMake':
{
exports: 'vfs_fonts'
},
'vfs_fonts':
{
deps: ['pdfMake'],
exports: 'vfs_fonts'
}
}
};
JS for the page:
define(["knockout", "text!./home.html"], function (ko, homeTemplate) {
function HomeViewModel(route) {
var thisVM = this;
this.VMInit = function () {
var thePDF = {
content: [
'My test invoice.',
]
};
pdfMake.createPdf(thePDF).download('pdf_test.pdf');
}
thisVM.VMInit();
}
return { viewModel: HomeViewModel, template: homeTemplate };
});
The Gulp file:
//-----------------------------------------------------------------------
// Node modules
var fs = require('fs'),
vm = require('vm'),
merge = require('deeply'),
chalk = require('chalk'),
es = require('event-stream');
//-----------------------------------------------------------------------
// Gulp and plugins
var gulp = require('gulp'),
rjs = require('gulp-requirejs-bundler'),
concat = require('gulp-concat'),
clean = require('gulp-clean'),
replace = require('gulp-replace'),
uglify = require('gulp-uglify'),
htmlreplace = require('gulp-html-replace');
// Config
var requireJsRuntimeConfig =
vm.runInNewContext(fs.readFileSync('src/app/require.config.js') + '; require;');
requireJsOptimizerConfig = merge(requireJsRuntimeConfig, {
out: 'scripts.js',
baseUrl: './src',
name: 'app/startup',
paths: {
requireLib: 'js-libs/require'
},
include: [
'requireLib',
'components/nav-bar/nav-bar',
'components/home-page/home',
'text!components/about-page/about.html'
],
insertRequire: ['app/startup'],
bundles: {
// If you want parts of the site to load on demand, remove them from the 'include' list
// above, and group them into bundles here.
// 'bundle-name': [ 'some/module', 'another/module' ],
// 'another-bundle-name': [ 'yet-another-module' ]
}
});
//-----------------------------------------------------------------------
// Discovers all AMD dependencies, concatenates together all required .js
files, minifies them
gulp.task('js', function () {
return rjs(requireJsOptimizerConfig)
.pipe(replace('Views/src/', ''))
.pipe(replace('img/', 'Assets/img/'))
.pipe(replace('css/', 'Assets/css/'))
.pipe(uglify({
preserveComments: 'some'
}))
.pipe(gulp.dest('./dist-app/Assets/js/'));
});
gulp.task('css', function () {
return gulp.src(['./src/css/bootstrap.css',
'./src/css/bootstrap-switch.css',
'./src/css/dataTables.bootstrap.css',
'./src/css/dataTables.colVis.css',
'./src/css/dataTables.responsive.css',
'./src/css/daterangePicker.css'])
.pipe(concat('styles.css'))
.pipe(gulp.dest('./dist-app/Assets/css/'));
});
// Copies index.html, replacing <script> and <link> tags to reference production
URLs
gulp.task('html', function () {
return gulp.src('./src/index.html')
.pipe(htmlreplace({
dependencies_top: '<link href="Assets/css/styles.css"
rel="stylesheet">',
dependencies_bottom: '<script src="Assets/js/scripts.js"></script>'
}))
.pipe(gulp.dest('./dist-app/'));
});
// Removes all files from ./dist/
gulp.task('clean', function () {
console.log("the clean task");
return gulp.src('./dist-app/**/*', { read: false })
.pipe(clean());
});
// All tasks in [] must complete before 'default' can begin
gulp.task('default', ['html', 'js', 'css'], function (callback) {
callback();
console.log('\nPlaced optimized files in ' + chalk.magenta('dist-app/\n'));
});
The Startup.js file if its helpful:
define(['jquery',
'knockout',
'./router',
'bootstrap',
'knockout-projections',
'pdfMake',
'vfs_fonts'], function ($, ko, router) {
// Components can be packaged as AMD modules, such as the following:
ko.components.register('nav-bar', { require: 'components/nav-bar/nav-bar' });
ko.components.register('home-page', { require: 'components/home-page/home'
});
// ... or for template-only components, you can just point to a .html file
directly:
ko.components.register('about-page', {
template: { require: 'text!components/about-page/about.html' }
});
ko.components.register('new-page', { require: 'components/new-page/new-page'
});
// [Scaffolded component registrations will be inserted here. To retain this
//feature, don't remove this comment.]
// Start the application
ko.applyBindings({ route: router.currentRoute });
});
Following code worked for me:
import pdfMake from "pdfmake/build/pdfmake";
import pdfFonts from "pdfmake/build/vfs_fonts";
pdfMake.vfs = pdfFonts.pdfMake.vfs;
I battled with this recently on stackblitz when using it with angular. the issue was pdfmake.vfs on the window object was not being set. so i had to manually set it in the constructor of my pdf service like so.
constructor() {
(window as any).pdfMake.vfs = pdfFonts.pdfMake.vfs;
}
I came across this issue and resolved it by including vfs_fonts.js just after the pdfmake Javascript file.
Here is my code, you should just need to set the file path to wherever your copy of the file is placed.
<script src="~/Content/DataTables/pdfmake-0.1.32/pdfmake.min.js"></script>
<script src="~/Content/DataTables/pdfmake-0.1.32/vfs_fonts.js"></script>
CDN LINK
<script src="https://cdnjs.cloudflare.com/ajax/libs/pdfmake/0.1.53/pdfmake.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/pdfmake/0.1.32/vfs_fonts.js"></script>
please follow the hierarchy/dependency of links else it won't work
It is just the sequence of the files, add first the pdfmake and then vfs_fonts.
#Rijo solution worked in one file, but oddly enough refused to work in another file.
In the other file I had to use:
import pdfMake from "pdfmake/build/pdfmake";
import pdfFonts from "pdfmake/build/vfs_fonts";
// Wherever you call createPdf, you have to pass VFS
pdfMake.createPdf(docDefinition, null, null, pdfFonts.pdfMake.vfs).open();
I'm new to grunt and file optimalisation. I have combined some JavaScript files (with grunt concat) into one file release.js and then minified it into release.min.js.
Now I am loading the combined files (they are jQuery ui widgets) with a requirejs script.
My question is, is this the right way to do this? Look's kinda strange to reference release.min.js four times. Is this causing requirejs to load the release.min.js four times? Or only once? It would be silly to load a big minified file a few times over.
requirejs.config({
baseUrl: '/site/scripts',
shim: {
layout: {
deps: ['jquery', 'jquery-ui']
} ,
serviceTraining: {
deps: ['jquery', 'jquery-ui']
},
serviceMail: {
deps: ['jquery', 'jquery-ui']
},
forms: {
deps: ['jquery', 'jquery-ui']
}
},
paths: {
layout: 'custom/release/release.min',
forms: 'custom/release/release.min',
serviceTraining: 'custom/release/release.min',
serviceMail: 'custom/release/release.min'
}
});
require(['layout', 'serviceMail', 'serviceTraining', 'forms'], function() {
$(function() {
// theme
var layoutPlugin = $( "body" ).layout({ siteName: "Website name", domainName: "website.com" });
layoutPlugin.layout( "init" );
// Forms
var formsPlugin = $( "body" ).forms();
formsPlugin.forms( "init" );
// Load mail services
var serviceMail = $.services.serviceMail();
serviceMail.subscribe( $(".form--subscribe") );
// Subscribe to the MailChimp e-mail list
serviceMail.contact( $(".form--contact") );
// Register for training
var serviceTraining = $.services.serviceTraining();
serviceTraining.register( $(".form--training-register") );
});
});
As always, all suggestions are welcome. Thank you!
Having two paths that point to the same file is in general a good way to get loading errors. (I say "in general" because there may be specific cases that will work. It is just luck though. You're not supposed to do it.)
What you should do is use instead of paths the bundles configuration option:
bundles: {
'custom/release/release.min': ['layout', 'forms',
'serviceTraining', 'serviceMail']
}
This tells RequireJS that when you want to load any of the four modules in the array, it should load the module custom/release/release.min to find them.
I'm new to working with RequireJS, and am trying to figure out shimming 3rd-party, interdependent scripts. Specifically, I'm trying to get the Stanford Crypto scripts imported.
Basically, the suite is comprised of the core (jsbn.js, jsbn2.js, base64.js, rng.js, and prng4.js), a basic RSA script (rsa.js), and an extended RSA script (rsa2.js).
rsa.js defines the global variable-object RSAKey, and rsa2.js references it.
function RSAKey() {
this.n = null;
this.e = 0;
this.d = null;
this.p = null;
this.q = null;
this.dmp1 = null;
this.dmq1 = null;
this.coeff = null;
}
I've set up my shim in a way that I thought was correct, but I get the error "RSAKey is not defined" in rsa2.js. The following is my shim:
require.config({
paths: {
'jsbn': "../StanfordRSA/jsbn.js",
'jsbn2': "../StanfordRSA/jsbn2.js",
'base64': "../StanfordRSA/base64.js",
'rng': "../StanfordRSA/rng.js",
'prng4': "../StanfordRSA/prng4.js",
'rsa': "../StanfordRSA/rsa.js",
'rsa2': "../StanfordRSA/rsa2.js"
},
shim: {
'rsa': {
deps: ['jsbn', 'jsbn2', 'base64', 'rng', 'prng4'],
exports: "RSAKey"
},
'rsa2': {
deps: ['rsa']
}
}
});
My understanding, then, is that if I set 'rsa2' as a requirement in one of my RequireJS modules, it would look at the shim and see that rsa2 is dependent on rsa, which is dependent on the core and exports RSAKey...But that's not what's happening, and it seems like either rsa isn't loading, or it isn't loading correctly. (Please note that all of this works using raw script tags. I'm trying to convert an already existing, already functioning webapp to RequireJS)
Thoughts?
Your basic setup is correct, except for 2 things:
(really important!) You have to omit the .js extensions!!!
You probably have missed the exact dependencies between the scripts.
After some experimentation and reading the comments at the top of the scripts, the working configuration is:
require.config({
paths: {
'jsbn': "../StanfordRSA/jsbn",
'jsbn2': "../StanfordRSA/jsbn2",
'base64': "../StanfordRSA/base64",
'rng': "../StanfordRSA/rng",
'prng4': "../StanfordRSA/prng4",
'rsa': "../StanfordRSA/rsa",
'rsa2': "../StanfordRSA/rsa2"
},
shim: {
'rng': {
deps: ['prng4']
},
'jsbn2': {
deps: ['jsbn']
},
'rsa': {
deps: ['jsbn', 'rng'],
exports: 'RSAKey'
},
'rsa2': {
deps: ['rsa', 'jsbn2'],
exports: 'RSAKey'
}
}
});
Check out a plunk here.
I need some help with the concept of only loading modules when they are needed using requireJS
this is my main.js
require(['jquery', 'path/somemodule'],
function($, somemodule) {
$(document).ready(function() {
somemodule.init()
})
})
and in the somemodule.js
define(['jquery', 'path/someothermodule'], function ($, someothermodule) {
"use strict";
var somemodule;
somemodule = {
init: function () {
someothermodule.init()
}
}
return somemodule;
)}
right now somemodule.js and someothermodule.js is loaded on all pages. How do I only load it when it's needed?
When you require a module2 from module1 using the standard define() syntax module1 will not load/run until module2 has been fully loaded. That looks like this:
// inside module1
define(['module2'], function(mod2) {
// we don't get here until AFTER module2 has already been loaded
});
An alternative to lazy-load module2 looks like this:
// inside module1
define([], function() {
require(['module2'], function(mod2) {
// we don't get here until AFTER module2 has already been loaded
});
// but we DO get here without having loaded module2
});
Now you have to program somewhat carefully to make sure you don't run into any issues with asynchronicity.
In your case you can modify your main.js file
require(['jquery'],
function($) {
// jquery is loaded, but somemodule has not
if(thisPageNeedsSomeModule) {
require(['path/somemodule'],
function(somemodule) {
// now somemodule has loaded
$(document).ready(function() {
somemodule.init()
})
});
}
})
Your main.js file will load any file paths provided to it, so long as other elements of your application specify them as dependencies. See my example main.js file:
require.config({
paths: {
'app': 'app',
'underscore':'bower_components/underscore/underscore-min',
'backbone':'bower_components/backbone/backbone-min',
'marionette':'bower_components/backbone.marionette/lib/backbone.marionette.min',
'jquery': 'bower_components/jquery/jquery.min',
'tpl':'bower_components/requirejs-tpl/tpl',
'bootstrap':'bower_components/bootstrap/dist/js/bootstrap.min',
'leaflet':'bower_components/leaflet/leaflet',
'leaflet.markercluster':'bower_components/leaflet/leaflet.markercluster',
},
shim: {
'underscore': {
exports: '_'
},
'leaflet': {
exports: 'L'
},
'leaflet.markercluster': {
deps: ['leaflet']
},
'backbone': {
deps: ['underscore']
},
'marionette': {
deps: ['backbone']
},
'jquery': {
exports: '$'
},
'bootstrap': {
deps: ['jquery']
},
'app': {
deps: ['jquery', 'leaflet','bootstrap', 'leaflet.markercluster', 'marionette', 'tpl']
},
'app.elem': {
deps:['app']
},
'app.api': {
deps:['app']
}
}
})
require(['app','app.api','app.elem'], function() {
App.start();
})
And my initial application file:
define(['router', 'collections/moments'], function(router, momentCollection) {
// Boot the app!
App = new Marionette.Application();
App.LocResolve = false; // Have we resolved the user's location?
App.Locating = true; // Are we actively tracking the user's location?
App.FileReader = window.FileReader ? new FileReader : null;
App.Position = null; // Instant access to Lat & Lng of user.
App.MomentsRaw = null; // Keep cached copy of returned data for comparison.
App.Moments = new momentCollection; // Current collection of moments.
App.Markers = new L.MarkerClusterGroup(); // Create Marker Cluster Group
App.View = null; // Current view.
// Marionette Regions
App.addRegions({
header: '#header',
map: '#map',
list: '#list',
modal: '#modal',
});
return App
})
I noticed that you aren't passing in a configuration object - is this intentional? If you use R.js, the build optimizer, it will automatically remove unused vendor files for you.
In short, sets paths to your vendor files in the require.js config, then call upon them via define() whenever you need a particular asset. This will ensure that only files you need are used. Hope this helps!