From 61beabadb34defa2f1d2f3218cb1684ce40e15fe Mon Sep 17 00:00:00 2001 From: hyb1996 <946994919@qq.com> Date: Thu, 6 Sep 2018 23:18:45 +0800 Subject: [PATCH] fix: require() supports npm module --- autojs/src/main/assets/.vscode/launch.json | 14 - autojs/src/main/assets/init.js | 19 +- autojs/src/main/assets/jvm-npm.js | 294 ++++++++++++++++++ .../inflater/inflaters/TabLayoutInflater.java | 2 - .../stardust/autojs/runtime/api/Files.java | 16 + 5 files changed, 315 insertions(+), 30 deletions(-) delete mode 100644 autojs/src/main/assets/.vscode/launch.json create mode 100644 autojs/src/main/assets/jvm-npm.js diff --git a/autojs/src/main/assets/.vscode/launch.json b/autojs/src/main/assets/.vscode/launch.json deleted file mode 100644 index 897fbc7a..00000000 --- a/autojs/src/main/assets/.vscode/launch.json +++ /dev/null @@ -1,14 +0,0 @@ -{ - // 使用 IntelliSense 了解相关属性。 - // 悬停以查看现有属性的描述。 - // 欲了解更多信息,请访问: https://go.microsoft.com/fwlink/?linkid=830387 - "version": "0.2.0", - "configurations": [ - { - "type": "node", - "request": "launch", - "name": "启动程序", - "program": "${file}" - } - ] -} \ No newline at end of file diff --git a/autojs/src/main/assets/init.js b/autojs/src/main/assets/init.js index 1fa0fe37..3734cf50 100644 --- a/autojs/src/main/assets/init.js +++ b/autojs/src/main/assets/init.js @@ -72,21 +72,12 @@ runtime.init(); Image = com.stardust.autojs.core.image.ImageWrapper; //重定向require以便支持相对路径 + (function(){ - var __require__ = require; - var builtInModules = ["lodash.js"]; - global.require = function(path){ - if(!path.endsWith(".js")){ - path = path + ".js"; - } - if(builtInModules.indexOf(path) >= 0 && !files.exists(path)){ - return __require__(path); - } - if(path.startsWith("http://") || path.startsWith("https://")){ - return __require__(path); - } - return __require__(files.path(path)); - }; + var loadAssets = function(path){ + eval(files.readAssets(path)); + } + loadAssets("jvm-npm.js"); })(); diff --git a/autojs/src/main/assets/jvm-npm.js b/autojs/src/main/assets/jvm-npm.js new file mode 100644 index 00000000..25de71e6 --- /dev/null +++ b/autojs/src/main/assets/jvm-npm.js @@ -0,0 +1,294 @@ +/** + * Copyright 2014-2016 Red Hat, Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License") + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +// Since we intend to use the Function constructor. +/* jshint evil: true */ + +module = (typeof module === 'undefined') ? {} : module; + +(function () { + var builtInModules = ["lodash.js"]; + + var System = java.lang.System; + var Scanner = java.util.Scanner; + var File = java.io.File; + + NativeRequire = (typeof NativeRequire === 'undefined') ? {} : NativeRequire; + if (typeof require === 'function' && !NativeRequire.require) { + NativeRequire.require = require; + } + + function Module (id, parent, core) { + this.id = id; + this.core = core; + this.parent = parent; + this.children = []; + this.filename = id; + this.loaded = false; + + Object.defineProperty(this, 'exports', { + get: function () { + return this._exports; + }.bind(this), + set: function (val) { + Require.cache[this.filename] = val; + this._exports = val; + }.bind(this) + }); + this.exports = {}; + + if (parent && parent.children) parent.children.push(this); + + this.require = function (id) { + return Require(id, this); + }.bind(this); + } + + Module._load = function _load (file, parent, core, main) { + var module = new Module(file, parent, core); + var body = readFile(module.filename, module.core); + var dir = new File(module.filename).getParent(); + var func = new Function('exports', 'module', 'require', '__filename', '__dirname', body); + func.apply(module, + [module.exports, module, module.require, module.filename, dir]); + module.loaded = true; + module.main = main; + return module.exports; + }; + + Module.runMain = function runMain (main) { + var file = Require.resolve(main); + Module._load(file, undefined, false, true); + }; + + function Require (id, parent) { + var normalizePath = normalizeName(id); + if(builtInModules.indexOf(normalizePath) >= 0 && !files.exists(normalizePath)){ + return NativeRequire.require(normalizePath); + } + if(id.startsWith("http://") || id.startsWith("https://")){ + return NativeRequire.require(id); + } + + var core; + var native_; + var file = Require.resolve(id, parent); + + if (!file) { + if (typeof NativeRequire.require === 'function') { + if (Require.debug) { + System.out.println(['Cannot resolve', id, 'defaulting to native'].join(' ')); + } + native_ = NativeRequire.require(id); + if (native_) return native_; + } + System.err.println('Cannot find module ' + id); + throw new ModuleError('Cannot find module ' + id, 'MODULE_NOT_FOUND'); + } + + if (file.core) { + file = file.path; + core = true; + } + try { + if (Require.cache[file]) { + return Require.cache[file]; + } else if (file.endsWith('.js')) { + return Module._load(file, parent, core); + } else if (file.endsWith('.json')) { + return loadJSON(file); + } + } catch (ex) { + if (ex instanceof java.lang.Exception) { + throw new ModuleError('Cannot load module ' + id, 'LOAD_ERROR', ex); + } else { + System.out.println('Cannot load module ' + id + ' LOAD_ERROR'); + throw ex; + } + } + } + + Require.resolve = function (id, parent) { + var roots = findRoots(parent); + for (var i = 0; i < roots.length; ++i) { + var root = roots[i]; + var result = resolveCoreModule(id, root) || + resolveAsFile(id, root, '.js') || + resolveAsFile(id, root, '.json') || + resolveAsDirectory(id, root) || + resolveAsNodeModule(id, root); + if (result) { + return result; + } + } + return false; + }; + + Require.root = files.cwd();//System.getProperty('user.dir'); + Require.NODE_PATH = undefined; + + function findRoots (parent) { + var r = []; + r.push(findRoot(parent)); + return r.concat(Require.paths()); + } + + function parsePaths (paths) { + if (!paths) { + return []; + } + if (paths === '') { + return []; + } + var osName = java.lang.System.getProperty('os.name').toLowerCase(); + var separator; + + if (osName.indexOf('win') >= 0) { + separator = ';'; + } else { + separator = ':'; + } + + return paths.split(separator); + } + + Require.paths = function () { + var r = []; + r.push(java.lang.System.getProperty('user.home') + '/.node_modules'); + r.push(java.lang.System.getProperty('user.home') + '/.node_libraries'); + + if (Require.NODE_PATH) { + r = r.concat(parsePaths(Require.NODE_PATH)); + } else { + var NODE_PATH = java.lang.System.getenv().NODE_PATH; + if (NODE_PATH) { + r = r.concat(parsePaths(NODE_PATH)); + } + } + // r.push( $PREFIX + "/node/library" ) + return r; + }; + + function findRoot (parent) { + if (!parent || !parent.id) { return Require.root; } + var pathParts = parent.id.split(/[\/|\\,]+/g); + pathParts.pop(); + return pathParts.join('/'); + } + + Require.debug = true; + Require.cache = {}; + Require.extensions = {}; + require = Require; + + module.exports = Module; + + function loadJSON (file) { + var json = JSON.parse(readFile(file)); + Require.cache[file] = json; + return json; + } + + function resolveAsNodeModule (id, root) { + var base = [root, 'node_modules'].join('/'); + return resolveAsFile(id, base) || + resolveAsDirectory(id, base) || + (root ? resolveAsNodeModule(id, new File(root).getParent()) : false); + } + + function resolveAsDirectory (id, root) { + var base = [root, id].join('/'); + var file = new File([base, 'package.json'].join('/')); + if (file.exists()) { + try { + var body = readFile(file.getCanonicalPath()); + var package_ = JSON.parse(body); + if (package_.main) { + return (resolveAsFile(package_.main, base) || + resolveAsDirectory(package_.main, base)); + } + // if no package.main exists, look for index.js + return resolveAsFile('index.js', base); + } catch (ex) { + throw new ModuleError('Cannot load JSON file', 'PARSE_ERROR', ex); + } + } + return resolveAsFile('index.js', base); + } + + function resolveAsFile (id, root, ext) { + var file; + if (id.length > 0 && id[0] === '/') { + file = new File(normalizeName(id, ext)); + if (!file.exists()) { + return resolveAsDirectory(id); + } + } else { + file = new File([root, normalizeName(id, ext)].join('/')); + } + if (file.exists()) { + return file.getCanonicalPath(); + } + } + + function resolveCoreModule (id, root) { + var name = normalizeName(id); + var classloader = java.lang.Thread.currentThread().getContextClassLoader(); + if (classloader.getResource(name)) { + return { path: name, core: true }; + } + } + + function normalizeName (fileName, ext) { + var extension = ext || '.js'; + if (fileName.endsWith(extension)) { + return fileName; + } + return fileName + extension; + } + + function readFile (filename, core) { + var input; + try { + if (core) { + var classloader = java.lang.Thread.currentThread().getContextClassLoader(); + input = classloader.getResourceAsStream(filename); + } else { + input = new File(filename); + } + // TODO: I think this is not very efficient + return new Scanner(input).useDelimiter('\\A').next(); + } catch (e) { + throw new ModuleError('Cannot read file [' + input + ']: ', 'IO_ERROR', e); + } + } + + function ModuleError (message, code, cause) { + this.code = code || 'UNDEFINED'; + this.message = message || 'Error loading module'; + this.cause = cause; + } + + // Helper function until ECMAScript 6 is complete + if (typeof String.prototype.endsWith !== 'function') { + String.prototype.endsWith = function (suffix) { + if (!suffix) return false; + return this.indexOf(suffix, this.length - suffix.length) !== -1; + }; + } + + ModuleError.prototype = new Error(); + ModuleError.prototype.constructor = ModuleError; +}()); \ No newline at end of file diff --git a/autojs/src/main/java/com/stardust/autojs/core/ui/inflater/inflaters/TabLayoutInflater.java b/autojs/src/main/java/com/stardust/autojs/core/ui/inflater/inflaters/TabLayoutInflater.java index 164a8093..cbb3d309 100644 --- a/autojs/src/main/java/com/stardust/autojs/core/ui/inflater/inflaters/TabLayoutInflater.java +++ b/autojs/src/main/java/com/stardust/autojs/core/ui/inflater/inflaters/TabLayoutInflater.java @@ -1,6 +1,5 @@ package com.stardust.autojs.core.ui.inflater.inflaters; -import android.content.Context; import android.content.res.ColorStateList; import android.graphics.Color; import android.support.annotation.Nullable; @@ -15,7 +14,6 @@ import com.stardust.autojs.core.ui.inflater.util.Colors; import com.stardust.autojs.core.ui.inflater.util.Dimensions; import com.stardust.autojs.core.ui.inflater.util.Gravities; import com.stardust.autojs.core.ui.inflater.util.ValueMapper; -import com.stardust.autojs.core.ui.widget.JsTabLayout; import java.util.Map; diff --git a/autojs/src/main/java/com/stardust/autojs/runtime/api/Files.java b/autojs/src/main/java/com/stardust/autojs/runtime/api/Files.java index 0cf659d5..15635ad4 100644 --- a/autojs/src/main/java/com/stardust/autojs/runtime/api/Files.java +++ b/autojs/src/main/java/com/stardust/autojs/runtime/api/Files.java @@ -4,9 +4,11 @@ import com.stardust.autojs.engine.ScriptEngine; import com.stardust.autojs.runtime.ScriptRuntime; import com.stardust.pio.PFileInterface; import com.stardust.pio.PFiles; +import com.stardust.pio.UncheckedIOException; import com.stardust.util.Func1; import java.io.File; +import java.io.IOException; /** * Created by Stardust on 2018/1/23. @@ -82,10 +84,23 @@ public class Files { return PFiles.read(path(path), encoding); } + public String read(String path) { return PFiles.read(path(path)); } + public String readAssets(String path, String encoding){ + try { + return PFiles.read(mRuntime.getUiHandler().getContext().getAssets().open(path), encoding); + } catch (IOException e) { + throw new UncheckedIOException(e); + } + } + + public String readAssets(String path){ + return readAssets(path, "UTF-8"); + } + public byte[] readBytes(String path){ return PFiles.readBytes(path(path)); } @@ -185,4 +200,5 @@ public class Files { public String getSimplifiedPath(String path) { return PFiles.getSimplifiedPath(path); } + }