0
点赞
收藏
分享

微信扫一扫

从 vue-cli 源码中,我发现了27行读取 json 文件有趣的 npm 包

1. 前言

大家好,我是若川。最近组织了源码共读活动,感兴趣的可以点此加我参与,每周大家一起学习200行左右的源码,共同进步。同时极力推荐订阅我写的《学习源码整体架构系列》 包含20余篇源码文章。

想学源码,极力推荐订阅我写的《学习源码整体架构系列》 包含20余篇源码文章。

本文仓库 https://github.com/lxchuan12/read-pkg-analysis.git,求个star^_^[1]

源码共读活动​ 每周一期,已进行到15期。源码群里有小伙伴提问,如何用 ​​import​​​ 加载 ​​json​​ 文件。同时我之前看到了vue-cli 源码 里有 read-pkg 这个包。源码仅27行,非常值得我们学习。

阅读本文,你将学到:

1. 如何学习调试源码
2. 学会如何获取 package.json
3. 学到 import.meta
4. 学到引入 json 文件的提案
5. JSON.parse 更友好的错误提示
6. 规范化 package 元数据
7. 等等

2. 场景

优雅的获取 ​​package.json​​ 文件。

read-pkg[3]

vue-cli 源码[4]

const fs = require('fs')
const path = require('path')
const readPkg = require('read-pkg')

exports.resolvePkg = function (context) {
if (fs.existsSync(path.join(context, 'package.json'))) {
return readPkg.sync({ cwd: context })
}
return {}
}

封装这个函数的commit 记录[5]

你也许会想直接 ​​require('package.json');​​​ 不就可以了。但在​​ES模块​​下,目前无法直接引入JSON文件。

在 stackoverflow 也有相关提问[6]

我们接着来看 阮一峰老师的 JSON 模块[7]

import 命令目前只能用于加载 ES 模块,现在有一个提案[8],允许加载 JSON 模块。import 命令能够直接加载 JSON 模块以后,就可以像下面这样写。

import configData from './config.json' assert { type: "json" };
console.log(configData.appName);

​import​​​ 命令导入 ​​JSON​​​ 模块时,命令结尾的 ​​assert {type: "json"}​​​ 不可缺 少。这叫做导入断言,用来告诉 ​​​JavaScript​​​ 引擎,现在加载的是 ​​JSON​​ 模块。

接下来我们学习 read-pkg 源码[9]

3. 环境准备

3.1 克隆

# 推荐克隆我的项目,保证与文章同步
git clone https://github.com/lxchuan12/read-pkg-analysis.git
# npm i -g yarn
cd read-pkg && yarn
# VSCode 直接打开当前项目
# code .

# 或者克隆官方项目
git clone https://github.com/sindresorhus/read-pkg.git
# npm i -g yarn
cd read-pkg && yarn
# VSCode 直接打开当前项目
# code .

看源码一般先看 ​​package.json​​​,再看 ​​script​​。

3.2 package.json

{
"name":
"scripts": {
"test": "xo && ava && tsd"
}
}

test命令有三个包,我们一一查阅了解。

xo[10]

JavaScript/TypeScript linter (ESLint wrapper) with great defaults JavaScript/TypeScript linter(ESLint 包装器)具有很好的默认值

tsd[11]

Check TypeScript type definitions 检查 TypeScript 类型定义

nodejs 测试工具 ava[12]

Node.js test runner that lets you develop with confidence

3.3 调试

提前在入口测试文件 ​​test/test.js​​​ 和入口文件 ​​index.js​​ 打好断点。

用最新的​​VSCode​​​ 打开项目,找到 ​​package.json​​​ 的 ​​scripts​​​ 属性中的 ​​test​​​ 命令。鼠标停留在​​test​​​命令上,会出现 ​​运行命令​​​ 和 ​​调试命令​​​ 的选项,选择 ​​调试命令​​ 即可。

调试如图所示:

从 vue-cli 源码中,我发现了27行读取 json 文件有趣的 npm 包_gitdebugger

更多调试细节可以看我的这篇文章:​​新手向:前端程序员必学基本技能——调试JS代码​​

我们跟着调试来看测试用例。

4. 测试用例

这个测试用例文件,主要就是主入口 ​​index.js​​​ 导出的两个方法 ​​readPackage​​​, ​​readPackageSync​​。异步和同步的方法。

判断读取的 ​​package.json​​​ 的 ​​name​​​ 属性与测试用例的 ​​name​​ 属性是否相等。

判断读取 ​​package.json​​​ 的 ​​_id​​ 是否是真值。

同时支持指定目录。​​{ cwd }​

// read-pkg/test/test.js
import {fileURLToPath} 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');
});

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');
});

这个测试用例文件,涉及到一些值得一提的知识点。接下来就简单讲述下。

4.1 url 模块

url 模块提供用于网址处理和解析的实用工具。

url 中文文档[13]

url.fileURLToPath(url)

url|要转换为路径的文件网址字符串或网址对象。返回:完全解析的特定于平台的 Node.js 文件路径。此函数可确保正确解码百分比编码字符,并确保跨平台有效的绝对路径字符串。

