From 262f2adb3090927c157712a9d0dc5e36873bf7d6 Mon Sep 17 00:00:00 2001 From: Avi Haiat Date: Wed, 6 Apr 2016 17:55:17 +0100 Subject: [PATCH] feat(app): Add hooks for ionic --- README.md | 2 + generators/app/templates/_package.json | 4 + generators/app/templates/webpack.config.js | 17 +++- generators/target/index.js | 29 ++++++ .../after_platform_add/010_install_plugins.js | 32 ++++++ .../after_plugin_add/010_register_plugin.js | 60 ++++++++++++ .../after_plugin_rm/010_deregister_plugin.js | 31 ++++++ .../after_prepare/010_add_platform_class.js | 98 +++++++++++++++++++ .../020_remove_sass_from_platforms.js | 32 ++++++ .../before_platform_add/init_directories.js | 27 +++++ .../target/templates/ionic.package.json | 2 + test/mocha/target.spec.js | 11 ++- 12 files changed, 338 insertions(+), 7 deletions(-) create mode 100755 generators/target/templates/hooks/after_platform_add/010_install_plugins.js create mode 100755 generators/target/templates/hooks/after_plugin_add/010_register_plugin.js create mode 100755 generators/target/templates/hooks/after_plugin_rm/010_deregister_plugin.js create mode 100755 generators/target/templates/hooks/after_prepare/010_add_platform_class.js create mode 100755 generators/target/templates/hooks/after_prepare/020_remove_sass_from_platforms.js create mode 100755 generators/target/templates/hooks/before_platform_add/init_directories.js create mode 100644 generators/target/templates/ionic.package.json diff --git a/README.md b/README.md index e091c95..da34a49 100644 --- a/README.md +++ b/README.md @@ -51,6 +51,8 @@ You have access to the following npm scripts * **npm run karma:watch** (run unit test in watch mode) * **npm run e2e** (run e2e test - make sure that browsersync or webpack:server is running in another console window) * **npm run e2e:live** (run e2e test and stop to allow debug - make sure that browsersync or webpack:server is running in another console window) +* **npm run ionic:serve** (run an ionic2 app in the browser) +* **npm run ionic:emulate** (run an ionic2 app in the emulator) ## Distribution Note that the code is distributed to the `dist` folder diff --git a/generators/app/templates/_package.json b/generators/app/templates/_package.json index 9035ea3..f7569d8 100644 --- a/generators/app/templates/_package.json +++ b/generators/app/templates/_package.json @@ -28,6 +28,9 @@ "pree2e": "npm run webdriver:update -- --standalone", "e2e": "ts-node node_modules/.bin/protractor", "e2e:live": "protractor --elementExplorer", + "ionic:serve": "npm run watch & cd dist/${TARGET-app}/${MODE-dev} && ionic serve", + "ionic:serve:lab": "npm run watch & cd dist/${TARGET-app}/${MODE-dev} && ionic serve --lab", + "ionic:emulate": "npm run watch & cd dist/${TARGET-app}/${MODE-dev} && ionic emulate", "fuse:preview": "cd dist/${TARGET-app}/${MODE-dev} && fuse preview", "fuse:build:ios": "cd dist/${TARGET-app}/${MODE-dev} && fuse build --target=iOS --run", "fuse:build:android": "cd dist/${TARGET-app}/${MODE-dev} && fuse build --target=Android --run", @@ -61,6 +64,7 @@ "file-loader": "^0.8.5", "html-loader": "^0.4.3", "html-webpack-plugin": "^2.9.0", + "ionic": "^2.0.0-beta.24", "istanbul-instrumenter-loader": "^0.2.0", "jasmine-spec-reporter": "^2.4.0", "json-loader": "^0.5.4", diff --git a/generators/app/templates/webpack.config.js b/generators/app/templates/webpack.config.js index ba9ac73..0da1f64 100644 --- a/generators/app/templates/webpack.config.js +++ b/generators/app/templates/webpack.config.js @@ -67,8 +67,7 @@ module.exports = { }, output: { - path: path.resolve(distFolder), - + path: isTargetIonic2(target) ? path.resolve(path.join(distFolder, 'www')) : path.resolve(distFolder), filename: '[name].js', sourceMapFilename: '[name].js.map', chunkFilename: '[id].chunk.js', @@ -270,10 +269,20 @@ module.exports = { from: './**/*.uno' }] : []) .concat(isTargetIonic2(target) ? [{ - from: './config.xml' + from: './config.xml', + to: '../config.xml' // we need to go up one folder in the case of ionic2 + }] : []) + .concat(isTargetIonic2(target) ? [{ + from: './ionic.config.json', + to: '../ionic.config.json' // we need to go up one folder in the case of ionic2 + }] : []) + .concat(isTargetIonic2(target) ? [{ + from: './package.json', + to: '../package.json' // we need to go up one folder in the case of ionic2 }] : []) .concat(isTargetIonic2(target) ? [{ - from: './ionic.config.json' + from: './hooks', + to: '../hooks' // we need to go up one folder in the case of ionic2 }] : []) ), diff --git a/generators/target/index.js b/generators/target/index.js index e5567bd..ee23556 100644 --- a/generators/target/index.js +++ b/generators/target/index.js @@ -133,6 +133,10 @@ module.exports = generators.Base.extend({ appname: this.config.get('appname') } ); + this.fs.copy( + this.templatePath('ionic.package.json'), + this.destinationPath(path.join(this.configOptions.clientFolder, 'scripts', this.targetname, 'package.json')) + ); this.fs.copyTpl( this.templatePath('config.ionic2.xml'), this.destinationPath(path.join(this.configOptions.clientFolder, 'scripts', this.targetname, 'config.xml')), { @@ -147,6 +151,31 @@ module.exports = generators.Base.extend({ this.templatePath('main.ionic2.scss'), this.destinationPath(path.join(this.configOptions.clientFolder, 'scripts', this.targetname, 'main.scss')) ); + this.fs.copy( + this.templatePath('hooks/after_platform_add/010_install_plugins.js'), + this.destinationPath(path.join(this.configOptions.clientFolder, 'scripts', this.targetname, 'hooks', 'after_platform_add', '010_install_plugins.js')) + ); + this.fs.copy( + this.templatePath('hooks/after_plugin_add/010_register_plugin.js'), + this.destinationPath(path.join(this.configOptions.clientFolder, 'scripts', this.targetname, 'hooks', 'after_plugin_add', '010_register_plugin.js')) + ); + this.fs.copy( + this.templatePath('hooks/after_plugin_rm/010_deregister_plugin.js'), + this.destinationPath(path.join(this.configOptions.clientFolder, 'scripts', this.targetname, 'hooks', 'after_plugin_rm', '010_deregister_plugin.js')) + ); + this.fs.copy( + this.templatePath('hooks/after_prepare/010_add_platform_class.js'), + this.destinationPath(path.join(this.configOptions.clientFolder, 'scripts', this.targetname, 'hooks', 'after_prepare', '010_add_platform_class.js')) + ); + this.fs.copy( + this.templatePath('hooks/after_prepare/020_remove_sass_from_platforms.js'), + this.destinationPath(path.join(this.configOptions.clientFolder, 'scripts', this.targetname, 'hooks', 'after_prepare', '020_remove_sass_from_platforms.js')) + ); + + this.fs.copy( + this.templatePath('hooks/before_platform_add/init_directories.js'), + this.destinationPath(path.join(this.configOptions.clientFolder, 'scripts', this.targetname, 'hooks', 'before_platform_add', 'init_directories.js')) + ); break; case 'fuse': diff --git a/generators/target/templates/hooks/after_platform_add/010_install_plugins.js b/generators/target/templates/hooks/after_platform_add/010_install_plugins.js new file mode 100755 index 0000000..4f643b4 --- /dev/null +++ b/generators/target/templates/hooks/after_platform_add/010_install_plugins.js @@ -0,0 +1,32 @@ +#!/usr/bin/env node + +/*eslint-disable */ + +/** + * Install all plugins listed in package.json + * https://raw.githubusercontent.com/diegonetto/generator-ionic/master/templates/hooks/after_platform_add/install_plugins.js + */ +var exec = require('child_process').exec; +var path = require('path'); +var sys = require('sys'); + +var packageJSON = null; + +try { + packageJSON = require('../../package.json'); +} catch(ex) { + console.log('\nThere was an error fetching your package.json file.') + console.log('\nPlease ensure a valid package.json is in the root of this project\n') + return; +} + +var cmd = process.platform === 'win32' ? 'cordova.cmd' : 'cordova'; +// var script = path.resolve(__dirname, '../../node_modules/cordova/bin', cmd); + +packageJSON.cordovaPlugins = packageJSON.cordovaPlugins || []; +packageJSON.cordovaPlugins.forEach(function (plugin) { + exec('cordova plugin add ' + plugin, function (error, stdout, stderr) { + sys.puts(stdout); + }); +}); +/*eslint-enable */ \ No newline at end of file diff --git a/generators/target/templates/hooks/after_plugin_add/010_register_plugin.js b/generators/target/templates/hooks/after_plugin_add/010_register_plugin.js new file mode 100755 index 0000000..1cf344d --- /dev/null +++ b/generators/target/templates/hooks/after_plugin_add/010_register_plugin.js @@ -0,0 +1,60 @@ +#!/usr/bin/env node + +/*eslint-disable */ +/** + * Push plugins to cordovaPlugins array after_plugin_add + */ +var fs = require('fs'), + packageJSON = require('../../package.json'), + path = require('path'); + +packageJSON.cordovaPlugins = packageJSON.cordovaPlugins || []; +process.env.CORDOVA_PLUGINS.split(',').forEach(function (plugin) { + var configString, + idRegEx, + id, + pluginXmlPath, + pluginToAdd; + + if(plugin.indexOf('https') != -1 || plugin.indexOf('git') != -1) { + console.log('Installing plugin from url'); + } + + if(plugin.indexOf('/') != -1) { + try { + pluginXmlPath = path.resolve(plugin, 'plugin.xml'); + console.log('got pluginXmlPath:', pluginXmlPath); + if (!fs.existsSync(pluginXmlPath)) { + var errorMessage = ['There was no plugin.xml file found for path: ', pluginXmlPath].join(''); + return; + } + + configString = fs.readFileSync(pluginXmlPath,{encoding: 'utf8'}); + idRegEx = new RegExp(']*id="(.*)"', 'i'); + id = idRegEx.exec(configString)[1] + pluginToAdd = {id: id, locator: plugin}; + } catch(ex) { + console.log('There was an error retrieving the plugin.xml filr from the 010_register_plugin.js hook', ex); + } + } else { + pluginToAdd = plugin; + } + + if(typeof pluginToAdd == 'string' && packageJSON.cordovaPlugins.indexOf(pluginToAdd) == -1) { + packageJSON.cordovaPlugins.push(pluginToAdd); + } else if (typeof pluginToAdd == 'object') { + var pluginExists = false; + packageJSON.cordovaPlugins.forEach(function(checkPlugin) { + if(typeof checkPlugin == 'object' && checkPlugin.id == pluginToAdd.id) { + pluginExists = true; + } + }) + if(!pluginExists) { + packageJSON.cordovaPlugins.push(pluginToAdd); + } + } +}); + +fs.writeFileSync('package.json', JSON.stringify(packageJSON, null, 2)); + +/*eslint-enable */ diff --git a/generators/target/templates/hooks/after_plugin_rm/010_deregister_plugin.js b/generators/target/templates/hooks/after_plugin_rm/010_deregister_plugin.js new file mode 100755 index 0000000..ee33ab3 --- /dev/null +++ b/generators/target/templates/hooks/after_plugin_rm/010_deregister_plugin.js @@ -0,0 +1,31 @@ +#!/usr/bin/env node + +/*eslint-disable */ + +/** + * Remove plugins from cordovaPlugins array after_plugin_rm + */ +var fs = require('fs'); +var packageJSON = require('../../package.json'); + +packageJSON.cordovaPlugins = packageJSON.cordovaPlugins || []; + +process.env.CORDOVA_PLUGINS.split(',').forEach(function (plugin) { + var index = packageJSON.cordovaPlugins.indexOf(plugin); + if (index > -1) { + packageJSON.cordovaPlugins.splice(index, 1); + } else { + //If it didnt find a match, it may be listed as {id,locator} + for(var i = 0, j = packageJSON.cordovaPlugins.length; i < j; i++) { + var packagePlugin = packageJSON.cordovaPlugins[i]; + if(typeof packagePlugin == 'object' && packagePlugin.id == plugin) { + packageJSON.cordovaPlugins.splice(index, 1); + break; + } + } + } +}); + +fs.writeFile('package.json', JSON.stringify(packageJSON, null, 2)); + +/*eslint-enable */ \ No newline at end of file diff --git a/generators/target/templates/hooks/after_prepare/010_add_platform_class.js b/generators/target/templates/hooks/after_prepare/010_add_platform_class.js new file mode 100755 index 0000000..2d96f8d --- /dev/null +++ b/generators/target/templates/hooks/after_prepare/010_add_platform_class.js @@ -0,0 +1,98 @@ +#!/usr/bin/env node + +/*eslint-disable */ + +// Add Platform Class +// v1.0 +// Automatically adds the platform class to the body tag +// after the `prepare` command. By placing the platform CSS classes +// directly in the HTML built for the platform, it speeds up +// rendering the correct layout/style for the specific platform +// instead of waiting for the JS to figure out the correct classes. + +var fs = require('fs'); +var path = require('path'); + +var rootdir = process.argv[2]; + +function addPlatformBodyTag(indexPath, platform) { + // add the platform class to the body tag + try { + var platformClass = 'platform-' + platform; + var cordovaClass = 'platform-cordova platform-webview'; + + var html = fs.readFileSync(indexPath, 'utf8'); + + var bodyTag = findBodyTag(html); + if (!bodyTag) return; // no opening body tag, something's wrong + + if (bodyTag.indexOf(platformClass) > -1) return; // already added + + var newBodyTag = bodyTag; + + var classAttr = findClassAttr(bodyTag); + if (classAttr) { + // body tag has existing class attribute, add the classname + var endingQuote = classAttr.substring(classAttr.length - 1); + var newClassAttr = classAttr.substring(0, classAttr.length - 1); + newClassAttr += ' ' + platformClass + ' ' + cordovaClass + endingQuote; + newBodyTag = bodyTag.replace(classAttr, newClassAttr); + + } else { + // add class attribute to the body tag + newBodyTag = bodyTag.replace('>', ' class="' + platformClass + ' ' + cordovaClass + '">'); + } + + html = html.replace(bodyTag, newBodyTag); + + fs.writeFileSync(indexPath, html, 'utf8'); + + process.stdout.write('add to body class: ' + platformClass + '\n'); + } catch (e) { + process.stdout.write(e); + } +} + +function findBodyTag(html) { + // get the body tag + try { + return html.match(/])(.*?)>/gi)[0]; + } catch (e) {} +} + +function findClassAttr(bodyTag) { + // get the body tag's class attribute + try { + return bodyTag.match(/ class=["|'](.*?)["|']/gi)[0]; + } catch (e) {} +} + +if (rootdir) { + + // go through each of the platform directories that have been prepared + var platforms = (process.env.CORDOVA_PLATFORMS ? process.env.CORDOVA_PLATFORMS.split(',') : []); + + for (var x = 0; x < platforms.length; x++) { + // open up the index.html file at the www root + try { + var platform = platforms[x].trim().toLowerCase(); + var indexPath; + + if (platform == 'android') { + indexPath = path.join('platforms', platform, 'assets', 'www', 'index.html'); + } else { + indexPath = path.join('platforms', platform, 'www', 'index.html'); + } + + if (fs.existsSync(indexPath)) { + addPlatformBodyTag(indexPath, platform); + } + + } catch (e) { + process.stdout.write(e); + } + } + +} + +/*eslint-enable */ \ No newline at end of file diff --git a/generators/target/templates/hooks/after_prepare/020_remove_sass_from_platforms.js b/generators/target/templates/hooks/after_prepare/020_remove_sass_from_platforms.js new file mode 100755 index 0000000..c9fb2fb --- /dev/null +++ b/generators/target/templates/hooks/after_prepare/020_remove_sass_from_platforms.js @@ -0,0 +1,32 @@ +#!/usr/bin/env node + +/*eslint-disable */ + +/** + * After prepare, files are copied to the platforms/ios and platforms/android folders. + * Lets clean up some of those files that arent needed with this hook. + */ +var fs = require('fs'); +var path = require('path'); + +var deleteFolderRecursive = function(removePath) { + if( fs.existsSync(removePath) ) { + fs.readdirSync(removePath).forEach(function(file,index){ + var curPath = path.join(removePath, file); + if(fs.lstatSync(curPath).isDirectory()) { // recurse + deleteFolderRecursive(curPath); + } else { // delete file + fs.unlinkSync(curPath); + } + }); + fs.rmdirSync(removePath); + } +}; + +var iosPlatformsDir = path.resolve(__dirname, '../../platforms/ios/www/lib/ionic/scss'); +var androidPlatformsDir = path.resolve(__dirname, '../../platforms/android/assets/www/lib/ionic/scss'); + +deleteFolderRecursive(iosPlatformsDir); +deleteFolderRecursive(androidPlatformsDir); + +/*eslint-enable */ \ No newline at end of file diff --git a/generators/target/templates/hooks/before_platform_add/init_directories.js b/generators/target/templates/hooks/before_platform_add/init_directories.js new file mode 100755 index 0000000..12b7059 --- /dev/null +++ b/generators/target/templates/hooks/before_platform_add/init_directories.js @@ -0,0 +1,27 @@ +#!/usr/bin/env node + +/*eslint-disable */ + +/** + * On a fresh clone, the local platforms/ and plugins/ directories will be + * missing, so ensure they get created before the first platform is added. + */ +var fs = require('fs'); +var path = require('path'); + +var platformsDir = path.resolve(__dirname, '../../platforms'); +var pluginsDir = path.resolve(__dirname, '../../plugins'); + +try { + fs.mkdirSync(platformsDir, function (err) { + if (err) { console.error(err); } + }); +} catch(ex) {} + +try { + fs.mkdirSync(pluginsDir, function (err) { + if (err) { console.error(err); } + }); +} catch(ex) {} + +/*eslint-enable */ \ No newline at end of file diff --git a/generators/target/templates/ionic.package.json b/generators/target/templates/ionic.package.json new file mode 100644 index 0000000..7a73a41 --- /dev/null +++ b/generators/target/templates/ionic.package.json @@ -0,0 +1,2 @@ +{ +} \ No newline at end of file diff --git a/test/mocha/target.spec.js b/test/mocha/target.spec.js index cdbf6cc..f27d635 100644 --- a/test/mocha/target.spec.js +++ b/test/mocha/target.spec.js @@ -127,8 +127,16 @@ describe(generatorShortname + ':target', function() { path.join(clientFolder, 'scripts', 'mobile-ionic', 'main.scss'), path.join(clientFolder, 'scripts', 'mobile-ionic', 'config.xml'), path.join(clientFolder, 'scripts', 'mobile-ionic', 'ionic.config.json'), + path.join(clientFolder, 'scripts', 'mobile-ionic', 'package.json'), path.join(clientFolder, 'scripts', 'mobile-ionic', 'vendor.ts'), path.join(clientFolder, 'scripts', 'mobile-ionic', 'bootstrap.ts'), + path.join(clientFolder, 'scripts', 'mobile-ionic', 'hooks', 'after_platform_add', '010_install_plugins.js'), + path.join(clientFolder, 'scripts', 'mobile-ionic', 'hooks', 'after_plugin_add', '010_register_plugin.js'), + path.join(clientFolder, 'scripts', 'mobile-ionic', 'hooks', 'after_plugin_rm', '010_deregister_plugin.js'), + path.join(clientFolder, 'scripts', 'mobile-ionic', 'hooks', 'after_prepare', '010_add_platform_class.js'), + path.join(clientFolder, 'scripts', 'mobile-ionic', 'hooks', 'after_prepare', '020_remove_sass_from_platforms.js'), + path.join(clientFolder, 'scripts', 'mobile-ionic', 'hooks', 'before_platform_add', 'init_directories.js'), + path.join(clientFolder, 'scripts', 'mobile-ionic', 'hooks', 'after_prepare', '010_add_platform_class.js'), path.join('test', 'e2e', 'mobile-ionic', 'mobile-ionic.e2e.ts'), path.join('test', 'e2e', 'mobile-ionic', 'index.e2e.ts') ]; @@ -140,9 +148,6 @@ describe(generatorShortname + ':target', function() { [path.join(clientFolder, 'scripts', 'mobile-ionic', 'config.xml'), /href="http:\/\/dummyappname\.com"/], [path.join(clientFolder, 'scripts', 'mobile-ionic', 'config.xml'), /dummyappname Team/], [path.join(clientFolder, 'scripts', 'mobile-ionic', 'ionic.config.json'), /"name": "dummyappname"/] - //[path.join(clientFolder, 'scripts', 'mobile-ionic', 'vendor.ts'), /fuse_polyfills/], - //[path.join(clientFolder, 'scripts', 'mobile-ionic', 'bootstrap.ts'), /fuse\/bootstrap/], - //[path.join(clientFolder, 'scripts', 'mobile-ionic', 'index.unoproj'), /"ApplicationLabel": "dummyappname"/] ]; assert.fileContent(expectedContents);