divshot-cli
Version:
CLI for Divshot
214 lines (190 loc) • 7.05 kB
JavaScript
var chalk = require('chalk');
var _ = require('lodash');
var path = require('path');
var fs = require('fs');
var promptly = require('promptly');
var async = require('async');
var divshot2Firebase = function(source) {
var warnings = [];
var actions = [];
var out = {};
var data = {};
var track = {};
out.firebase = source.name;
out.public = source.root || ".";
if (source.exclude) {
out.ignore = source.exclude;
} else {
out.ignore = ["firebase.json","**/.*","**/node_modules/**"]
}
if (source.clean_urls) {
warnings.push(chalk.bold("clean_urls") + " support is planned for Firebase Hosting, but is not yet available");
track.clean_urls = true;
}
if (source.error_page && source.error_page !== '404.html')
actions.push("Rename " + chalk.bold(source.error_page) + " to " + chalk.bold("404.html"));
if (source.proxy) {
warnings.push("Firebase Hosting does not support AJAX proxying");
track.ajax_proxy = true;
}
if (source.forms) {
warnings.push("Firebase Hosting does not support form-to-email");
track.forms = true;
}
if (source.prerender) {
warnings.push("Firebase Hosting does not presently support JS prerendering");
track.prerender = true;
}
if (fs.existsSync('.env.json')) {
warnings.push("Firebase Hosting does not support JS environment variables");
track.env = true;
}
if (_.isObject(source.routes)) {
source.routes = [source.routes];
}
_.forEach(source.routes || [], function(routeObject) {
_.forEach(routeObject, function(val, key) {
out.rewrites = out.rewrites || [];
out.rewrites.push({
source: key,
destination: path.normalize('/' + val)
});
});
});
if (_.isObject(source.redirects)) {
source.redirects = [source.redirects];
}
_.forEach(source.redirects || [], function(redirectObject) {
_.forEach(redirectObject, function(val, key) {
out.redirects = out.redirects || [];
if (_.isString(val)) {
var obj = {
source: key,
destination: val,
type: 301
};
} else {
var obj = {
source: key,
destination: val.url,
type: val.status
}
}
if (_.contains(key, ':')) {
warnings.push('Redirect for ' + chalk.bold(key) + ' contains path segments. This is not yet supported by Firebase Hosting.');
track.redirect_segments = true;
}
out.redirects.push(obj);
});
});
_.forEach(source.headers || {}, function(val, key) {
var obj = {source: key, headers: []};
_.forEach(val || {}, function(headerVal, headerKey) {
if (_.contains([
'Cache-Control',
'Access-Control-Allow-Origin',
'X-UA-Compatible',
'X-Content-Type-Options',
'X-Frame-Options',
'X-XSS-Protection'
])) {
obj.headers.push({key: headerKey, value: headerVal});
} else {
warnings.push('The ' + chalk.bold(headerKey) + ' header is not allowed by Firebase Hosting.');
track.invalid_header = true;
}
});
out.headers = out.headers || [];
out.headers.push(obj);
});
_.forEach(source.cache_control || {}, function(val, key) {
out.headers = out.headers || [];
var outVal;
if (_.isNumber(val)) {
outVal = 'public, max-age=' + val.toString();
} else if (_.isString(val)) {
outVal = val;
}
if (outVal) {
out.headers.push({
source: key,
headers: [{key: 'Cache-Control', value: outVal}]
});
}
});
return {
warnings: warnings,
actions: actions,
track: track,
output: out
}
}
module.exports = function (cli) {
cli.command('migrate')
.description('migrate a Divshot app to Firebase Hosting')
.handler(function (done) {
var divshotConfigPath = path.resolve('divshot.json');
var firebaseConfigPath = path.resolve('firebase.json');
if (!fs.existsSync(divshotConfigPath)) {
return done("Couldn't find divshot.json. Please run in a Divshot app dierctory.");
}
if (fs.existsSync(firebaseConfigPath)) {
return done("Can't proceed, there is already a firebase.json in this directory.");
}
var source = require(divshotConfigPath);
var result = divshot2Firebase(source);
console.log();
console.log(chalk.underline('Migrating Divshot app ' + chalk.bold(source.name) + ' to Firebase Hosting'));
console.log();
async.series({
firebase: function(callback) {
promptly.prompt('What is your Firebase project\'s name? (' + result.output.firebase + ') ', { default: result.output.firebase, }, callback)
},
track: function(callback) {
promptly.confirm('Can we anonymously report which features your app used (for Firebase Hosting roadmap planning, etc.)? (y/n)', callback);
},
write: function(callback) {
promptly.confirm('Write firebase.json? (y/n)', callback);
}
}, function(err, inputs) {
var warningSign = process.platform === 'win32' ? '!' : '⚠';
if (result.warnings.length) {
console.log();
console.log(chalk.yellow.bold.underline(warningSign + ' Warning: The following incompatible features were detected.'));
_.forEach(result.warnings, function(warning) {
console.log('- ' + warning);
});
console.log();
console.log(' ' + chalk.bold("Don't panic!"), "Some features may be coming soon or there may be workarounds.\n Contact", chalk.bold.underline("support@firebase.com"), "with any questions.");
}
if (result.actions.length) {
console.log();
console.log(chalk.cyan.bold.underline('You need to take the following actions to fully migrate your app:'));
_.forEach(result.actions, function(action) {
console.log('- ' + action);
});
}
result.output.firebase = inputs.firebase;
if (inputs.write) {
fs.writeFileSync(firebaseConfigPath, JSON.stringify(result.output, null, 2), 'utf-8');
console.log();
console.log(chalk.green('Wrote firebase.json configuration file.'));
}
console.log();
console.log('If you have the latest version of ' + chalk.bold('firebase-tools') + ' you should now be able');
console.log('to run ' + chalk.bold('firebase serve') + ' in this directory to start a local server and');
console.log(chalk.bold('firebase deploy') + ' to deploy to Firebase Hosting.');
console.log();
console.log('See ' + chalk.bold.underline('http://docs.divshot.com/migrate') + ' for additional migration info.');
console.log();
if (inputs.track) {
var mixpanel = require('mixpanel').init('df1a3bd1b07c3caa45a6ea4fd0f163f1');
mixpanel.track('Firebase Migration', result.track, function(err) {
done();
});
} else {
done();
}
});
});
};