【若川视野 x 源码共读】第27期 | read-pkg

208 阅读2分钟

本文参加了由公众号@若川视野 发起的每周源码共读活动, 点击了解详情一起参与。

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。