4.2 import.meta.url

import.meta.url[14]

(1)import.meta.url import.meta.url返回当前模块的 URL 路径。举例来说,当前模块主文件的路径是​​https://foo.com/main.js​​​,import.meta.url就返回这个路径。如果模块里面还有一个数据文件 ​​data.txt​​​,那么就可以用下面的代码,获取这个数据文件的路径。new URL('data.txt', import.meta.url) 注意,Node.js 环境中,​​​import.meta.url​​​ 返回的总是本地路径,即是file:URL协议的字符串,比如 ​​file:///home/user/foo.js​​。

4.3 process.chdir

​process.chdir()​​​ 方法更改 ​​Node.js​​​ 进程的当前工作目录,如果失败则抛出异常(例如,如果指定的 ​​directory​​ 不存在)。

5. 27行主入口源码

导出异步和同步的两个方法,支持传递参数对象,​​cwd​​​ 默认是 ​​process.cwd()​​​,​​normalize​​ 默认标准化。

分别是用 ​​fsPromises.readFile​​​ ​​fs.readFileSync​​​ 读取 ​​package.json​​ 文件。

parse-json[15] 解析 json 文件。

npm 官方库 normalize-package-data[16] 规范化 ​​package​​ 元数据。

import process from 'node:process';
import fs, {promises as fsPromises} from 'node:fs';
import path from 'node:path';
import parseJson from 'parse-json';
import normalizePackageData from 'normalize-package-data';

export async function readPackage({cwd = process.cwd(), normalize = true} = {}) {
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 = process.cwd(), normalize = true} = {}) {
const filePath = path.resolve(cwd, 'package.json');
const json = parseJson(fs.readFileSync(filePath, 'utf8'));

if (normalize) {
normalizePackageData(json);
}

return json;
}

5.1 process 进程模块

很常用的模块。

process 中文文档[17]

process 对象提供有关当前 Node.js 进程的信息并对其进行控制。虽然它作为全局可用,但是建议通过 require 或 import 显式地访问它:

import process from 'node:process';

Node 文档[18]

也就是说引用 ​​node​​​ 原生库可以加 ​​node:​​​ 前缀,比如 ​​import util from 'node:util'​

5.2 path 路径模块

很常用的模块。

path 中文文档[19]

path 模块提供了用于处理文件和目录的路径的实用工具。

5.3 fs 文件模块

很常用的模块。

fs 中文文档[20]

5.4 parseJson 解析 JSON

parse-json[21]

文档介绍:

Parse JSON with more helpful errors

更多有用的错误提示。

// 源码有删减
const fallback = require('json-parse-even-better-errors');
const parseJson = (string, reviver, filename) => {
if (typeof reviver === 'string') {
filename = reviver;
reviver = null;
}

try {
try {
return JSON.parse(string, reviver);
} catch (error) {
fallback(string, reviver);
throw error;
}
} catch (error) {
// 省略
}
}

5.5 normalizePackageData 规范化包元数据

npm 官方库 normalize-package-data[22]

normalizes package metadata, typically found in package.json file.

规范化包元数据

module.exports = normalize
function normalize (data, warn, strict) {
// 省略若干代码
data._id = data.name + '@' + data.version
}

这也就是为啥测试用例中用了​​t.truthy(package_._id);​​​ 来检测 ​​_id​​ 属性是否为真值。

6. 总结

最后总结下我们学到了如下知识:

1. 如何学习调试源码
2. 学会如何获取 package.json
3. 学到 import.meta
4. 学到引入 json 文件的提案
5. JSON.parse 更友好的错误提示
6. 规范化 package 元数据
7. 等等

read-pkg 源码[23] 整体而言相对比较简单,但是也有很多可以学习深挖的学习的知识点。

作为一个 npm 包,拥有完善的测试用例。

学 ​​Node.js​​​ 可以多找找简单的 ​​npm​​ 包学习。比直接看官方文档有趣多了。不懂的就去查官方文档。查的多了,自然常用的就熟练了。

建议读者克隆 我的仓库[24] 动手实践调试源码学习。

最后可以持续关注我@若川。欢迎加我微信 ​​ruochuan12​​​ 交流,参与 ​​源码共读​​ 活动,大家一起学习源码,共同进步。

参考资料

[1]本文仓库 https://github.com/lxchuan12/read-pkg-analysis.git,求个star^_^: https://github.com/lxchuan12/read-pkg-analysis.git

[2]

read-pkg: https://npm.im/read-pkg


················· 若川简介 ·················

你好,我是​​若川​​,毕业于江西高校。现在是一名前端开发“工程师”。写有《学习源码整体架构系列》20余篇,在知乎、掘金收获超百万阅读。

从2014年起,每年都会写一篇年度总结,已经写了7篇,点击查看​​年度总结​​。同时,最近组织了​​源码共读活动​​,帮助3000+前端人学会看源码。

略。分享、收藏、点赞我的文章就是对我最大的支持~

举报

相关推荐

0 条评论