Last active
August 18, 2018 05:14
-
-
Save axemclion/cb19bd30a6f7a944b47f to your computer and use it in GitHub Desktop.
Using browser-sync with Cordova
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
// Browser-Sync support for Cordova projects | |
// To use this, add the following snippet as a after_run hook using something like | |
// <hook type="after_prepare" src="hooks/browser-sync.js" /> | |
// Also add ws: 'unsafe-inline' to the CSP in index.html | |
// The do Cordova run, and changing anything in www/ will live-reload the cordova app on emulator/device | |
module.exports = function(context) { | |
if (context.opts.options[0] === '--browser-sync-mode') { | |
// Prepare was called by this script, so don't run the script again | |
return; | |
} | |
var path = require('path'); | |
var fs = require('fs'); | |
var Url = require('url'); | |
var debug = console.log.bind(console); | |
var npm = context.requireCordovaModule('npm'); | |
var Q = context.requireCordovaModule('q'); | |
var glob = context.requireCordovaModule('glob') | |
var et = context.requireCordovaModule('elementtree'); | |
function parseXml(filename) { | |
return new et.ElementTree(et.XML(fs.readFileSync(filename, "utf-8").replace(/^\uFEFF/, ""))); | |
} | |
// Installs browser-sync and other packages locally | |
function installDependencies() { | |
return Q(); | |
debug('Starting npm'); | |
return Q.ninvoke(npm, 'load').then(function() { | |
debug('Installing dependencies'); | |
return Q.ninvoke(npm.commands, 'install', ['browser-sync']); | |
}); | |
} | |
/** | |
* Cordova is usually served from index.html on the device. This function serves changes that to be served from a server instead | |
* @param configLocation - The place where platform specific config.xml is located, relative to platforms folder | |
* @param hostedPage - Location from where the www/index.html file should be served, relative to platforms folder | |
**/ | |
function changeHost(hostedPage, configLocation) { | |
var cwd = path.join(context.opts.projectRoot, 'platforms', configLocation); | |
debug('Searching for config files at', cwd); | |
return configs = glob.sync('**/config.xml', { | |
cwd: cwd | |
}).map(function(filename) { | |
var filename = path.join(cwd, filename); | |
debug('Changing ', filename); | |
configXml = parseXml(filename); | |
var contentTag = configXml.find('content[@src]'); | |
if (contentTag) { | |
contentTag.attrib.src = hostedPage; | |
} | |
// Also add allow nav in case of | |
var allowNavTag = et.SubElement(configXml.find('.'), 'allow-navigation'); | |
allowNavTag.set('href', '*'); | |
fs.writeFileSync(filename, configXml.write({ | |
indent: 4 | |
}), "utf-8"); | |
return filename; | |
}); | |
} | |
/** | |
* Starts the browser sync server, and when files are changed, does the reload | |
* @param location - where to watch | |
* @returns location where files are served from | |
*/ | |
function browserSyncServer(location) { | |
debug('Starting browser-sync server'); | |
location = location + '**/*.*' | |
return Q.promise(function(resolve, reject, notify) { | |
var bs = require('browser-sync').create(); | |
bs.watch(location, function(event, files) { | |
if (event !== 'change') { | |
return; | |
} | |
// TODO Prepare only the platform that was run | |
context.cordova.prepare({ | |
options: ['--browser-sync-mode'] // to indicate who is calling prepare | |
}); | |
bs.reload(files); | |
}); | |
bs.init({ | |
server: { | |
baseDir: context.opts.projectRoot, | |
directory: true | |
}, | |
files: location, | |
open: false, | |
snippetOptions: { | |
rule: { | |
match: /<\/body>/i, | |
fn: function(snippet, match) { | |
return '<script>window.__karma__=true</script>' + snippet + monkeyPatch() + match; | |
} | |
} | |
}, | |
minify: false | |
}, function(err, bs) { | |
var server = bs.options.getIn(['urls', 'external']); | |
resolve(Url.resolve(server, 'platforms')); | |
}); | |
}); | |
} | |
// Main workfow | |
return installDependencies().then(function() { | |
return browserSyncServer(path.join(context.opts.projectRoot, 'www/')); | |
}).then(function(server) { | |
// TODO - Change host based on platform | |
changeHost(server + '/android/assets/www/index.html', 'android/res'); | |
changeHost(server + '/ios/www/index.html', 'ios'); | |
}); | |
}; | |
function monkeyPatch() { | |
var script = function() { | |
(function patch() { | |
if (typeof window.__bs === 'undefined') { | |
window.setTimeout(patch, 500); | |
} else { | |
var oldCanSync = window.__bs.prototype.canSync; | |
window.__bs.prototype.canSync = function(data, optPath) { | |
data.url = window.location.pathname.substr(0, window.location.pathname.indexOf('/www')) + data.url.substr(data.url.indexOf('/www')) | |
return oldCanSync.apply(this, [data, optPath]); | |
}; | |
} | |
}()); | |
}; | |
return '<script>(' + script.toString() + '());</script>'; | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment