使用browserify 解决 graalvm js 引擎加载js 模块的问题

browserify 可以实现模块化处理,同时合并依赖在一个文件中,有好处也有坏处,以下是一个尝试

环境准备

  • pom.xml
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>
    <groupId>com.dalong.ex</groupId>
    <artifactId>qlex-learnint</artifactId>
    <version>1.0-SNAPSHOT</version>
    <properties>
        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
        <encoding>UTF-8</encoding>
        <java.version>1.8</java.version>
        <maven.compiler.source>1.8</maven.compiler.source>
        <maven.compiler.target>1.8</maven.compiler.target>
    </properties>
    <dependencies>
        <dependency>
            <groupId>com.alibaba</groupId>
            <artifactId>QLExpress</artifactId>
            <version>3.2.0</version>
        </dependency>
<!--        如果使用了js-scriptengine 以下可选-->
<!--        <dependency>-->
<!--            <groupId>org.graalvm.truffle</groupId>-->
<!--            <artifactId>truffle-api</artifactId>-->
<!--            <version>20.2.0</version>-->
<!--        </dependency>-->
<!--        <dependency>-->
<!--            <groupId>org.graalvm.sdk</groupId>-->
<!--            <artifactId>graal-sdk</artifactId>-->
<!--            <version>20.2.0</version>-->
<!--        </dependency>-->
        <dependency>
            <groupId>org.graalvm.js</groupId>
            <artifactId>js-scriptengine</artifactId>
            <version>20.2.0</version>
        </dependency>
        <dependency>
            <groupId>org.graalvm.js</groupId>
            <artifactId>js</artifactId>
            <version>20.2.0</version>
        </dependency>
    </dependencies>
    <build>
        <!-- Maven Shade Plugin -->
        <finalName>my-expression-app</finalName>
        <plugins>
            <plugin>
                <groupId>org.apache.maven.plugins</groupId>
                <artifactId>maven-shade-plugin</artifactId>
                <version>2.3</version>
                <executions>
                    <execution>
                        <phase>package</phase>
                        <goals>
                            <goal>shade</goal>
                        </goals>
                        <configuration>
                            <transformers>
                                <transformer implementation="org.apache.maven.plugins.shade.resource.ManifestResourceTransformer">
                                    <mainClass>Application</mainClass>
                                </transformer>
                            </transformers>
                        </configuration>
                    </execution>
                </executions>
            </plugin>
        </plugins>
    </build>
</project>
 
 
  • 代码
    application.java
 
import org.graalvm.polyglot.*;
import java.io.IOException;
/**
 @author dalong
 */
