@ainc/script
Version:
Script compiler for typescript
266 lines (227 loc) • 7.35 kB
text/typescript
/**
*****************************************
* Created by edonet@163.com
* Created on 2021-07-17 16:02:55
*****************************************
*/
'use strict';
/**
*****************************************
* 加载依赖
*****************************************
*/
import * as babel from '@babel/core';
import transformTypescript from '@babel/plugin-transform-typescript';
import transformReactJSX from '@babel/plugin-transform-react-jsx';
import transformModulesCommonjs from '@babel/plugin-transform-modules-commonjs';
import proposalDynamicImport from '@babel/plugin-proposal-dynamic-import';
import proposalDecorators from '@babel/plugin-proposal-decorators';
import proposalClassProperties from '@babel/plugin-proposal-class-properties';
import proposalExportNamespaceFrom from '@babel/plugin-proposal-export-namespace-from';
import transformModulePath, { Options as ModulePathsOptions } from './helpers/module-paths-plugin';
import { compilerOptions } from './helpers/tsconfig';
/**
*****************************************
* 解析路径配置
*****************************************
*/
const modulePathsOptions = {
baseUrl: compilerOptions.baseUrl,
paths: compilerOptions.paths,
alias: compilerOptions.alias,
calls: [] as string[],
useRelativePath: false,
};
/**
*****************************************
* 测试环境
*****************************************
*/
if (process.env.BABEL_ENV === 'test') {
modulePathsOptions.calls.push(
'jest.genMockFromModule',
'jest.mock',
'jest.unmock',
'jest.doMock',
'jest.dontMock',
'jest.setMock',
'jest.requireActual',
'jest.requireMock',
);
}
/**
*****************************************
* 解析模块配置
*****************************************
*/
const moduleEntries = ['index.js', 'index.ts'];
const importInterop = compilerOptions.esModuleInterop ? 'babel' : 'none';
/**
*****************************************
* 是否使用懒加载
*****************************************
*/
function lazy(file: string): () => boolean {
const isEntry = !!moduleEntries.find(name => file.endsWith(name));
// 返回匹配函数
return () => isEntry;
}
/**
*****************************************
* 转换器
*****************************************
*/
export interface Transform {
(filename: string, code: string): string;
}
/**
*****************************************
* 默认配置
*****************************************
*/
const babelOptions: babel.TransformOptions = {
ast: false,
babelrc: false,
babelrcRoots: false,
configFile: false,
comments: false,
sourceMaps: 'inline',
};
/**
*****************************************
* 转换代码
*****************************************
*/
export function transform(code: string, options: babel.TransformOptions): string {
const result = babel.transformSync(code, options);
// 返回结果
if (result) {
return result.code || '';
}
// 返回空
return '';
}
/**
*****************************************
* 转码【JavaScript】
*****************************************
*/
export function transformJavaScript(filename: string, code: string): string {
// 过滤第三方模块
if (filename.indexOf('node_modules') > -1) {
return code;
}
// 过滤非ES6模块
if (code.indexOf('import ') === -1 && code.indexOf('export ') === -1) {
return code;
}
// 转换代码
return transform(code, {
...babelOptions,
filename,
plugins: [
[proposalExportNamespaceFrom],
[transformModulePath, modulePathsOptions],
[transformModulesCommonjs, { lazy: lazy(filename), importInterop }],
[proposalDynamicImport],
[proposalDecorators, { legacy: true }],
[proposalClassProperties, { loose: true }],
],
});
}
/**
*****************************************
* 转码【TypeScript】
*****************************************
*/
export function transformTypeScript(filename: string, code: string): string {
return transform(code, {
...babelOptions,
filename,
plugins: [
[proposalExportNamespaceFrom],
[transformTypescript, { isTSX: true, allowDeclareFields: true }],
[transformModulePath, modulePathsOptions],
[transformModulesCommonjs, { lazy: lazy(filename), importInterop }],
[proposalDynamicImport],
[proposalDecorators, { legacy: true }],
[proposalClassProperties, { loose: true }],
],
});
}
/**
*****************************************
* 转码【JSX】
*****************************************
*/
export function transformJSXScript(filename: string, code: string): string {
return transform(code, {
...babelOptions,
filename,
plugins: [
[proposalExportNamespaceFrom],
[transformReactJSX],
[transformModulePath, modulePathsOptions],
[transformModulesCommonjs, { lazy: lazy(filename), importInterop }],
[proposalDynamicImport],
[proposalDecorators, { legacy: true }],
[proposalClassProperties, { loose: true }],
],
});
}
/**
*****************************************
* 转码【TSX】
*****************************************
*/
export function transformTSXScript(filename: string, code: string): string {
return transform(code, {
...babelOptions,
filename,
plugins: [
[proposalExportNamespaceFrom],
[transformTypescript, { isTSX: true, allowDeclareFields: true }],
[transformReactJSX],
[transformModulePath, modulePathsOptions],
[transformModulesCommonjs, { lazy: lazy(filename), importInterop }],
[proposalDynamicImport],
[proposalDecorators, { legacy: true }],
[proposalClassProperties, { loose: true }],
],
});
}
/**
*****************************************
* 转换配置
*****************************************
*/
export interface TransformOptions extends babel.TransformOptions {
typescript?: Record<string, unknown>;
jsx?: Record<string, unknown>;
modulePaths?: ModulePathsOptions;
}
/**
*****************************************
* 转码脚本
*****************************************
*/
export function transformScript(filename: string, code: string, options: TransformOptions = {}): string {
const plugins = options.plugins || [];
const opts = { ...babelOptions, ...options, filename, plugins };
// 添加语法插件
plugins.unshift(
[proposalExportNamespaceFrom],
);
// 添加模块解析
plugins.push(
[transformTypescript, { isTSX: true, allowDeclareFields: true, ...opts.typescript }],
[transformReactJSX, opts.jsx],
[transformModulePath, { ...modulePathsOptions, ...opts.modulePaths }],
[transformModulesCommonjs, { lazy: lazy(filename), importInterop }],
[proposalDynamicImport],
[proposalDecorators, { legacy: true }],
[proposalClassProperties, { loose: true }],
);
// 执行转换
return transform(code, opts);
}