cordova-template-framework7-vue-webpack
Version:
Cordova template with framework7, vue 2 and webpack 2.
432 lines (344 loc) • 15.8 kB
JavaScript
module.exports = function (ctx) {
const Q = require('q'),
path = require('path'),
fs = require('fs'),
cp = require('child_process'),
os = require('os'),
ifaces = os.networkInterfaces(),
spawn = cp.spawn,
exec = cp.exec,
pRoot = ctx.opts.projectRoot,
nodeModulesPath = path.resolve(pRoot, 'node_modules/'),
wwwFolder = path.resolve(pRoot, 'www/'),
staticFolder = path.resolve(pRoot, 'src/static/'),
targetStaticFolder = path.resolve(wwwFolder, 'static/'),
manifestFileSrcPath = path.resolve(pRoot, 'src/manifest.json'),
manifestFileCopyPath = path.resolve(wwwFolder, 'manifest.json'),
webpackPath = path.resolve(nodeModulesPath, '.bin/webpack'),
epipeBombPath = path.resolve(nodeModulesPath, '.bin/epipebomb'),
webpackDevServerPath = path.resolve(nodeModulesPath, '.bin/webpack-dev-server'),
packageJsonPath = path.resolve(__dirname, '../package.json'),
packageJson = require(packageJsonPath)
function getRouterIpAddr () {
for (let key in ifaces) {
if (ifaces.hasOwnProperty(key)) {
for (let ipInfoKey in ifaces[key]) {
if (ifaces[key].hasOwnProperty(ipInfoKey)) {
let ipInfo = ifaces[key][ipInfoKey]
if (ipInfo.family === 'IPv4' && !ipInfo.internal)
return ipInfo.address
}
}
}
}
return '127.0.0.1'
}
const sys = {
toKebabCase (txt) {
return txt.replace(/(\s)+/g, '-').replace(/[A-Z]/g, function (t) {
return t.toLowerCase()
})
},
checkPackageName () {
if (typeof packageJson.name === 'undefined' || packageJson.name === '') {
packageJson.name = 'hello-world'
} else if (/\s/g.test(packageJson.name)) {
packageJson.name = sys.toKebabCase(packageJson.name)
fs.writeFileSync(packageJsonPath, JSON.stringify(packageJson), 'utf-8')
}
},
deleteFolderRecursive (path, doNotDeleteSelf = false) {
if (fs.existsSync(path)) {
fs.readdirSync(path).forEach((file) => {
let curPath = path + '/' + file
if (fs.lstatSync(curPath).isDirectory())
sys.deleteFolderRecursive(curPath);
else
fs.unlinkSync(curPath)
})
if (!doNotDeleteSelf)
fs.rmdirSync(path)
}
},
cleanWww () {
let wwwDir = path.resolve(__dirname, '../www/')
sys.deleteFolderRecursive(wwwDir, true)
},
checkManifestFile () {
if (fs.existsSync(manifestFileSrcPath)) {
console.log('Manifest.json found in src folder. Copying...')
fs.writeFileSync(manifestFileCopyPath, fs.readFileSync(manifestFileSrcPath))
}
},
checkNodeModules () {
let defer = new Q.defer()
console.log('Checking is node modules installed...')
if (!fs.existsSync(nodeModulesPath) || !fs.existsSync(path.resolve(nodeModulesPath, 'cheerio/'))) {
console.log('Node modules not found. Installing...')
exec('npm i', { cwd: pRoot, maxBuffer: 1024 * 1024 * 5 }, (error) => {
if (error) {
console.error(`Error happened when npm install: ${error}`);
defer.reject(new Error(`Error happened when npm install: ${error}`))
}
console.log('Node modules installed successfully!')
defer.resolve()
})
}
else {
console.log('Node modules already installed.')
defer.resolve()
}
return defer.promise
},
makeNonDevServerChanges () {
let defer = new Q.defer(),
cheerio = require('cheerio'),
configFile = path.resolve(__dirname, '../config.xml'),
conf = cheerio.load(fs.readFileSync(configFile), { xmlMode: true })
if (conf('allow-navigation').length > 0) {
let target = conf('allow-navigation')
if (target.attr('data-href') !== '') {
target.attr('href', target.attr('data-href'))
target.removeAttr('data-href')
}
}
fs.writeFileSync(configFile, conf.html(), 'utf-8')
sys.cleanWww()
defer.resolve()
return defer.promise
},
makeDevServerChanges () {
let defer = new Q.defer(),
configFile = path.resolve(__dirname, '../config.xml'),
srcFile = path.resolve(__dirname, '../webpack/dev_helpers/device_router.html'),
targetFile = path.resolve(wwwFolder, 'index.html'),
defaultCsp = `default-src *; script-src 'self' data: 'unsafe-inline' 'unsafe-eval' http://127.0.0.1:8081 http://LOCIP:8081; object-src 'self' data: http://127.0.0.1:8081 http://LOCIP:8081; style-src 'self' 'unsafe-inline' data: ; img-src *; media-src 'self' data: http://127.0.0.1:8081 http://LOCIP:8081; frame-src 'self' data: http://127.0.0.1:8081 http://LOCIP:8081; font-src *; connect-src 'self' data: http://127.0.0.1:8081 http://LOCIP:8081`,
cheerio = require('cheerio'),
$ = cheerio.load(fs.readFileSync(srcFile, 'utf-8')),
conf = cheerio.load(fs.readFileSync(configFile), { xmlMode: true })
sys.cleanWww()
$('head').prepend(`<meta http-equiv="content-security-policy" content="${defaultCsp.replace(/LOCIP/g, getRouterIpAddr())}">`)
$('body').prepend(`<script>const localServerIp = '${getRouterIpAddr()}'</script>`).append(`<script src="cordova.js"></script>`)
fs.writeFileSync(targetFile, $.html())
if (conf('allow-navigation').length === 0)
conf('widget').append('<allow-navigation href="*" />')
else {
let target = conf('allow-navigation')
if (target.attr('href') !== '*')
target.attr('data-href', target.attr('href')).attr('href', '*')
}
fs.writeFileSync(configFile, conf.html(), 'utf-8')
sys.deleteFolderRecursive(targetStaticFolder, true)
sys.copyRecursiveSync(staticFolder, targetStaticFolder)
sys.watchStaticFolder()
defer.resolve()
return defer.promise
},
startWebpackBuild (isRelease) {
let defer = new Q.defer()
console.log('Starting webpack build...')
let wpPath = webpackPath + (os.platform() === 'win32' ? '.cmd' : '')
exec(`"${wpPath}"` + (isRelease ? ' --env.release' : ''), { cwd: pRoot, maxBuffer: 1024 * 1024 * 5 }, (error, log) => {
if (error) {
console.error(`Error happened when webpack build: ${error}`);
defer.reject(new Error(`Error happened when webpack build: ${error}`))
}
console.log(`Webpack log: ${log}`);
sys.deleteFolderRecursive(targetStaticFolder, true)
sys.copyRecursiveSync(staticFolder, targetStaticFolder)
sys.checkManifestFile()
console.log('Webpack build completed to www folder successfully!')
defer.resolve()
})
return defer.promise
},
startWebpackDevServer (platform) {
let defer = new Q.defer(),
outText = '',
isResultFound = false,
args = [`"${webpackDevServerPath}"`, '--hot', '--inline', '--env.devserver', '--' + platform, `--public ${getRouterIpAddr()}:8081`, '--env.devserver'],
run = epipeBombPath
if (os.platform() === 'win32') {
args = ['--hot', '--inline', '--env.devserver', '--' + platform, `--public ${getRouterIpAddr()}:8081`, '--env.devserver']
run = `"${webpackDevServerPath}.cmd"`
}
let devServerSpawn = spawn(run, args, {
shell: true,
cwd: pRoot,
stdio: [process.stdin, 'pipe', process.stderr]
})
devServerSpawn.on('error', (err) => {
console.log('Failed to start webpack dev server!')
console.log(err)
defer.reject(err)
})
devServerSpawn.stdout.on('data', (data) => {
process.stdout.write(data)
if (!isResultFound) {
outText += data
if (outText.indexOf('bundle is now VALID.') > -1 || outText.indexOf('Compiled successfully.') > -1 || outText.indexOf('Compiled with warnings') > -1) {
isResultFound = true
outText = ''
defer.resolve()
}
}
})
return defer.promise
},
emptyDefer () {
let defer = new Q.defer()
defer.resolve()
return defer.promise
},
checkOption (name) {
return (
typeof ctx.opts !== 'undefined' &&
typeof ctx.opts.options !== 'undefined' &&
typeof ctx.opts.options[name] !== 'undefined' &&
ctx.opts.options[name] === true
)
},
checkArgv (name) {
return (
typeof ctx.opts !== 'undefined' &&
typeof ctx.opts.options !== 'undefined' &&
typeof ctx.opts.options.argv !== 'undefined' &&
(
Array.isArray(ctx.opts.options.argv) &&
ctx.opts.options.argv.indexOf(name) > -1 ||
ctx.opts.options.argv[name] === true
)
)
},
isFoundInCmdline (cmdCommand) {
return (
ctx.cmdLine.indexOf(`cordova ${cmdCommand}`) > -1 ||
ctx.cmdLine.indexOf(`phonegap ${cmdCommand}`) > -1
)
},
deleteFolderRecursive (path, doNotDeleteSelf = false) {
if (fs.existsSync(path)) {
fs.readdirSync(path).forEach((file) => {
let curPath = path + '/' + file
if (fs.lstatSync(curPath).isDirectory())
sys.deleteFolderRecursive(curPath);
else
fs.unlinkSync(curPath)
})
if (!doNotDeleteSelf)
fs.rmdirSync(path)
}
},
copyRecursiveSync (src, dest) {
let exists = fs.existsSync(src),
stats = exists && fs.statSync(src),
isDirectory = exists && stats.isDirectory()
if (exists && isDirectory) {
if (!fs.existsSync(dest))
fs.mkdirSync(dest)
fs.readdirSync(src).forEach((childItemName) => {
sys.copyRecursiveSync(path.join(src, childItemName), path.join(dest, childItemName))
})
} else
fs.linkSync(src, dest)
},
createFile (p) {
let rel = path.relative('./src/static/', p)
sys.copyFile(p, path.resolve(targetStaticFolder, rel));
console.log(`${p} created`)
},
addUpdate (p) {
let rel = path.relative('./src/static/', p)
console.log(`${p} copied to ${path.resolve(targetStaticFolder, rel)}`)
},
addDir (p) {
let rel = path.relative('./src/static/', p)
fs.mkdir(path.resolve(targetStaticFolder, rel), () => {
console.log(`Found ${p} folder. Created to ${path.resolve(targetStaticFolder, rel)}`)
})
},
delete (p) {
let rel = path.relative('./src/static/', p)
fs.access(path.resolve(targetStaticFolder, rel), fs.constants.W_OK, (err) => {
if (!err) {
fs.unlink(path.resolve(targetStaticFolder, rel), () => {
console.log(`${path.resolve(targetStaticFolder, rel)} deleted.`)
})
} else {
console.log(`${path.resolve(targetStaticFolder, rel)} not deleted.`)
}
})
},
copyFile (source, target, cb) {
let cbCalled = false
let rd = fs.createReadStream(source)
rd.on("error", done)
let wr = fs.createWriteStream(target)
wr.on("error", done)
wr.on("close", done)
rd.pipe(wr)
function done (err) {
if (!cbCalled && typeof cb === 'function') {
cb(err)
cbCalled = true
}
}
},
watchStaticFolder () {
if (fs.existsSync(staticFolder)) {
const chokidar = require('chokidar'),
watcher = chokidar.watch(staticFolder, {
persistent: true
})
watcher.on('ready', () => {
console.log('Watcher ready!')
watcher
.on('add', sys.createFile)
.on('change', sys.addUpdate)
.on('unlink', sys.delete)
.on('addDir', sys.addDir)
.on('unlinkDir', sys.delete)
})
}
}
}
let deferral = new Q.defer(),
isBuild = sys.isFoundInCmdline('build'),
isRun = sys.isFoundInCmdline('run'),
isEmulate = sys.isFoundInCmdline('emulate'),
isPrepare = sys.isFoundInCmdline('prepare'),
isServe = sys.isFoundInCmdline('serve'),
isLiveReload = sys.checkArgv('--live-reload') || sys.checkArgv('--lr') || sys.checkArgv('lr') || sys.checkArgv('live-reload'),
isNoBuild = sys.checkOption('no-build'),
isRelease = sys.checkOption('release')
if (ctx.opts.platforms.length === 0 && !isPrepare) {
console.log('Update happened. Skipping...')
deferral.resolve()
}
else {
console.log('Before deploy hook started...')
// if package name contains space characters, we'll convert it to kebab case. Required for npm install command to work.
sys.checkPackageName()
sys.checkNodeModules()
.then(() => {
if (isBuild || ((isRun || isEmulate || isPrepare) && !isLiveReload && !isNoBuild)) {
return sys.makeNonDevServerChanges().then(() => sys.startWebpackBuild(isRelease))
} else if (isServe || (isRun || isEmulate) && isLiveReload) {
return sys.makeDevServerChanges().then(() => sys.startWebpackDevServer(ctx.opts.platforms[0]))
}
else
return sys.emptyDefer()
})
.then(() => {
console.log('Cordova hook completed. Resuming to run your cordova command...')
deferral.resolve()
})
.catch((err) => {
console.log('Error happened on main chain:')
console.log(err)
deferral.reject(err)
})
.done()
}
return deferral.promise
}