在第一篇分析我们曾经举例,创建一个新工程,

cordova create hello hellotest com.xxx.hellotest

cli.js文件分析命令行参数后,会走到

else if (cmd == 'create' || cmd == 'serve') {

cordova[cmd].apply(this, tokens);

}

将会执行create函数

create.js

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文件。

hello/

|--.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

Cordova CLI源码分析(四)——创建工程的更多相关文章

  1. Cordova CLI源码分析(三)——初始化

    本部分主要涉及以下三个文件 1 cli.js 2 cordova.js 3 events.js 通过前一篇package.json的分析,可以知道,当命令行执行cordova相关命令时,首先调用mai ...

  2. Cordova CLI源码分析(一)——简介

    本系列文章分析基于node.js的命令行工具Cordova CLI,所以如果对node.js基础不是很了解,建议参考http://nodejs.gamesys.net/node-js提供的基础教程 文 ...

  3. Cordova CLI源码分析(二)——package.json

    每个包需要在其顶层目录下包含一个package.json文件,该文件不仅是包的说明,也影响npm安装包时的配置选项 更多参数详见参考文档https://npmjs.org/doc/json.html ...

  4. 使用react全家桶制作博客后台管理系统 网站PWA升级 移动端常见问题处理 循序渐进学.Net Core Web Api开发系列【4】:前端访问WebApi [Abp 源码分析]四、模块配置 [Abp 源码分析]三、依赖注入

    使用react全家桶制作博客后台管理系统   前面的话 笔者在做一个完整的博客上线项目,包括前台.后台.后端接口和服务器配置.本文将详细介绍使用react全家桶制作的博客后台管理系统 概述 该项目是基 ...

  5. ABP源码分析四:Configuration

    核心模块的配置 Configuration是ABP中设计比较巧妙的地方.其通过AbpStartupConfiguration,Castle的依赖注入,Dictionary对象和扩展方法很巧妙的实现了配 ...

  6. docker 源码分析 四(基于1.8.2版本),Docker镜像的获取和存储

    前段时间一直忙些其他事情,docker源码分析的事情耽搁了,今天接着写,上一章了解了docker client 和 docker daemon(会启动一个http server)是C/S的结构,cli ...

  7. ABP源码分析四十二:ZERO的身份认证

    ABP Zero模块通过自定义实现Asp.Net Identity完成身份认证功能, 对Asp.Net Identity做了较大幅度的扩展.同时重写了ABP核心模块中的permission功能,以实现 ...

  8. ABP源码分析四十五:ABP ZERO中的EntityFramework模块

    AbpZeroDbContext:配置ABP.Zero中定义的entity的Dbset EntityFrameworkModelBuilderExtensions:给PrimitiveProperty ...

  9. ABP源码分析四十七:ABP中的异常处理

    ABP 中异常处理的思路是很清晰的.一共五种类型的异常类. AbpInitializationException用于封装ABP初始化过程中出现的异常,只要抛出AbpInitializationExce ...

随机推荐

  1. HDU4549 M斐波那契数

    M斐波那契数列 题目分析: M斐波那契数列F[n]是一种整数数列,它的定义例如以下: F[0] = a F[1] = b F[n] = F[n-1] * F[n-2] ( n > 1 ) 如今给 ...

  2. BootStrap - FileUpload美化样式

    效果: 代码: <div class="panel panel-default" style="border: 1px solid #ffd800;"&g ...

  3. ASP.NET - 记录错误日志

    不需要像log4net/Nlog/Common Logging配置,简单好用. 不用增加声明logger对象,可记录当前执行状况. 可以定义 维护功能模板的开发人员,以便用功能模块对于开发人员. 出处 ...

  4. Mybatis 数据库物理分页插件 PageHelper

    以前使用ibatis/mybatis,都是自己手写sql语句进行物理分页,虽然稍微有点麻烦,但是都习惯了.最近试用了下mybatis的分页插件 PageHelper,感觉还不错吧.记录下其使用方法. ...

  5. 基础知识(10)- 部署应用程序和applet

    10.1 JAR文件  10.1.1 清单文件  10.1.2 可运行JAR文件  10.1.3 资源  10.1.4 密封 10.2 Java Web Start  10.2.1 沙箱  10.2. ...

  6. javascript属性一览

    getElementsByTagName() 方法可返回带有指定标签名的对象的集合. getElementsByName() 方法可返回带有指定名称的对象的集合. getAttribute() 方法返 ...

  7. v$lock 视图访问慢解决方法

    V$ 视图访问慢 --解决方法 分析:可能是有数据字典统计信息过久,造成. exec dbms_stats.gather_fixed_objects_stats; ------收集所有数据字典的fix ...

  8. Android应用开发之(通过ClipboardManager, ClipData进行复制粘贴)

    在开发一些系统应用的时候,我们会用到Android的剪贴板功能,比如将文本文件.或者其他格式的内容复制到剪贴板或者从剪贴板获取数据等操作.Android平台中每个常规的应用运行在自己的进程空间中,相对 ...

  9. Find the minimum线段树成段更新

    问题 G: Find the minimum 时间限制: 2 Sec   内存限制: 128 MB 提交: 83   解决: 20 [ 提交][ 状态][ 讨论版] 题目描述 Given an int ...

  10. WAS ND集群中的HTTP内存会话复制对Java应用程序序列化编程的要求

    应用程序需要遵守的约定 在会话中没有自定义对象时,WAS 集群的 HTTP 会话内存复制特性成功地实现了高可用性,使用户在宕机过程中的会话信息没有丢失,操作没有受到任何影响. 在会话中包含自定义对象时 ...