demo 执行器 之 @typescript/ata
本文是在看过 如何在网页实现 TypeScript 编辑器? 这篇文章后, 使用@typescript/ata对demo执行器改进, 达到满意效果后有感而发, 后面用ata代指@typescript/ata, ata的功能概括成一句话: 把代码作为字符串输入ata, 解析出代码里用到的库, 从jsdeliver找到库对应的类型定义文件并下载.
感兴趣的朋友可以加v :
ata也是typescript官网用来做类型定义文件*.d.ts的自动获取.
结合monaco-editor使用时, 在代码新增或变化时, 使用ata解析代码,下载类型定义文件, 并导入到ts的类型提示里.
起因
大概在17年秋~19年夏期间, 利用业余时间, 基于qt5 + nwjs做了一款软件, 可参考enterApp使用说明
从21年开始陆陆续续的这款软件做了些改进:
- 替换
nw.js, 改成electron js,ts的demo执行器从codemirror改成monaco-editor浏览器功能更加强大: 特别是在页面,可同时开多个查找器,每个查找器用不同颜色标记查找内容.
但是, demo执行器有两个缺陷:
- 不能在敲代码时, 对于任意使用的
npm库, 自动导入它的类型定义文件 - 不能在运行时, 对于任意使用的
npm库, 自动加载它的js文件
使用ata一番改造后, 已克服上面的两个缺陷, 另外:
- 通过
本地服务代理esm链接的文件导入, 既避免频繁调用jsdeliver, 也解决了通过esm引入antd时的react多实例问题.
引入ata之前demo执行器的做法
缺点是不能自动化, 比如在使用前, 只对lodash库的*.d.ts类型提示文件和<script>链接等配置好放在本地, 只能对lodash可用, 对于其它在npm的库无效.
类型提示
把要用库的类型提示文件*.d.ts手动提取出来, 再通过monaco-editor的monaco.languages.typescript.typescriptDefaults.setExtraLibs()接口导入, 主要逻辑如下:
//react 的 .d.ts
let fCntReact = fs.readFileSync(`react.development.d.ts`, "utf8");
fCntReact += fs.readFileSync(`react-dom.development.d.ts`, "utf8");
let deps = [
//react 的类型提示配置
{
content: fCntReact,
filePath: "file:///node_modules/@types/react.d.ts",
},
];
function find_ds(dir) {
var dobj = fs.readdirSync(dir);
dobj.forEach((d) => {
let p = dir + "/" + d;
if (d.includes(".d.ts")) {
///antd 的类型提示文件导入
deps.push({
content: fs.readFileSync(p, "utf-8"),
filePath: "file:///node_modules/@types/antd" + p,
});
} else if (d !== "style" && senipc.isDirectory(p)) {
find_ds(p);
}
});
}
//antd 的类型提示配置
find_ds("./antd_dts/");
monaco.languages.typescript.typescriptDefaults.setExtraLibs(deps);
运行时库文件处理
试过的如下两种方式都能用:
react,react-dom,ant-design,dayjs等库文件,用他们在jsdeliver的dist文件夹的打包产物, 通过script标签引入- 把用到的
react,react-dom,ant-design,dayjs等库, 使用esbuild把它们打包成一个独立的文件, 通过script标签引入
引入ata后demo执行器的做法
自动引入对应库的*.d.ts类型提示文件, 通过import导入远程的esm库文件, 连npm install都不需要, 直接用.
ata流程介绍
ata通过使用typescript.js的preProcessFile(codeStr), 解析出import的库名, 然后使用jsdeliver提供的接口,拿到库对应的*.d.ts并下载, 再通过monaco-editor的monaco.languages.typescript.typescriptDefaults.addExtraLib();方法, 把库的类型定义添加到ts提示数据里.
结合monaco-editor来说, 增加一个文本内容变化的监听,解析import的库,并进行类型定义下载和导入, 如下:
export function createATA(
onDownloadFile: (code: string, path: string) => void
) {
const ata = setupTypeAcquisition({
projectName: "demoAta",
//通过`<script src="https://fastly.jsdelivr.net/npm/typescript">`引入到全局的`ts`
typescript: global.ts,
logger: console,
delegate: {
receivedFile: (code, path) => {
onDownloadFile(code, path);
},
},
});
return ata;
}
const ata = createATA((code, path) => {
monaco.languages.typescript.typescriptDefaults.addExtraLib(
code,
`file://${path}`
);
});
editor.onDidChangeModelContenFt(() => {
ata(vObj.mn_et.getValue());
});
主要流程截图展示如下:
- 引入
ata, 因为对ata做了适配,缓存文件下载,避免频繁调jsdeliver, 放在/@ui/lib/ata下面
- 绑定回调
onDownloadFile
ata内部解析代码里的import
下图,getReferencesForModule()找出import的库名
下图,ts.preProcessFile(codeStr)
下图,Fconfig.delegate.receivedFile?.(dtsCode, dts.vfsPath); 执行绑定的回调onDownloadFile
ata适配
-
缓存下载的
*.d.ts, 本地已有时从本地读取下图, 缓存后的效果
-
缓存要
import的js文件,import时用本地服务的链接,文件不存在时,通过本地服务从远程获取, 另外通过本地服务也可以解决部分问题,如下:下图, 用的是
react@18.2.0, 与在编辑器代码导入的版本不同, 导致react多实例问题
下图, 从本地服务读取, 可以对返回的内容做处理, 保证导入的是同一版本的react链接
最终demo执行器效果
- html
- js/ts
- 运行