I am new to webpack and I love the benefits of using require/import vs. adding multiple script tags in the index.html.
However, I wonder whether there is a way to slice a 13MB output file to chunks so I can have the benefits of the browser's cache like I had with the script-approach.
For instance:
bundle.react.components.js
bundle.utils.js
bundle.redux.reducers.js
That way the loading time of the app will be faster when I make a small change that only affects bundle.utils.js.
Cheers,
// your slicing gulp file
var gulp = require('gulp')
var webpackStream = require('webpack-stream')
var path = require('path')
var webpack = require('webpack')
gulp.task('slice', function () {
return gulp.src('')
.pipe(webpackStream({
entry: {
react_1st: ["react", "react-dom"],
utils_2nd: [
"fixed-data-table"
, "./class_1"
, "./class_2"
, "./constants" ],
remainder_3rd: "./render_component.js" // ReactDOM.render(your_component ...
},
output: {
path: path.join(__dirname, "js"),
filename: "bundle.[name].js",
},
plugins: [
new webpack.optimize.CommonsChunkPlugin({
names: ["remainder_3rd", "utils_2nd", "react_1st"],
minChunks: Infinity
}),
]
}))
.pipe(gulp.dest('./'))
})
<script src="//cdnjs.cloudflare.com/ajax/libs/babel-core/5.6.15/browser-polyfill.min.js"></script>
<script src="bundle.react_1st.js"></script>
<script src="bundle.utils_2nd.js"></script>
<script src="bundle.remainder_3rd.js"></script>
Related
I have a gulpfile as below with a rollupTask. But at the last task which zipTask, it output without bundled js from rollup. The only way i found that fix this is to add wait time before the ziptask. There seems to be a fraction of delay with rollup output and the next gulp series. Is this the expected behavior or there is something that fix this without add waiting time ? Is my rollupTask is correct ? the zip tasks simply zip the output folder into a different folder. The output folder itself contain the expected bundle.
const gulp = require('gulp');
const rollup = require('rollup');
async function rollupTask() {
const rollupBuild = await rollup({
input: 'index.js',
plugins: rollupPlugins,
});
await rollupBuild.write({
file: 'bundle.js',
format: 'es',
sourcemap: true,
});
await rollupBuild.close();
}
exports.default = series(taskOne, parallel(taskTwo, taskThree, rollupTask), zipTask);
The easiest way is to use a plugin written for gulp. gulp-rollup
const { src, dest } = require('gulp');
const rollup = require('gulp-rollup');
function rollupTask() {
const options = { input: './src/main.js' } // any option supported by Rollup can be set here.
return src('./src/**/*.js')
.pipe(rollup(options)) // transform the files here.
.pipe(dest('./dist'));
}
exports.build = series(rollupTask);
On start: gulp build
Say I have
main1.less
main2.less
main3.less
other1.less
other2.less
other3.less
I need the output
main.css
other1.css
other2.css
other3.css
I do
gulp.task('less', function () {
return gulp.src(['./Content/css/**/*.less'])
.pipe(less({
paths: [path.join(__dirname, 'less', 'includes')]
}))
.pipe(gulp.dest(paths.webroot + 'css'));
});
Actually it generates a css for a less file... Is there a way to combine several files in one, and let others in separate files?
You can accomplish this using gulp-concat to combine the styles you want into one file from a separate task.
const gulp = require('gulp4');
const less = require('gulp-less');
const concat = require('gulp-concat');
const path = require('path');
const config = {
paths: [ path.join(__dirname, 'less', 'includes') ]
};
gulp.task('less:separate', () => gulp
.src(path.join(__dirname, 'Content', 'css', '**', 'other*.less'))
.pipe(less(config))
.pipe(gulp.dest(paths.webroot + 'css')));
gulp.task('less:together', () => gulp
.src(path.join(__dirname, 'Content', 'css', '**', 'main*.less'))
.pipe(less(config))
.pipe(concat('main.css')) // combine them into one file
.pipe(gulp.dest(paths.webroot + 'css')));
gulp.task('less', gulp.parallel('less:separate', 'less:together'));
Is it possible to get the list of files coming from a gulp.src stream as an array, e.g.:
var files = convertToArray(gulp.src('**/*.js'));
Update:
I was trying to move away from the gulp-karma plugin:
gulp.task('test', function () {
return gulp.src(files)
.pipe($.order(ordering))
.pipe($.karma({
karma.conf.js'
});
});
So my idea was:
gulp.task('test', function (done) {
var karmaFiles = convertToArray(gulp.src(files)
.pipe($.order(ordering)));
new Server({
configFile: karma.conf.js',
files: karmaFiles
}, done).start();
});
But as pointed out, this won't work because of it being async. Here's my solution:
gulp.task('test', function (done) {
gulp.src(files)
.pipe($.order(ordering)))
.pipe(gutil.buffer())
.on('data', function(data) {
var karmaFiles = data.map(function(f) { return f.path; });
new Server({
configFile: __dirname + '/karma.conf.js',
files: karmaFiles
}, done).start();
});
});
Gulp streams are always asynchronous so your hypothetical convertToArray function (which takes a stream and returns an array) is impossible.
The only way to get all the files in a stream is through some kind of callback function. The gulp-util package, which bundles various helper functions, provides the nice gutil.buffer() :
var gutil = require('gulp-util');
gulp.src('**/*.js').pipe(gutil.buffer(function(err, files) {
console.log('Path of first file:');
console.log(files[0].path);
console.log('Contents of first file:');
console.log(files[0].contents.toString());
}));
In the above files will be an array of vinyl files. That means for each file you have access to both the contents and the path of the file.
If you don't care about the file contents and only want the path of each file you shouldn't be using gulp.src() at all. You should be using glob instead (which is what gulp is using internally). It gives you a synchronous method that returns an array of matching file paths:
var glob = require('glob');
var files = glob.sync('**/*.js');
console.log(files);
GitHub Repository
Summary
After changing my folder structure to keep development and production separate I get a Uncaught SyntaxError: Unexpected token < error.
Note
When I upload my production folder contents via FTP, the site works fine. Only when local do I get the above stated error. Before changing the folder structure, my local server (gulp-connect) worked just fine.
Issue
I an encountering the following errors:
Uncaught SyntaxError: Unexpected token <
My application was working great and ready to launch. I had been working locally with a gulp-connect server. I wanted to create a gulp task that would minify code, copy/paste dependencies, compress image. My end goal would be to have a folder that contained only production ready files.
To do this I created the following folder structure:
Folder Structure
The first and biggest change was to copy/paste (using gulp) the node modules dependencies into appropriate folders. As these changed I updated my index.html to reflect to new locations of the scripts from the node_modules folder to /js.
index.html
<!doctype html>
<html lang="en">
<head>
<link rel="icon"
type="image/png"
href="images/HWA_Emblem.png">
<base href="/">
<meta charset="UTF-8">
<title>Halifax Wildlife Association</title>
<link rel="stylesheet" href="css/style.css">
<script src="js/lib/shim.min.js"></script>
<script src="js/lib/Reflect.js"></script>
<script src="js/lib/system.src.js"></script>
<script src="js/lib/zone.js"></script>
<script src="systemjs.config.js"></script>
<script>
System.import('app').catch(function(err){ console.error(err); });
</script>
</head>
<body>
<my-app></my-app>
<script src="js/lib/jquery-3.1.1.min.js"></script>
<script src="js/scripts.js"></script>
<script src="js/lib/format-google-calendar.js"></script>
</body>
</html>
systemjs.config.js
/**
* System configuration for Angular 2 samples
* Adjust as necessary for your application needs.
*/
(function (global) {
// map tells the System loader where to look for things
var map = {
'app': 'app', // 'dist',
'#angular': '/js/lib/#angular',
'ng2-page-scroll': '/js/lib/ng2-page-scroll.umd.js',
'rxjs': '/js/lib/rxjs'
};
// packages tells the System loader how to load when no filename and/or no extension
var packages = {
'app': { main: 'main.js', defaultExtension: 'js' },
'rxjs': { defaultExtension: 'js' }
};
var ngPackageNames = [
'common',
'compiler',
'core',
'forms',
'http',
'platform-browser',
'platform-browser-dynamic',
'router'
];
// Individual files (~300 requests):
function packIndex(pkgName) {
packages['#angular/' + pkgName] = { main: 'index.js', defaultExtension: 'js' };
}
// Bundled (~40 requests):
function packUmd(pkgName) {
packages['#angular/' + pkgName] = { main: '/bundles/' + pkgName + '.umd.js', defaultExtension: 'js' };
}
// Most environments should use UMD; some (Karma) need the individual index files
var setPackageConfig = System.packageWithIndex ? packIndex : packUmd;
// Add package entries for angular packages
ngPackageNames.forEach(setPackageConfig);
var config = {
map: map,
packages: packages
};
System.config(config);
})(this);
config.rb
I use compass to compile my SASS so I then updated this accordingly:
require 'compass/import-once/activate'
require 'breakpoint'
# Require any additional compass plugins here.
# Set this to the root of your project when deployed:
http_path = "/builds/development"
css_dir = 'stylesheets'
sass_dir = 'sass'
images_dir = 'images'
javascripts_dir = 'javascripts'
sass_options = false
#tsconfig.json#
My TSConfig file had no changes
{
"compilerOptions": {
"target": "es5",
"module": "commonjs",
"moduleResolution": "node",
"sourceMap": true,
"emitDecoratorMetadata": true,
"experimentalDecorators": true,
"removeComments": false,
"noImplicitAny": false
}
}
gulpfile.js
I am not done of this file just yet, it however does correctly make a production folder.
// Add requiements
var gulp = require ('gulp'),
gutil = require ('gulp-util'),
concat = require ('gulp-concat'),
browserify = require ('gulp-browserify'),
compass = require ('gulp-compass'),
connect = require ('gulp-connect');
sourcemaps = require ('gulp-sourcemaps');
typescript = require ('gulp-typescript');
tsConfig = require ('./tsconfig.json');
gulpif = require ('gulp-if');
uglify = require ('gulp-uglify');
var env,
htmlSources,
sassSource,
jsSources,
appSources,
imageSources,
tsSources,
depSources,
outputDir
env = process.env.NODE_ENV || 'development';
if (env==='development') {
outputDir = 'builds/development/';
} else{
outputDir = 'builds/production/';
}
//Setup path variables
htmlSources = [
'builds/development/partials/*.html',
'builds/development/index.html'
];
sassSources = [
'builds/development/components/sass/style.scss'
];
jsSources = [
'builds/development/components/scripts/*.js'
];
depSources = [
'js/lib'
];
tsSrc = [
'builds/development/components/typescript/*.ts'
];
imageSources= [
'builds/development/images/**/*.*'
];
gulp.task('CopyDep', function(){
gulp.src('node_modules/core-js/client/shim.min.js').pipe(gulp.dest(outputDir + depSources));
gulp.src('node_modules/zone.js/dist/zone.js').pipe(gulp.dest(outputDir + depSources));
gulp.src('node_modules/reflect-metadata/Reflect.js').pipe(gulp.dest(outputDir + depSources));
gulp.src('node_modules/systemjs/dist/system.src.js').pipe(gulp.dest(outputDir + depSources));
gulp.src('builds/development/components/scripts/format-google-calendar.js').pipe(gulp.dest(outputDir + depSources));
gulp.src('builds/development/components/scripts/jquery-3.1.1.min.js').pipe(gulp.dest(outputDir + depSources));
gulp.src('builds/development/components/scripts/jquery-3.1.1.min.js').pipe(gulp.dest(outputDir + depSources));
gulp.src('builds/development/systemjs.config.js').pipe(gulpif(env === 'production', gulp.dest(outputDir)))
gulp.src('node_modules/#angular/**/*').pipe(gulp.dest(outputDir + 'js/lib/#angular'));
gulp.src('node_modules/ng2-page-scroll/bundles/ng2-page-scroll.umd.js').pipe(gulp.dest(outputDir + 'js/lib'));
gulp.src('node_modules/rxjs/**/*.*').pipe(gulp.dest(outputDir + 'js/lib/rxjs'));
});
//Combine Javascript Files
gulp.task('js', function(){
gulp.src(jsSources)
.pipe(concat('scripts.js'))
.pipe(browserify())
.pipe(gulp.dest(outputDir + 'js'))
.pipe(connect.reload())
});
//Process Typescript
gulp.task('typescript', function () {
return gulp
.src(tsSrc)
.pipe(sourcemaps.init())
.pipe(typescript(tsConfig.compilerOptions))
.pipe(sourcemaps.write('.'))
.pipe(gulp.dest(outputDir + 'app'))
.pipe(connect.reload());
});
//Process SASS files
gulp.task('compass', function(){
gulp.src(sassSources)
.pipe(compass({
config_file: 'config.rb',
css: 'temp',
sass: 'builds/development/components/sass'
}))
.on('error', gutil.log)
.pipe(gulp.dest(outputDir + 'css'))
.pipe(connect.reload())
});
//Reload HTML files if change is recorded.
gulp.task('html', function(){
gulp.src(htmlSources[1])
.pipe(gulpif(env === 'production', gulp.dest(outputDir)))
gulp.src(htmlSources[0])
.pipe(gulpif(env === 'production', gulp.dest(outputDir + 'partials')))
.pipe(connect.reload());
});
// Watch all sources file directorys and run tasks if changes happen.
gulp.task('watch', function(){
gulp.watch(jsSources, ['js']);
gulp.watch(sassSources, ['compass']);
gulp.watch(htmlSources, ['html']);
gulp.watch(tsSrc, ['typescript']);
});
// copy images in production
gulp.task('images', function(){
gulp.src(imageSources)
.pipe(gulpif(env === 'production', gulp.dest(outputDir + 'images')))
});
// Start a server for project.
gulp.task('connect', function(){
connect.server({
root : '',
livereload : true,
fallback: 'builds/development/index.html'
});
});
// Run all tasks above by using default command "Gulp".
gulp.task('default', ['CopyDep', 'images', 'js', 'images', 'typescript', 'compass', 'html', 'watch', 'connect']);
I am trying to migrate our build process to gulp from existing custom bash build script. We concatenate several unminified opensource JS files like bootstrap, lazyload, ... and our own JS files. We uglify each JS file (removing their licenses as well) in an order, prepend custom license text to some of them as required and concatenate to create the output JS file. The custom license text are currently kept as strings in the bash script.
How to achieve this in gulp without creating intermediate files?
Will it also be possible to selectively avoid uglifying some JS scripts?
Ok, I spent some time learning up gulp and it's plugins and here is a working version. The points here are using the foreach on each JS retrieved from the JSON config file, pushing the streams to an array and finally using merge on the array streams.
Here are the plugins used and the JSON structure defined:
var gulp = require('gulp');
var each = require('foreach');
var debug = require('gulp-debug');
var gulpif = require('gulp-if');
var jshint = require('gulp-jshint');
var uglify = require('gulp-uglify');
var concat = require('gulp-concat-util');
var es = require('event-stream');
var cache = require('gulp-cached');
var remember = require('gulp-remember');
// Structure that holds the various JS files and their handling
var Config = {
js: {
output_dir: 'path/to/output/file/',
output_file: 'outputfile.js',
src: [{
name: 'bootstrap',
src: ['path/to/bootstrap.js'],
run_lint: false,
run_uglify: true,
license: '/* bootstrap license */'
}, {
name: 'lazyload',
src: ['path/to/lazyload.js'],
run_lint: false,
run_uglify: true,
license: '/* lazyload license */'
}, {
name: 'inhouse-js',
src: ['path/to/inhouse/ih-1.js', 'path/to/inhouse/ot/*.js'],
run_lint: true,
run_uglify: true,
license: ''
}]
}
}
The build task, with caching as we will be using it in development also:
gulp.task('build', ['build:js']);
gulp.task('build:js', function() {
var streams = [];
each(Config.js.src, function(val, key, array) {
var stream = gulp.src(val.src)
.pipe(cache('scripts'))
.pipe(gulpif(val.run_lint, jshint('.jshintrc')))
.pipe(gulpif(val.run_lint, jshint.reporter('jshint-stylish')))
.pipe(gulpif(val.run_uglify, uglify({
compress: {
drop_console: true
}
})))
.pipe(concat.header(val.license + '\n'));
streams.push(stream);
});
es.merge.apply(this, streams)
.pipe(remember('scripts')) // add back all files to the stream
.pipe(concat(Config.js.output_file))
.pipe(gulp.dest(Config.js.output_dir));
});
If you would like to debug, a good option will be to insert debug plugin like this example around the 'gulp-remember' plugin call above:
.pipe(debug({title: 'before remember:'}))
.pipe(remember('scripts')) // add back all files to the stream
.pipe(debug({title: 'after remember:'}))
And here's the watch task:
gulp.task('watch', function() {
var watch_list = [];
each(Config.js.src, function(val, key, array) {
watch_list.push.apply(watch_list, val.src);
});
// Watch .js files
var watcher = gulp.watch(watch_list, ['build']);
watcher.on('change', function(event) {
console.log('File '+ event.path +' was '+ event.type +', running tasks..');
if (event.type === 'deleted') { // if a file is deleted, forget it
delete cache.caches['scripts'][event.path];
remember.forget('scripts', event.path);
}
})
});
You can use lazypipe() to reuse parts of the build:js task with normal build.