Configuring modules with RequireJS when config depends on RequireJS - javascript

Apologies if I have missed this in the docs. Basically I want to use the RequireJS module configuration feature. I would like to centrally manage the config values given to modules in a package.
This is an example from the docs:
requirejs.config({
config: {
'bar': {
size: 'large'
},
'baz': {
color: 'blue'
}
}
});
//bar.js, which uses simplified CJS wrapping:
define(function (require, exports, module) {
//Will be the value 'large'
var size = module.config().size;
});
//baz.js which uses a dependency array,
define(['module'], function (module) {
//Will be the value 'blue'
var color = module.config().color;
});
My problem is that my configuration info will be a little more complex, and will itself have dependencies. I would like to do:
requirejs.config({
config: {
'bar': {
path: path.dirname(module.uri)
key: crypto.randomBytes(64)
},
}
});
Where variables in my config need to use requireJS to evaluate.
To me it would make sense for there to be a logical separation between the RequireJS configuration - the config necessary to load modules - and the user's module configuration. But I am currently struggling to find this :(

For this sort of solution, I would have the module depend on a "config" module that you can swap for a different one using paths config. So if "bar" needed some config, "bar.js" would look like:
define(['barConfig'], function (config) {
});
Then barConfig.js could have your other dependencies:
define(['crypto'], function (crypto) {
return {
key: crypto.randomBytes(64)
}
});
Then, if you needed different configs for say, production vs. dev, use paths config to map barConfig to other values:
requirejs.config({
paths: {
barConfig: 'barConfig-prod'
}
});

I think the proper way to do this is to make a config module...
// config.js
define(['module', 'path', 'crypto'], function(module, path, crypto) {
return {
path: path.dirname(module.uri)
key: crypto.randomBytes(64)
};
});
Then use it in other modules...
// bar.js
define(['config'], function (config) {
var key = config.key;
});
You can then make it as complicated as you like!
EDIT: You could pollute the global namespace for this special class...
define(['module', 'path', 'crypto'], function(module, path, crypto) {
window.config = {
path: path.dirname(module.uri)
key: crypto.randomBytes(64)
};
});
Add it to the top level require call:
require(['config', 'main']);
Then you can use it without always adding it to your define:
// bar.js
define([], function() {
var key = config.key;
});

Having thought about this a little more I have come up with a workaround. It is not particularly pretty but it does seem to work.
I simply do requireJS(...) twice, first to create the config, and second to load the application modules with the config..
requireJSConfig =
baseUrl: __dirname
nodeRequire: require
# Create the require function with basic config
requireJS = require('requirejs').config(requireJSConfig)
requireJS ['module', 'node.extend', 'crypto', 'path'], (module, extend, crypto, path) ->
# Application configuration
appConfig =
'bar':
path: path.dirname(module.uri)
key: crypto.randomBytes(64) # for doing cookie encryption
# get a new requireJS function with CONFIG data
requireJS = require('requirejs').config(extend(requireJSConfig, config: appConfig))
requireJS ['bar'], (app) ->
###
Load and start the server
###
appServer = new app()
# And start the app on that interface (and port).
appServer.start()
And in bar.coffee
# bar.coffee
define ['module'], (module) ->
# config is now available in this module
console.log(module.config().key)

Riffing on what #jrburke is saying, I found the following pattern to be quite useful: define a config module and it's dependencies in the main.js just before the invocation of require.config().
main.js
define('config', ['crypto'], function (crypto) {
return {
'bar': {
key: crypto.randomBytes(64)
},
};
});
requirejs.config({
deps: ['app'],
});
app.js
require(['config'], function (config){
// outputs value of: crypto.bar.key
console.log(config.bar.key);
});
Plnkr Demo: http://plnkr.co/edit/I35bEgaazEAMD0u4cNuj

Related

PDFMAKE: 'Roboto-Regular.ttf' not found in virtual file system ONLY AFTER GULP

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();

RequireJS load only concatenated file

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'
};

How do I properly alias a module in RequireJS on Node.js

