I have two questions.
I am trying to learn RequireJS and use it along with ASP.NET MVC bundling & minification. I am using a separate config file for RequireJS which holds the bundling information. My first problem is how do I pass on the bundle path generated by MVC to the require.config.js file. A clean way to do that will be as below:
index.cshtml
<script id="requirescript" type="text/javascript" src="~/Scripts/require.config.js"
data-baseurl="#Url.Content("~/Scripts")"
data-bundlepath="#System.Web.Optimization.Scripts.Url("~/bundles/scripts").ToString()"></script>
require.config.js
var reqScript = document.getElementById('requirescript');
var baseUrl = reqScript.getAttribute('data-baseurl');
var bundlePath = reqScript.getAttribute('data-bundlepath');
var require = {
baseUrl: baseUrl,
bundles: {
bundlePath : ['jquery','jqueryui','mymodule']
}
}
};
When I do the above, RequireJS tries to load a non-existing script named bundlePath.js, instead what I want is to load the bundled script which is '/bundles/scripts?v=GZ0QWPB4G0soItEmlsPC6Yp3zftCRVleVTcH3LseMWo1' which contains my modules. So first, my question is how do I pass the bundle URL, as generated by the server, to RequireJS in the require.config.js file without hard-coding the bundle path?
Secondly, the jqueryui module seems to be not loading. I have added the module name in the AMD code in jquery ui min file. How do I make jquery ui work with RequireJS and ASP.NET bundling?
There is a NuGet package RequireJs.NET https://www.nuget.org/packages/RequireJsNet/ which is an implementation of RequireJs for .NET MVC.
RequireJS is an implementation of Asynchronous Module Definition (AMD) that provides all the tools you need to write modular JavaScript. If you are working on a large project with a lot of JavaScript code, many external components and frameworks, RequireJS will help you manage the complexity of dependencies.
You will have access to a configuration file (json) which will look like this:
{
"paths": {
"jquery": "jquery-1.10.2",
"jquery-validate": "jquery.validate",
"jquery-validate-unobtrusive": "jquery.validate.unobtrusive",
"bootstrap": "bootstrap",
"respond": "respond",
"i18n": "Components/RequireJS/i18n",
"text": "Components/RequireJS/text",
"menu-module" : "Controllers/Common/menu-module"
},
"shim": {
"jquery-validate": {
"deps": [ "jquery" ]
},
"jquery-validate-unobtrusive": {
"deps": [ "jquery", "jquery-validate" ]
},
"bootstrap": {
"deps": ["jquery"]
}
},
"autoBundles": {
"main-app": {
"outputPath": "Scripts/Bundles/",
"include": [
{
"directory": "Controllers/Root"
}
]
},
"require-plugins": {
"outputPath": "Scripts/Bundles/",
"include": [
{
"file": "Components/RequireJS/i18n"
},
{
"file": "Components/RequireJS/text"
}
]
}
}
}
And after that you will render RequireJs config into your layout.
#using RequireJsNet
<!DOCTYPE html>
<html>
<head>
<!-- head content -->
</head>
<body>
<!-- body content -->
#Html.RenderRequireJsSetup(new RequireRendererConfiguration
{
// the url from where require.js will be loaded
RequireJsUrl = Url.Content("~/Scripts/Components/RequireJS/require.js"),
// baseUrl to be passed to require.js, will be used when composing urls for scripts
BaseUrl = Url.Content("~/Scripts/"),
// a list of all the configuration files you want to load
ConfigurationFiles = new[] { "~/RequireJS.json" },
// root folder for your js controllers, will be used for composing paths to entrypoint
EntryPointRoot = "~/Scripts/",
// whether we should load overrides or not, used for autoBundles, disabled on debug mode
LoadOverrides = !HttpContext.Current.IsDebuggingEnabled,
// compute the value you want locale to have, used for i18n
LocaleSelector = html => System.Threading.Thread.CurrentThread.CurrentUICulture.Name.Split('-')[0],
// instance of IRequireJsLogger
Logger = null,
// extensability point for the config object
ProcessConfig = config => { },
// extensability point for the options object
ProcessOptions = options => { },
// value for urlArgs to be passed to require.js, used for versioning
UrlArgs = RenderHelper.RenderAppVersion()
})
</body>
</html>
For further reading you can access the documentation page: http://requirejsnet.veritech.io/ .
Or the github project (for issues and questions) : https://github.com/vtfuture/RequireJSDotNet
In this package exists a compressor too for bundling and minification (based on YUI compressor).
Instead of bundlePath use the bundle path "/Scripts/bundles/scripts" . it'll work.
An old question but you can use #Scripts.RenderFormat() to get MVC to output the filename of the bundle on it's own. e.g.
Bundle
bundles.Add(new ScriptBundle("~/bundles/bundleName").Include(
"~/Scripts/filename1.js",
"~/Scripts/filename2.js",
"~/Scripts/filename3.js"
));
View
<script type="javascript">
var arrayOfFiles = [#Scripts.RenderFormat("\"{0}\",","~/bundles/bundlename")];
</script>
This sets arrayOfFiles to
["/Scripts/filename1.js","/Scripts/filename2.js","/Scripts/filename3.js"]
or if the bundling is enabled you just get
["/bundles/bundleName?v=13232424"]
You can then pass this array to other javascript libraries.
Related
Closed. This question does not meet Stack Overflow guidelines. It is not currently accepting answers.
This question does not appear to be about programming within the scope defined in the help center.
Closed 4 years ago.
Improve this question
I would like to share how to bundle an application that acts as a plugin host and how it can load installed plugins dynamically.
Both the application and the plugins are bundled with Webpack
The application and plugins are compiled and distributed independently.
There are several people on the net who are looking for a solution to this problem:
Multi-project build and dynamically loading modules with webpack
Loading prebuilt webpack bundles at runtime
How to expose objects from Webpack bundle and inject external libs into compiled bundle?
Dynamic Requires
https://github.com/webpack/webpack/issues/118
The solution described here is based on #sokra's Apr 17, 2014 comment on Webpack issue #118 and is slightly adapted in order to work with Webpack 2.
https://github.com/webpack/webpack/issues/118
Main points:
A plugin needs an ID (or "URI") by which it registers at the backend server, and which is unique to the application.
In order to avoid chunk/module ID collisions for every plugin, individual JSONP loader functions will be used for loading the plugin's chunks.
Loading a plugin is initiated by dynamically created <script> elements (instead of require()) and let the main application eventually consume the plugin's exports through a JSONP callback.
Note: You may find Webpack's "JSONP" wording misleading as actually no JSON is transferred but the plugin's Javascript wrapped in a "loader function". No padding takes place at server-side.
Building a plugin
A plugin's build configuration uses Webpack's output.library and output.libraryTarget options.
Example plugin configuration:
module.exports = {
entry: './src/main.js',
output: {
path: path.resolve(__dirname, './dist'),
publicPath: '/' + pluginUri + '/',
filename: 'js/[name].js',
library: pluginIdent,
libraryTarget: 'jsonp'
},
...
}
It's up to the plugin developer to choose an unique ID (or "URI") for the plugin and make it available in the plugin configuration. Here I use the variable pluginURI:
// unique plugin ID (using dots for namespacing)
var pluginUri = 'com.companyX.pluginY'
For the library option you also have to specify an unique name for the plugin. Webpack will use this name when generating the JSONP loader functions. I derive the function name from the plugin URI:
// transform plugin URI into a valid function name
var pluginIdent = "_" + pluginUri.replace(/\./g, '_')
Note that when the library option is set Webpack derives a value for the output.jsonpFunction option automatically.
When building the plugin Webpack generates 3 distribution files:
dist/js/manifest.js
dist/js/vendor.js
dist/js/main.js
Note that vendor.js and main.js are wrapped in JSONP loader functions whose names are taken from output.jsonpFunction and output.library respectively.
Your backend server must serve the distribution files of each installed plugin. For example, my backend server serves the content of a plugin's dist/ directory under the plugin's URI as the 1st path component:
/com.companyX.pluginY/js/manifest.js
/com.companyX.pluginY/js/vendor.js
/com.companyX.pluginY/js/main.js
That's why publicPath is set to '/' + pluginUri + '/' in the example plugin config.
Note: The distribution files can be served as static resources. The backend server is not required to do any padding (the "P" in JSONP). The distribution files are "padded" by Webpack already at build time.
Loading plugins
The main application is supposed to retrieve the list of the installed plugin (URI)s from the backend server.
// retrieved from server
var pluginUris = [
'com.companyX.pluginX',
'com.companyX.pluginY',
'org.organizationX.pluginX',
]
Then load the plugins:
loadPlugins () {
pluginUris.forEach(pluginUri => loadPlugin(pluginUri, function (exports) {
// the exports of the plugin's main file are available in `exports`
}))
}
Now the application has access to the plugin's exports. At this point, the original problem of loading an independently compiled plugin is basically solved :-)
A plugin is loaded by loading its 3 chunks (manifest.js, vendor.js, main.js) in sequence. Once main.js is loaded the callback will be invoked.
function loadPlugin (pluginUri, mainCallback) {
installMainCallback(pluginUri, mainCallback)
loadPluginChunk(pluginUri, 'manifest', () =>
loadPluginChunk(pluginUri, 'vendor', () =>
loadPluginChunk(pluginUri, 'main')
)
)
}
Callback invocation works by defining a global function whose name equals output.library as in the plugin config. The application derives that name from the pluginUri (just like we did in the plugin config already).
function installMainCallback (pluginUri, mainCallback) {
var _pluginIdent = pluginIdent(pluginUri)
window[_pluginIdent] = function (exports) {
delete window[_pluginIdent]
mainCallback(exports)
}
}
A chunk is loaded by dynamically creating a <script> element:
function loadPluginChunk (pluginUri, name, callback) {
return loadScript(pluginChunk(pluginUri, name), callback)
}
function loadScript (url, callback) {
var script = document.createElement('script')
script.src = url
script.onload = function () {
document.head.removeChild(script)
callback && callback()
}
document.head.appendChild(script)
}
Helper:
function pluginIdent (pluginUri) {
return '_' + pluginUri.replace(/\./g, '_')
}
function pluginChunk (pluginUri, name) {
return '/' + pluginUri + '/js/' + name + '.js'
}
I would like to use Webpack in a multi-page application in such a way that some pre-determined dependencies are bundled into a "vendor" chunk and the rest of the dependencies is bundled into a "commons" chunk.
For example, assuming two entry points (effectively representing a different page each), pageA.js and pageB.js contain both this code (in EC6, processed via Babel), followed by their own code:
import $ from 'jquery';
require('bootstrap/dist/css/bootstrap.css');
import angular from 'angular';
import uitree from 'angular-ui-tree';
I'd like jQuery and Bootstrap to be bundled into a "vendor" chunk, and the rest (whatever that is) to be bundled into a "commons" chunk.
The objectives are:
I would like to be able to have another separate build that would be able to rely on that same vendor chunk, without it needing to re-include the vendor libraries (I would explicitly declare that set of vendor libraries, to make it available to any sub-build that needs it).
I would also like not to have to re-process the vendor chunk every time I make a change to a page's script.
Here is the configuration I've tried:
module.exports = {
entry : {
"vendor" : [ "jquery", "bootstrap" ],
"pageA" : "./src/pageA.js",
"pageB" : "./src/pageB.js"
},
output : {
path : path.join(__dirname, "./dest"),
filename : "[name].chunk.js"
},
module : {
loaders : [ {
test : /bootstrap\/js\//,
loader : 'imports?jQuery=jquery'
},
/* ... Some modules for angular and CSS processing ... */
{
test : /\.js?$/,
include : [ path.resolve(__dirname, "./src") ],
loader : 'babel',
query : {
presets : [ 'es2015' ]
}
}
/* ... Some other settings for the fonts ... */ ]
},
plugins : [
new webpack.ProvidePlugin({
$ : "jquery",
jQuery : "jquery"
}),
new webpack.optimize.UglifyJsPlugin({
sourceMap : false,
mangle : false,
compress : false
}),
new CommonsChunkPlugin({
name : "vendor",
minChunks : Infinity
}),
new CommonsChunkPlugin({
name : "commons",
minChunks : 2
})
]
};
With the configuration above, I get jQuery and Bootstrap in vendor.chunk.js, as required, but the commons.chunk.js file is almost empty, all the rest of what's commonly used by pageA.js and pageB.js is then put into pageA.chunk.js and pageB.chunk.js (effectively duplicating that code).
If I swap the order of the last two plugins, commons.chunk.js now contains almost everything (unless what's actually specific to pageA.js and pageB.js), and vendor.chunk.js is almost empty:
plugins : [
// ...,
new CommonsChunkPlugin({
name : "commons",
minChunks : 2
}),
new CommonsChunkPlugin({
name : "vendor",
minChunks : Infinity
})
]
Is there a way to bundle a pre-defined list of libraries (e.g. [ "jquery", "jquery-ui", "bootstrap" ] into one particular chunk (in such a way that it can be used by completely independent scripts) and also have another common chunk for whatever else is in commonly used between the entry points?
The aim of all this would be to be able to build a completely separate piece of code for another page later, and tell it it doesn't need to re-bundle those pre-defined libraries.
Here is a diagram representing what I'm trying to achieve:
I would then use the generated scripts as follows on page A:
<script src="vendor.js"></script>
<script src="common.js"></script>
<script src="pageA.chunk.js"></script>
And on page C (built completely independently from pages A and B):
<script src="vendor.js"></script>
<script src="common2.js"></script>
<script src="pageC.chunk.js"></script>
(I am using Webpack 1.12.14.)
I have tried the solution suggested in the only answer so far. While this makes it indeed possible to separate the vendor chunk from the commons chunk, the vendor chunks (with the same definition) made from two separate builds generally cannot be swapped between each other. This does not make it possible to use only one of those vendor chunks across two builds (even though they are virtually the same).
I was working on something similar. I observed the behaviour you desire by having this configuration:
const entryPath = path.resolve(__dirname, "src", "modules"),
entryPoints = {
vendor: ["babel-polyfill", "react", "react-dom", "react-pure-render", "react-simpletabs", "react-redux", "redux", "redux-thunk"],
a: entryPath + "/a.js",
b: entryPath + "/b.js",
c: entryPath + "/c.js",
d: entryPath + "/d.js",
...
}, plugins = [
new CommonsChunkPlugin({
name: "commons",
filename: "commons.bundle.js",
minChunks: 2,
chunks: Object.keys(entryPoints).filter(key => key !== "vendor")
}),
new CommonsChunkPlugin("vendor", "vendor.bundle.js"),
new ExtractTextPlugin("[name].bundle.css", {
//allChunks: true
})
];
so this code above left in b.bundle.js still some import of React and other React libraries. after I added "fixed-data-table" to the vendor list, that disappeared and the only react source code there was was in vendor.bundle.js I assume that is what you were looking for? (in the end each vendor not listed in vendor list ended up in each own module.bundle.js or in commons.bundle.js if it was re-used in multiple bundles)
Regards
Jonas
As far as your request to be able to use the vendor chunk created in one webpack build with bundles from another build, the only way I have found to do this is through a combination of expose-loader and externals.
In my case, I successfully used this to bundle jQuery (and Bootstrap) into a "common.js" in one build, and then use that jQuery in modules belonging to another build, as follows:
1. Expose jQuery from build A chunk
module: {
loaders: [
{
// export jQuery globals for other modules to use as externals
test: require.resolve('jquery'),
loader: 'expose?$!expose?jQuery'
}
]
}
2. Consume jQuery in build B modules
externals: {
'jquery': 'jQuery'
}
Of course, the drawback to this is that you're bridging between the two sets of bundles by using manually-managed global variables, which you probably started using webpack to avoid :-)
However, I don't know of any other way at this point, given that every webpack build creates its own namespace of "private" IDs internally to reference modules, with no guarantee of stability or global unique-ness.
I want to add some JavaScript module to my application made by meanjs-generator for Yeoman, but the modules’ script tags aren't generated to index.html. I just added the modules through bower and I didn’t touch any other files because generated files by the generator seem to look for .js files and add them to index.html automatically.
What is the correct way to add JavaScript module?
After executed bower command, you have to add your .js or .css file paths to config/env/all.js like as follows:
module.exports = {
...
assets: {
lib: {
css: [
'public/lib/bootstrap/dist/css/bootstrap.css',
'public/lib/bootstrap/dist/css/bootstrap-theme.css',
],
js: [
'public/lib/ng-file-upload/angular-file-upload-shim.js',
'public/lib/angular/angular.js',
'public/lib/angular-resource/angular-resource.js',
'public/lib/angular-cookies/angular-cookies.js',
'public/lib/angular-animate/angular-animate.js',
'public/lib/angular-touch/angular-touch.js',
'public/lib/angular-sanitize/angular-sanitize.js',
'public/lib/angular-ui-router/release/angular-ui-router.js',
'public/lib/angular-ui-utils/ui-utils.js',
'public/lib/angular-bootstrap/ui-bootstrap-tpls.js',
'public/lib/ng-file-upload/angular-file-upload.js'
]
},
...
}
I've read through the documentation and the example app.build.js file but just can't get my js files to concatenate and minify into one single file. I think I'm just not understanding exactly what settings I need in the build script and was hoping for some help.
My app is set up like this:
src >
js >
build.js
r.js
config.js
app >
main.js
lib >
module1.js
module2.js
module3.js
vendor >
require.js
jquery.js
jquery.validation.js
build >
// Where concat and minified file would go
config.js looks like this:
requirejs.config({
"baseUrl" : "src/js/lib", // Used because when setting dependencies in modules, this is used
"paths" : {
"app" : "../app",
"jquery" : [
"https://code.jquery.com/jquery-1.11.1.min",
"../vendor/jquery"
],
"validate" : "../vendor/jquery.validate.min"
},
"shim" : {
// Allow plugins with dependencies to load asynchronously
validate : ["jquery"]
}
});
// Load the main app module to start the app
requirejs(["app/main"]);
main.js looks like this:
require(["module1", "module2", "module3"], function(Module1, Module2, Module3) {
return [
Module1.init(),
Module2.init(),
Module3.init(),
Module4.init()
];
});
And then the build.js is where I'm lost. I thought I should load a mainConfigFile because I'm using the shim, but I'm not sure. If I do load that config file, it uses the baseUrl from that config file. I'm not sure what name: is supposed to refer to exactly and whether I'm missing some necessary configuration options.
({
//baseUrl: ".",
paths: {
jquery: "empty:",
//main: "../app/main",
//app: "app"
},
name: "app/main",
out: "../build/main.js",
//mainConfigFile: "config"
})
If I run that build file as it is (with those lines commented out) I get:
Error: ENOENT, no such file or directory
'/Users/davidpaul/Sites/require/src/js/module1.js' In module tree:
app/main
I'm not really sure what's being referred to when it says 'module tree'. I keep making changes to paths in the build file but not making progress so hoping for someone to explain this a bit to me.
The builder parses all paths relative to the build file location (unless changed via the baseUrl property). If you look relative to src/js/build.js, your main.js is in ./app/ and module1/2/3.js are in ./lib/. All paths inside modules have to be specified relatively to the common root, so to make your example work it's enough to change the signature of main.js to:
require(["lib/module1", "lib/module2", "lib/module3"], function(M1, M2, M3) {
// (...)
})
Note that config.js doesn't take part in the build process, you may need to change it as well to make your application work both "raw" and optimized.
I'm having issues trying to load ckeditor via requirejs (I've tried converting the main ckeditor js file into individual modules but that has just caused all hell to break loose) and so I'm now checking to see if there is a very simple way to do this that I've missed.
I know requirejs allows you to load normal js scripts so maybe just loading the ckeditor.js file (un-edited, so it's still an IIFE/self-executing function) - would that work with requirejs or if you're using requirejs for loading modules, does the entire project then need to be module based?
Any help appreciated.
Kind regards,
Mark
Alternatively, you can create a RequireJS shim to load things in the correct order, and alias proper RequireJS module names to the CKEditor distribution files.
This means your module still declares it is dependant on CKEditor, which is a lot nicer than having it just show up by magic.
require.config({
shim: {
'ckeditor-jquery':{
deps:['jquery','ckeditor-core']
}
},
paths: {
"jquery": '/javascript/jquery-1.7.1/jquery.min',
'ckeditor-core':'/javascript/ckeditor-3.6.4/ckeditor',
'ckeditor-jquery':'/javascript/ckeditor-3.6.4/adapters/jquery'
}
});
then in a module you can depend on ckeditor-jquery (or ckeditor-core for that matter, if you don't need the jQuery integration) and know it'll be available:
require(
[
"jquery",
"ckeditor-jquery"
],
function( _jquery_ ) {
$('#editorContent2').ckeditor({
customConfig : '',
skin:'office2003'
});
}
}
Another way to do that:
var require = {
"shim": {
"path/foo/ckeditor/ckeditor": { "exports": "CKEDITOR" }
}
};
define(['moduleX', 'path/foo/ckeditor/ckeditor'], function (x, ckeditor) {
ckeditor.editor.prototype.fooFunc = function() {
};
});
OK, it seems I answered my own question here.
Instead of trying to break ckeditor down into modules I just used RequireJs to load the script in it's entirety.
require(['require', 'dependancy-A', 'dependancy-B', 'dependancy-C'], function(require, A, B, C){
// this = [object DOMWindow]
// CKEDITOR_BASEPATH is a global variable
this.CKEDITOR_BASEPATH = '/ckeditor/';
require(['/ckeditor/ckeditor'], function(){
// Code to create a new editor instance
});
});
```