diff --git a/README.md b/README.md index 1bfac080..e809f4ec 100644 --- a/README.md +++ b/README.md @@ -77,7 +77,7 @@ module.exports = { Before preparing your application, it is bundled with webpack. -``` +```shell $ cordova build ``` @@ -87,7 +87,7 @@ Processing flow: ### Live Reload (Hot Module Replacement) the App -``` +```shell $ cordova prepare -- --livereload $ cordova build -- --livereload $ cordova run -- -l @@ -95,7 +95,7 @@ $ cordova run -- -l Processing flow: -`webpack compile` > `cordova prepare` > `webpack serve` > `cordova compile` +`cordova prepare` > `webpack serve` > `cordova compile` ### Customize webpack configuration @@ -135,7 +135,7 @@ By defaults: If you want to customize `devServer` options, modify `webpack.config.js` file as follows: -``` +```js ... module.exports = { ... @@ -144,13 +144,25 @@ module.exports = { host: 'localhost', port: '8000', }, + ... }; ``` +## CLI examples + +```shell +$ cordova prepare # webpack compile +$ cordova prepare -- --livereload # webpack live reload +$ cordova build # webpack compile +$ cordova build -- --webpackConfig path/to/dir/webpack.config.babel.js # webpack compile +$ cordova build -- --w path/to/dir/webpack.config.js --livereload # webpack live reload +$ cordova run -- --w path/to/dir/webpack.config.j -l # webpack live reload +``` + ## TODO - [x] Bundle with webpack before preparing. -- [x] Live Reload (Hot Module Replacement) with webpack-dev-server. +- [x] Live Reload (Hot Module Replacement) with webpack-dev-server after preparing. - [x] Emulator - [x] Device diff --git a/package-lock.json b/package-lock.json index 3dccc5db..2289465d 100644 --- a/package-lock.json +++ b/package-lock.json @@ -848,6 +848,11 @@ "@types/webpack": "*" } }, + "@types/yargs-parser": { + "version": "13.0.0", + "resolved": "https://registry.npmjs.org/@types/yargs-parser/-/yargs-parser-13.0.0.tgz", + "integrity": "sha512-wBlsw+8n21e6eTd4yVv8YD/E3xq0O6nNnJIquutAsFGE7EyMKz7W6RNT6BRu1SmdgmlCZ9tb0X+j+D6HGr8pZw==" + }, "@typescript-eslint/eslint-plugin": { "version": "1.7.0", "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-1.7.0.tgz", @@ -10681,6 +10686,11 @@ "yargs-parser": "^11.1.1" }, "dependencies": { + "camelcase": { + "version": "5.3.1", + "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-5.3.1.tgz", + "integrity": "sha512-L28STB170nwWS63UjtlEOE3dldQApaJXZkOI1uMFfzf3rRuPegHaHesyee+YxQ+W6SvRDQV6UrdOdRiR153wJg==" + }, "cross-spawn": { "version": "6.0.5", "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-6.0.5.tgz", @@ -10795,13 +10805,22 @@ "version": "3.0.0", "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-3.0.0.tgz", "integrity": "sha1-zg6+ql94yxiSXqfYENe1mwEP1RU=" + }, + "yargs-parser": { + "version": "11.1.1", + "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-11.1.1.tgz", + "integrity": "sha512-C6kB/WJDiaxONLJQnF8ccx9SEeoTTLek8RVbaOIsrAUS8VrBEXfmeSnCZxygc+XC2sNMBIwOOnfcxiynjHsVSQ==", + "requires": { + "camelcase": "^5.0.0", + "decamelize": "^1.2.0" + } } } }, "yargs-parser": { - "version": "11.1.1", - "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-11.1.1.tgz", - "integrity": "sha512-C6kB/WJDiaxONLJQnF8ccx9SEeoTTLek8RVbaOIsrAUS8VrBEXfmeSnCZxygc+XC2sNMBIwOOnfcxiynjHsVSQ==", + "version": "13.1.0", + "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-13.1.0.tgz", + "integrity": "sha512-Yq+32PrijHRri0vVKQEm+ys8mbqWjLiwQkMFNXEENutzLPP0bE4Lcd4iA3OQY5HF+GD3xXxf0MEHb8E4/SA3AA==", "requires": { "camelcase": "^5.0.0", "decamelize": "^1.2.0" diff --git a/package.json b/package.json index b0f9c861..87253943 100644 --- a/package.json +++ b/package.json @@ -45,6 +45,7 @@ "@types/rechoir": "^0.6.1", "@types/webpack": "^4.4.31", "@types/webpack-dev-server": "^3.1.5", + "@types/yargs-parser": "^13.0.0", "cordova-common": "^3.1.0", "csp-parse": "0.0.2", "current-device": "^0.8.0", @@ -59,7 +60,8 @@ "source-map-support": "^0.5.12", "webpack": "^4.31.0", "webpack-dev-server": "^3.3.1", - "webpack-inject-plugin": "^1.5.0" + "webpack-inject-plugin": "^1.5.0", + "yargs-parser": "^13.1.0" }, "devDependencies": { "@commitlint/cli": "^7.6.0", diff --git a/src/utils/webpackHelper.ts b/src/utils/webpackHelper.ts index f965b6ec..d655bb18 100644 --- a/src/utils/webpackHelper.ts +++ b/src/utils/webpackHelper.ts @@ -4,7 +4,7 @@ import * as rechoir from 'rechoir'; import findup from 'findup-sync'; import webpack from 'webpack'; -export const webpackConfigPath = (cwd: string) => { +export const defaultWebpackConfigPath = (cwd: string) => { const extensions = Object.keys(interpret.extensions); const defaultConfigFileNames = ['webpack.config', 'webpackfile']; const configFileRegExp = `(${defaultConfigFileNames.join( @@ -17,8 +17,16 @@ export const webpackConfigPath = (cwd: string) => { return configPath; }; -export const webpackConfig = (configPath: string, cwd?: string) => { - const reslvedConfigPath = cwd ? path.resolve(configPath, cwd) : configPath; +export const webpackConfig = (cwd: string, configPath?: string) => { + const reslvedConfigPath = (() => { + if (!configPath) { + return defaultWebpackConfigPath(cwd); + } + if (path.isAbsolute(configPath)) { + return path.resolve(configPath); + } + return path.resolve(cwd, configPath); + })(); // register module loaders rechoir.prepare(interpret.extensions, reslvedConfigPath); diff --git a/src/webpackCompile.ts b/src/webpackCompile.ts index 72270d6e..8dc2013e 100644 --- a/src/webpackCompile.ts +++ b/src/webpackCompile.ts @@ -1,19 +1,34 @@ import 'source-map-support/register'; +import argvParse from 'yargs-parser'; import webpack from 'webpack'; import * as webpackHelper from './utils/webpackHelper'; module.exports = (ctx: any) => new Promise((resolve, reject) => { + const platforms = ['browser', 'android', 'ios']; + if (!platforms.some(platform => ctx.opts.platforms.includes(platform))) { + resolve(); + return; + } + + const argv = argvParse(ctx.opts.options.argv.join(' ')); + if (argv.livereload || argv.l) { + resolve(); + return; + } + const customWebpackConfig: webpack.Configuration = webpackHelper.webpackConfig( - webpackHelper.webpackConfigPath(ctx.opts.projectRoot), + ctx.opts.projectRoot, + argv.webpackConfig || argv.w, ); const compiler = webpack(customWebpackConfig); compiler.run((err, stats) => { - if (err) { - reject(err); + if (err && err.message) { + console.log(err.message); + reject(); + return; } - console.log( stats.toString({ chunks: false, diff --git a/src/webpackServe.ts b/src/webpackServe.ts index 30b4d47d..78136e70 100644 --- a/src/webpackServe.ts +++ b/src/webpackServe.ts @@ -3,6 +3,7 @@ import * as path from 'path'; import * as fs from 'fs'; import * as glob from 'glob'; import * as ip from 'ip'; +import argvParse from 'yargs-parser'; import webpack from 'webpack'; import WebpackInjectPlugin from 'webpack-inject-plugin'; import WebpackDevServer from 'webpack-dev-server'; @@ -14,127 +15,123 @@ import CordovaConfigParser from './utils/CordovaConfigParser'; module.exports = (ctx: any) => new Promise(async (resolve, reject) => { const platforms = ['android', 'ios']; - const argv = ['--livereload', '-l']; - if (!platforms.some(platform => ctx.opts.platforms.includes(platform))) { + resolve(); return; } - if (!argv.some(val => ctx.opts.options.argv.includes(val))) { + + const argv = argvParse(ctx.opts.options.argv.join(' ')); + if (!argv.livereload && !argv.l) { + resolve(); return; } - try { - const defaultHost = '0.0.0.0'; - const defaultPort = 8080; - const defaultAccessHosts = { - android: '10.0.2.2', - ios: 'localhost', - }; + const defaultHost = '0.0.0.0'; + const defaultPort = 8080; + const defaultAccessHosts = { + android: '10.0.2.2', + ios: 'localhost', + }; - const port = await choosePort(defaultHost, defaultPort); - if (!port) { - resolve(); - return; - } + const port = await choosePort(defaultHost, defaultPort); + if (!port) { + resolve(); + return; + } - platforms.forEach(platform => { - glob - .sync( - `${path.join( - ctx.opts.projectRoot, - 'platforms', - platform, - )}/**/*/config.xml`, - ) - .forEach(configXmlPath => { - const configXml = new CordovaConfigParser(configXmlPath); - configXml.setContent( - `http://${ - platform === 'android' - ? ip.address() || defaultAccessHosts.android - : ip.address() || defaultAccessHosts.ios - }:${port}`, - ); - if (platform === 'ios') - configXml.setElement('allow-navigation', { href: '*' }); - configXml.write(); - }); - }); + platforms.forEach(platform => { + glob + .sync( + `${path.join( + ctx.opts.projectRoot, + 'platforms', + platform, + )}/**/*/config.xml`, + ) + .forEach(configXmlPath => { + const configXml = new CordovaConfigParser(configXmlPath); + configXml.setContent( + `http://${ + platform === 'android' + ? ip.address() || defaultAccessHosts.android + : ip.address() || defaultAccessHosts.ios + }:${port}`, + ); + if (platform === 'ios') + configXml.setElement('allow-navigation', { href: '*' }); + configXml.write(); + }); + }); - const customWebpackConfig: webpack.Configuration = webpackHelper.webpackConfig( - webpackHelper.webpackConfigPath(ctx.opts.projectRoot), - ); - const webpackConfig: webpack.Configuration = { - ...customWebpackConfig, - mode: 'development', - plugins: [ - ...(customWebpackConfig.plugins || []), - new WebpackInjectPlugin(() => - fs.readFileSync( - path.resolve(__dirname, '../scripts/www/injectCSP.js'), - 'utf8', - ), + const customWebpackConfig: webpack.Configuration = webpackHelper.webpackConfig( + ctx.opts.projectRoot, + argv.webpackConfig || argv.w, + ); + const webpackConfig: webpack.Configuration = { + ...customWebpackConfig, + mode: 'development', + plugins: [ + ...(customWebpackConfig.plugins || []), + new WebpackInjectPlugin(() => + fs.readFileSync( + path.resolve(__dirname, '../scripts/www/injectCSP.js'), + 'utf8', ), - new WebpackInjectPlugin(() => - fs.readFileSync( - path.resolve(__dirname, '../scripts/www/injectCordovaScript.js'), - 'utf8', - ), + ), + new WebpackInjectPlugin(() => + fs.readFileSync( + path.resolve(__dirname, '../scripts/www/injectCordovaScript.js'), + 'utf8', ), - ], - }; - - const platformWwwPaths = { - android: path.join( - ctx.opts.projectRoot, - 'platforms/android/platform_www', ), - ios: path.join(ctx.opts.projectRoot, 'platforms/ios/platform_www'), - }; + ], + }; - const customDevServerConfig: WebpackDevServer.Configuration = - webpackConfig.devServer || {}; - const devServerConfig: WebpackDevServer.Configuration = { - contentBase: path.join(ctx.opts.projectRoot, 'www'), - historyApiFallback: true, - host: defaultHost, - port, - watchContentBase: true, - ...customDevServerConfig, - hot: true, - before: (app, server) => { - if (customDevServerConfig.before) - customDevServerConfig.before(app, server); - (Object.keys(platformWwwPaths) as Array< - keyof typeof platformWwwPaths - >).forEach(platform => { - app.use(`/${platform}`, express.static(platformWwwPaths[platform])); - }); - }, - }; + const platformWwwPaths = { + android: path.join( + ctx.opts.projectRoot, + 'platforms/android/platform_www', + ), + ios: path.join(ctx.opts.projectRoot, 'platforms/ios/platform_www'), + }; - // HMR - WebpackDevServer.addDevServerEntrypoints(webpackConfig, devServerConfig); + const customDevServerConfig: WebpackDevServer.Configuration = + webpackConfig.devServer || {}; + const devServerConfig: WebpackDevServer.Configuration = { + contentBase: path.join(ctx.opts.projectRoot, 'www'), + historyApiFallback: true, + host: defaultHost, + port, + watchContentBase: true, + ...customDevServerConfig, + hot: true, + before: (app, server) => { + if (customDevServerConfig.before) + customDevServerConfig.before(app, server); + (Object.keys(platformWwwPaths) as Array< + keyof typeof platformWwwPaths + >).forEach(platform => { + app.use(`/${platform}`, express.static(platformWwwPaths[platform])); + }); + }, + }; - const compiler = webpack(webpackConfig); - const server = new WebpackDevServer(compiler, devServerConfig); - server.listen( - devServerConfig.port || defaultPort, - devServerConfig.host || defaultHost, - err => { - if (err && err.message) { - console.log(err.message); - reject(); - return; - } - console.log('Starting the development server...\n'); - resolve(); - }, - ); - } catch (err) { - if (err && err.message) { - console.log(err.message); - reject(); - } - } + // HMR + WebpackDevServer.addDevServerEntrypoints(webpackConfig, devServerConfig); + + const compiler = webpack(webpackConfig); + const server = new WebpackDevServer(compiler, devServerConfig); + server.listen( + devServerConfig.port || defaultPort, + devServerConfig.host || defaultHost, + err => { + if (err && err.message) { + console.log(err.message); + reject(); + return; + } + console.log('Starting the development server...\n'); + resolve(); + }, + ); });