diff --git a/.travis.yml b/.travis.yml index dc2df6866b16..e4642d2c0571 100644 --- a/.travis.yml +++ b/.travis.yml @@ -48,7 +48,7 @@ install: - npm config set loglevel http - npm install -g npm@2.5 # Instal npm dependecies and ensure that npm cache is not stale - - scripts/npm/install-dependencies.sh + - npm install before_script: - mkdir -p $LOGS_DIR diff --git a/Gruntfile.js b/Gruntfile.js index 53aaf59a9ad6..018f08b16f1c 100644 --- a/Gruntfile.js +++ b/Gruntfile.js @@ -305,7 +305,7 @@ module.exports = function(grunt) { shell: { "npm-install": { - command: path.normalize('scripts/npm/install-dependencies.sh') + command: 'node scripts/npm/check-node-modules.js' }, "promises-aplus-tests": { diff --git a/package.json b/package.json index 4168a8384d1a..6a4fa39ca179 100644 --- a/package.json +++ b/package.json @@ -13,6 +13,10 @@ "npm": "~2.5" }, "engineStrict": true, + "scripts": { + "preinstall": "node scripts/npm/check-node-modules.js --purge", + "postinstall": "node scripts/npm/copy-npm-shrinkwrap.js" + }, "devDependencies": { "angular-benchpress": "0.x.x", "benchmark": "1.x.x", diff --git a/scripts/npm/check-node-modules.js b/scripts/npm/check-node-modules.js new file mode 100644 index 000000000000..a9bc24a9a593 --- /dev/null +++ b/scripts/npm/check-node-modules.js @@ -0,0 +1,74 @@ +// Implementation based on: +// https://github.com/angular/angular/blob/3b9c08676a4c921bbfa847802e08566fb601ba7a/tools/npm/check-node-modules.js +'use strict'; + +// Imports +var fs = require('fs'); +var path = require('path'); + +// Constants +var PROJECT_ROOT = path.join(__dirname, '../../'); +var NODE_MODULES_DIR = 'node_modules'; +var NPM_SHRINKWRAP_FILE = 'npm-shrinkwrap.json'; +var NPM_SHRINKWRAP_CACHED_FILE = NODE_MODULES_DIR + '/npm-shrinkwrap.cached.json'; + +// Run +_main(); + +// Functions - Definitions +function _main() { + var purgeIfStale = process.argv.indexOf('--purge') !== -1; + + process.chdir(PROJECT_ROOT); + checkNodeModules(purgeIfStale); +} + +function checkNodeModules(purgeIfStale) { + var nodeModulesOk = compareMarkerFiles(NPM_SHRINKWRAP_FILE, NPM_SHRINKWRAP_CACHED_FILE); + + if (nodeModulesOk) { + console.log(':-) npm dependencies are looking good!'); + } else if (purgeIfStale) { + console.log(':-( npm dependencies are stale or in an unknown state!'); + console.log(' Purging \'' + NODE_MODULES_DIR + '\'...'); + deleteDirSync(NODE_MODULES_DIR); + } else { + var separator = new Array(81).join('!'); + + console.warn(separator); + console.warn(':-( npm dependencies are stale or in an unknown state!'); + console.warn('You can rebuild the dependencies by running `npm install`.'); + console.warn(separator); + } + + return nodeModulesOk; +} + +function compareMarkerFiles(markerFilePath, cachedMarkerFilePath) { + if (!fs.existsSync(cachedMarkerFilePath)) return false; + + var opts = {encoding: 'utf-8'}; + var markerContent = fs.readFileSync(markerFilePath, opts); + var cachedMarkerContent = fs.readFileSync(cachedMarkerFilePath, opts); + + return markerContent === cachedMarkerContent; +} + +// Custom implementation of `rm -rf` that works consistently across OSes +function deleteDirSync(path) { + if (fs.existsSync(path)) { + fs.readdirSync(path).forEach(deleteDirOrFileSync); + fs.rmdirSync(path); + } + + // Helpers + function deleteDirOrFileSync(subpath) { + var curPath = path + '/' + subpath; + + if (fs.lstatSync(curPath).isDirectory()) { + deleteDirSync(curPath); + } else { + fs.unlinkSync(curPath); + } + } +} diff --git a/scripts/npm/copy-npm-shrinkwrap.js b/scripts/npm/copy-npm-shrinkwrap.js new file mode 100644 index 000000000000..78bfe3baaafa --- /dev/null +++ b/scripts/npm/copy-npm-shrinkwrap.js @@ -0,0 +1,60 @@ +'use strict'; + +// Imports +var fs = require('fs'); +var path = require('path'); + +// Constants +var PROJECT_ROOT = path.join(__dirname, '../../'); +var NODE_MODULES_DIR = 'node_modules'; +var NPM_SHRINKWRAP_FILE = 'npm-shrinkwrap.json'; +var NPM_SHRINKWRAP_CACHED_FILE = NODE_MODULES_DIR + '/npm-shrinkwrap.cached.json'; + +// Run +_main(); + +// Functions - Definitions +function _main() { + process.chdir(PROJECT_ROOT); + copyFile(NPM_SHRINKWRAP_FILE, NPM_SHRINKWRAP_CACHED_FILE, onCopied); +} + +// Implementation based on: +// https://stackoverflow.com/questions/11293857/fastest-way-to-copy-file-in-node-js#answer-21995878 +function copyFile(srcPath, dstPath, callback) { + var callbackCalled = false; + + if (!fs.existsSync(srcPath)) { + done(new Error('Missing source file: ' + srcPath)); + return; + } + + var rs = fs.createReadStream(srcPath); + rs.on('error', done); + + var ws = fs.createWriteStream(dstPath); + ws.on('error', done); + ws.on('finish', done); + + rs.pipe(ws); + + // Helpers + function done(err) { + if (callback && !callbackCalled) { + callbackCalled = true; + callback(err); + } + } +} + +function onCopied(err) { + if (err) { + var separator = new Array(81).join('!'); + + console.error(separator); + console.error( + 'Failed to copy `' + NPM_SHRINKWRAP_FILE + '` to `' + NPM_SHRINKWRAP_CACHED_FILE + '`:'); + console.error(err); + console.error(separator); + } +} diff --git a/scripts/npm/install-dependencies.sh b/scripts/npm/install-dependencies.sh deleted file mode 100755 index 1851d4ea6ae4..000000000000 --- a/scripts/npm/install-dependencies.sh +++ /dev/null @@ -1,16 +0,0 @@ -#!/bin/bash - -set -e - -SHRINKWRAP_FILE=npm-shrinkwrap.json -SHRINKWRAP_CACHED_FILE=node_modules/npm-shrinkwrap.cached.json - -if diff -q $SHRINKWRAP_FILE $SHRINKWRAP_CACHED_FILE; then - echo 'No shrinkwrap changes detected. npm install will be skipped...'; -else - echo 'Blowing away node_modules and reinstalling npm dependencies...' - rm -rf node_modules - npm install - cp $SHRINKWRAP_FILE $SHRINKWRAP_CACHED_FILE - echo 'npm install successful!' -fi