在第一篇分析咱們曾經舉例,建立一個新工程,javascript
cordova create hello hellotest com.xxx.hellotesthtml
cli.js文件分析命令行參數後,會走到java
else if (cmd == 'create' || cmd == 'serve') {android
cordova[cmd].apply(this, tokens);ios
}git
將會執行create函數web
create.jsshell
var path = require('path'), fs = require('fs'), shell = require('shelljs'), platforms = require('../platforms'), help = require('./help'), events = require('./events'), config = require('./config'), lazy_load = require('./lazy_load'), util = require('./util'); var DEFAULT_NAME = "HelloCordova", DEFAULT_ID = "io.cordova.hellocordova"; /** * Usage: * create(dir) - creates in the specified directory * create(dir, name) - as above, but with specified name * create(dir, id, name) - you get the gist **/ module.exports = function create (dir, id, name, callback) { var options = []; if (arguments.length === 0) { return help();//src/help.js 讀取doc/help.txt內容,在終端顯示幫助信息 } // Massage parameters var args = Array.prototype.slice.call(arguments, 0); //arguments不是數組,可是能夠經過arguments.length取到長度 //Array.prototype.slice.call(arguments,0)就相似於arguments.slice(0), //但由於arguments不是真正的Array,因此它沒有slice這個方法.能用slice方法的,只要有length屬性就行。 //Array.prototype已經被call改爲arguments了,由於知足slice執行的條件(有length屬性),因此沒有報錯。 //簡單說做用就是:把arguments這個僞數組轉換爲真正的數組 if (typeof args[args.length-1] == 'function') { callback = args.pop(); } else if (typeof callback !== 'function') { callback = undefined; } //判斷是否有回調函數 if (args.length === 0) { dir = process.cwd();//得到當前路徑 id = DEFAULT_ID; name = DEFAULT_NAME; } else if (args.length == 1) { id = DEFAULT_ID; name = DEFAULT_NAME; } else if (args.length == 2) { name = DEFAULT_NAME; } else { dir = args.shift();//shift() 方法用於把數組的第一個元素從其中刪除,並返回第一個元素的值 id = args.shift(); name = args.shift(); options = args; } // Make absolute. dir = path.resolve(dir); events.emit('log', 'Creating a new cordova project with name "' + name + '" and id "' + id + '" at location "' + dir + '"'); var dotCordova = path.join(dir, '.cordova'); var www_dir = path.join(dir, 'www'); // Create basic project structure. shell.mkdir('-p', dotCordova); shell.mkdir('-p', path.join(dir, 'platforms')); shell.mkdir('-p', path.join(dir, 'merges')); shell.mkdir('-p', path.join(dir, 'plugins')); shell.mkdir('-p', www_dir); var hooks = path.join(dotCordova, 'hooks'); shell.mkdir('-p', hooks); //建立一系列工做目錄 //當前dir/.cordova , dir/www , dir/platforms, dir/merges ,dir/plugins ,dir/.cordova/hooks // Add directories for hooks shell.mkdir(path.join(hooks, 'after_build')); shell.mkdir(path.join(hooks, 'after_compile')); shell.mkdir(path.join(hooks, 'after_docs')); shell.mkdir(path.join(hooks, 'after_emulate')); shell.mkdir(path.join(hooks, 'after_platform_add')); shell.mkdir(path.join(hooks, 'after_platform_rm')); shell.mkdir(path.join(hooks, 'after_platform_ls')); shell.mkdir(path.join(hooks, 'after_plugin_add')); shell.mkdir(path.join(hooks, 'after_plugin_ls')); shell.mkdir(path.join(hooks, 'after_plugin_rm')); shell.mkdir(path.join(hooks, 'after_prepare')); shell.mkdir(path.join(hooks, 'after_run')); shell.mkdir(path.join(hooks, 'before_build')); shell.mkdir(path.join(hooks, 'before_compile')); shell.mkdir(path.join(hooks, 'before_docs')); shell.mkdir(path.join(hooks, 'before_emulate')); shell.mkdir(path.join(hooks, 'before_platform_add')); shell.mkdir(path.join(hooks, 'before_platform_rm')); shell.mkdir(path.join(hooks, 'before_platform_ls')); shell.mkdir(path.join(hooks, 'before_plugin_add')); shell.mkdir(path.join(hooks, 'before_plugin_ls')); shell.mkdir(path.join(hooks, 'before_plugin_rm')); shell.mkdir(path.join(hooks, 'before_prepare')); shell.mkdir(path.join(hooks, 'before_run')); // Write out .cordova/config.json file with a simple json manifest //使用指定id和name參數寫入config.json require('../cordova').config(dir, { id:id, name:name }); var config_json = config.read(dir); //讀取.cordova/config.json //finalize函數做用:判斷下載的tar包是否完整,刪除原有www目錄下內容,拷貝新內容到www目錄,添加配置文件 var finalize = function(www_lib) { while (!fs.existsSync(path.join(www_lib, 'index.html'))) { www_lib = path.join(www_lib, 'www'); if (!fs.existsSync(www_lib)) { var err = new Error('downloaded www assets in ' + www_lib + ' does not contain index.html, or www subdir with index.html'); if (callback) return callback(err); else throw err; } } //刪除當前www目錄下因此文件 shell.cp('-rf', path.join(www_lib, '*'), www_dir); var configPath = util.projectConfig(dir); //得到config.xml路徑 www/config.xml // Add template config.xml for apps that are missing it if (!fs.existsSync(configPath)) { var template_config_xml = path.join(__dirname, '..', 'templates', 'config.xml'); shell.cp(template_config_xml, www_dir); } //若是config.xml不存在,從cli模板目錄拷貝此文件到當前應用目錄 // Write out id and name to config.xml id和name寫入config.xml var config = new util.config_parser(configPath); config.packageName(id); config.name(name); if (callback) callback(); }; // Check if www assets to use was overridden. if (config_json.lib && config_json.lib.www) { //判斷config.josn的lib和www字段是否爲空 events.emit('log', 'Using custom www assets ('+config_json.lib.www.id+').'); //下載config.json中uri地址的hello-world文件包 lazy_load.custom(config_json.lib.www.uri, config_json.lib.www.id, 'www', config_json.lib.www.version, function(err) { if (err) { if (callback) callback(err); else throw err; } else { events.emit('log', 'Copying custom www assets into "' + www_dir + '"'); //下載成功後拷貝到指定目錄 finalize(path.join(util.libDirectory, 'www', config_json.lib.www.id, config_json.lib.www.version)); } }); } else { // Nope, so use stock cordova-hello-world-app one. events.emit('log', 'Using stock cordova hello-world application.'); //根據platform.js中參數下載默認文件 lazy_load.cordova('www', function(err) { if (err) { if (callback) callback(err); else throw err; } else { events.emit('log', 'Copying stock Cordova www assets into "' + www_dir + '"'); finalize(path.join(util.libDirectory, 'www', 'cordova', platforms.www.version)); } }); } };
1)參數解析,建立工做目錄:參數存在狀況下,按照dir, id, name,這幾個參數指定內容建立目錄,並將id和name參數寫入config.json文件。apache
hello/npm
|--.cordova/
| | -- hooks/
| | -- config.json
|-- merges/
|-- www/
| `-- config.xml
|-- platforms/
`-- plugins/
在.cordova/hooks目錄下建立了不少以事件名稱命名的目錄,如after_build ,after_compile等,在這些目錄下,用戶能夠添加自定義文件,當這些系統事件發生時,這些目錄下的文件至關於回調函數,會被執行
這部分還涉及到config.js文件,其中定義了對config.json文件讀寫操做的函數
2)判斷config.josn的lib和www字段是否爲空,非空時,按照.cordova/config_json中指定的參數下載默認的web頁面部分文件(即www目錄下內容),下載成功後會調用finalize函數,該函數會刪除原www目錄下文件,拷貝下載的文件到www目錄。
3)lazy_load 函數是提供下載功能的接口文件(見src/lazy_load.js),主要包含兩個函數
lazy_load.cordova
lazy_load.custom
lazy_load.js
module.exports = { cordova:function lazy_load(platform, callback) { if (!(platform in platforms)) { var err = new Error('Cordova library "' + platform + '" not recognized.'); if (callback) return callback(err); else throw err; } var url = platforms[platform].url + ';a=snapshot;h=' + platforms[platform].version + ';sf=tgz'; module.exports.custom(url, 'cordova', platform, platforms[platform].version, function(err) { if (err) { if (callback) return callback(err); else throw err; } else { if (callback) callback(); } }); }, custom:function(url, id, platform, version, callback) { var download_dir = (platform == 'wp7' || platform == 'wp8' ? path.join(util.libDirectory, 'wp', id, version) : path.join(util.libDirectory, platform, id, version)); if (fs.existsSync(download_dir)) { events.emit('log', id + ' library for "' + platform + '" already exists. No need to download. Continuing.'); if (callback) return callback(); } hooker.fire('before_library_download', { platform:platform, url:url, id:id, version:version }, function() { var uri = URL.parse(url); if (uri.protocol && uri.protocol[1] != ':') { // second part of conditional is for awesome windows support. fuuu windows npm.load(function() { // Check if NPM proxy settings are set. If so, include them in the request() call. var proxy; if (uri.protocol == 'https:') { proxy = npm.config.get('https-proxy'); } else if (uri.protocol == 'http:') { proxy = npm.config.get('proxy'); } shell.mkdir('-p', download_dir); var size = 0; var request_options = {uri:url}; if (proxy) { request_options.proxy = proxy; } events.emit('log', 'Requesting ' + JSON.stringify(request_options) + '...'); request.get(request_options, function(err, req, body) { size = body.length; }) .pipe(zlib.createUnzip()) .pipe(tar.Extract({path:download_dir})) .on('error', function(err) { shell.rm('-rf', download_dir); if (callback) callback(err); else throw err; }) .on('end', function() { events.emit('log', 'Downloaded, unzipped and extracted ' + size + ' byte response.'); var entries = fs.readdirSync(download_dir); var entry = path.join(download_dir, entries[0]); shell.mv('-f', path.join(entry, (platform=='blackberry10'?'blackberry10':''), '*'), download_dir); shell.rm('-rf', entry); hooker.fire('after_library_download', { platform:platform, url:url, id:id, version:version, path:download_dir, size:size, symlink:false }, function() { if (callback) callback(); }); }); }); } else { // local path // symlink instead of copying fs.symlinkSync((uri.protocol && uri.protocol[1] == ':' ? uri.href : uri.path), download_dir, 'dir'); hooker.fire('after_library_download', { platform:platform, url:url, id:id, version:version, path:download_dir, symlink:true }, function() { if (callback) callback(); }); } }); }, based_on_config:function(project_root, platform, callback) { var custom_path = config.has_custom_path(project_root, platform); if (custom_path) { var dot_file = config.read(project_root); module.exports.custom(dot_file.lib[platform].uri, dot_file.lib[platform].id, platform, dot_file.lib[platform].version, callback); } else { module.exports.cordova(platform, callback); } } };
lazy_load.cordova首先查找傳入的platform參數是否在頂層目錄下platforms.js文件中的對象參數中是否存在;
platforms.js
module.exports = { 'ios' : { parser : require('./src/metadata/ios_parser'), url : 'https://git-wip-us.apache.org/repos/asf?p=cordova-ios.git', version: '3.0.0' }, 'android' : { parser : require('./src/metadata/android_parser'), url : 'https://git-wip-us.apache.org/repos/asf?p=cordova-android.git', version: '3.0.0' }, 'wp7' : { parser : require('./src/metadata/wp7_parser'), url : 'https://git-wip-us.apache.org/repos/asf?p=cordova-wp8.git', version: '3.0.0' }, 'wp8' : { parser : require('./src/metadata/wp8_parser'), url : 'https://git-wip-us.apache.org/repos/asf?p=cordova-wp8.git', version: '3.0.0' }, blackberry10 : { parser : require('./src/metadata/blackberry10_parser'), url : 'https://git-wip-us.apache.org/repos/asf?p=cordova-blackberry.git', version: '3.0.0' }, 'www':{ url : 'https://git-wip-us.apache.org/repos/asf?p=cordova-app-hello-world.git', version: '3.0.0' } };
platforms.js中列出了全部cordova支持的平臺,每一個平臺包括三個參數:
parser : 解析平臺配置參數的解析文件位置;
url : 源碼存在的git路徑
version:版本號
lazy_load.cordova會按照傳入參數在這個表中找到對應url下載文件。
lazy_load.custom與lazy_load.cordova不一樣之處是,從.cordova/config_json中指定的url參數下載文件
shelljs,create函數中屢次用到shelljs,它是一個可移植的相似unix shell的命令行工具,支持Windows/Linux/OS X,詳見https://npmjs.org/package/shelljs