写点什么

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

作者:若川
  • 2022 年 5 月 27 日
  • 本文字数:3839 字

    阅读完需:约 13 分钟

1. 前言

大家好,我是若川为了能帮助到更多对源码感兴趣、想学会看源码、提升自己前端技术能力的同学。我倾力组织了源码共读活动,已进行 4 个月,每周大家一起学习 200 行左右的源码,共同进步,很多人都表示收获颇丰。


想学源码,极力推荐关注我写的专栏(目前 1.8K 人关注)《学习源码整体架构系列》 包含jQueryunderscorelodashvuexsentryaxiosreduxkoavue-devtoolsvuex4koa-composevue 3.2 发布vue-thiscreate-vue玩具vite等 20 余篇源码文章。


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


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


阅读本文,你将学到:


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

2. 场景

优雅的获取 package.json 文件。


read-pkg


vue-cli 源码


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 记录


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


在 stackoverflow 也有相关提问


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


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


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


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


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

3. 环境准备

3.1 克隆

# 推荐克隆我的项目,保证与文章同步git clone https://github.com/lxchuan12/read-pkg-analysis.git# npm i -g yarncd read-pkg && yarn# VSCode 直接打开当前项目# code .
# 或者克隆官方项目git clone https://github.com/sindresorhus/read-pkg.git# npm i -g yarncd read-pkg && yarn# VSCode 直接打开当前项目# code .
复制代码


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

3.2 package.json

{  "name":   "scripts": {    "test": "xo && ava && tsd"  }}
复制代码


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


xo


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


tsd


Check TypeScript type definitions 检查 TypeScript 类型定义


nodejs 测试工具 ava


Node.js test runner that lets you develop with confidence

3.3 调试

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


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


调试如图所示:



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


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

4. 测试用例

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


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


判断读取 package.json_id 是否是真值。


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


// read-pkg/test/test.jsimport {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 中文文档


url.fileURLToPath(url)


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

4.2 import.meta.url

import.meta.url


(1)import.meta.urlimport.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 解析 json 文件。


npm 官方库 normalize-package-data 规范化 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 中文文档


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


import process from 'node:process';
复制代码


Node 文档


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

5.2 path 路径模块

很常用的模块。


path 中文文档


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

5.3 fs 文件模块

很常用的模块。


fs 中文文档

5.4 parseJson 解析 JSON

parse-json


文档介绍:


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


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


规范化包元数据


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


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

6. 总结

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


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


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


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


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


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


最后可以持续关注我@若川。欢迎与我交流,参与 源码共读 活动,每周大家一起学习 200 行左右的源码,共同进步。

发布于: 刚刚阅读数: 3
用户头像

若川

关注

还未添加个人签名 2018.09.11 加入

https://lxchuan12.gitee.io

评论

发布
暂无评论
从 vue-cli 源码中,我发现了27行读取 json 文件有趣的 npm 包_JavaScript_若川_InfoQ写作社区