WindiCSS是如何实现加载windi.config.ts配置文件
- 本文作者:AFine970
背景
我们知道WindiCSS
的配置文件既支持js
后缀也支持ts
后缀,即:windi.config.js
和windi.config.ts
我们在 vscode 安装的WindiCSS IntelliSense
也支持读取多种后缀格式的配置文件。vscode 基于 electron 实现,electron 底层是一个node.js + v8
的集成终端,支持运行 js 格式的代码,WindiCSS IntelliSense
最终打包出来的运行的代码也是 js 格式的代码
那
WindiCSS IntelliSense
是怎么实现 js 文件加载 ts 文件的呢?
这个问题困惑了我大半天,正好最近在写脚手架,需要支持加载 ts 后缀的配置文件
于是下定决心,把这个问题搞清楚,最后终于在源码里找到了答案
解惑
WindiCSS IntelliSense
源码
- 我们将
WindiCSS IntelliSense
源码直接clone
下来
$ git clone https://github.com/windicss/windicss-intellisense.git
- 找到读取配置文件的核心代码
<!--src\lib\index.ts-->
// ...
async init() {
try {
const config = await this.loadConfig();
this.processor = new Processor(
config
) as Processor;
this.attrPrefix = this.processor.config('attributify.prefix') as
| string
| undefined;
this.variants = this.processor.resolveVariants();
this.colors = flatColors(
this.processor.theme('colors', {}) as colorObject
);
this.register();
} catch (error) {
Log.error(error);
}
}
// ...
- 关键实现
<!--src\lib\index.ts-->
// ...
import { loadConfig } from 'unconfig';
// ...
async loadConfig(file?: string) {
if(!workspace.workspaceFolders) return;
const { config, sources } = await loadConfig<Config>({
sources: [
{
files: 'windi.config',
extensions: ['ts', 'mts', 'cts', 'js', 'mjs', 'cjs'],
},
{
files: 'tailwind.config',
extensions: ['ts', 'mts', 'cts', 'js', 'mjs', 'cjs'],
},
],
merge: false,
cwd: workspace.workspaceFolders[0].uri.fsPath,
});
Log.info(`Loading Config File: ${sources}`);
return config;
}
// ...
从关键实现的代码上看,我们就找到了答案:使用unconfig来实现加载不同后缀的文件
看到这里,我们还是没有搞懂:为什么js
能加载ts
,我们继续深入了解一下unconfig
的内部实现
unconfig
源码
- 我们将
unconfig
源码直接clone
下来
git clone https://github.com/antfu/unconfig.git
- 核心代码
<!--src\index.ts-->
// ...
import jiti from 'jiti'
// ...
async function loadConfigFile<T>(filepath: string, source: LoadConfigSource<T>): Promise<LoadConfigResult<T> | undefined> {
let config: T | undefined
let parser = source.parser || 'auto'
let bundleFilepath = filepath
let code: string | undefined
async function read() {
if (code == null)
code = await fs.readFile(filepath, 'utf-8')
return code
}
if (source.transform) {
const transformed = await source.transform(await read(), filepath)
if (transformed) {
bundleFilepath = join(dirname(filepath), `__unconfig_${basename(filepath)}`)
await fs.writeFile(bundleFilepath, transformed, 'utf-8')
code = transformed
}
}
if (parser === 'auto') {
try {
config = JSON.parse(await read())
parser = 'json'
}
catch {
parser = 'require'
}
}
try {
if (!config) {
if (typeof parser === 'function') {
config = await parser(filepath)
}
else if (parser === 'require') {
config = await jiti(filepath, {
interopDefault: true,
cache: false,
requireCache: false,
v8cache: false,
esmResolve: true,
})(bundleFilepath)
}
else if (parser === 'json') {
config = JSON.parse(await read())
}
}
if (!config)
return
const rewritten = source.rewrite
? await source.rewrite(config, filepath)
: config
if (!rewritten)
return undefined
return {
config: rewritten,
sources: [filepath],
}
}
catch (e) {
if (source.skipOnError)
return
throw e
}
finally {
if (bundleFilepath !== filepath)
await fs.unlink(bundleFilepath).catch()
}
}
// ...
- 把核心代码进行精简,找到关键实现
<!--src\index.ts-->
// ...
import jiti from 'jiti'
// ...
async function loadConfigFile<T>(filepath: string, source: LoadConfigSource<T>): Promise<LoadConfigResult<T> | undefined> {
// ...
try {
if (!config) {
if (typeof parser === 'function') {
config = await parser(filepath)
}
else if (parser === 'require') {
config = await jiti(filepath, {
interopDefault: true,
cache: false,
requireCache: false,
v8cache: false,
esmResolve: true,
})(bundleFilepath)
}
else if (parser === 'json') {
config = JSON.parse(await read())
}
}
// ...
}
// ...
从关键实现的代码上看,我们就找到了答案:使用jiti来实现 js 文件加载 ts 文件时,动态编译 ts 文件并返回结果。
jiti
文档中这么描述:Runtime typescript and ESM support for Node.js (CommonJS)
,我们可以更加粗暴地理解为require-ts
。
为了让大家更好地理解unconfig
的工作流程,楼主根据上述的unconfig
核心代码,整理出一个unconfig核心工作原理
流程图
关于js
文件如何加载ts
文件的疑惑得以解开
代码实践
看过没练过,等于没看过 - B 站梦觉教游泳
我们在写脚手架的时候可以直接使用unconfig
读取配置文件即可,例如:读取vite.config.ts
可以这么实现
import { loadConfig } from 'unconfig'
const { config } = await loadConfig({
sources: [
{
files: 'vite.config',
async rewrite(config) {
return await (typeof config === 'function' ? config() : config)
},
},
]
})
最后
如果你觉得这篇文章对你有帮助,记得动动鼠标点个赞,你的点赞是我更新文章的最大动力
好好学习不会差,我是 970,咋们下期再见(此处应有 bgm)
转载自:https://juejin.cn/post/7197205708646219833