简体   繁体   English

如何为 r.js 提供伪文件系统?

[英]How can I provide a pseudo file system for r.js?

Ok, so r.js can run on Rhino .好的,这样r.js就可以在Rhino 上运行了。 Which is great.这很棒。

To do the stuff it needs to do.做它需要做的事情。

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.在犀牛上,它基本上使用java.io.Filejava.io.FileOutputStreamjava.io.FileInputStream来实现它需要做的文件系统修改。

(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 .) (背景:我致力于为基于 Maven 的 Java/Javascript 开发人员提供更好的开发体验。作为 Maven,有约定俗成的力量和自以为是的力量。你可以在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

The /webapp/src/build/js/profile.js should look something like this: /webapp/src/build/js/profile.js应该是这样的:

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

Such that这样的

  • when r.js asks for new File("src/main.js") I will actually give it new File("/webapp/src/main/js/main.js")当 r.js 请求new File("src/main.js")我实际上会给它new File("/webapp/src/main/js/main.js")

  • when it asks for new File("profile.js") I will give it new File("/webapp/src/build/js/profile.js")当它要求new File("profile.js")我会给它new File("/webapp/src/build/js/profile.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("controllers/controller.js")我会给它new File("/module1/src/main/js/controllers/controller.js")

  • when it asks for new File("target") I will give it new File("/webapp/target/webapp-1.0-SNAPSHOT") .当它要求new File("target")我会给它new File("/webapp/target/webapp-1.0-SNAPSHOT")

I have no issue writing the three mock classes required, ie the ones to use in place of java.io.File , java.io.FileInputStream and java.io.FileOutputStream ,我编写所需的三个模拟类没有问题,即用于代替java.io.Filejava.io.FileInputStreamjava.io.FileOutputStream

Some questions such as this have answers that point to things like ClassShutter, which I can see I could use like this:有些问题,比如有答案那点东西像ClassShutter,我可以看到我可以用这样的:

        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;
            }
        });

To hide the original implementations.隐藏原始实现。

The problem is then getting Rhino to resolve the sandboxed equivalents... I keep on getting然后问题是让 Rhino 解决沙盒等效项......我一直在得到

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

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即使我在调用之前执行java.io.File = org.jszip.rhino.SandboxFile将我的沙盒实现映射到现在丢失的java.io.File

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

Does anyone have any hints?有人有任何提示吗?

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);

There is a helper method:有一个辅助方法:

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;
}

And where ProxyNativeJavaPackage is something likeProxyNativeJavaPackage就像

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();
    }
}

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.这仍然保留Packages.java.io.File等中的原始类,但对于r.js要求,这已经足够了,其他人应该可以将此技巧扩展到一般情况。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM