Im new to Gulp and have been playing with gulp-minify and gulp-uglify to create an overall main.js file via gulp-requirejs.
However, i need to create 2 versions of this main.js file, one for my DEV environment, and one for PROD.
Reason being, in some .js files, i have listed URLs, for example:
currentPage == '/products/chairs.html'
However, when moving these html files into my .Net structure, the actual URL is:
currentPage == 'goodsandservices/products/our-chairs.html'
My current rJS task is as follows:
gulp.task('rjs', function() {
rjs({
baseUrl: config.src.js,
out: 'main.js',
name: 'main',
mainConfigFile: config.src.js + '/main.js',
exclude: [ 'angular']
})
.pipe(prod ? uglify({ mangle: false, compress: { drop_console: true } }) : gutil.noop())
.pipe(gulp.dest(config.dest.js))
});
I have tried using gulp-replace, and updated my rjs task as:
gulp.task('rjs', function() {
rjs({
baseUrl: config.src.js,
out: 'main.js',
name: 'main',
mainConfigFile: config.src.js + '/main.js',
exclude: [ 'angular']
})
.pipe(prod ? uglify({ mangle: false, compress: { drop_console: true } }) : gutil.noop())
.pipe(replace(/NETStructure/g, 'goodsandservices'))
.pipe(gulp.dest(config.dest.js))
});
And in my .js files, i have updated the URLs to:
currentPage == /NETStructure/g'/products/chairs.html'
However this didnt appear to work.
I suggest you to keep those strings in an object and have two different files for the same object, like:
constants.dev.js
var constants = {
chairs: '/products/chairs.html'
};
constants.prod.js
var constants = {
chairs: 'goodsandservices/products/our-chairs.html'
};
Then you only need to include the correct file (or concatenate wrapped in a closure) and:
currentPage == constants.chairs
Related
I'm building a project on an ejected react cli. The reason I ejected it is because there will be multiple pages being generated and some stand alone scripts that I wanted to make and have them take advantage of the ES6 and debugging tools provided by the boilerplate.
My problem is that while using the technique to build out multiple pages with html-webpack-plugin builds out the generated HTML files with both scripts from each page.
So, lets have a look at the basic entry point
Here's my basic web pack config.
...
entry: {
devServerClient : require.resolve('react-dev-utils/webpackHotDevClient'),
// Finally, this is your app's code:
...pages,
},
...
As you can see, I'm using the same hot module reloading stuff that came with the boiler plate, but then I deviate to spread in values from the pages variable which is being required from another page:
const glob = require("glob");
const path = require("path");
const input_path = path.resolve(__dirname, "../src/apps/*/index.js");
const apps_directories = glob.sync(input_path);
let pages = {};
// Loop through them and append them to the pages object for processing.
apps_directories.map(function (app_directory) {
const split_name = app_directory.split("/");
pages[split_name[split_name.length - 2]] = app_directory;
});
module.exports = pages;
Thats how I generate multiple entries dynamically. This portion of the code works fine, but I thought I'd post it just in case something needs to happen here that I'm missing.
Next We have the actual plugin section. I take a similar approach in modularizing the code, so here's that portion in the config (root level of the web pack object):
...
plugins: [
// Generates an `index.html` file with the <script> injected.
...HtmlWebpackPlugins,
...
]
...
Again, I spread them in, the script that generates these looks like so:
const HtmlWebpackPlugin = require('html-webpack-plugin');
const pages = require("./multiplePaths");
const paths = require("./paths");
const NODE_ENV = process.env.NODE_ENV;
let HtmlWebpackPlugins = [];
Object.keys(pages).map(function (fileName) {
if (NODE_ENV === "development") {
HtmlWebpackPlugins.push( new HtmlWebpackPlugin({
inject: true,
template: paths.appHtml,
filename: `${fileName}.html`,
}));
return;
}
HtmlWebpackPlugins.push(new HtmlWebpackPlugin({
inject: true,
template: paths.appHtml,
filename: `${fileName}.html`,
minify: {
removeComments: true,
collapseWhitespace: true,
removeRedundantAttributes: true,
useShortDoctype: true,
removeEmptyAttributes: true,
removeStyleLinkTypeAttributes: true,
keepClosingSlash: true,
minifyJS: true,
minifyCSS: true,
minifyURLs: true,
},
}));
});
module.exports = HtmlWebpackPlugins;
This script here generates the multiple instances of the HtmlWebpackPlugin class per entry, and we also name the html file based on the folder the name of the folder the app resides in. This satisfies the technique in their issues section.
The problem then happens in the generated HTML page. Notice that the scripts for all the folders are injected in each app:
As you can see, each app's CSS and JS are added. This happens to both pages. I only want the relevant CSS and JS to each page.
Any idea what's going on here?
If you want to add only some of the chunks inside each page you need to specify which chunks exactly you want to have inside your html as scripts:
HtmlWebpackPlugins.push(new HtmlWebpackPlugin({
inject: true,
template: paths.appHtml,
filename: `${fileName}.html`,
chunks: [filename], // add any chunks you need here (for example, chunk with libraries
....
});
I used something like this to create entries for HtmlWebpackPlugin based on webpack entries:
// Webpack entries
const entry = {
"nameA": "./xy/nameA/main.js",
"nameB": "./xy/nameB/main.js",
};
// Plugins
let plugins = [];
// HTML injection
Object.keys(entry).forEach((ent) => {
const htmlPlugin = new HtmlWebpackPlugin({
inject: true,
title: "Your Title",
template: "./html/index.html",
filename: `formats/${ent}/index.html`,
chunks: [ent],
});
plugins.push(htmlPlugin);
});
// Insert more plugins
plugins = plugins.concat([
... your plugins
]);
// Return Config
return {
// define entry point
entry,
// define output file
output: {
...
},
...
// define Plugins
plugins,
...
};
I want to set up an Angular 1.x app from scratch using webpack 2.
I am having trouble finding the best configuration for webpack.config, with optimal entry and output for production (meaning, all code, style and templating minified and gziped with no code repetition).
My main problem is how to set up webpack.config so that it recognizes all partials within the folder structure of my project, like these:
My current config file, for reference (which can't see subfolders):
var HtmlWebpackPlugin = require( 'html-webpack-plugin' );
var ExtractTextPlugin = require( 'extract-text-webpack-plugin' );
var path = require( 'path' );
module.exports = {
devServer: {
compress: true,
contentBase: path.join( __dirname, '/dist' ),
open: true,
port: 9000,
stats: 'errors-only'
},
entry: './src/app.js',
output: {
path: path.join( __dirname, '/dist' ),
filename: 'app.bundle.js'
},
module: {
rules: [ {
test: /\.scss$/,
use: ExtractTextPlugin.extract( {
fallback: 'style-loader',
use: [
'css-loader',
'sass-loader'
],
publicPath: '/dist'
} )
} ]
},
plugins: [
new HtmlWebpackPlugin( {
hash: true,
minify: { collapseWhitespace: true },
template: './src/index.html',
title: 'Prov'
} ),
new ExtractTextPlugin( {
filename: 'main.css',
allChunks: true
} )
]
};
Note that this isn't an exhaustive solution, as there are many optimizations one can make in the frontend, and I've kept the code snippets fairly short.
With webpack, there are a few routes that you can take to include partials into your app.js.
Solution 1
You can import/require your partials within app.js as such:
app.js
var angular = require('angular');
var proverbList = require('./proverb/list/proverb.list');
// require other components
// set up your app as normal
This allows the app.bundle.js to include your component js files in the main bundle. You can also use html-loader to include templates in the final bundle.
This isn't ideal, as all it does is create a large bundle.js (which doesn't leverage multiple downloads with http2 nor does it allow loading of components/files when the user explicitly requires it).
Solution 2
Importing partials as separate entry files into your webpack bundle:
webpack.config.js
const globby = require('globby');
const sourceDir = 'src';
var webpackentry = {
app: `${__dirname}/src/app.js`
};
const glob = globby.sync(`${__dirname}/${sourceDir}/**/*.js`)
.map((file)=>{
let name = file.split('/').pop().replace('.js', '');
webpackentry[name] = file;
});
const config = {
entry: webpackentry,
...
}
The second solution is unorthodox but it can be useful if you wanted to split all your partials as <script> tags in your html (for example if your company/team uses that as a means to include your directive/components/controllers), or if you have an app-2.bundle.js.
Solution 3
Use CommonsChunkPlugin:
webpack.config.js
let webpackentry = {
vendor: [
'module1',
'module2',
'module3',
]
}
...
plugins: [
new webpack.optimize.CommonsChunkPlugin({
name: ['vendor'] //... add other modules
})
]
CommonsChunkPlugin allows webpack to scrawl through your entry files and discern common modules that are shared among them. This means that even if you are importing module1 in different files, they will be compiled only once in your final bundle.
I am switching my SPA web app from Durandal to Aurelia and am now taking a look at the bundling process.
I use gulp to bundle the app and I followed the instructions on this Aurelia documentation page and other resources on the web. It works but there are some unclear things to me.
This is my gulpfile.js
var gulp = require('gulp'),
bundler = require('aurelia-bundler'),
uglify = require('gulp-uglify'),
htmlmin = require('gulp-htmlmin'),
del = require('del');
var config = {
force: true,
baseURL: '.', // baseURL of the application
configPath: './config.js', // config.js file. Must be within `baseURL`
bundles: {
"Scripts/build/app-build": { // bundle name/path. Must be within `baseURL`. Final path is: `baseURL/dist/app-build.js`.
includes: [
'[Scripts/app/**/*.js]',
'Scripts/app/**/*.html!text',
'Content/*.css!text'
],
options: {
inject: true,
minify: true,
depCache: true,
rev: true
}
},
"Scripts/build/vendor-build": {
includes: [
'jspm_packages/npm/babel-runtime#5.8.38/helpers/class-call-check.js',
'jspm_packages/npm/babel-runtime#5.8.38/helpers/create-class.js',
'jspm_packages/npm/babel-runtime#5.8.38/core-js/object/define-property.js',
'jspm_packages/npm/core-js#1.2.7/library/fn/object/define-property.js',
'jspm_packages/npm/babel-runtime#5.8.38/core-js/object/define-properties.js',
'jspm_packages/npm/core-js#1.2.7/library/fn/object/define-properties.js',
'npm:aurelia-framework#1.0.7',
'npm:aurelia-loader-default#1.0.0',
'npm:aurelia-logging-console#1.0.0',
'npm:aurelia-templating-binding#1.0.0',
'npm:aurelia-templating-resources#1.1.1',
'npm:aurelia-templating-router#1.0.0',
'npm:aurelia-knockout#1.0.2',
'npm:aurelia-history-browser#1.0.0',
'npm:aurelia-bootstrapper#1.0.0',
'npm:aurelia-fetch-client#1.0.1',
'npm:aurelia-router#1.0.6',
'npm:aurelia-animator-css#1.0.1',
'npm:babel-core#5.8.38',
'npm:babel-runtime#5.8.38',
'npm:core-js#1.2.7',
'github:systemjs/plugin-text#0.0.9'
],
options: {
inject: true,
minify: true,
depCache: true,
rev: true
}
}
}
};
gulp.task('build', ['minify'], function () {
return bundler.bundle(config);
});
And this is config.js
System.config({
baseURL: "/",
defaultJSExtensions: true,
transpiler: "babel",
babelOptions: {
"optional": [
"es7.decorators",
"es7.classProperties",
"runtime"
],
"compact": true
},
paths: {
"github:*": "jspm_packages/github/*",
"npm:*": "jspm_packages/npm/*"
},
bundles: {
},
map: //some mappings
}
If I run the gulp task to bundle, it works, and I can load my app using the bundled files, but there are some things I don't understand:
After the bundled files are created, the config.js file is updated within the "bundles:" property with the files which have been created for the bundle, and a random versioning number (since I had set 'rev: true' in the options)
bundles: [
"Scripts/build/vendor-build-4c2789cace.js": [
//list of files
]
]
When I run the task again, maybe after some changes, the new bundled file has been added to the config.js file like that:
bundles: [
"Scripts/build/vendor-build-4c2789cace.js": [
//list of files
],
"Scripts/build/vendor-build-t67uj8e5f4.js": [
//list of files
]
]
but as you can see, the old one is still there. How do I tell him to "clear" the bundles property when I create a new bundle?
I am using Grunt and receiving this particular error. Destination build/vendor/vendor.min.js,
build/js/allModules.min.js,
build/js/restFiles.min.js,
not written because src files were empty.
Is there some kind of other log file somewhere?
And This is my grunt file code
var vendor = [
"bower_components/jquery/dist/jquery.min.js",
"bower_components/angular/angular.min.js",
"bower_components/angular-route/angular-route.min.js",
"bower_components/angular-ui-router/release/angular-ui-router.min.js",
"bower_components/angular-bootstrap/ui-bootstrap-tpls.min.js",
"bower_components/jquery-steps/build/jquery.steps.min.js",
"bower_components/angular-resource/angular-resource.min.js" ];
uglify: {
options: {
mangle: false,
compress: {
drop_console: true
}
},
dist: {
files: {
'build/vendor/vendor.min.js': vendor,
'build/js/allModules.min.js': modFiles,
'build/js/restFiles.min.js': userFiles
}
}
}});
Any help would be appreciated!
I'd like the same functionality like RequireJS empty: http://requirejs.org/docs/optimization.html#empty
My use-case is that I include jquery-migrate in development, but I'd like to exclude this when built for production.
Using IgnorePlugin just makes it not included, and when requireing it in the code, it then throws an error (Uncaught Error: Cannot find module "jquery-migrate").
What I'd like to happen is for it to just return undefined, or something of the like (like empty: in RequireJS). Id like to not touch the import in the code, just configuring it to return undefined.
EDIT: Using NormalModuleReplacementPlugin works, if I point the replacement to an empty file. But keeping an empty file around just for this seems unnecessary.
I use the null-loader for blanking out modules.
The noop-loader can be used for a less awkward if-else in the config.
Try:
rules: [{
test: /jquery-migrate/,
use: IS_DEV ? 'null-loader' : 'noop-loader'
}]
You can try to make a resolve.alias in webpack.config:
resolve: {
alias: {
"jquery-migrate": process.env.NODE_ENV === 'production' ? "empty-module": "jquery-migrate"
}
}
Use Webpack's DefinePlugin in combination with the normal production plugins (Dedupe and Uglify).
Then in your code you can write:
if(DEBUG) {
var test = require('test');
alert(test);
}
And when it's built in production, the DEBUG will be replaced with a literal if(false) { ... } which will be completely removed by the uglify plugin, so test will only be required in a debug build.
Here's a sample Grunt task config for grunt-webpack that has development and production targets for the task:
development: {
devtool: "sourcemap",
output: {
pathinfo: true,
},
debug: true,
production: false,
plugins: [
new webpack.DefinePlugin({
DEBUG: true,
PRODUCTION: false
})
]
},
production: {
plugins: [
new webpack.DefinePlugin({
DEBUG: false,
PRODUCTION: true
}),
new webpack.optimize.DedupePlugin(),
new webpack.optimize.UglifyJsPlugin({
output: {
comments: false,
}
})
]
},