I'm using RequireJS on my Node.js server to aid in code re-use. I'm sharing modules between client and server, and therefore I want to keep these modules as general as possible.
The problem is that I have a 3rd party library (easeljs) that provides both a client and a server-side implementation, with the server-side implementation provided by npm as 'node-easel'.
The way I expected to deal with this doesn't work. I provided a paths config for the server side application as follows:
requirejs.config({
nodeRequire: require,
paths: {
'easeljs': 'node-easel'
}
})
The above code will first look for node-easel.js in the root directory, before falling back onto node's require('easeljs'), which is not what I want. I want requireJS to fall back onto require('node-easel'). What would be the best way to accomplish this?
I struggled a little with this myself, but actually was able to use a combination of RequireJS map configuration and a NPM module loader. The code is as follows:
requirejs.config({
paths: {
npm: 'helpers/requirejs/npmloader'
},
map: {
'*': {
dust: 'npm!dustjs-linkedin'
}
}
});
And then for helpers/requirejs/npmloader.js:
define({
normalize: function (name, normalize) {
return name;
},
load: function (name, req, onload, config) {
require(name);
}
});
With this you can easily alias NodeJS modules using RequireJS.
In package.json I also use the following:
"main": "./bin/server.js",
"bin": {
"server.js": "./bin/server.js"
}
With top level bin/server,js being a symbolic link to build/init.js and being executable chmod a+x build/server.js.
Instead of using the amdefine loader, which is rather lacking without the ability to use plugins, I use the full requirejs module with NodeJS, and then I just have define be available globally for all my modules as you can see in the code below.
Then src/init.js (or after building - build/init.js) contains the following which might be a useful example:
#!/usr/bin/env node
/**
* #file The AMD loader module configuration.
* #author runelabs <http://github.com/runelabs>
* #version $Id$
* #description
*
* MODULE 'init' OVERVIEW:
* This defines the init RequireJS configuration module,
* in contrast to other modules which are using the AMD define() pattern.
*
*/
/*global require, define, global*/ // using JShint linter
(function() {
'use strict';
var requirejs, mocha, mainProg;
global.requirejs = global.define = requirejs = require('requirejs');
mainProg = ['main'];
var config = {
deps: mainProg,
paths: {
lib: 'lib/',
tests: '../tests',
app: '.',
config: 'models/config',
packages: 'packages',
logger: 'packages/logger/logger',
pkg: 'helpers/requirejs/pkgloader',
npm: 'helpers/requirejs/npmloader',
text: 'lib/requirejs-text/text'
/* ... more paths */
},
map: {
'*': {
marionette:'npm!backbone.marionette',
dust: 'npm!dustjs-linkedin'
}
}
};
requirejs.config(config);
requirejs.onError = function (err, u, l) {
var LOG = require('./packages/logger/logger');
LOG.error('RequireJS error: ', err.requireType);
LOG.error(' mod: ', err.requireModules);
LOG.error(' map: ', err.requireMap);
if (err.requireType === 'timeout') {
LOG.error(' modules: ', err.requireModules);
}
LOG.error('ERR: ', err);
LOG.error('ERR error: ', err.error);
LOG.error('ERR type: ', typeof err);
if (err['toSource']) {
LOG.error('ERR src: ', err.toSource());
}
LOG.error('ERR name: ', err.name);
LOG.error('ERR message: ', err.message);
LOG.error('ERR file: ', err.fileName);
LOG.error('ERR line: ', err.lineNumber);
LOG.error('ERR col: ', err.columnNumber);
LOG.error('ERR stack: ', err.stack);
throw(err);
};
})();
Hope this helps someone else looking to use RequireJS to load stuff from node_modules.
EDIT: A smaller example is from RequireJS creator James Burke at:
https://groups.google.com/forum/#!msg/requirejs/ur_UQLr04rc/sSpM8y87VNMJ

Set lodash / underscore template settings globally using require.js