public class Application {
    public static void main(String[] args) throws ScriptException, NoSuchMethodException, IOException {
        method4();
    }
    public  static void method4() throws IOException {
        Value value =null;
        // this not work current only support localfs
        Source mysource =Source.newBuilder("js","(function(){function r(e,n,t){function o(i,f){if(!n[i]){if(!e[i]){var c=\"function\"==typeof require&&require;if(!f&&c)return c(i,!0);if(u)return u(i,!0);var a=new Error(\"Cannot find module '\"+i+\"'\");throw a.code=\"MODULE_NOT_FOUND\",a}var p=n[i]={exports:{}};e[i][0].call(p.exports,function(r){var n=e[i][1][r];return o(n||r)},p,p.exports,r,e,n,t)}return n[i].exports}for(var u=\"function\"==typeof require&&require,i=0;i<t.length;i++)o(t[i]);return o}return r})()({1:[function(require,module,exports){\n" +
                "function demo() {\n" +
                "    if (typeof Graal != 'undefined') {\n" +
                "        print(Graal.versionJS);\n" +
                "        print(Graal.versionGraalVM);\n" +
                "        print(Graal.isGraalRuntime);\n" +
                "        var BigDec = Java.type('java.math.BigDecimal');\n" +
                "        var bd = new BigDec(\"0.1\");\n" +
                "        console.log(bd.add(bd).toString());\n" +
                "    }\n" +
                "    return \"dalong rong feng\"\n" +
                "}\n" +
                "module.exports = demo\n" +
                "},{}],2:[function(require,module,exports){\n" +
                "const  demo =  require(\"./app\")\n" +
                "const  shortid  = require(\"shortid\")\n" +
                "console.log(demo())\n" +
                "console.log(shortid.generate())\n" +
                "\n" +
                "},{\"./app\":1,\"shortid\":4}],3:[function(require,module,exports){\n" +
                "// This file replaces `format.js` in bundlers like webpack or Rollup,\n" +
                "// according to `browser` config in `package.json`.\n" +
                "\n" +
                "module.exports = function (random, alphabet, size) {\n" +
                "  // We can’t use bytes bigger than the alphabet. To make bytes values closer\n" +
                "  // to the alphabet, we apply bitmask on them. We look for the closest\n" +
                "  // `2 ** x - 1` number, which will be bigger than alphabet size. If we have\n" +
                "  // 30 symbols in the alphabet, we will take 31 (00011111).\n" +
                "  // We do not use faster Math.clz32, because it is not available in browsers.\n" +
                "  var mask = (2 << Math.log(alphabet.length - 1) / Math.LN2) - 1\n" +
                "  // Bitmask is not a perfect solution (in our example it will pass 31 bytes,\n" +
                "  // which is bigger than the alphabet). As a result, we will need more bytes,\n" +
                "  // than ID size, because we will refuse bytes bigger than the alphabet.\n" +
                "\n" +
                "  // Every hardware random generator call is costly,\n" +
                "  // because we need to wait for entropy collection. This is why often it will\n" +
                "  // be faster to ask for few extra bytes in advance, to avoid additional calls.\n" +
                "\n" +
                "  // Here we calculate how many random bytes should we call in advance.\n" +
                "  // It depends on ID length, mask / alphabet size and magic number 1.6\n" +
                "  // (which was selected according benchmarks).\n" +
                "\n" +
                "  // -~f => Math.ceil(f) if n is float number\n" +
                "  // -~i => i + 1 if n is integer number\n" +
                "  var step = -~(1.6 * mask * size / alphabet.length)\n" +
                "  var id = ''\n" +
                "\n" +
                "  while (true) {\n" +
                "    var bytes = random(step)\n" +
                "    // Compact alternative for `for (var i = 0; i < step; i++)`\n" +
                "    var i = step\n" +
                "    while (i--) {\n" +
                "      // If random byte is bigger than alphabet even after bitmask,\n" +
                "      // we refuse it by `|| ''`.\n" +
                "      id += alphabet[bytes[i] & mask] || ''\n" +
                "      // More compact than `id.length + 1 === size`\n" +
                "      if (id.length === +size) return id\n" +
                "    }\n" +
                "  }\n" +
                "}\n" +
                "\n" +
                "},{}],4:[function(require,module,exports){\n" +
                "'use strict';\n" +
                "module.exports = require('./lib/index');\n" +
                "\n" +
                "},{\"./lib/index\":8}],5:[function(require,module,exports){\n" +
                "'use strict';\n" +
                "\n" +
                "var randomFromSeed = require('./random/random-from-seed');\n" +
                "\n" +
                "var ORIGINAL = '0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ_-';\n" +
                "var alphabet;\n" +
                "var previousSeed;\n" +
                "\n" +
                "var shuffled;\n" +
                "\n" +
                "function reset() {\n" +
                "    shuffled = false;\n" +
                "}\n" +
                "\n" +
                "function setCharacters(_alphabet_) {\n" +
                "    if (!_alphabet_) {\n" +
                "        if (alphabet !== ORIGINAL) {\n" +
                "            alphabet = ORIGINAL;\n" +
                "            reset();\n" +
                "        }\n" +
                "        return;\n" +
                "    }\n" +
                "\n" +
                "    if (_alphabet_ === alphabet) {\n" +
                "        return;\n" +
                "    }\n" +
                "\n" +
                "    if (_alphabet_.length !== ORIGINAL.length) {\n" +
                "        throw new Error('Custom alphabet for shortid must be ' + ORIGINAL.length + ' unique characters. You submitted ' + _alphabet_.length + ' characters: ' + _alphabet_);\n" +
                "    }\n" +
                "\n" +
                "    var unique = _alphabet_.split('').filter(function(item, ind, arr){\n" +
                "       return ind !== arr.lastIndexOf(item);\n" +
                "    });\n" +
                "\n" +
                "    if (unique.length) {\n" +
                "        throw new Error('Custom alphabet for shortid must be ' + ORIGINAL.length + ' unique characters. These characters were not unique: ' + unique.join(', '));\n" +
                "    }\n" +
                "\n" +
                "    alphabet = _alphabet_;\n" +
                "    reset();\n" +
                "}\n" +
                "\n" +
                "function characters(_alphabet_) {\n" +
                "    setCharacters(_alphabet_);\n" +
                "    return alphabet;\n" +
                "}\n" +
                "\n" +
                "function setSeed(seed) {\n" +
                "    randomFromSeed.seed(seed);\n" +
                "    if (previousSeed !== seed) {\n" +
                "        reset();\n" +
                "        previousSeed = seed;\n" +
                "    }\n" +
                "}\n" +
                "\n" +
                "function shuffle() {\n" +
                "    if (!alphabet) {\n" +
                "        setCharacters(ORIGINAL);\n" +
                "    }\n" +
                "\n" +
                "    var sourceArray = alphabet.split('');\n" +
                "    var targetArray = [];\n" +
                "    var r = randomFromSeed.nextValue();\n" +
                "    var characterIndex;\n" +
                "\n" +
                "    while (sourceArray.length > 0) {\n" +
                "        r = randomFromSeed.nextValue();\n" +
                "        characterIndex = Math.floor(r * sourceArray.length);\n" +
                "        targetArray.push(sourceArray.splice(characterIndex, 1)[0]);\n" +
                "    }\n" +
                "    return targetArray.join('');\n" +
                "}\n" +
                "\n" +
                "function getShuffled() {\n" +
                "    if (shuffled) {\n" +
                "        return shuffled;\n" +
                "    }\n" +
                "    shuffled = shuffle();\n" +
                "    return shuffled;\n" +
                "}\n" +
                "\n" +
                "/**\n" +
                " * lookup shuffled letter\n" +
                " * @param index\n" +
                " * @returns {string}\n" +
                " */\n" +
                "function lookup(index) {\n" +
                "    var alphabetShuffled = getShuffled();\n" +
                "    return alphabetShuffled[index];\n" +
                "}\n" +
                "\n" +
                "function get () {\n" +
                "  return alphabet || ORIGINAL;\n" +
                "}\n" +
                "\n" +
                "module.exports = {\n" +
                "    get: get,\n" +
                "    characters: characters,\n" +
                "    seed: setSeed,\n" +
                "    lookup: lookup,\n" +
                "    shuffled: getShuffled\n" +
                "};\n" +
                "\n" +
                "},{\"./random/random-from-seed\":11}],6:[function(require,module,exports){\n" +
                "'use strict';\n" +
                "\n" +
                "var generate = require('./generate');\n" +
                "var alphabet = require('./alphabet');\n" +
                "\n" +
                "// Ignore all milliseconds before a certain time to reduce the size of the date entropy without sacrificing uniqueness.\n" +
                "// This number should be updated every year or so to keep the generated id short.\n" +
                "// To regenerate `new Date() - 0` and bump the version. Always bump the version!\n" +
                "var REDUCE_TIME = 1567752802062;\n" +
                "\n" +
                "// don't change unless we change the algos or REDUCE_TIME\n" +
                "// must be an integer and less than 16\n" +
                "var version = 7;\n" +
                "\n" +
                "// Counter is used when shortid is called multiple times in one second.\n" +
                "var counter;\n" +
                "\n" +
                "// Remember the last time shortid was called in case counter is needed.\n" +
                "var previousSeconds;\n" +
                "\n" +
                "/**\n" +
                " * Generate unique id\n" +
                " * Returns string id\n" +
                " */\n" +
                "function build(clusterWorkerId) {\n" +
                "    var str = '';\n" +
                "\n" +
                "    var seconds = Math.floor((Date.now() - REDUCE_TIME) * 0.001);\n" +
                "\n" +
                "    if (seconds === previousSeconds) {\n" +
                "        counter++;\n" +
                "    } else {\n" +
                "        counter = 0;\n" +
                "        previousSeconds = seconds;\n" +
                "    }\n" +
                "\n" +
                "    str = str + generate(version);\n" +
                "    str = str + generate(clusterWorkerId);\n" +
                "    if (counter > 0) {\n" +
                "        str = str + generate(counter);\n" +
                "    }\n" +
                "    str = str + generate(seconds);\n" +
                "    return str;\n" +
                "}\n" +
                "\n" +
                "module.exports = build;\n" +
                "\n" +
                "},{\"./alphabet\":5,\"./generate\":7}],7:[function(require,module,exports){\n" +
                "'use strict';\n" +
                "\n" +
                "var alphabet = require('./alphabet');\n" +
                "var random = require('./random/random-byte');\n" +
                "var format = require('nanoid/format');\n" +
                "\n" +
                "function generate(number) {\n" +
                "    var loopCounter = 0;\n" +
                "    var done;\n" +
                "\n" +
                "    var str = '';\n" +
                "\n" +
                "    while (!done) {\n" +
                "        str = str + format(random, alphabet.get(), 1);\n" +
                "        done = number < (Math.pow(16, loopCounter + 1 ) );\n" +
                "        loopCounter++;\n" +
                "    }\n" +
                "    return str;\n" +
                "}\n" +
                "\n" +
                "module.exports = generate;\n" +
                "\n" +
                "},{\"./alphabet\":5,\"./random/random-byte\":10,\"nanoid/format\":3}],8:[function(require,module,exports){\n" +
                "'use strict';\n" +
                "\n" +
                "var alphabet = require('./alphabet');\n" +
                "var build = require('./build');\n" +
                "var isValid = require('./is-valid');\n" +
                "\n" +
                "// if you are using cluster or multiple servers use this to make each instance\n" +
                "// has a unique value for worker\n" +
                "// Note: I don't know if this is automatically set when using third\n" +
                "// party cluster solutions such as pm2.\n" +
                "var clusterWorkerId = require('./util/cluster-worker-id') || 0;\n" +
                "\n" +
                "/**\n" +
                " * Set the seed.\n" +
                " * Highly recommended if you don't want people to try to figure out your id schema.\n" +
                " * exposed as shortid.seed(int)\n" +
                " * @param seed Integer value to seed the random alphabet.  ALWAYS USE THE SAME SEED or you might get overlaps.\n" +
                " */\n" +
                "function seed(seedValue) {\n" +
                "    alphabet.seed(seedValue);\n" +
                "    return module.exports;\n" +
                "}\n" +
                "\n" +
                "/**\n" +
                " * Set the cluster worker or machine id\n" +
                " * exposed as shortid.worker(int)\n" +
                " * @param workerId worker must be positive integer.  Number less than 16 is recommended.\n" +
                " * returns shortid module so it can be chained.\n" +
                " */\n" +
                "function worker(workerId) {\n" +
                "    clusterWorkerId = workerId;\n" +
                "    return module.exports;\n" +
                "}\n" +
                "\n" +
                "/**\n" +
                " *\n" +
                " * sets new characters to use in the alphabet\n" +
                " * returns the shuffled alphabet\n" +
                " */\n" +
                "function characters(newCharacters) {\n" +
                "    if (newCharacters !== undefined) {\n" +
                "        alphabet.characters(newCharacters);\n" +
                "    }\n" +
                "\n" +
                "    return alphabet.shuffled();\n" +
                "}\n" +
                "\n" +
                "/**\n" +
                " * Generate unique id\n" +
                " * Returns string id\n" +
                " */\n" +
                "function generate() {\n" +
                "  return build(clusterWorkerId);\n" +
                "}\n" +
                "\n" +
                "// Export all other functions as properties of the generate function\n" +
                "module.exports = generate;\n" +
                "module.exports.generate = generate;\n" +
                "module.exports.seed = seed;\n" +
                "module.exports.worker = worker;\n" +
                "module.exports.characters = characters;\n" +
                "module.exports.isValid = isValid;\n" +
                "\n" +
                "},{\"./alphabet\":5,\"./build\":6,\"./is-valid\":9,\"./util/cluster-worker-id\":12}],9:[function(require,module,exports){\n" +
                "'use strict';\n" +
                "var alphabet = require('./alphabet');\n" +
                "\n" +
                "function isShortId(id) {\n" +
                "    if (!id || typeof id !== 'string' || id.length < 6 ) {\n" +
                "        return false;\n" +
                "    }\n" +
                "\n" +
                "    var nonAlphabetic = new RegExp('[^' +\n" +
                "      alphabet.get().replace(/[|\\\\\\{}()[\\]^$+*?.-]/g, '\\\\$&') +\n" +
                "    ']');\n" +
                "    return !nonAlphabetic.test(id);\n" +
                "}\n" +
                "\n" +
                "module.exports = isShortId;\n" +
                "\n" +
                "},{\"./alphabet\":5}],10:[function(require,module,exports){\n" +
                "'use strict';\n" +
                "\n" +
                "var crypto = typeof window === 'object' && (window.crypto || window.msCrypto); // IE 11 uses window.msCrypto\n" +
                "\n" +
                "var randomByte;\n" +
                "\n" +
                "if (!crypto || !crypto.getRandomValues) {\n" +
                "    randomByte = function(size) {\n" +
                "        var bytes = [];\n" +
                "        for (var i = 0; i < size; i++) {\n" +
                "            bytes.push(Math.floor(Math.random() * 256));\n" +
                "        }\n" +
                "        return bytes;\n" +
                "    };\n" +
                "} else {\n" +
                "    randomByte = function(size) {\n" +
                "        return crypto.getRandomValues(new Uint8Array(size));\n" +
                "    };\n" +
                "}\n" +
                "\n" +
                "module.exports = randomByte;\n" +
                "\n" +
                "},{}],11:[function(require,module,exports){\n" +
                "'use strict';\n" +
                "\n" +
                "// Found this seed-based random generator somewhere\n" +
                "// Based on The Central Randomizer 1.3 (C) 1997 by Paul Houle (houle@msc.cornell.edu)\n" +
                "\n" +
                "var seed = 1;\n" +
                "\n" +
                "/**\n" +
                " * return a random number based on a seed\n" +
                " * @param seed\n" +
                " * @returns {number}\n" +
                " */\n" +
                "function getNextValue() {\n" +
                "    seed = (seed * 9301 + 49297) % 233280;\n" +
                "    return seed/(233280.0);\n" +
                "}\n" +
                "\n" +
                "function setSeed(_seed_) {\n" +
                "    seed = _seed_;\n" +
                "}\n" +
                "\n" +
                "module.exports = {\n" +
                "    nextValue: getNextValue,\n" +
                "    seed: setSeed\n" +
                "};\n" +
                "\n" +
                "},{}],12:[function(require,module,exports){\n" +
                "'use strict';\n" +
                "\n" +
                "module.exports = 0;\n" +
                "\n" +
                "},{}]},{},[2]);\n","demoeeee").mimeType("application/javascript+module").build();
        try (Context context = Context.newBuilder().allowAllAccess(true).build()) {
            value = context.parse(mysource);
            value.execute();
        } catch (PolyglotException e) {
            if (e.isSyntaxError()) {
                SourceSection location = e.getSourceLocation();
            } else {
            }
            throw e;
        }
        finally {
        }
    }
}
 
  • js 代码生成说明
    上边的代码基于以下方式生成,代码使用了browserify,同时引用了一个其他的shortid 的npm包,同时基于browserify 将代码聚合在一起
    npm 项目结构

     

     


    app.js
    基于Graal 做兼容处理
 
