本文参加了由公众号@若川视野 发起的每周源码共读活动, 点击了解详情一起参与。
1.学习内容
2.了解用法
官网举例其用法如下:
import {readPackage} from 'read-pkg';
console.log(await readPackage());
//=> {name: 'read-pkg', …}
console.log(await readPackage({cwd: 'some-other-directory'}));
//=> {name: 'unicorn', …}
API有:
readPackage(options?)
Returns a Promise<object> with the parsed JSON.
readPackageSync(options?)
Returns the parsed JSON.
3.猜测实现思路
使用node.js的fs模块读文件的内容;path模块解析路径
4.阅读源码
index.js:
import process from 'node:process';
import fs, {promises as fsPromises} from 'node:fs';
import path from 'node:path';
import {fileURLToPath} from 'node:url';
import parseJson from 'parse-json';
import normalizePackageData from 'normalize-package-data';
const toPath = urlOrPath => urlOrPath instanceof URL ? fileURLToPath(urlOrPath) : urlOrPath;
export async function readPackage({cwd, normalize = true} = {}) {
cwd = toPath(cwd) || process.cwd();
const filePath = path.resolve(cwd, 'package.json');
const json = parseJson(await fsPromises.readFile(filePath, 'utf8'));
if (normalize) {
normalizePackageData(json);
}
return json;
}
export function readPackageSync({cwd, normalize = true} = {}) {
cwd = toPath(cwd) || process.cwd();
const filePath = path.resolve(cwd, 'package.json');
const json = parseJson(fs.readFileSync(filePath, 'utf8'));
if (normalize) {
normalizePackageData(json);
}
return json;
}
异步方法使用了readFile,同步方法使用了readFileSync;都用到了parseJson解析;如果需要规范化则使用normalizePackageData。
流程图:
test.js:
import {fileURLToPath, pathToFileURL} from 'url';
import path from 'path';
import test from 'ava';
import {readPackage, readPackageSync} from '../index.js';
const dirname = path.dirname(fileURLToPath(import.meta.url));
process.chdir(dirname);
const rootCwd = path.join(dirname, '..');
test('async', async t => {
const package_ = await readPackage();
t.is(package_.name, 'unicorn');
t.truthy(package_._id);
});
test('async - cwd option', async t => {
const package_ = await readPackage({cwd: rootCwd});
t.is(package_.name, 'read-pkg');
t.deepEqual(
await readPackage({cwd: pathToFileURL(rootCwd)}),
package_,
);
});
test('sync', t => {
const package_ = readPackageSync();
t.is(package_.name, 'unicorn');
t.truthy(package_._id);
});
test('sync - cwd option', t => {
const package_ = readPackageSync({cwd: rootCwd});
t.is(package_.name, 'read-pkg');
t.deepEqual(
readPackageSync({cwd: pathToFileURL(rootCwd)}),
package_,
);
});
一共4个测试用例,前两个测试readPackage,后两个测试readPackageSync。
import.meta.url: import.meta是JavaScript模块暴露特定上下文的元数据属性的对象。import.meta.url返回当前模块的url路径。process.chdir: 用于改变node.js当前进程的工作目录。
源码以及测试用例中使用到了很多node.js相关的模块:
process: 进程模块,获取进程相关信息
fs: 文件读写和目录读写操作
path: 文件路径相关
url:解析url
5.调试过程
第一个测试用例执行第一个函数:
dirname:
rootCwd:
cwd:
filePath:
第二个测试用例执行第一个测试用例:
第三个测试用例执行第二个函数:
第二个函数读取的json:
6.收获总结
收获:node.js相关知识
总结: read-pkg实现上不是特别复杂。同步方法使用fs模块readFileSync方法;异步方法使用fs模块的 readFile。读完文件内容之后使用parseJson进行解析。如果需要规范化会使用normalizePackageData。