Is there a way to set the templateSettings for lodash when using RequireJS?
Right now in my main startup I have,
require(['lodash', 'question/view'], function(_, QuestionView) {
var questionView;
_.templateSettings = {
interpolate: /\{\{(.+?)\}\}/g,
evaluate: /\{\%(.+?)\%\}/g
};
questionView = new QuestionView();
return questionView.render();
});
but it doesn't seem to want to set the templateSettings globally because when I use _.template(...) in a module it wants to use the default templateSettings. The problem is that I don't want to change this setting in every module that uses _.template(...).
Based onĀ #Tyson Phalp suggestion, that means this SO question.
I adapted it to your question and I tested it using RequireJS 2.1.2 and SHIM configuration.
This is the main.js file, that is where the requireJS config is:
require.config({
/* The shim config allows us to configure dependencies for
scripts that do not call define() to register a module */
shim: {
underscoreBase: {
exports: '_'
},
underscore: {
deps: ['underscoreBase'],
exports: '_'
}
},
paths: {
underscoreBase: '../lib/underscore-min',
underscore: '../lib/underscoreTplSettings',
}
});
require(['app'],function(app){
app.start();
});
Then you should create the underscoreTplSettings.js file with your templateSettings like so:
define(['underscoreBase'], function(_) {
_.templateSettings = {
evaluate: /\{\{(.+?)\}\}/g,
interpolate: /\{\{=(.+?)\}\}/g,
escape: /\{\{-(.+?)\}\}/g
};
return _;
});
So your module underscore will contain the underscore library and your template settings.
From your application modules just require the underscore module, in this way:
define(['underscore','otherModule1', 'otherModule2'],
function( _, module1, module2,) {
//Your code in here
}
);
The only doubt I have is that I'm exporting the same symbol _ two times, even tough this work I'm not sure if this is considered a good practice.
=========================
ALTERNATIVE SOLUTION:
This also works fine and I guess it's a little bit more clean avoiding to create and requiring an extra module as the solution above. I've changed the 'export' in the Shim configuration using an initialization function. For further understanding see the Shim config reference.
//shim config in main.js file
shim: {
underscore: {
exports: '_',
init: function () {
this._.templateSettings = {
evaluate:/\{\{(.+?)\}\}/g,
interpolate:/\{\{=(.+?)\}\}/g,
escape:/\{\{-(.+?)\}\}/g
};
return _; //this is what will be actually exported!
}
}
}
You should pass your _ variable with template settings as function argument or as property in global object (window for browsers or proccess for nodejs).
_.templateSettings = {
interpolate: /\{\{(.+?)\}\}/g,
evaluate: /\{\%(.+?)\%\}/g
};
questionView = new QuestionView(_);
Or
_.templateSettings = {
interpolate: /\{\{(.+?)\}\}/g,
evaluate: /\{\%(.+?)\%\}/g
};
window._ = _
First option is better.
Bear in mind that if you're using underscore >=1.6.0 or lodash-amd the solution is pretty simple:
"main.js" configuration file
require.config({
baseUrl: './', // Your base URL
paths: {
// Path to a module you create that will require the underscore module.
// You cannot use the "underscore" name since underscore.js registers "underscore" as its module name.
// That's why I use "_".
_: 'underscore',
// Path to underscore module
underscore: '../../bower_components/underscore/underscore',
}
});
Your "_.js" file:
define(['underscore'], function(_) {
// Here you can manipulate/customize underscore.js to your taste.
// For example: I usually add the "variable" setting for templates
// here so that it's applied to all templates automatically.
// Add "variable" property so templates are able to render faster!
// #see http://underscorejs.org/#template
_.templateSettings.variable = 'data';
return _;
});
A module file. It requires our "_" module which requires "underscore" and patches it.
define(['_'], function(_){
// You can see the "variable" property is there
console.log(_.templateSettings);
});

requireJs order plugin: can it exist in a different path than the require.js basePath?

when using the order plugin require.js looks for it in the given basePath/order.js
example:
require(
{
baseUrl: '/public/js/'
},
[
'require',
'order!http://cdnjs.cloudflare.com/ajax/libs/underscore.js/1.1.7/underscore-min.js',
'order!http://cdnjs.cloudflare.com/ajax/libs/backbone.js/0.5.3/backbone-min.js',
'order!http://cdnjs.cloudflare.com/ajax/libs/json2/20110223/json2.js'
],
function (require) {
require(['application'], function (app) {
app.run();
});
}
);
it looks for the order plugin in http://url.com/public/js/order.js could I somehow store it not in the root of my javascript folder?
You can place your plugins anywhere you like and point to their paths when configuring RequireJS:
require({
baseUrl: "/public/js/",
paths: {
order: "requirejs/plugins/order"
},
...

Categories

Resources