function demo() {
    if (typeof Graal != 'undefined') {
        print(Graal.versionJS);
        print(Graal.versionGraalVM);
        print(Graal.isGraalRuntime);
        var BigDec = Java.type('java.math.BigDecimal');
        var bd = new BigDec("0.1");
        console.log(bd.add(bd).toString());
    }
    return "dalong rong feng"
}
module.exports = demo
 
 

demo.js

const  demo =  require("./app")
const  shortid  = require("shortid")
console.log(demo())
console.log(shortid.generate())

package.json

{
  "name": "mjs",
  "version": "1.0.0",
  "main": "index.js",
  "license": "MIT",
  "mjs": true,
  "scripts": {
    "main": "browserify  demo.js > main.js"
  },
  "devDependencies": {
    "browserify": "^16.5.2"
  },
  "dependencies": {
    "shortid": "^2.2.15"
  }
}
 

构建

yarn app
  • 运行效果

 

 

说明

以上是一个集成的试用,基于browserify 解决了模块化以及依赖管理的问题,代码很简单,实际那部分代码我们可以基于browserify 的api 生成,然后存储到
cache中,整体来说还是很灵活的,es4x 内部对于模块以及依赖的处理就是自己实现了一套类似 common js 的模式

参考资料

https://www.graalvm.org/reference-manual/js/JavaScriptCompatibility/
https://github.com/graalvm/graaljs/blob/master/docs/user/JavaScriptCompatibility.md
http://browserify.org/

posted on 2020-08-26 20:48  荣锋亮  阅读(619)  评论(0编辑  收藏  举报

导航