package.json字段
官网英文 https://docs.npmjs.com/files/package.json
中文 https://blog.csdn.net/u013909970/article/details/80496704
学习参考文档 http://javascript.ruanyifeng.com/nodejs/packagejson.html
学习参考文档 – package.json 非官方字段集合
1)使用 npm init 即可在当前目录创建一个 package.json 文件
2)scripts 指定了运行脚本命令的npm命令行缩写,比如start指定了运行npm run start 时,所要执行的命令。
3)dependencies 字段指定了项目运行所依赖的模块,devDependencies 指定项目开发所需要的模块;–-save:模块名将被添加到 dependencies,可以简化为参数 -S 。-–save-dev:模块名将被添加到 devDependencies,可以简化为参数 -D 。
指定版本:比如”classnames”: “2.2.5”,表示安装2.2.5的版本
波浪号~+指定版本:比如 ”babel-plugin-import”: “~1.1.0”,表示安装1.1.x的最新版本(不低于1.1.0),但是不安装1.2.x,也就是说安装时不改变大版本号和次要版本号
^+指定版本:比如 ”antd”: “^3.1.4”,,表示安装3.1.4及以上的版本,但是不安装4.0.0,也就是说安装时不改变大版本号。
4)bin 项用来指定各个内部命令对应的可执行文件的位置。
"bin": { "someTool": "./bin/someTool.js"}
上面代码指定,someTool 命令对应的可执行文件为 bin 子目录下的 someTool.js。Npm会寻找这个文件,在node_modules/.bin/目录下建立符号链接。在上面的例子中,someTool.js会建立符号链接npm_modules/.bin/someTool。由于node_modules/.bin/目录会在运行时加入系统的PATH变量,因此在运行npm时(也就是执行npm run someTool),就可以不带路径,直接通过命令来调用这些脚本
5)main 字段指定了加载的入口文件,require(‘moduleName’)就会加载这个文件。这个字段的默认值是模块根目录下面的index.js
6)files 字段是一个被项目包含的文件名数组,如果你在里面放一个文件夹名,那么这个文件夹中的所有文件都会被包含进项目中(除非是那些在其他规则中被忽略的文件)
—-以下是非官方字段
7) unpkg: 让 npm 上所有的文件都开启 cdn 服务; 例如正常情况下,访问 jquery 的发布文件通过 https://unpkg.com/jquery@3.3.1/dist/jquery.js,当你使用省略的 url https://unpkg.com/jquery 时,便会按照如下的方式获取文件:# 定义了 unpkg 属性时 https://unpkg.com/jquery@[latestVersion]/[pkg.unpkg] # 未定义 unpkg 属性时,将回退到 main 属性 https://unpkg.com/jquery@[latestVersion]/[pkg.main]
8)types, typings 像 main 字段一样,定义一个针对 TypeScript 的入口文件
9)browserslist: 设置项目的浏览器兼容情况
npm 介绍
注意attention: 转载文章-来源 NPM 学习笔记整理https://segmentfault.com/p/1210000009653830/read
1)npm init 用来初始化生成一个新的 package.json 文件。它会向用户提问一系列问题,如果你觉得不用修改默认配置,一路回车就可以了。
如果使用了 -f(代表force)、-y(代表yes),则跳过提问阶段,直接生成一个新的 package.json 文件。
2)npm info 命令可以查看每个模块的具体信息。比如,查看 underscore 模块的信息 npm info underscore
3)npm list 命令以树形结构列出当前项目安装的所有模块,以及它们依赖的模块。
4)包的发布:在发布前,我们还需要获得一个账号用于今后维护自己的包,使用 npm adduser 根据提示完成账号的创建,完成后可以使用 npm whoami 检测是否已经取得了账号。接下来,在 package.json 所在目录下运行 npm publish,稍等片刻就可以完成发布了。
如果你的包将来有更新,只需要在 package.json 文件中修改 version 字段,然后重新使用 npm publish命令就行了。
如果你对已发布的包不满意,可以使用 npm unpublish 命令来取消发布。
5)npm link 创建全局链接,在本地包和全局包之间创建符号链接,适合用于包的开发与调试;学习参考-npm link的使用
6)npm 全局安装,npm install 添加参数 –unsafe-perm
7)npm之version和tag:
npm version [<newversion> | major | minor | patch | premajor | preminor | prepatch | prerelease | from-git]
–更改 package.json 中version,保存修改并生产一个新的commit
npm publish --tag beta
与 npm install somepkg@beta
–使用标签发布和安装
npm dist-tag ls
–查看当前的tag和对应的version
npm dist-tag add <pkg>@<version> [<tag>]
–标记添加到包的特定版本,比如更新tag到latest,npm dist-tag add <pkg>@<version> latest
npm中一般情况下,我们可以不指定tag,这时默认就会用latest这个tag,所有发布或者安装都是最新的正式版。而指定tag之后,我们可以在这个tag上发布一个更新的版本,用户安装的时候如果也指定这个tag,则会安装这个tag下的最新版。因此,npm中的tag类似于git中的branch。
commander.js
github来源 https://github.com/tj/commander.js
需要注意 When .command() is invoked with a description argument, no .action(callback) should be called to handle sub-commands, otherwise there will be an error. This tells commander that you’re going to use separate executables for sub-commands, much like git(1) and other popular tools.
The commander will try to search the executables in the directory of the entry script (like ./examples/pm) with the name program-command, like pm-install, pm-search.
#!/usr/bin/env node
var program = require('commander');
function range(val) {
return val.split('..').map(Number);
}
function list(val) {
return val.split(',');
}
function collect(val, memo) {
memo.push(val);
return memo;
}
function increaseVerbosity(v, total) {
return total + 1;
}
program
.version('0.0.1')
.usage('test')
.option('-i, --integer <n>', 'An integer argument', parseInt)
.option('-f, --float <n>', 'A float argument', parseFloat)
.option('-r, --range <a>..<b>', 'A range', range)
.option('-l, --list <items>', 'A list', list)
.option('-o, --optional [value]', 'An optional value')
.option('-c, --collect [value]', 'A repeatable value', collect, [])
.option('-v, --verbose', 'A value that can be increased', increaseVerbosity, 0);
program
.command('exec <cmd>')
.alias('ex')
.description('execute the given remote cmd')
.option("-e, --exec_mode <mode>", "Which exec mode to use")
.action(function (cmd, options) {
console.log('exec "%s" using %s mode', cmd, options.exec_mode);
}).on('--help', function () {
console.log(' Examples:');
console.log();
console.log(' $ deploy exec sequential');
console.log(' $ deploy exec async');
console.log();
});
program.parse(process.argv);
console.log(' int: %j', program.integer);
console.log(' float: %j', program.float);
console.log(' optional: %j', program.optional);
program.range = program.range || [];
console.log(' range: %j..%j', program.range[0], program.range[1]);
console.log(' list: %j', program.list);
console.log(' collect: %j', program.collect);
console.log(' verbosity: %j', program.verbose);
console.log(' args: %j', program.args);
node ./test-commander.js exec async -e llmode -i 5 -f 2.99 -r 8..9 -l 7,9,89 -o rr -v 99
exec "async" using llmode mode
int: 5
float: 2.99
optional: "rr"
range: 8..9
list: ["7","9","89"]
collect: []
verbosity: 1
args: [Circular]
vue-cli源码
注意attention: 文章转载来源 https://segmentfault.com/a/1190000013975247
1.node中文网
node中文网http://nodejs.cn/api/
const name = inPlace ? path.relative('../', process.cwd()) : rawName //如果在当前目录下构建项目,当前目录名为项目构建目录名,否则是当前目录下的子目录【rawName】为项目构建目录名
//process cwd() 方法返回 Node.js 进程当前工作的目录
//path.relative() 方法返回从 from 到 to的相对路径(基于当前工作目录)。
const to = path.resolve(rawName || '.') //项目构建目录的绝对路径
2.handlebars
学习文档 英文http://handlebarsjs.com/block_helpers.html
中文http://keenwon.com/992.html
Handlebars.registerHelper('if', function(conditional, options) {
if(conditional) {
return options.fn(this);
} else {
return options.inverse(this);
}
});
3.metalsmith静态网页生成器
学习文档 https://github.com/segmentio/metalsmith
学习参考文档 http://www.metalsmith.io/
Metalsmith(__dirname) // instantiate Metalsmith in the cwd
.source('sourcepath') // specify source directory
.destination('destpath') // specify destination directory
.use(markdown()) // transpile markdown into html
.use(layouts()) // wrap a nunjucks layout around the html
.build(function(err) { // this is the actual build process
if (err) throw err; // throwing errors is required
});
// 插件编写
/**
* Expose `plugin`.
*/
module.exports = plugin;
/**
* Metalsmith plugin to hide drafts from the output.
*
* @return {Function}
*/
function plugin() {
return function(files, metalsmith, done){
setImmediate(done);
Object.keys(files).forEach(function(file){
var data = files[file];
if (data.draft) delete files[file];
});
};
}
4.mikefile文件
学习参考文档 http://www.ruanyifeng.com/blog/2015/03/build-website-with-make.html
学习参考文档 前端入门->makefile https://segmentfault.com/a/1190000004437816
//makefile的基本格式为:
target: prerequisities
[TAB]command
// target就是你要执行的命令套件,prerequisties就是依赖,而command就是实际执行的命令。
5.download
学习文档github https://github.com/kevva/download
//download(url, [destination], [options])
//----Returns both a Promise<Buffer> and a Duplex stream with additional events.
// 内部用的是get-stream
const fs = require('fs');
const download = require('download');
download('http://unicorn.com/foo.jpg', 'dist').then(() => {
console.log('done!');
});
download('http://unicorn.com/foo.jpg').then(data => {
fs.writeFileSync('dist/foo.jpg', data);
});
download('unicorn.com/foo.jpg').pipe(fs.createWriteStream('dist/foo.jpg'));
Promise.all([
'unicorn.com/foo.jpg',
'cats.com/dancing.gif'
].map(x => download(x, 'dist'))).then(() => {
console.log('files downloaded!');
});
6.get-stream
学习文档github https://github.com/sindresorhus/get-stream
const fs = require('fs');
const getStream = require('get-stream');
(async () => {
const stream = fs.createReadStream('unicorn.txt');
console.log(await getStream(stream));
})();
// get-stream部分源码
let len = 0;
const ret = [];
const stream = new PassThrough({ objectMode });
if (encoding) {
stream.setEncoding(encoding);
}
stream.on('data', chunk => {
ret.push(chunk);
if (objectMode) {
len = ret.length;
} else {
len += chunk.length;
}
});
stream.getBufferedValue = () => {
if (array) {
return ret;
}
return buffer ? Buffer.concat(ret, len) : ret.join('');
};
stream.getBufferedLength = () => len;
return stream;
6.consolidate
学习文档github https://github.com/tj/consolidate.js
//模板引擎打包库 关于Handlebars的源码
/**
* Handlebars support.
*/
exports.handlebars = fromStringRenderer('handlebars');
/**
* Handlebars string support.
*/
exports.handlebars.render = function(str, options, cb) {
return promisify(cb, function(cb) {
var engine = requires.handlebars || (requires.handlebars = require('handlebars'));
try {
for (var partial in options.partials) {
engine.registerPartial(partial, options.partials[partial]);
}
for (var helper in options.helpers) {
engine.registerHelper(helper, options.helpers[helper]);
}
var tmpl = cache(options) || cache(options, engine.compile(str, options));
cb(null, tmpl(options));
} catch (err) {
cb(err);
}
});
};
// 在vue-cli里面使用
const render = require('consolidate').handlebars.render
render(str, metalsmithMetadata, (err, res) => {
if (err) {
err.message = `[${file}] ${err.message}`
return next(err)
}
files[file].contents = new Buffer(res)
next()
})
7.引入的包
const download = require('download-git-repo') //用于下载远程仓库至本地 支持GitHub、GitLab、Bitbucket
const program = require('commander') //命令行处理工具
const exists = require('fs').existsSync //node自带的fs模块下的existsSync方法,用于检测路径是否存在。(会阻塞)
const path = require('path') //node自带的path模块,用于拼接路径
const ora = require('ora') //用于命令行上的加载效果
const home = require('user-home') //用于获取用户的根目录
const tildify = require('tildify') //将绝对路径转换成带波浪符的路径,例如tildify('/Users/sindresorhus/dev'); //=> '~/dev'
const chalk = require('chalk')// 用于高亮终端打印出的信息 console.log('\x1b[91m','message')
const inquirer = require('inquirer') //用于命令行与开发者交互,require('readline')处理
const rm = require('rimraf').sync // 相当于UNIX的“rm -rf”命令
const render = require('consolidate').handlebars.render // 模板引擎集成
// generate.js
const chalk = require('chalk')
const Metalsmith = require('metalsmith') // 静态网站生成器
const Handlebars = require('handlebars') // 知名的模板引擎。
const async = require('async') //非常强大的异步处理工具。
const render = require('consolidate').handlebars.render //支持各种模板引擎的渲染。
const path = require('path')
const multimatch = require('multimatch') // 可以支持多个条件的匹配。
const getOptions = require('./options')//自定义工具-用于获取模板配置。
const ask = require('./ask')//自定义工具-用于询问开发者。
const filter = require('./filter') //自定义工具-用于文件过滤。
const logger = require('./logger') //自定义工具-用于日志打印。
8.generate.js
module.exports = function generate (name, src, dest, done) {
const opts = getOptions(name, src) //获取meta.json,meta.js配置
const metalsmith = Metalsmith(path.join(src, 'template')) //初始化Metalsmith对象
const data = Object.assign(metalsmith.metadata(), {
destDirName: name,
inPlace: dest === process.cwd(),
noEscape: true
})//添加一些变量至metalsmith中,并获取metalsmith中全部变量
//注册配置对象中的helper
opts.helpers && Object.keys(opts.helpers).map(key => {
Handlebars.registerHelper(key, opts.helpers[key])
})
const helpers = { chalk, logger }
//配置对象是否有before函数,是则执行
if (opts.metalsmith && typeof opts.metalsmith.before === 'function') {
opts.metalsmith.before(metalsmith, opts, helpers)
}
metalsmith.use(askQuestions(opts.prompts)) //询问问题
.use(filterFiles(opts.filters)) //过滤文件
.use(renderTemplateFiles(opts.skipInterpolation)) //渲染模板文件
//配置对象是否有after函数,是则执行
if (typeof opts.metalsmith === 'function') {
opts.metalsmith(metalsmith, opts, helpers)
} else if (opts.metalsmith && typeof opts.metalsmith.after === 'function') {
opts.metalsmith.after(metalsmith, opts, helpers)
}
metalsmith.clean(false)
.source('.') // start from template root instead of `./src` which is Metalsmith's default for `source`
.destination(dest)
.build((err, files) => {
done(err)
if (typeof opts.complete === 'function') {
//配置对象有complete函数则执行
const helpers = { chalk, logger, files }
opts.complete(data, helpers)
} else {
//配置对象有completeMessage,执行logMessage函数
logMessage(opts.completeMessage, data)
}
})
return data
}
/**
* Create a middleware for asking questions.
*
* @param {Object} prompts
* @return {Function}
*/
function askQuestions (prompts) {
return (files, metalsmith, done) => {
ask(prompts, metalsmith.metadata(), done)
}
}
/**
* Create a middleware for filtering files.
*
* @param {Object} filters
* @return {Function}
*/
function filterFiles (filters) {
return (files, metalsmith, done) => {
filter(files, filters, metalsmith.metadata(), done)
}
}
/**
* Template in place plugin.
*
* @param {Object} files
* @param {Metalsmith} metalsmith
* @param {Function} done
*/
function renderTemplateFiles (skipInterpolation) {
skipInterpolation = typeof skipInterpolation === 'string'
? [skipInterpolation]
: skipInterpolation //保证skipInterpolation是一个数组
return (files, metalsmith, done) => {
const keys = Object.keys(files) //获取files的所有key
const metalsmithMetadata = metalsmith.metadata() //获取metalsmith的所有变量
async.each(keys, (file, next) => { //异步处理所有files
// skipping files with skipInterpolation option
// 跳过符合skipInterpolation的要求的file
if (skipInterpolation && multimatch([file], skipInterpolation, { dot: true }).length) {
return next()
}
//获取文件的文本内容
const str = files[file].contents.toString()
// do not attempt to render files that do not have mustaches
//跳过不符合handlebars语法的file
if (!/\{\{([^{}]+)\}\}/g.test(str)) {
return next()
}
//渲染文件
render(str, metalsmithMetadata, (err, res) => {
if (err) {
err.message = `[${file}] ${err.message}`
return next(err)
}
files[file].contents = new Buffer(res)
next()
})
}, done)
}
}
/**
* Display template complete message.
*
* @param {String} message
* @param {Object} data
*/
function logMessage (message, data) {
if (!message) return //没有message直接退出函数
render(message, data, (err, res) => {
if (err) {
console.error('\n Error when rendering template complete message: ' + err.message.trim()) //渲染错误打印错误信息
} else {
console.log('\n' + res.split(/\r?\n/g).map(line => ' ' + line).join('\n'))
//渲染成功打印最终渲染的结果
}
})
}