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

490 阅读3分钟

持续创作,加速成长!这是我参与「掘金日新计划 · 6 月更文挑战」的第7天,点击查看活动详情

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

参考:juejin.cn/post/703727…

场景

vue-cli 源码

由于es模块里无法直接require('package.json'),所以要用依赖

提案是有了:

import configData from './config.json' assert { type: "json" };

一些小知识点

  • xo

可以认为是一个小型的eslint吧

Check TypeScript type definitions 检查 TypeScript 类型定义

要建什么test-d.ts里面搞检查类型输出

  • ava

测试用的

github.com/avajs/ava/b…

  • import.meta.url

developer.mozilla.org/zh-CN/docs/…

es6.ruanyifeng.com/#docs/propo…

指明模块的基本URL "file:///home/user/my-module.mjs"

  • 虽然process是node的全局对象,但是建议引用一下 import process from 'node:process';
  • url 中文文档
    url.fileURLToPath(url)
    url | 要转换为路径的文件网址字符串或网址对象。 返回: 完全解析的特定于平台的 Node.js 文件路径。 此函数可确保正确解码百分比编码字符,并确保跨平台有效的绝对路径字符串。
  • process.chdir
    process.chdir() 方法更改 Node.js 进程的当前工作目录,如果失败则抛出异常(例如,如果指定的 directory 不存在)。
  • fs
    很常用的模块。
    fs 中文文档

parse-json

Parse JSON with more helpful errors 更好的json化,错误时有提示

npm 官方库 normalize-package-data

github.com/npm/normali…

normalizes package metadata, typically found in package.json file. 规范化包元数据,通常在 package.json 文件中找到。

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

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

源码

倒是很容易就理解两者的区别,一个异步一个同步嘛

import process from 'node:process';
//promises: http://nodejs.cn/api/fs.html#fspromisesreadfilepath-options
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);//data._id = data.name + '@' + data.version
	}

	return json;
}

测试

// read-pkg/test/test.js
import {fileURLToPath} from 'url';
import path from 'path';
import test from 'ava';
import {readPackage, readPackageSync} from '../index.js';
// 在源码文件目录可以发现,测试用的文件是放在test文件夹下
// 获取测试文件的路径 test
const dirname = path.dirname(fileURLToPath(import.meta.url));
//read-pkg-analysis-main\read-pkg\test
// 更改当前工作文件目录到test下
process.chdir(dirname);
const rootCwd = path.join(dirname, '..');
//read-pkg-analysis-main\read-pkg\
test('async', async t => {
	const package_ = await readPackage();
	t.is(package_.name, 'unicorn');
        // id 是那个规范化包赋予的
	t.truthy(package_._id);
});

test('async - cwd option', async t => {
// 根目录的Package
	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');
});

疑问与解读

github.com/vuejs/vue-c…

vue-cli中
"read-pkg": "^5.1.1",
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 {}
}
  • 疑问:readPkg.sync这个api?有这个api?我怎么没看到
  • 果然,老版本的,现在7.0了,这个还用5.1,老版本是这么用的~
  • github.com/sindresorhu…

总结

  • 流程

用fs库的同步fsPromises.readFile 或异步fs.readFileSync 读取 package.json 文件。

parse-json 解析 json 文件。

npm 官方库 normalize-package-data 规范化 package 元数据。

返回

  • import.meta.url :指明模块的基本url,这里用来测试环境下得到当前文件的路径 url.fileURLToPath(url) :确保正确解码百分比编码字符,并确保跨平台有效的绝对路径字符串。
  • normalize-package-data,校验package.json的数据是否合格
  • parse-json解析成json,报错提示丰富
  • 上面两个库我看了源码,其实也很复杂。