mihawk
Version:
A tiny & simple mock server tool, support json,js,cjs,ts(typescript).
256 lines (255 loc) • 12.1 kB
JavaScript
;
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
if (k2 === undefined) k2 = k;
var desc = Object.getOwnPropertyDescriptor(m, k);
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
desc = { enumerable: true, get: function() { return m[k]; } };
}
Object.defineProperty(o, k2, desc);
}) : (function(o, m, k, k2) {
if (k2 === undefined) k2 = k;
o[k2] = m[k];
}));
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
Object.defineProperty(o, "default", { enumerable: true, value: v });
}) : function(o, v) {
o["default"] = v;
});
var __importStar = (this && this.__importStar) || function (mod) {
if (mod && mod.__esModule) return mod;
var result = {};
if (mod != null) for (var k in mod) if (k !== "default" && Object.prototype.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k);
__setModuleDefault(result, mod);
return result;
};
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
return new (P || (P = Promise))(function (resolve, reject) {
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
step((generator = generator.apply(thisArg, _arguments || [])).next());
});
};
var __importDefault = (this && this.__importDefault) || function (mod) {
return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.readPackageJson = exports.loadFileFromRoot = exports.enableRequireTsFile = exports.refreshTsOrJs = exports.refreshJson = exports.loadTS = exports.loadJS = exports.loadJson = void 0;
const vm_1 = __importDefault(require("vm"));
const path_1 = __importDefault(require("path"));
const typescript_1 = require("typescript");
const fs_extra_1 = require("fs-extra");
const json5 = __importStar(require("json5"));
const color_cc_1 = __importDefault(require("color-cc"));
const lru_cache_1 = __importDefault(require("lru-cache"));
const consts_1 = require("../consts");
const path_2 = require("../utils/path");
const print_1 = require("../utils/print");
const is_1 = require("../utils/is");
const LOGFLAG_LOADER = color_cc_1.default.cyan('[loader]') + color_cc_1.default.gray(':');
const _cacheJson = new lru_cache_1.default({ max: 50 });
function loadJson(jsonFilePath, options) {
return __awaiter(this, void 0, void 0, function* () {
const { noCache = false, noLogPrint = false } = options || {};
jsonFilePath = (0, path_2.absifyPath)(jsonFilePath);
const json = yield _loadFileWithCache(jsonFilePath, {
cacheObj: _cacheJson,
forceRefresh: noCache,
noLogPrint,
resolveData: (JsonStr) => __awaiter(this, void 0, void 0, function* () {
let jsonData = {};
try {
jsonData = JsonStr ? json5.parse(JsonStr) : {};
}
catch (error) {
print_1.Printer.error(LOGFLAG_LOADER, 'Parse json file failed!', color_cc_1.default.gray(jsonFilePath), '\n', error);
jsonData = {};
}
return jsonData;
}),
});
return json;
});
}
exports.loadJson = loadJson;
function loadJS(jsFilePath, options) {
return __awaiter(this, void 0, void 0, function* () {
const { noCache = false, noLogPrint = false } = options || {};
jsFilePath = (0, path_2.absifyPath)(jsFilePath);
if (noCache) {
refreshTsOrJs(jsFilePath);
}
try {
const mod = require(jsFilePath);
!noLogPrint && print_1.Printer.log(LOGFLAG_LOADER, `LoadJS${noCache ? color_cc_1.default.gray('(nocache)') : ''}: ${color_cc_1.default.gray((0, path_2.unixifyPath)((0, path_2.relPathToCWD)(jsFilePath)))}`);
return mod;
}
catch (error) {
print_1.Printer.error(LOGFLAG_LOADER, color_cc_1.default.red('Load js file failed!'), color_cc_1.default.gray(jsFilePath), '\n', error);
return null;
}
});
}
exports.loadJS = loadJS;
function loadTS(tsFilePath, options) {
return __awaiter(this, void 0, void 0, function* () {
const { noCache = false, noLogPrint = false } = options || {};
tsFilePath = (0, path_2.absifyPath)(tsFilePath);
if (!require.extensions['.ts']) {
print_1.Printer.warn(LOGFLAG_LOADER, color_cc_1.default.warn('Need to invoke enableRequireTsFile() first before load ts file'));
return null;
}
if (noCache) {
refreshTsOrJs(tsFilePath);
}
try {
const mod = require(tsFilePath);
!noLogPrint && print_1.Printer.log(LOGFLAG_LOADER, `LoadTS${noCache ? color_cc_1.default.gray('(nocache)') : ''}: ${color_cc_1.default.gray((0, path_2.unixifyPath)((0, path_2.relPathToCWD)(tsFilePath)))}`);
const res = mod === null || mod === void 0 ? void 0 : mod.default;
if ((0, is_1.isNil)(res)) {
print_1.Printer.warn(LOGFLAG_LOADER, color_cc_1.default.yellow('ts file should export default, but not found'), res);
}
return res;
}
catch (error) {
print_1.Printer.error(LOGFLAG_LOADER, color_cc_1.default.red('Load ts file failed!'), color_cc_1.default.gray(tsFilePath), '\n', error);
return null;
}
});
}
exports.loadTS = loadTS;
function refreshJson(jsonFilePath) {
return _cacheJson.has(jsonFilePath) && _cacheJson.del(jsonFilePath);
}
exports.refreshJson = refreshJson;
function refreshTsOrJs(filePath) {
return _clearSelfAndAncestorsCache(filePath);
}
exports.refreshTsOrJs = refreshTsOrJs;
function enableRequireTsFile(tsconfig) {
if (!require.extensions['.ts']) {
require.extensions['.ts'] = _genTsFileRequireHandle(tsconfig || {});
}
}
exports.enableRequireTsFile = enableRequireTsFile;
function loadFileFromRoot(relFilePath) {
const data = require(path_1.default.resolve((0, path_2.getRootAbsPath)(), relFilePath));
return data;
}
exports.loadFileFromRoot = loadFileFromRoot;
function readPackageJson() {
return loadFileFromRoot('./package.json');
}
exports.readPackageJson = readPackageJson;
function _loadFileWithCache(filePath, options) {
return __awaiter(this, void 0, void 0, function* () {
const { cacheObj, resolveData, forceRefresh = false, noLogPrint = false } = options;
let cacheData = null;
if (!forceRefresh && cacheObj.has(filePath)) {
cacheData = cacheObj.get(filePath);
}
else {
let fileContent = null;
const isFileExist = (0, fs_extra_1.existsSync)(filePath);
try {
if (isFileExist) {
fileContent = (0, fs_extra_1.readFileSync)(filePath, 'utf-8');
!noLogPrint && print_1.Printer.log(LOGFLAG_LOADER, `LoadJson${forceRefresh ? color_cc_1.default.gray('(nocache)') : ''}: ${color_cc_1.default.gray((0, path_2.unixifyPath)((0, path_2.relPathToCWD)(filePath)))}`);
}
}
catch (error) {
print_1.Printer.error(LOGFLAG_LOADER, 'Read file failed!', color_cc_1.default.gray(filePath), '\n', error);
}
if (typeof resolveData === 'function') {
cacheData = yield resolveData(fileContent);
}
else {
cacheData = fileContent;
}
cacheData && cacheObj.set(filePath, cacheData);
}
return cacheData;
});
}
function _genTsFileRequireHandle(tsconfig) {
tsconfig = (tsconfig || {});
const tsTranspileOption = Object.assign(Object.assign({}, tsconfig), { compilerOptions: Object.assign(Object.assign({}, tsconfig.compilerOptions), { module: typescript_1.ModuleKind.CommonJS, target: typescript_1.ScriptTarget.ES2015, moduleResolution: 'node', allowSyntheticDefaultImports: true, allowJs: true, resolveJsonModule: true, esModuleInterop: true }) });
return function (module, tsFilePath) {
const tsCode = (0, fs_extra_1.readFileSync)(tsFilePath, 'utf8');
const result = (0, typescript_1.transpileModule)(tsCode, Object.assign(Object.assign({}, tsTranspileOption), { fileName: tsFilePath }));
const jsCode = result.outputText;
print_1.Debugger.log('jsCode:\n', jsCode, '\n');
const vmContext = vm_1.default.createContext(Object.assign({}, global));
vmContext.global = global;
vmContext.require = require;
vmContext.module = module;
vmContext.exports = module.exports;
vmContext.__dirname = path_1.default.dirname(tsFilePath);
vmContext.__filename = tsFilePath;
vmContext.console = console;
vmContext.process = process;
vmContext.Buffer = Buffer;
vm_1.default.runInNewContext(jsCode, vmContext, {
filename: tsFilePath,
displayErrors: true,
});
};
}
function _clearRequireCache(filename) {
var _a;
if (consts_1.CWD === filename || !(0, path_2.isPathInDir)(filename, consts_1.CWD)) {
return;
}
print_1.Debugger.log('_clearRequireCache:', filename);
filename = (0, path_2.absifyPath)(filename);
const mod = require.cache[filename];
if (!mod) {
return;
}
const parent = mod === null || mod === void 0 ? void 0 : mod.parent;
mod.loaded = false;
delete require.cache[filename];
print_1.Debugger.log('_clearRequireCache: √ clear require.cache success!', filename, 'parent:', parent === null || parent === void 0 ? void 0 : parent.filename);
if (parent && typeof parent === 'object') {
try {
const parentChildList = parent.children;
if (Array.isArray(parentChildList)) {
const index = parentChildList.findIndex(item => item.filename === filename);
if (index > -1) {
parentChildList.splice(index, 1);
}
}
const pathCache = (_a = module === null || module === void 0 ? void 0 : module.constructor) === null || _a === void 0 ? void 0 : _a._pathCache;
if (pathCache && typeof pathCache === 'object') {
Object.keys(pathCache).forEach(key => {
var _a;
if ((_a = pathCache[key]) === null || _a === void 0 ? void 0 : _a.includes(filename)) {
delete pathCache[key];
}
});
}
}
catch (error) {
print_1.Printer.error(LOGFLAG_LOADER, 'Clear require.cache failed!', color_cc_1.default.gray(filename), '\n', error);
}
}
}
function _clearSelfAndAncestorsCache(filename) {
filename = (0, path_2.absifyPath)(filename);
print_1.Debugger.log('_clearSelfAndAncestorsCache:', filename);
const PKG_ROOT = (0, path_2.getRootAbsPath)();
if (!(filename === consts_1.CWD || (0, path_2.isPathInDir)(filename, consts_1.CWD) || filename === PKG_ROOT || (0, path_2.isPathInDir)(filename, PKG_ROOT))) {
print_1.Debugger.log('_clearSelfAndAncestorsCache Skip danger path:', filename);
return;
}
const mod = require.cache[filename];
if (!mod) {
print_1.Debugger.log('_clearSelfAndAncestorsCache Skip empty cache:', filename);
return;
}
const parent = mod === null || mod === void 0 ? void 0 : mod.parent;
const parentId = parent === null || parent === void 0 ? void 0 : parent.id;
_clearRequireCache(filename);
parentId && _clearSelfAndAncestorsCache(parentId);
}