diff --git a/.gitignore b/.gitignore index c257b2da901cd7..c5d3f7c62b62fb 100644 --- a/.gitignore +++ b/.gitignore @@ -30,7 +30,7 @@ coverage .remote-sync.json server/*.bundle.js -public/js/*.bundle.js +public/js/bundle* *.map diff --git a/gulpfile.js b/gulpfile.js index 7ba2066df00044..146fd5d0eec68f 100644 --- a/gulpfile.js +++ b/gulpfile.js @@ -6,6 +6,8 @@ var Rx = require('rx'), // utils plumber = require('gulp-plumber'), notify = require('gulp-notify'), + reduce = require('gulp-reduce-file'), + sortKeys = require('sort-keys'), debug = require('debug')('freecc:gulp'), // react app @@ -38,9 +40,10 @@ var paths = { serverIgnore: [ 'gulpfile.js', 'public/', - '!public/js/bundle.js', + '!public/js/bundle*', 'node_modules/', - 'client/' + 'client/', + 'server/rev-manifest.json' ], publicJs: './public/js', @@ -70,7 +73,7 @@ var paths = { less: './client/less/main.less', - manifest: 'server/', + manifest: 'server/manifests/', node: { src: './client', @@ -87,13 +90,6 @@ var paths = { ] }; -var manifestName = 'rev-manifest.json'; -var manifestOptions = { - path: paths.manifest + manifestName, - base: path.join(__dirname, paths.manifest), - merge: true -}; - var webpackOptions = { devtool: 'inline-source-map' }; @@ -111,38 +107,6 @@ function errorHandler() { this.emit('end'); } -gulp.task('pack-client', function() { - return gulp.src(webpackConfig.entry) - .pipe(plumber({ errorHandler: errorHandler })) - .pipe(webpack(Object.assign( - {}, - webpackConfig, - webpackOptions - ))) - .pipe(gulp.dest(webpackConfig.output.path)); -}); - -gulp.task('pack-watch', function() { - return gulp.src(webpackConfig.entry) - .pipe(plumber({ errorHandler: errorHandler })) - .pipe(webpack(Object.assign( - {}, - webpackConfig, - webpackOptions, - { watch: true } - ))) - .pipe(gulp.dest(webpackConfig.output.path)); -}); - -gulp.task('pack-node', function() { - return gulp.src(webpackConfigNode.entry) - .pipe(plumber({ errorHandler: errorHandler })) - .pipe(webpack(webpackConfigNode)) - .pipe(gulp.dest(webpackConfigNode.output.path)); -}); - -gulp.task('pack', ['pack-client', 'pack-node']); - gulp.task('serve', function(cb) { var called = false; nodemon({ @@ -174,7 +138,16 @@ gulp.task('serve', function(cb) { }); }); -gulp.task('sync', ['serve', 'js', 'less', 'dependents'], function() { +var syncDepenedents = [ + 'serve', + 'js', + 'less', + 'dependents', + 'pack-client', + 'build-manifest' +]; + +gulp.task('sync', syncDepenedents, function() { sync.init(null, { proxy: 'http://localhost:3000', logLeval: 'debug', @@ -199,7 +172,51 @@ gulp.task('lint-json', function() { gulp.task('test-challenges', ['lint-json']); -gulp.task('less', ['js'], function() { +gulp.task('pack-client', function() { + return gulp.src(webpackConfig.entry) + .pipe(plumber({ errorHandler: errorHandler })) + .pipe(webpack(Object.assign( + {}, + webpackConfig, + webpackOptions + ))) + .pipe(gulp.dest(webpackConfig.output.path)) + .pipe(rev()) + // copy files to public + .pipe(gulp.dest(paths.css)) + // create and merge manifest + .pipe(rev.manifest('react-manifest.json')) + .pipe(gulp.dest(paths.manifest)); +}); + +gulp.task('pack-watch', function() { + return gulp.src(webpackConfig.entry) + .pipe(plumber({ errorHandler: errorHandler })) + .pipe(webpack(Object.assign( + {}, + webpackConfig, + webpackOptions, + { watch: true } + ))) + .pipe(gulp.dest(webpackConfig.output.path)) + .pipe(rev()) + // copy files to public + .pipe(gulp.dest(webpackConfig.output.path)) + // create manifest + .pipe(rev.manifest('react-manifest.json')) + .pipe(gulp.dest(paths.manifest)); +}); + +gulp.task('pack-node', function() { + return gulp.src(webpackConfigNode.entry) + .pipe(plumber({ errorHandler: errorHandler })) + .pipe(webpack(webpackConfigNode)) + .pipe(gulp.dest(webpackConfigNode.output.path)); +}); + +gulp.task('pack', ['pack-client', 'pack-node']); + +gulp.task('less', function() { return gulp.src(paths.less) .pipe(plumber({ errorHandler: errorHandler })) // copile @@ -212,7 +229,7 @@ gulp.task('less', ['js'], function() { // copy files to public .pipe(gulp.dest(paths.css)) // create and merge manifest - .pipe(rev.manifest(manifestOptions)) + .pipe(rev.manifest('css-manifest.json')) .pipe(gulp.dest(paths.manifest)); }); @@ -225,7 +242,7 @@ gulp.task('js', function() { // copy revisioned assets to dest .pipe(gulp.dest(paths.publicJs)) // create manifest file - .pipe(rev.manifest(manifestOptions)) + .pipe(rev.manifest('js-manifest.json')) // copy manifest file to dest .pipe(gulp.dest(paths.manifest)); }); @@ -234,7 +251,7 @@ gulp.task('js', function() { // sandbox depends on plugin gulp.task('dependents', ['js'], function() { var manifest = gulp.src( - path.join(__dirname, paths.manifest, manifestName) + path.join(__dirname, paths.manifest, 'js-manifest.json') ); return gulp.src(paths.dependents) @@ -242,17 +259,58 @@ gulp.task('dependents', ['js'], function() { .pipe(revReplace({ manifest: manifest })) .pipe(rev()) .pipe(gulp.dest(paths.publicJs)) - .pipe(rev.manifest(manifestOptions)) + .pipe(rev.manifest('dependents-manifest.json')) .pipe(gulp.dest(paths.manifest)); }); -gulp.task('build', ['less', 'js', 'dependents']); +function collector(file, memo) { + return Object.assign({}, JSON.parse(file.contents), memo); +} + +function done(manifest) { + return sortKeys(manifest); +} + +function buildManifest() { + return gulp.src(paths.manifest + '*.json') + .pipe(reduce('rev-manifest.json', collector, done, {})) + .pipe(gulp.dest('server/')); +} + +var buildDependents = ['less', 'js', 'dependents']; + +gulp.task('build-manifest', buildDependents, function() { + return buildManifest(); +}); + +gulp.task('build-manifest-watch', function() { + return buildManifest(); +}); -gulp.task('watch', ['less', 'js', 'dependents', 'serve', 'sync'], function() { +gulp.task('build', [ + 'less', + 'js', + 'dependents', + 'pack-client', + 'build-manifest' +]); + +var watchDependents = [ + 'less', + 'js', + 'dependents', + 'serve', + 'sync', + 'build-manifest' +]; + +gulp.task('watch', watchDependents, function() { gulp.watch(paths.less, ['less']); gulp.watch(paths.js, ['js']); gulp.watch(paths.challenges, ['test-challenges']); - gulp.watch(paths.dependents.concat(paths.js), ['js', 'dependents']); + gulp.watch(paths.js, ['js', 'dependents']); + gulp.watch(paths.dependents, ['dependents']); + gulp.watch(paths.manifest + '/*.json', ['build-manifest-watch']); }); gulp.task('default', ['less', 'serve', 'sync', 'watch', 'pack-watch']); diff --git a/package.json b/package.json index 768cd4be71996b..f3bf88e793336b 100644 --- a/package.json +++ b/package.json @@ -7,9 +7,8 @@ }, "scripts": { "build": "gulp build", - "build-production": "webpack", "start": "babel-node server/server.js", - "prestart-production": "bower cache clean && bower install && gulp build && npm run build-production", + "prestart-production": "bower cache clean && bower install && gulp build", "start-production": "node pm2Start", "lint": "eslint --ext=.js,.jsx .", "test": "mocha --compilers js:babel/register" @@ -60,6 +59,7 @@ "github-api": "~0.7.0", "gulp-less": "^3.0.3", "gulp-minify-css": "~0.5.1", + "gulp-reduce-file": "0.0.1", "gulp-rev": "^6.0.1", "gulp-rev-replace": "^0.4.2", "gulp-webpack": "^1.5.0", @@ -101,6 +101,7 @@ "request": "~2.53.0", "rx": "^2.5.3", "sanitize-html": "~1.6.1", + "sort-keys": "^1.1.1", "source-map-support": "^0.3.2", "thundercats": "^2.1.0", "thundercats-react": "^0.1.0", diff --git a/client/less/lib/Vimeo.css b/public/css/Vimeo.css similarity index 100% rename from client/less/lib/Vimeo.css rename to public/css/Vimeo.css diff --git a/server/manifests/css-manifest.json b/server/manifests/css-manifest.json new file mode 100644 index 00000000000000..dfac7264c77687 --- /dev/null +++ b/server/manifests/css-manifest.json @@ -0,0 +1,3 @@ +{ + "main.css": "main-6645810137.css" +} \ No newline at end of file diff --git a/server/manifests/dependents-manifest.json b/server/manifests/dependents-manifest.json new file mode 100644 index 00000000000000..53bc4daf4beb32 --- /dev/null +++ b/server/manifests/dependents-manifest.json @@ -0,0 +1,3 @@ +{ + "commonFramework.js": "commonFramework-f2a3a38931.js" +} \ No newline at end of file diff --git a/server/manifests/js-manifest.json b/server/manifests/js-manifest.json new file mode 100644 index 00000000000000..802a1037a5dec5 --- /dev/null +++ b/server/manifests/js-manifest.json @@ -0,0 +1,5 @@ +{ + "iFrameScripts.js": "iFrameScripts-fee9f17adb.js", + "main.js": "main-c4d88948ff.js", + "plugin.js": "plugin-4564d5d4ec.js" +} \ No newline at end of file diff --git a/server/manifests/react-manifest.json b/server/manifests/react-manifest.json new file mode 100644 index 00000000000000..73bc562cd3dd0b --- /dev/null +++ b/server/manifests/react-manifest.json @@ -0,0 +1,3 @@ +{ + "bundle.js": "bundle-5c9b5836a9.js" +} \ No newline at end of file diff --git a/server/middlewares/revision-helpers.js b/server/middlewares/revision-helpers.js index a5e957fc5c421c..aa8a7bfed825a0 100644 --- a/server/middlewares/revision-helpers.js +++ b/server/middlewares/revision-helpers.js @@ -1,13 +1,26 @@ import manifest from '../rev-manifest.json'; +const __DEV__ = process.env.NODE_ENV === 'development'; export default function({ globalPrepend = '' } = {}) { - function rev(scopedPrepend, asset) { + function rev(manifest, scopedPrepend, asset) { return `${globalPrepend}${scopedPrepend}/${ manifest[asset] || asset }`; } + const boundRev = rev.bind(null, manifest); return function(req, res, next) { - res.locals.rev = rev; + // in dev environment, we reread the manifest on every call + // this means we do not need to restart server on every change to + // client code + if (__DEV__) { + const manifest = require('../rev-manifest.json'); + res.locals.rev = rev.bind(null, manifest); + return next(); + } + + // in production we take use the initially loaded manifest + // since this should not change in production + res.locals.rev = boundRev; next(); }; } diff --git a/server/views/layout-react.jade b/server/views/layout-react.jade index 70093ecb054741..ad669cd20d620f 100644 --- a/server/views/layout-react.jade +++ b/server/views/layout-react.jade @@ -11,4 +11,4 @@ html(ng-app='profileValidation', lang='en') include partials/flash #fcc!= markup script!= state - script(src='/js/bundle.js') + script(src=rev('/js', 'bundle.js')) diff --git a/server/views/partials/small-head.jade b/server/views/partials/small-head.jade index 585d55498db5b4..9116a6a32a868b 100644 --- a/server/views/partials/small-head.jade +++ b/server/views/partials/small-head.jade @@ -3,7 +3,7 @@ script(src="//cdnjs.cloudflare.com/ajax/libs/twitter-bootstrap/3.3.4/js/bootstra link(rel="stylesheet" type="text/css" href="//fonts.googleapis.com/css?family=Lato:400|Inconsolata") link(rel='stylesheet', href='/bower_components/font-awesome/css/font-awesome.min.css') link(rel='stylesheet', href=rev('/css', 'main.css')) -link(rel='stylesheet', href='/css/lib/Vimeo.css') +link(rel='stylesheet', href='/css/Vimeo.css') // End **REQUIRED** includes include meta