1. 前言
大家好,我是若川。最近組織了源碼共讀活動,感興趣的可以加我微信 ruochuan12 參與,每周大家一起學習200行左右的源碼,共同進步。已進行四個月了,很多小伙伴表示收獲頗豐。
想學源碼,極力推薦訂閱我寫的《學習源碼整體架構系列》 包含20余篇源碼文章。
本文倉庫 https://github.com/lxchuan12/read-pkg-analysis.git,求個star^_^[1]
閱讀本文,你將學到:
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
命令上,會出現 運行命令
和 調試命令
的選項,選擇 調試命令
即可。
調試如圖所示:

更多調試細節可以看我的這篇文章:新手向:前端程序員必學基本技能——調試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
更多點擊閱讀原文查看
最近組建了一個江西人的前端交流群,如果你是江西人可以加我微信?ruochuan12?私信 江西?拉你進群。
推薦閱讀
整整4個月了,盡全力組織了源碼共讀活動~
我歷時3年才寫了10余篇源碼文章,但收獲了100w+閱讀
老姚淺談:怎么學JavaScript?
我在阿里招前端,該怎么幫你(可進面試群)
·················?若川簡介?·················
你好,我是若川,畢業于江西高校。現在是一名前端開發“工程師”。寫有《學習源碼整體架構系列》10余篇,在知乎、掘金收獲超百萬閱讀。
從2014年起,每年都會寫一篇年度總結,已經寫了7篇,點擊查看年度總結。
同時,最近組織了源碼共讀活動,幫助1000+前端人學會看源碼。公眾號愿景:幫助5年內前端人走向前列。
識別上方二維碼加我微信、拉你進源碼共讀群
今日話題
略。分享、收藏、點贊、在看我的文章就是對我最大的支持~