写在前面:
- 我正在参加「掘金·启航计划」
- 本文参加了由**公众号@若川视野 发起的每周源码共读活动, 点击了解详情一起参与。
- 这是源码共读的第27期,链接:juejin.cn/post/708743…。
read-pkg
Read a package.json file
github地址:github.com/sindresorhu…
思考:es6 中如何导入 json 模块?
import 命令目前只能用于加载 ES 模块,现在有一个提案,允许加载 JSON 模块。
假定有一个 JSON 模块文件config.json。
{
"appName": "My App"
}
目前,只能使用fetch()加载 JSON 模块。
const response = await fetch('./config.json');
const json = await response.json();
import 命令能够直接加载 JSON 模块以后,就可以像下面这样写。
import configData from './config.json' assert { type: "json" };
console.log(configData.appName);
import 命令导入 JSON 模块时,命令结尾的 assert {type: "json"} 不可缺少。这叫做导入断言,用来告诉 JavaScript 引擎,现在加载的是 JSON 模块。(经测试,不写 assert断言 会报错)
动态加载模块的 import() 函数也支持加载 JSON 模块。
import('./config.json', { assert: { type: 'json' } })
动手测试一下:用node执行文件,可行!
async function resolveJson() {
const configData = await import("./config.json", { assert: { type: "json" } });
return configData;
}
resolveJson().then((res) => {
console.log(res.default); // {"appName": "My App"}
});
接下来看下read-pkg包
首先克隆源码,安装依赖
git clone https://github.com/sindresorhus/read-pkg.git
npm i -g yarn
cd read-pkg && yarn
用法
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 with the parsed JSON.
readPackageSync(options?)
Returns the parsed JSON.
该库暴露了两个函数来读取 json 文件。传参options格式如下:
可选参数 cwd :默认当前工作目录
可选参数 normalize:默认true
index.d.ts文件中的 Options 定义如下:
export interface Options {
/**
Current working directory.
@default process.cwd()
*/
readonly cwd?: URL | string;
/**
[Normalize](https://github.com/npm/normalize-package-data#what-normalization-currently-entails) the package data.
@default true
*/
readonly normalize?: boolean;
}
接下来看下源码。
源码分析
首先看下 package.json 文件
"scripts": {
"test": "xo && ava && tsd"
}
调试
在想看的地方打上断点。鼠标悬停在 “test”上方,点击 调试脚本。
啊哦,遇到了报错。。
错误信息:Path *** is not in cwd ***
一番搜索后发现:项目文件夹命名成了中文,就会遇到这个坑。改成英文名后成功了。
index.js
导出异步和同步的两个方法,支持传递参数对象,cwd 默认是 process.cwd(),normalize 默认标准化。
分别是用 fsPromises.readFile 、fs.readFileSync 读取 package.json 文件。
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;
}
process 进程模块
process 对象提供有关当前 Node.js 进程的信息并对其进行控制。 虽然它作为全局可用,但是建议通过 require 或 import 显式地访问它。
引用 node 原生库可以加 node: 前缀,比如 import util from 'node:util'
process.cwd()
process.cwd() 方法返回 Node.js 进程的当前工作目录。
测试文件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);
});
// ...省略
import.meta.url
import.meta.url返回当前模块的 URL 路径。
注意,Node.js 环境中,import.meta.url返回的总是本地路径,即是file:URL协议的字符串,比如file:///home/user/foo.js。
fileURLToPath
返回: 完全解析的特定于平台的 Node.js 文件路径。 此函数可确保正确解码百分比编码字符,并确保跨平台有效的绝对路径字符串。
path.dirname
用法:path.dirname( path )
传入路径,返回该路径的目录字符串。
process.chdir
process.chdir() 方法更改 Node.js 进程的当前工作目录,如果失败则抛出异常(例如,如果指定的 directory不存在)。
小结
这个库内容比较简单,不过涵盖了node.js很多的知识点。