如何为 r.js 提供伪文件系统? [英] How can I provide a pseudo file system for r.js?

查看:54
本文介绍了如何为 r.js 提供伪文件系统?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

好的,所以 r.js 可以在 Rhino.这很棒.

Ok, so r.js can run on Rhino. Which is great.

去做它需要做的事情.

在rhino上它基本上使用java.io.Filejava.io.FileOutputStreamjava.io.FileInputStream来实现文件系统它需要做的修改.

On rhino it basically uses java.io.File, java.io.FileOutputStream and java.io.FileInputStream to achieve the filesystem modifications that it needs to do.

(背景:我致力于为基于 Maven 的 Java/Javascript 开发人员提供更好的开发体验.作为 Maven,有约定俗成的力量和自以为是的力量.您可以在 jszip.org.)

(Background: I am working on delivering a better development experience for Maven based Java/Javascript developers. Being Maven, there is the power of convention and the power of being opinionated. You can see the progress at jszip.org.)

所以我想要做的是让磁盘结构神奇地显示为虚拟文件系统.

So what I want to do is have the on-disk structure appear by magic as a virtual file system.

所以在磁盘上我们将有这样的结构:

So on disk we will have a structure like so:

/
/module1/src/main/js/controllers/controller.js
/module2/src/main/js/models/model.js
/module3/src/main/js/views/view.js
/webapp/src/build/js/profile.js
/webapp/src/main/js/main.js
/webapp/src/main/webapp/index.html

/webapp/src/build/js/profile.js 应该看起来像这样:

({
    appDir: "src",
    baseUrl:".",
    dir: "target",
    optimize: "closure",
    modules:[
        {
            name:"main"
        }
    ]
})

这样

  • 当 r.js 要求 new File("src/main.js") 我实际上会给它 new File("/webapp/src/main/js/main.js")

当它要求 new File("profile.js") 我会给它 new File("/webapp/src/build/js/profile.js")

when it asks for new File("profile.js") I will give it new File("/webapp/src/build/js/profile.js")

当它要求 new File("controllers/controller.js") 我会给它 new File("/module1/src/main/js/controllers/controller.js")

when it asks for new File("controllers/controller.js") I will give it new File("/module1/src/main/js/controllers/controller.js")

当它要求 new File("target") 我会给它 new File("/webapp/target/webapp-1.0-SNAPSHOT").

when it asks for new File("target") I will give it new File("/webapp/target/webapp-1.0-SNAPSHOT").

我可以编写所需的三个模拟类,即用于代替 java.io.Filejava.io.FileInputStream 的模拟类>java.io.FileOutputStream,

I have no issue writing the three mock classes required, i.e. the ones to use in place of java.io.File, java.io.FileInputStream and java.io.FileOutputStream,

一些问题,例如这个 的答案指向 ClassShutter 之类的东西,我可以看到我可以这样使用:

Some questions such as this have answers that point to things like ClassShutter, which I can see I could use like this:

        context.setClassShutter(new ClassShutter() {
            public boolean visibleToScripts(String fullClassName) {
                if (File.class.getName().equals(fullClassName)) return false;
                if (FileOutputStream.class.getName().equals(fullClassName)) return false;
                if (FileInputStream.class.getName().equals(fullClassName)) return false;
                return true;
            }
        });

隐藏原始实现.

问题是让 Rhino 解决沙盒等效项......我一直在得到

The problem is then getting Rhino to resolve the sandboxed equivalents... I keep on getting

TypeError: [JavaPackage java.io.File] is not a function, it is object.

即使我在调用前加上 java.io.File = org.jszip.rhino.SandboxFile 将我的沙盒实现映射到现在丢失的 java.io.File

Even if I prefix the call with a prior execution of java.io.File = org.jszip.rhino.SandboxFile map my sandboxed implementation over the now missing java.io.File

