MiaoScript/src/main/resources/core/require.js

221 lines
7.3 KiB
JavaScript
Raw Normal View History

/**
* 符合 CommonJS 规范的 类似 Node 的模块化加载
* . : MiaoScript require.main 不存在
* . 加载 require 流程 例如 dir 目录下 调用 require('xx');
* a) 加载流程
* 1. 如果xx模块是一个內建模块
* a. 编译并返回该模块
* b. 停止执行
* 2. 如果模块以 `./` `../` 开头
* a. 尝试使用 resolveAsFile(dir/xx) 加载文件
* b. 尝试使用 resolveAsDirectory(dir/xx) 加载目录
* 3. 尝试去 root root/core root/modules 用方法2加载模块
* 4. 抛出 not found 异常
* b) resolveAsFile 解析流程
* 1. 如果 xx 是一个文件 则作为 `javascript` 文本加载 并停止执行
* 2. 如果 xx.js 是一个文件 则作为 `javascript` 文本加载 并停止执行
* 暂不支持 3. 如果 xx.json 是一个文件 则使用 `JSON.parse(xx.json)` 解析为对象加载 并停止执行
* 暂不支持 4. 如果 xx.ms 是一个文件 则使用MScript解析器解析 并停止执行
* c) resolveAsDirectory 解析流程
* 1. 如果 xx/package.json 存在 则使用 `JSON.parse(xx/package.json)` 解析并取得 main 字段使用 resolveAsFile(main) 加载
* 2. 如果 xx/index.js 存在 则使用 resolveAsFile(xx/index.js) 加载
* 暂不支持 3. 如果 xx/index.json 存在 则使用 `xx/index.json` 解析为对象加载 并停止执行
* 暂不支持 4. 如果 xx/index.ms 是一个文件 则使用MScript解析器解析 并停止执行
* : MiaoScript 暂不支持多层 modules 加载 暂时不需要(估计以后也不会需要)
*/
/*global Java, base*/
(function (parent) {
'use strict';
var File = Java.type("java.io.File");
var separatorChar = File.separatorChar;
var paths = [parent, parent + separatorChar + 'core', parent + separatorChar + 'modules'];
/**
* 解析模块名称为文件
* 按照下列顺序查找
* 当前目录 ./
* 父目录 ../
* 核心目录 /core
* 模块目录 /modules
* @param name 模块名称
* @param parent 父目录
*/
2017-10-11 17:39:51 +00:00
function resolve(name, parent) {
2017-10-30 03:17:42 +00:00
name = _canonical(name) || name;
// 解析本地目录
if (name.startsWith('./') || name.startsWith('../')) {
return resolveAsFile(parent, name) || resolveAsDirectory(parent, name) || undefined;
} else {
// 查找可能存在的路径
for (var i in paths) {
var path = paths[i];
var result = resolveAsFile(path, name) || resolveAsDirectory(path, name);
if (result) {
return result;
}
2017-10-11 17:39:51 +00:00
}
}
return undefined;
}
/**
* 解析文件
* @returns {*}
*/
2017-10-11 17:39:51 +00:00
function resolveAsFile(dir, file) {
file = ext.notNull(dir) ? new File(dir, file) : new File(file);
2017-10-11 17:39:51 +00:00
if (file.isFile()) {
return file;
}
2017-10-11 17:39:51 +00:00
var ef = new File(normalizeName(_canonical(file)));
if (ef.isFile()) {
return ef;
}
}
2017-10-11 17:39:51 +00:00
/**
* 解析目录
* @returns {*}
*/
function resolveAsDirectory(dir, file) {
dir = ext.notNull(dir) ? new File(dir, file) : new File(file);
2017-10-14 06:27:22 +00:00
var _package = new File(dir, 'package.json');
2017-10-11 17:39:51 +00:00
if (_package.exists()) {
var json = JSON.parse(base.read(_package));
if (json.main) {
2017-10-14 06:27:22 +00:00
return resolveAsFile(dir, json.main);
2017-10-11 17:39:51 +00:00
}
}
// if no package or package.main exists, look for index.js
2017-10-14 06:27:22 +00:00
return resolveAsFile(dir, 'index.js');
}
function normalizeName(fileName, ext) {
var extension = ext || '.js';
if (fileName.endsWith(extension)) {
return fileName;
}
return fileName + extension;
}
/**
* 预编译模块
* @param file JS文件
* @param optional 附加选项
* @returns {Object}
*/
2017-10-14 06:27:22 +00:00
function compileJs(file, optional) {
var cacheFile = _cacheFile(file);
2017-10-14 06:27:22 +00:00
var origin = base.read(file);
if (optional.hook) {
origin = optional.hook(origin);
}
base.save(cacheFile, "(function (module, exports, require, __dirname, __filename) {" + origin + "});");
// 使用 load 可以保留行号和文件名称
var obj = load(cacheFile);
try {
base.delete(cacheFile);
} catch (ex) {
cacheFile.deleteOnExit();
}
return obj;
}
/**
* 编译模块
* @param id 模块ID
* @param name 模块名称
* @param file 模块文件
* @param optional 附加选项
* @returns {Object}
*/
2017-10-14 06:27:22 +00:00
function compileModule(id, name, file, optional) {
console.debug('加载模块', name, '位于', id, 'Optional', JSON.stringify(optional));
// noinspection JSUnresolvedVariable
var module = {
id: id,
exports: {},
loaded: false,
require: exports(file.parentFile)
};
try {
2017-10-14 06:27:22 +00:00
var compiledWrapper = compileJs(file, optional);
compiledWrapper.apply(module.exports, [
module, module.exports, module.require, file.parentFile, file
]);
console.debug('模块', name, '编译成功!');
module.loaded = true;
} catch (ex) {
console.console('§4警告! §c模块§a', name, '§c编译失败! §4ERR:', ex);
console.ex(ex);
}
return module;
}
function _isFile(file) {
return file.isFile && file.isFile();
}
/**
* 获得文件规范路径
* @param file
* @returns {*}
* @private
*/
function _canonical(file) {
// noinspection JSUnresolvedVariable
return file.canonicalPath;
}
function _cacheFile(file) {
return cacheDir + separatorChar + file.name;
}
/**
* 加载模块
* @param name 模块名称
* @param path 路径
* @param optional 附加选项
* @returns {*}
* @private
*/
2017-10-14 06:27:22 +00:00
function _require(name, path, optional) {
var file = new File(name);
file = _isFile(file) ? file : resolve(name, path);
if (file === undefined) {
console.console('§c模块§a', name, '§c加载失败! §4未找到该模块!');
return {exports: {}};
}
if (!optional) optional = {cache: true};
2017-10-11 17:39:51 +00:00
// 重定向文件名称和类型
name = file.name.split(".")[0];
var id = _canonical(file);
var module = cacheModules[id];
2017-10-14 06:27:22 +00:00
if (optional.cache && module) {
return module;
}
2017-10-14 06:27:22 +00:00
cacheModules[id] = module = compileModule(id, name, file, optional);
return module;
}
/**
* 闭包方法
* @param parent 父目录
* @returns {Function}
*/
function exports(parent) {
2017-10-14 06:27:22 +00:00
return function (path, optional) {
return _require(path, parent, optional).exports;
};
}
var cacheDir = parent + separatorChar + "runtime";
// 判断是否存在 isFile 不存在说明 parent 是一个字符串 需要转成File
if (parent.isFile) {
parent = new File(parent);
}
var cacheModules = [];
console.debug("初始化 require 模块组件 父目录 ", _canonical(parent));
return exports(parent);
});