Code Monkey home page Code Monkey logo

Comments (6)

nebhale avatar nebhale commented on August 28, 2024

I'd submit that this isn't a great solution (but might lead to one), but you can use absolute module names safely. So instead of require('./foo') you can do require(__dirname + '/foo').

from wire.

briancavalier avatar briancavalier commented on August 28, 2024

Thanks for jumping in, @nebhale!

I tend to agree that it's not ideal. One trick is that we need a solution that allows spec-local module ids in both CommonJS/Node, and AMD, where something like __dirname won't be available. It's also tricky in that wire ultimately loads modules (i.e. calls require) from inside one of its own modules that lives in the wire package, so in that case, __dirname will be something like "path/to/node_modules/wire/lib/" rather than the dir containing the spec.

Like you said, though, if we continue to whittle away at ideas, we might find our way to something better.

@unscriptable and I were recently discussion the possibility of having wire programmatically install a node module loader extension (i.e. require.extensions), and try to intercept the loading of spec modules. I have no idea yet if that's even possible, but I plan to do some experiments soon. If you have any insight on `require.extensions, we'd love to hear :)

Let's keep bouncing ideas here, and I'll post the results of my experiments with require.extensions.

from wire.

briancavalier avatar briancavalier commented on August 28, 2024

After thinking about this more last night, I'll try to break the problem down a bit more, and highlight something that works today, but probably is not an ideal solution.

There are two levels of module loading that need to happen when a spec is wired. We need to load:

  1. The spec itself.
  2. Modules referenced from within the spec. In the most common case, these will be on the right hand side of a create or module factory. However, because wire plugins can define their own factories, it's possible that module ids could occur in other places in a wire spec. At least for now, let's ignore this fact :)

Loading the spec itself

The wire() function accepts either a string or an object (or an array of mixed string & object items, but let's also tackle that later). If a string, it is interpreted as a module id and passed directly to whatever loader wire has sniffed from the environment--which, practically speaking, will be a require() function in both Node and AMD that is local to something.

In practice, loading specs hasn't been a problem, since using wire! as an AMD plugin and specifying a module id relative to baseUrl has been convenient. It's possible that using wire() programmatically in AMD may have the same problem as Node ... which brings us to ...

Wiring specs in Node using a string is not intuitive, e.g. wire('path/to/spec') since it's not clear what the spec module id is relative to.

Interestingly, there seems to be an easy, and fairly natural solution to that: don't pass a string :) You can use the local require to load the spec and pass it as an object: wire(require('path/to/spec')) which makes it obvious, imho. That will also work in AMD as long as you pass a string literal to require, which is a well-known restriction of using require as an r-value in AMD.

Ok, so wire(require(string)) works today and seems reasonably intuitive in Node. It's unfortunate that wire(string) is less intuitive right now.

Loading modules referenced within the spec

The bigger problem is relative module ids within a spec, e.g. { create: './foo/Bar' }. It seems like the ideal would be for relative module ids within a spec always to be treated as if they are relative to the spec itself. It seems intuitive, and allows packaging up complex components (such as views, widgets, or entire sections of an application) into their own dir, with their own wire spec, and easily moving the whole bundle around the filesystem without breaking anything.

In order to make that work inside of wire, it would need to gain access to the require() function that is localized for the spec. As far as I know, that is impossible in AMD (@unscriptable: when wire executes as an AMD plugin, what require() is handed to it?), and is only possible in Node by implementing a require.extension, which is passed the module object whose module.require property is the localized require() for that module.

I'm planning to investigate doing something via a require.extension today.

However, there is something that may already work: Use require in the spec. I tried this in the node-paths branch of the hello-wire-node example, and it works, at least in that limited case.

Pretty cool that it just works, but there are some concerns:

  1. Will it work with all forms of create and module? One case I already know will be a problem is module that is a string (e.g. module.exports = "Hello world". While that's probably not very common, it would most likely cause both create and module to do the wrong thing--they would interpret that string as a module id and try to load it.
  2. Does it get visually crowded/ugly when you start sprinkling require() throughout your wire spec?
  3. There is nothing that forces people to do this, so if it's the right way to go, how do we guide developers in this direction? Examples and documentation, probably :)

Other thoughts

There is an oddly interesting side effect to using require inside a wire spec: being able to piecemeal together a wire spec by require()ing chunks of it:

module.exports = {
  component1: require('./component1-spec'),
  component2: require('./component2-spec'),
  // ...
};

It's not immediately obvious to me how this could be useful, but just wanted to point it out. It may also start to become tempting to programmatically generate a wire spec:

var spec1 = require('./spec1');
var spec2 = require('./spec2');
module.exports = merge(spec1, spec2);

Wire has some facilities for doing that already by passing an array instead of a string or object, but like I said near the beginning of this post, let's ignore that for now.

from wire.

nebhale avatar nebhale commented on August 28, 2024

In my work, I'd already adopted the require() of the spec file as it was your solution in the original gist.

When it comes to require() the modules, I have just tried and can confirm that both

exports.configuration = {
    module: require('./configuration')
};

and

exports.server = {
    create: {
        module: require('./server'),
        args: [{
            $ref: 'configuration'
        }, {
            $ref: 'handlers'
        }]
    }
};

work perfectly. However this

exports.handlers = {
    wire: require('./handlers/spec')
};

does not. I'm guessing that this is more of a bug than a limitation and in the mean time, an uglier alternative

var wire = require('wire');
exports.handlers = wire(require('./handlers/spec'));

does appear to work.


Looking beyond what works today however, I don't have a problem at all requiring those modules. Once you know that it needs to be done, it's pretty obvious that any time you need to load a module off of the filesystem, require() is the way it must be done. As require() is a global and doesn't require any other code to reference, I don't think that it's particularly ugly either. The one (largish) downside I see here is that it just doesn't jibe with what you'd write for AMD.

from wire.

briancavalier avatar briancavalier commented on August 28, 2024

Great, thanks for reporting all of that, @nebhale. For the one example you posted that doesn't work, try this:

exports.handlers = {
    wire: { spec: require('./handlers/spec') }
};

I think that should work. I just checked the code, and the wire factory inspects its r-value, and if it's not a string, expects an object with at least a spec property.

from wire.

briancavalier avatar briancavalier commented on August 28, 2024

Pretty sure this was covered in 0.10.4, but if we missed some cases, please post here so we can investigate!

from wire.

Related Issues (20)

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.