我什至可以考虑在编译之前对加载的 r.js 文件使用搜索和替换...但我觉得必须有更好的方法.

I could even consider using search and replace on the loaded r.js file just prior to compiling it... but I feel there must be a better way.

有人有任何提示吗?

推荐答案

好的,经过多次实验,这似乎是这样做的方法:

Ok, after much experimentation, this seems to be the way to do this:

Scriptable scope = context.newObject(global);
scope.setPrototype(global);
scope.setParentScope(null);

NativeJavaTopPackage $packages = (NativeJavaTopPackage) global.get("Packages");
NativeJavaPackage $java = (NativeJavaPackage) $packages.get("java");
NativeJavaPackage $java_io = (NativeJavaPackage) $java.get("io");

ProxyNativeJavaPackage proxy$java = new ProxyNativeJavaPackage($java);
ProxyNativeJavaPackage proxy$java_io = new ProxyNativeJavaPackage($java_io);
proxy$java_io.put("File", scope, get(scope, "Packages." + PseudoFile.class.getName()));
proxy$java_io.put("FileInputStream", scope,
        get(scope, "Packages." + PseudoFileInputStream.class.getName()));
proxy$java_io.put("FileOutputStream", scope,
        get(scope, "Packages." + PseudoFileOutputStream.class.getName()));
proxy$java.put("io", scope, proxy$java_io);
scope.put("java", scope, proxy$java);

有一个辅助方法:

private static Object get(Scriptable scope, String name) {
    Scriptable cur = scope;
    for (String part : StringUtils.split(name, ".")) {
        Object next = cur.get(part, scope);
        if (next instanceof Scriptable) {
            cur = (Scriptable) next;
        } else {
            return null;
        }
    }
    return cur;
}

其中 ProxyNativeJavaPackage 类似于

public class ProxyNativeJavaPackage extends ScriptableObject implements Serializable {
    static final long serialVersionUID = 1L;

    protected final NativeJavaPackage delegate;
    private final Map<String, Object> mutations = new HashMap<String, Object>();

    public ProxyNativeJavaPackage(NativeJavaPackage delegate) {
        delegate.getClass();
        this.delegate = delegate;
    }

    @Override
    public String getClassName() {
        return delegate.getClassName();
    }

    @Override
    public boolean has(String id, Scriptable start) {
        return mutations.containsKey(id) ? mutations.get(id) != null : delegate.has(id, start);
    }

    @Override
    public boolean has(int index, Scriptable start) {
        return delegate.has(index, start);
    }

    @Override
    public void put(String id, Scriptable start, Object value) {
        mutations.put(id, value);
    }

    @Override
    public void put(int index, Scriptable start, Object value) {
        delegate.put(index, start, value);
    }

    @Override
    public Object get(String id, Scriptable start) {
        if (mutations.containsKey(id)) {
            return mutations.get(id);
        }
        return delegate.get(id, start);
    }

    @Override
    public Object get(int index, Scriptable start) {
        return delegate.get(index, start);
    }

    @Override
    public Object getDefaultValue(Class<?> ignored) {
        return toString();
    }

    @Override
    public String toString() {
        return delegate.toString();
    }

    @Override
    public boolean equals(Object obj) {
        if (obj instanceof ProxyNativeJavaPackage) {
            ProxyNativeJavaPackage that = (ProxyNativeJavaPackage) obj;
            return delegate.equals(that.delegate) && mutations.equals(that.mutations);
        }
        return false;
    }

    @Override
    public int hashCode() {
        return delegate.hashCode();
    }
}

这仍然保留了 Packages.java.io.File 等中的原始类,但是对于 r.js 要求,这已经足够了,并且应该可以其他人将此技巧扩展到一般情况.

That still leaves the original classes at Packages.java.io.File etc, but for the r.js requirement this is sufficient, and it should be possible for others to extend this trick to the general case.

这篇关于如何为 r.js 提供伪文件系统?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