Last active
May 2, 2016 10:13
-
-
Save isochronous/65fa6031e2bf5ad8b6c4 to your computer and use it in GitHub Desktop.
The gulpfile we use for our webapp-in-android-app project
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
var gulp = require('gulp'), | |
// Used to process `include` statements in source files | |
includer = require('gulp-includer'), | |
// Compiles SASS files using LibSass | |
sass = require('gulp-sass'), | |
// Renames files in the file stream | |
rename = require('gulp-rename'), | |
// JavaScript minifier | |
uglify = require('gulp-uglify'), | |
// Filter files out of the vinyl stream | |
filter = require('gulp-filter'), | |
// Creates zip files | |
zip = require('gulp-zip'), | |
// CSS minifier | |
csso = require('gulp-csso'), | |
// Used for working with streams | |
es = require('event-stream'), | |
// File system utilities | |
fs = require('fs'), | |
// Search for files/directories matching certain patterns - regex-lite | |
glob = require('glob'), | |
// Used for working with file system paths | |
path = require('path'), | |
// Promise library used in more complex tasks (like `zip`) | |
Q = require('q'); | |
// TODO: Implement something like MEF, where we scan for particular JSON files, | |
// and then assemble this config by merging all of the contents. | |
var pathConfig = { | |
ScriptSrc : 'Scripts/src', | |
// These three core files are required by damn near everything, so instead | |
// of processing them over and over again, we'll process them once in the | |
// `core` task and then include the output files (i.e. the ones not under | |
// `/Scripts/src`) instead of the `src` version to cut down on needless | |
// repetition of the include process. | |
models : 'Scripts/models', | |
Graph : 'Scripts/graph', | |
Report : 'Scripts/report', | |
Collectors : 'Scripts/src/report/collectors', | |
ReportTemplates : 'Scripts/src/report/template', | |
// Foo | |
FooBasePkg : 'Packages/src/Bar', | |
FooAuditPkg : 'Packages/src/Bar/Foo Audit', | |
FooSvaAuditPkg : 'Packages/src/Bar/Foo SVA Audit', | |
FooSvaUnitPkg : 'Packages/src/Bar/Foo SVA Unit', | |
FooUnitPkg : 'Packages/src/Bar/Foo Unit', | |
FooDealerTradePkg : 'Packages/src/Bar/Foo Dealer Trade', | |
FooManualAuditPkg : 'Packages/src/Bar/Foo Manual Audit', | |
FooManualUnitPkg : 'Packages/src/Bar/Foo Manual Unit', | |
FooPartialInventoryAuditPkg: 'Packages/src/Bar/Foo Partial Inventory Audit', | |
FooPartialInventoryUnitPkg : 'Packages/src/Bar/Foo Partial Inventory Unit', | |
// Bar | |
BarBasePkg : 'Packages/src/Bar', | |
BarDscAuditPkg : 'Packages/src/Bar/DSC Audit', | |
BarDscUnitPkg : 'Packages/src/Bar/DSC Unit', | |
BarHondaAuditPkg : 'Packages/src/Bar/Honda Audit', | |
BarHondaAutoPkg : 'Packages/src/Bar/Honda Auto', | |
BarHondaMotoEtAlPkg : 'Packages/src/Bar/Honda Moto, Utility, Watercraft and Marine', | |
BarHondaPePkg : 'Packages/src/Bar/Honda PE', | |
BarManualAuditPkg : 'Packages/src/Bar/Manual Audit', | |
BarManualUnitPkg : 'Packages/src/Bar/Manual Unit', | |
// Test Client | |
TestClientBasePkg : 'Packages/src/TestClient', | |
TestClientTcAuditPkg : 'Packages/src/TestClient/TC Audit' | |
}; | |
// Where to save our package zip files | |
var packageZipDest = '../../Shared'; | |
// Files to copy | |
var themeSourceFiles = [ | |
'Content/Layouts/Layout.html', | |
'Content/themes/default.min.css' | |
], | |
// Factor these out to make structure changes easier | |
themeDestPrefix = 'Packages/bin', | |
themeDestSuffix = '/Reporting', | |
// All of the packages that require Layout.html and default.min.css | |
themeDests = [ | |
'/Bar/DSC Audit', | |
'/Bar/Honda Audit', | |
'/Bar/Manual Audit', | |
'/Bar/Foo Audit', | |
'/Bar/Foo SVA Audit', | |
'/Bar/Foo Manual Audit', | |
'/Bar/Foo Partial Inventory Audit' | |
]; | |
// Used by the `gulp-rename` plugin to rename `foo.combined.js` to `foo.min.js`. | |
var combined2min = function (path) { | |
path.basename = path.basename.replace('.combined', '.min'); | |
}; | |
// We use the same includer options in every task, so just define them here to | |
// avoid code duplication. | |
var includerOpts = { | |
// By default includer will wrap each include in an immediately-executing | |
// closure, so override the `wrap` method with a simple identify function. | |
wrap: function (src) { return src; }, | |
// Pass the aliases defined above to includer to resolve path mappings. | |
paths: pathConfig | |
}; | |
// Processes the three "core" files that are used in a majority of other | |
// packages. This is a dependency of both the `scripts` and `packages` tasks, | |
// which allows the files handled by those tasks to include the output of this | |
// task when requiring any of these three files, rather than recursively | |
// building these files over and over again, which is what would happen if | |
// the `@Report`, `@models`, and `@Graph` mappings above pointing to the | |
// versions in the `Scripts/src` directory instead. | |
gulp.task('core', function () { | |
// If we want tasks dependent on this task to wait until this task has | |
// completely finished before they themselves execute, we have to return | |
// the pipeline. | |
return gulp.src([ | |
'./Scripts/src/**/models.combined.js', | |
'./Scripts/src/**/graph.combined.js', | |
'./Scripts/src/**/report.combined.js' | |
], | |
// Because nothing but includer actually needs to know the contents | |
// of the files (and it will handle its own reading), tell gulp not | |
// to bother reading the file content - just pass the paths along. | |
{ read: false } | |
) | |
.pipe(includer(includerOpts)) | |
.pipe(gulp.dest('./Scripts')); | |
}); | |
// Processes includes in all JavaScript files under `Scripts/src` and writes | |
// the output to the base `Scripts` directory while preserving folder structure. | |
// Make sure the `core` task executes completely before this runs. | |
gulp.task('scripts', ['core'], function () { | |
// Because the `core` task has already handled processing these three files, | |
// filter them out from the file stream to avoid processing them again. | |
var coreFilter = filter('**/!(models.combined|graph.combined|report.combined).js'); | |
return gulp.src('./Scripts/src/**/*.combined.js', { read: false }) | |
// Here we pass the file stream through the filter defined above. | |
.pipe(coreFilter) | |
// Now that we've filtered out the files we don't want to process, | |
// pass the files that remain to includer. | |
.pipe(includer(includerOpts)) | |
.pipe(gulp.dest('./Scripts')); | |
}); | |
// Minifies all processed `*.combined.js` files in 'Scripts' (i.e. the files NOT | |
// in the `Scripts/src` directory, which are unprocessed) and writes them to | |
// disk as `*.min.js`. Make sure the `scripts` task executes completely before | |
// this runs. Get the right fileset by first retrieving all *.combined.js files, | |
// then removing any files from `Scripts/src`. | |
gulp.task('scriptMin', ['scripts'], function () { | |
return gulp.src(['Scripts/**/*.combined.js', '!Scripts/src{,/**}']) | |
.pipe(uglify()) | |
.pipe(rename(combined2min)) | |
.pipe(gulp.dest('./Scripts')); | |
}); | |
// Processes includes in all JavaScript files under `Packages/src` and writes | |
// the output to the base `Packages/bin` directory while preserving the folder | |
// structure. Make sure the `scripts` task executes completely before this runs. | |
gulp.task('packages', ['scripts'], function () { | |
return gulp.src('./Packages/src/**/*.combined.js', { read: false }) | |
.pipe(includer(includerOpts)) | |
.pipe(gulp.dest('./Packages/bin')); | |
}); | |
// Minifies all processed `*.combined.js` files in `Packages` (i.e. the files | |
// NOT in the `Packages/src` directory, which are unprocessed) and writes them | |
// to disk as `*.min.js`. Make sure the `packages` task executes completely | |
// before this runs. You can't use a directory negation in the middle of a glob | |
// (whoops) so instead you have to basically do a diff between "all combined | |
// files" and "all files in Packages/src". | |
gulp.task('packageMin', ['packages'], function () { | |
return gulp.src(['Packages/bin/**/*.combined.js']) | |
.pipe(uglify()) | |
.pipe(rename(combined2min)) | |
.pipe(gulp.dest('./Packages/bin')); | |
}); | |
// Copies all of the txt, JSON, html, etc. files that exist in the | |
// `Packages/src` directory tree to the root `Packages` tree. Apparently | |
// glob wildcards have some undocumented bugs, because this was previously | |
// just `gulp.src('Packages/src/!(*.js)')`, but that omitted .JSON files | |
// as well as .js files. | |
gulp.task('packageCopy', function () { | |
return gulp.src(['Packages/src/**/*.*', '!Packages/src/**/*.js']) | |
.pipe(gulp.dest('./Packages/bin')); | |
}); | |
// Compile all `*.scss` files and write their output to the `/Content` | |
// directory. The existing folder structure will be preserved. | |
gulp.task('sass', function () { | |
return gulp.src('./Content/**/*.scss', { read: false }) | |
.pipe(sass({outputStyle: 'nested'})) | |
.pipe(gulp.dest('./Content')); | |
}); | |
// Minifies the CSS output by the SASS compiler, and saves the file as | |
// filename.min.css. Makes sure the `SASS` task runs before this task executes. | |
gulp.task('cssMin', ['sass'], function () { | |
return gulp.src('./Content/**/!(*.min).css', { read: false }) | |
.pipe(csso()) | |
.pipe(rename(function (path) { | |
path.basename += '.min'; | |
})) | |
.pipe(gulp.dest('./Content')); | |
}); | |
// Copies the `Layout.html` and `default.min.css` files from the /Content dir | |
// into the package directories where they are used. Make sure the `sass` task | |
// executes completely before this runs. | |
gulp.task('themeDist', ['cssMin'], function () { | |
var i, il, stream, merged; | |
for (i=0, il=themeDests.length; i<il; i++) { | |
stream = gulp.src(themeSourceFiles, { read: false }) | |
.pipe(gulp.dest(themeDestPrefix + themeDests[i] + themeDestSuffix)); | |
// merge the stream with stream(s) from previous loop iterations | |
merged = (merged) ? es.merge(merged, stream) : stream; | |
} | |
// By returning this we can make dependent tasks wait for execution of this | |
// task to finish before exeuting themselves. | |
return merged; | |
}); | |
// Creates the package zip files in the "Shared" directory. Makes sure that the | |
// `packageMin`, `packageCopy`, and `themeDist` tasks execute completely before | |
// this runs to ensure zip content is always up-to-date. | |
gulp.task('zip', ['packageMin', 'packageCopy', 'themeDist'], function () { | |
var promises = []; | |
// First, get a list of all sub-directories in "Packages", omitting 'src'. | |
// folderPath should be path to Packages/Foo, Packages/Bar, etc. | |
glob.sync('Packages/bin/*').forEach(function (folderPath) { | |
var bankName; | |
// make sure it's a directory | |
if (fs.statSync(folderPath).isDirectory()) { | |
// Get just "Foo" or "Bar" etc. | |
bankName = path.basename(folderPath); | |
// Now find all sub-dirs (packages) of that bank | |
glob.sync(folderPath + '/*').forEach(function (packagePath) { | |
var defer = Q.defer(), | |
stream, packageName; | |
// packagePath should be path to Foo/Foo Audit, Foo/SVA, etc | |
// make sure we are only processing directories | |
if (fs.statSync(packagePath).isDirectory()) { | |
// Okay, so now we have the bank name, the package name, and | |
// a path to the package files - time to zip stuff up and | |
// write it to disk | |
packageName = path.basename(packagePath); | |
// The glob pattern means "all files and folders inside | |
// a folder inside packagePath", and set packagePath as base | |
// so that folders will be relative to the package rather | |
// than the project root. Do NOT set read:false here, as the | |
// zip package relies on gulp to read the file contents. | |
stream = gulp.src(packagePath + '/*/**/!(*.combined.js|setup.min.js)', { | |
base: packagePath | |
}) | |
.pipe(zip(packageName + '.zip')) | |
.pipe(gulp.dest(packageZipDest + '/' + bankName)); | |
// Make sure the deferred is resolved when done | |
stream.on('end', function () { | |
defer.resolve(); | |
}); | |
// add our promise to the array | |
promises.push(defer); | |
} | |
}); | |
} | |
}); | |
// Return a promise that will resolve when all streams end | |
return Q.all(promises); | |
}); | |
// Watch task for active development. Whenever a file is changed that matches | |
// the glob pattern, run the specified task(s). | |
gulp.task('watch', function () { | |
gulp.watch('./Scripts/src/**/*.js', ['scripts']); | |
gulp.watch('./Packages/src/**/*.js', ['packages']); | |
// The `packages` task will handle copying/creating all `*.js`, `*.min.js`, | |
// and `*.combined.js` files to the base `Packages` directory, so use the | |
// `!(pattern)` glob functionality to avoid re-copying those files. | |
gulp.watch(['Packages/src/**/*.*', '!Packages/src/**/*.js'], ['packageCopy']); | |
gulp.watch('./Content/**/*.scss', ['sass']); | |
}); | |
// Watch task for active development on handhelds. Whenever a file is changed | |
// in Scripts, Packages, or a sass file in Content is updated, the `zip` task | |
// will run automatically. | |
gulp.task('zipWatch', function () { | |
gulp.watch([ | |
'./Scripts/src/**/*.js', | |
'./Packages/src/**/*.*', | |
'./Content/**/*.scss' | |
], ['zip']); | |
}); | |
// These tasks will run in series when calling gulp with no arguments. | |
gulp.task('default', ['scripts', 'packages', 'packageCopy', 'sass']); | |
// The minification tasks take WAY longer to run than anything else, so put them | |
// in a `build` task that will only be called when building the project. | |
gulp.task('build', ['scriptMin', 'zip']); | |
// Uncomment this to use node-debug on the `packageCopy` task. To do this, you'd first | |
// `npm install -g node-debug`, then change the task to whichever task you'd like to debug, | |
// then call `node-debug gulpfile.js`. | |
/* gulp.start('packageCopy'); */ |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment