I have an issue when trying to optimize client scripts that use client-side jade templating. Initially, I had an error as described in Require.js + Backbone optimization, but then I removed the "text" module from the paths configuration, added it to excludes and made text.js available locally. After doing that, I get an error where the optmizer tries to resolve the parameter being passed to the text module.
Here's my client-side template:
define([
'Handlebars',
'text!/templates/product/something.jade'
], function(Handlebars, txtSomething) {
var template = Handlebars.template,
templates = Handlebars.templates = Handlebars.templates || {};
templates['something'] = Handlebars.compile(txtSomething);
});
And then the optimizer configuration:
({
baseUrl: ".",
name: "product",
out: "product.js",
paths: {
Handlebars: "empty:"
},
exclude: ["text"]
})
And the error I get:
Error: ENOENT, no such file or directory 'C:\templates\product\something.jade'
In module tree:
product
modules/something
templates/something
text
Any advice on how to resolve the issue?
I precompile the handlebars templates before running optimization. that removes the text references
Related
I'd like to build html/index.pug into dist/index.html using Webpack 5.
With Webpack 4 I used to use file-loader for this, but it seems deprecated in Webpack 5: no mentions of it in the Loaders page. The Webpack 5 solutions seems to be using Asset Modules: that page makes it clear that file-loader was the old solution, for Webpack 4.
So far I failed to get it to work though. These are a couple of configurations I tried in my webpack.config.js's module.rules:
1. Using the pug-loader only
{
test:path.resolve('./html/index.pug'),
type:'asset/resource',
loader:'pug-loader',
generator:{filename:'index.html'}}
}
This creates a dist/index.html file which contains the following:
var pug = require("!../node_modules/pug-runtime/index.js");
function template(locals) {var pug_html = "", pug_mixins = {}, pug_interp;pug_html = pug_html + "\u003C!DOCTYPE html\u003E\u003Chtml lang=\"en\"\u003E\u003Chead\u003E\u003Ctitle\u003E" + (pug.escape(null == (pug_interp = "Hello World") ? "" : pug_interp)) + "\u003C\u002Ftitle\u003E\u003C\u002Fhead\u003E\u003Cbody\u003E\u003Cp\u003EHello World\u003C\u002Fp\u003E\u003C\u002Fbody\u003E\u003C\u002Fhtml\u003E";;return pug_html;};
module.exports = template;
It looks like pug-loader converts the pug file into a JavaScript module which generates the html code when it's called. What I want is the resulting HTML code, instead of a JS function that generates it.
2. Using val-loader to execute the JavaScript module generated above
{
test:path.resolve('./html/index.pug'),
type:'asset/resource',
use:['val-loader','pug-loader'],
generator:{filename:'index.html'}}
}
This doesn't work either: Webpack throws an error when it tries to build dist/index.pug:
ERROR in ./html/index.pug
Module build failed (from ./node_modules/val-loader/dist/cjs.js):
Error: Unable to execute "/home/bluenebula/work/webpack5-test/html/index.pug": Error: Cannot find module '!../node_modules/pug-runtime/index.js'
Note that /home/bluenebula/work/webpack5-test/node_modules/pug-runtime/index.js does exist.
Question
Are Asset Modules the right tool to generate an HTML file from a Pug one using Webpack 5?
What else am I supposed to do?
How do I get it to work?
(Pug 3.0.2)
I replaced the loader 'pug-loader' with '#webdiscus/pug-loader',
and it works.
plugins: [
new HtmlWebpackPlugin({
template: path.join(__dirname, 'src/index.pug'),
filename: 'index.html',
}),
],
module: {
rules: [
{
test: /\.pug$/,
loader: '#webdiscus/pug-loader',
},
],
},
The reference I followed :
https://github.com/webdiscus/pug-loader#usage-in-javascript
I am new to r.js optimization but a fan of requirejs
build-config.js
({
appDir: "./src/main/webapp/webresources/javascript/",
baseUrl: "./",
dir: "./target/webresources/js",
optimizeCss: "none",
mainConfigFile: "./src/main/webapp/webresources/javascrip/app.js",
inlineText: true,
removeCombined: true,
modules: [
{
name: "app",
}
]
})
app.js looks something like
if (typeof _JSHome=== "undefined") {
_JSHome = "/webresources/javascript/edulastic";
}
if (typeof EditorValue === "undefined") {
EditorValue = "";
}
require.config({
baseUrl: '/webresources/javascript',
waitSeconds: 180,
paths: {
tpl : _JSHome+'/js/tpl',
underscore : 'underscore-min',
backbone : 'backbone-min',
text : 'text/text',
jquery : 'jquery',
jqueryuitouchpunch : "jquery.ui.touch-punch",
modernizr : 'modernizr',
hammer : 'hammer.min',
detectizr : 'detectizr',
bootstrap : _edulasticJSHome+'/bootstrap/js/bootstrap.min',
fastClick : "mobileutils/fastclick/fastclick.min",
asMetaData : _JSHome+'/js/app/metaData',
highCharts : '/webresources/javascript/highcharts/highcharts-min',
},
});
When I run
r.js -o build-config.js in my project root directory, I'm getting the following error :
Try only using a config that is also valid JSON, or do not use mainConfigFile and instead copy the config values needed into a build file or command line arguments given to the optimizer.
Source error from parsing: e:/mongrel_mpq/src/main/webapp/webresources/javascript/app.js: > ReferenceError: _JSHome is not defined
Duplicate but without solution - Require.js optimizer and variables in paths
The configuration you have in app.js is a computed configuration. For instance, you set _JSHome to a value and then use it in the configuration. At runtime, there is no problem with this. However, r.js is not designed to execute what you pass to mainConfigFile. What it does is look for a JSON object that is passed to require.config (or requirejs.config) and uses the JSON object. It is as if r.js were to go into your app.js file and copy all the text inside require.config(...) and then paste it into its own execution context. When it tries to use what it captured from your app.js file, you get the error that _JSHome is not defined because in the context where r.js interprets your configuration, _JSHome is not defined.
The simple solution would be to have a configuration which is not computed. However, if you need to have a computed configuration, then to get r.js to work with it, it needs to be computed before r.js sees it. This could be done as part of a build system (with Grunt, Gulp, make, etc.).
Pulling my hair out here looking for a simple solution to share code, required via NPM, across multiple Browserify or Webpack bundles. Thinking, is there such a thing as a file "bridge"?
This isn't due to compile time (I'm aware of watchify) but rather the desire to extract out all of my vendor specific libs into vendor.js so to keep my app.js filesize down and to not crash the browser with massive sourcemaps. Plus, I find it way cleaner should the need to view the compiled js arise. And so:
// vendor.js
require('react');
require('lodash');
require('other-npm-module');
require('another-npm-module');
Its very important that the code be loaded from NPM as opposed to Bower, or saved into some 'vendor' directory in order to be imported via a relative path and identified via a shim. I'd like to keep every library reference pulled via NPM except for my actual application source.
In app.js I keep all of my sourcecode, and via the externals array, exclude vendor libraries listed above from compilation:
// app.js
var React = require('react');
var _ = require('lodash');
var Component = React.createClass()
// ...
And then in index.html, I require both files
// index.html
<script src='vendor.js'></script>
<script src='app.js'></script>
Using Browserify or Webpack, how can I make it so that app.js can "see" into those module loaded via npm? I'm aware of creating a bundle with externals and then referencing the direct file (in, say, node_modules) via an alias, but I'm hoping to find a solution that is more automatic and less "Require.js" like.
Basically, I'm wondering if it is possible to bridge the two so that app.js can look inside vendor.js in order to resolve dependencies. This seems like a simple, straightforward operation but I can't seem to find an answer anywhere on this wide, wide web.
Thanks!
Listing all the vendor files/modules and using CommonChunkPlugin is indeed the recommended way. This gets pretty tedious though, and error prone.
Consider these NPM modules: fastclick and mprogress. Since they have not adopted the CommonJS module format, you need to give webpack a hand, like this:
require('imports?define=>false!fastclick')(document.body);
require('mprogress/mprogress.min.css');
var Mprogress = require('mprogress/mprogress.min.js'),
Now assuming you would want both fastclick and mprogress in your vendor chunk, you would probably try this:
module.exports = {
entry: {
app: "./app.js",
vendor: ["fastclick", "mprogress", ...]
Alas, it doesn't work. You need to match the calls to require():
module.exports = {
entry: {
app: "./app.js",
vendor: [
"imports?define=>false!fastclick",
"mprogress/mprogress.min.css",
"mprogress/mprogress.min.js",
...]
It gets old, even with some resolve.alias trickery. Here is my workaround. CommonChunkPlugin lets you specify a callback that will return whether or not you want a module to be included in the vendor chunk. If your own source code is in a specific src directory, and the rest is in the node_modules directory, just reject the modules based on their path:
var node_modules_dir = path.join(__dirname, 'node_modules'),
app_dir = path.join(__dirname, 'src');
module.exports = {
entry: {
app: "./app.js",
},
output: {
filename: "bundle.js"
},
plugins: [
new webpack.optimize.CommonsChunkPlugin(
/* chunkName= */"vendor",
/* filename= */"vendor.bundle.js"
function (module, count) {
return module.resource && module.resource.indexOf(app_dir) === -1;
}
)
]
};
Where module.resource is the path to the module being considered. You could also do the opposite, and include only the module if it is inside node_modules_dir, i.e.:
return module.resource && module.resource.indexOf(node_modules_dir) === 0;
but in my situation, I'd rather say: "put everything that is not in my source source tree in a vendor chunk".
Hope that helps.
With webpack you'd use multiple entry points and the CommonChunkPlugin.
Taken from the webpack docs:
To split your app into 2 files, say app.js and vendor.js, you can require the vendor files in vendor.js. Then pass this name to the CommonChunkPlugin as shown below.
module.exports = {
entry: {
app: "./app.js",
vendor: ["jquery", "underscore", ...],
},
output: {
filename: "bundle.js"
},
plugins: [
new webpack.optimize.CommonsChunkPlugin(
/* chunkName= */"vendor",
/* filename= */"vendor.bundle.js"
)
]
};
This will remove all modules in the vendor chunk from the app chunk. The bundle.js will now contain just your app code, without any of it’s dependencies. These are in vendor.bundle.js.
In your HTML page load vendor.bundle.js before bundle.js.
<script src="vendor.bundle.js"></script>
<script src="bundle.js"></script>
// vendor anything coming from node_modules
minChunks: module => /node_modules/.test(module.resource)
Source: https://github.com/webpack/webpack/issues/2372#issuecomment-213149173
I was under the impression that the RequireJS Optimizer would look through the defined dependencies and gather up all of the referenced js files in an application and bundle them up into a single, large js file.
You'd then be able to reference that single file in your html script include.
But this doesn't seem to be the case. When I run this, I get a large file, but it includes the original main.js file that includes paths to files in a directory structure.
What is the point of that? Why does the new large file contain paths outside of itself if everything needed is contained within? It seems like the optimizer would rewrite the paths to point to "./" or something.
When I bundle up the entire app and reference that in the page, I'm getting errors about missing files that ARE included in the large js file:
Uncaught object require.js:70
GET http://localhost/ui/js/modules/mod_limeLight.js 404 (Not Found) require.js:729
Uncaught Error: Script error for: mod_limelight
http://requirejs.org/docs/errors.html#scripterror
build.js:
({
baseUrl: "./src/ui/scripts",
name: "main",
mainConfigFile : "src/ui/scripts/main.js",
out: "dist/ui/scripts/main-built.js"
})
main.js
'use strict';
require.config({
"paths": {
"jquery": "libs/jquery-1.11.0.min",
"twitter_bootstrap": "../bower_components/bootstrap/dist/js/bootstrap.min",
"respondjs": "../bower_components/respond/dest/respond.min",
"debouncejs": "libs/dw-debounce",
"carousel": "libs/jquery.carouFredSel-6.2.1-packed",
"swipe": "libs/jquery.touchSwipe.min",
"app": "app",
"OOo_config": 'libs/oo_conf_entry-ck', // Opinion Lab pop-up
//modules
"addthis": "//s7.addthis.com/js/300/addthis_widget",
"mod_addThis": "modules/mod_AddThis",
"limelight": "//assets.delvenetworks.com/player/embed",
"mod_limelight": "modules/mod_limeLight"
},
"shim": {
"twitter_bootstrap": ["jquery"],
"carousel": ["jquery"],
"swipe": ["jquery"],
"packeryjs": ["jquery"]
}
});
require([
"jquery",
"app",
"OOo_config",
"respondjs",
"mod_addThis",
"mod_limelight"
], function ($, app) {
app.init();
});
example module starts off like:
define([
"jquery", "debouncejs", "limelight"
],
function ($) {
'use strict';
var playerElement = ...
});
Then running:
node r.js -o build.js
What am I missing? Why is it trying to fetch files that are contained in that large js file?
Thanks,
Scott
It identifies the included modules using their usual paths, because that’s simple and unambiguous, and it works. The files aren’t fetched, of course.
Project Intro
My project is a single page storefront. The project has multiple modules, and each module contains a set of controller.js, view.js and model.js files, as well as a template.html file. And uses requirejs to manage dependencies.
Problem Statement
I want to use mainConfigFile to provide paths to reference modules in grunt-requirejs.
Part of my mainConfigFile's require.config is stored in separate file (base.dependency.config), and require.config.paths are pieced together by underscore at runtime.
base.dependency.config
config = {
baseDependencyConfig: {
paths: { ... }
shim: { ... }
}
}
main.js
var dependencies = config.baseDependencyConfig;
var basePaths = config.baseDependencyConfig.paths;
var extensionPaths = {
// extra sets of paths
};
// combine base paths and extension paths at runtime using underscore
var dependencyPaths = _.extend(basePaths, extensionPaths);
dependencies.paths = dependencyPaths;
require.config(dependencies);
// application startup
require(['app', 'eventbus']) {
// code
}
Error
However, grunt requirejs is ignoring mainConfigFile, grunt requirejs tries to find 'app.js' under root, when in fact, 'app' is defined under require.config paths as
'app': 'modules/base/app/base.app.controller'
my gruntFile:
module.exports = function (grunt) {
grunt.initConfig({
// ... other plugin config
requirejs: {
options: {
baseUrl: 'public',
// the paths for the named modules such as 'app' are defined
// in main.js under require.config paths
name: 'main',
include: [
'app',
'cart',
'category'
],
out: 'public/build/app-optimized.js',
mainConfigFile: 'public/main.js',
findNestedDependencies: true,
optimizeCss: 'none',
cssImportIgnore: 'style/style.css, style/mocha.css',
}
}
})
}
my file structure
public
|--modules/
| |--base/
| | |--cart
| | |--category
| | |--category.controller.js
| | |--category.view.js
| | |--category.model.js
| | └-category.template.html
| |
| └--extension/
|
|--style/
|--image/
|--main.js <-- main config file
|--other .js files
mainConfigFile, main.js lives in root, along with a few other application startup js files
main bulk of application files lives inside modules folder
each module folder contains its controller, view and model js file, as well as a template.html file
Edit
the gruntFile worked before, with different mainConfigFile (main.js) setup:
require.config({
paths: {...}
shim: {...}
})
// application startup
require(['app', 'eventbus']) {
// code
}
r.js uses Esprima as Javascript parser to extract the config object from the specified mainConfigFile. It only looks for certain signatures in the code.
Look at
hasRequire(): determine the AST node is a configuration call candidate
findConfig(): calls the above deciding how to extract the config
I've created a patch making it aware of recognizing
requirejs.config(_VariableToExport = { ... });
This is limited and the Javascript parser approach makes it very complicated to make r.js able to extract configurations that were created by functions (like in your code) or so. This patch has not been submitted to the project yet. I'm struggling with bureaucracy yet.
I think the only working workaround so far is
not to use mainConfigFile
exporting the config as NodeJS module
requiring the main.js/config.js in Node (Grunt)
passing the object as value to the config attribute or method
See my comment to this issue for a scribble.
This approach is proven in another, a bit older project I'm working on.
Related to r.js 2.1.11.