Comments (6)
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.
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.
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:
- The spec itself.
- Modules referenced from within the spec. In the most common case, these will be on the right hand side of a
create
ormodule
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:
- Will it work with all forms of
create
andmodule
? 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 bothcreate
andmodule
to do the wrong thing--they would interpret that string as a module id and try to load it. - Does it get visually crowded/ugly when you start sprinkling
require()
throughout your wire spec? - 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.
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.
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.
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)
- wire/builder/cram interprets any property called "create" as a factory HOT 1
- duplicate wire/dom wire/dom HOT 1
- Wire does not work with 'when' 3.0.0. HOT 3
- Empty `create: {}` should cause a loud error
- Add when 3.0 compat HOT 6
- Context Usage HOT 5
- BDD + CujoJS HOT 5
- wire/builder/cram should call require.toUrl() before fetching modules HOT 1
- 'import' a context into another HOT 4
- Allow import of a context-spec into another. HOT 5
- Remove artificial dot traversal limit HOT 2
- Container Compilation? HOT 1
- wirejs and dojo using the dojo build system seems impossible
- AOP plugin causing potentially unhandled rejections HOT 2
- Using properties or methods of other modules or components as components
- Using components from child and sibling contexts
- create factory should invoke function with context if module is a method of an object
- Dependencies when destroying a context?
- Bundling with Webpack doesn't work well HOT 3
- Not es6 ready - does not work with es6 classes HOT 2
Recommend Projects
-
React
A declarative, efficient, and flexible JavaScript library for building user interfaces.
-
Vue.js
🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
-
Typescript
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
-
TensorFlow
An Open Source Machine Learning Framework for Everyone
-
Django
The Web framework for perfectionists with deadlines.
-
Laravel
A PHP framework for web artisans
-
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.
-
Visualization
Some thing interesting about visualization, use data art
-
Game
Some thing interesting about game, make everyone happy.
Recommend Org
-
Facebook
We are working to build community through open source technology. NB: members must have two-factor auth.
-
Microsoft
Open source projects and samples from Microsoft.
-
Google
Google ❤️ Open Source for everyone.
-
Alibaba
Alibaba Open Source for everyone
-
D3
Data-Driven Documents codes.
-
Tencent
China tencent open source team.
from wire.