Closed tj closed 10 years ago
need to think on this some more, it might be better to just do "local": ["./client/boot"]
etc and lookup boot's deps relative to its dirname, tough call. full paths is annoying when you have many, but more explicit
+1 to having the path within the local
value. dependencies
, remote
, etc. are self contained, so seems like it would fit in nicely for it to behave that way too and just toss out the paths
confusion.
yea the only downside is the verbosity but it's probably worth avoiding the ambiguity. This would prevent users from having say ./{controllers,models,views}
though
another alternative if we're going with relative paths would be the somewhat obvious:
{
"name": "myapp",
"dependencies": [
"some/foo": "*",
"some/bar": "1.0.0",
"user": "./lib/user",
"admin": "./lib/admin",
"login": "./lib/login",
"signup": "./lib/signup"
]
}
this shouldn't really make anything more or less complex, about the same, but they would have to start with ./
Hmmm dig it... Though I think you mistakenly left off username/project, what if that played a role too? Meaning the lack of a username implied the component should be resolved locally. Meh?
yeah that's true, lack of /
could imply that, though the array is less redundant but it looks a bit better to use the existing object (even though i made it an array there hahahah...)
This way, if I want to use component create for local components, I wouldn't HAVE to put in a username for something that it didn't really need it.
yeah for sure, you dont have to currently either. It would be lame doing ../foo
for deps though I'm still not a huge fan of that
I could be missing a use case, but it seems possible to distinguish between *
, semver, and everything else, unless v1.0.0
is also supported. What about keeping a paths key and going with:
"dependencies": {
"some/foo": "*",
"some/bar": "1.0.0",
"user": "user",
"admin": "admin",
"login": "login",
"signup": "signup"
}
although it does look a bit redundant after looking at it for a bit.
one thing im thinking about is whether or not the left-hand name should be used over the component.json's name. For example if you have "user": "./user/view"
mapped, if it should use the ./user/view/component.json
name or "user". That leads to another problem though, when a user starts nesting things they need to know that you still need component.json files, they still have to be "real" components, this may not be obvious, and slightly awkward really
I vote for keeping the local components separate from the remote dependencies, as I think this is more intuitive. I don't think that
"dependencies": {
"some/foo": "*",
"user": "user"
}
communicates clearly where the two dependencies can be found.
local
. (slightly verbose and ugly)paths
and defining and implementing the behavior for nested components. (slightly complex)It would be very nice to reach some kind of stability in the basic component json format.
"some/foo": "*",
They're not versioned. I'd vote for "useName": "/path/to/component/",
@Nami-Doc yes, I understand. Well, for me, the most important thing is that we reach an agreement about the format. I'm fine with listing them together with remote dependencies also, even though I think it's a bit redundant to have "user": "user", "login": "login", "yada1": "yada1" etc
Since the ambiguity is around the key, why not make all the rules around it. IMO, the following rule(s) seem sane and like @karlbohlmark said, its very clear by the value where the component is coming from:
{
dependencies: {
"redventures/pixel": "0.0.1"
"pixel": "git@github.com:redventures/pixel#0.0.1",
"pixel": "./lib/path/to/pixel",
"pixel": "/some/random-ass/directory/not-sure-why-anyone-might-do-this/but-maybe"
}
}
@visionmedia is there actually a use case for needing just a nested component? In your example, I can't see why one would require user/view
instead of user
. I think the rule of thumb should be if you find yourself saying "nested" in front of your component... it should probably be its own component, especially if you need to require it outside the parent context.
While where discussing the json format, I have another case to handle, I think it's pretty self explanatory, tell me if it's not:
"some-dude/some-lib": {
"version": "*",
"redirect": {
"some-dude/jquery": "component/jquery"
}
}
I.e. How do you adhere to the "dependency inversion principle"? How do you switch in another implementation of that interface?
@gjohnson a few people have been using it that way, kinda "reaching inside" a module for namespacing, rather than say user-view
. I can't think of too many reasons personally not to just have a flat list but most people seem to like regular traditional MVC stuff, it would be nice to at least facilitate it. For the flat-list approach paths are pretty ugly "user": "../user"
etc.
I dont see a lot of reason to have nested components at the moment, because you might as well just add those scripts/stylus to the parent's component.json, they're private to that component anyway. I'll try and mess with some more structuring possibilities soon and see which pattern works out best
@visionmedia @gjohnson
TJ's screencast shows how to build modular web application with express, it's not MVC, it's modular. not sure if TJ actually does this, but in my case each "module" has it's own component.
currently with some hacks i can build my app successfully, but it's not pretty.
yeah we do that with a lookup path for ./lib
don't you guys have to add multiple .paths
for that ?
"paths": [
"modules/something/components",
"modules/login/components",
"modules/signup/components"
....
]
or maybe i'm doing something wrong here.. ?
not personally, we just have one big flat list. If it's specific to say login then we just have a few script/css/whatever files in lib/login/*, but those are all still just the one login component. Heavy nesting is definitely where lookup paths fail, I'd rather not have lookup paths and something else, we could have both if we need to but that's definitely not ideal
Personally I am looking to do something like TJs screencast, but with a little bit deeper level of organization. My directory structure looks something like:
lib/
models/
pages/
...
The problem then comes for adding paths, I would really like to just add lib
and then set local to something like this:
component.json
...
"paths": ["lib"]
"local": [
"models/user"
"pages/user"
]
Does anyone have suggestions how I can make my folder structure work with components as it exists today?
Idea being that I can use component to require a client-version of the user model, and even use it to render pages client-side or server side with (mostly) the same code...
...
"paths": ["lib"]
"local": [
"models/user"
"pages/user"
]
@rschmukler Is this the root component.json ?
@yields Correct and then lib/models/user
would have it's own component.json etc...
maybe you can do that with builder, i'm not sure though (didn't play with it yet)
addLookup
the nested .paths thing is a bug but we just haven't fixed it yet since I'm not sure which direction to go yet, appending lookup paths to traverse a really nested app is definitely not a good thing
@yields That's what I am doing right now, but it doesn't solve the namespace collision that I'll run into. I end up having to do addLookup('lib/models')
.
@visionmedia I like gjohnson's solution, personally. But at this point, any solution would be fine. Is there something you're waiting for to make the decision beyond certainty?
mostly just a time issue right now, but it definitely seems like we need to cater to both people who want to nest things, and people who want to modularize things in a linear fashion. I might have some time for this tomorrow
No rush, you do amazing things. Just was wondering if there were questions left to be asked or whether it was a "matter of time" thing
@visionmedia What were you thinking for the spec on this? I don't know if I'll have time/be able to figure it out, but if you post what you're thinking I can at least take a look.
I would like to vote for the following:
1) Deprecate .paths
2) Deprecate .local
3) Specify local dependencies as
{
"dependencies": {
"foo": "*",
"component/event": "*"
}
}
4) add --path
option to component-build(1)
5) Support nesting via components/
convention (like node with node_modules
)
6) No relative paths!
@eldargab thanks for the input. With recent changes in the builder lookup paths are a bit less scary for now at least. Now when a non-root component specifies "paths" they affect that single component only, so other components will not accidentally resolve to "paths" specified in other components.
I'm not a huge fan of node's nesting to be honest, it's actually completely unnecessary, it would be more convenient and easier to implement if node used a project-level dir with <pkg>-<version>
like we do, but I know what you're getting at as far as implying that ./components
are always require()able. Plus most applications don't check node_modules into git
it would be more convenient and easier to implement if node used a project-level dir with
-
Ha, that's why I am trying to get components work on server as well :)
What about .paths
vs components/
I just feel they add additional bit of complexity. For example someone can specify "paths": ["../foo"]
then everyone else should be aware about that if he wants to move things around.
But, fortunately all that "local-paths-components" stuff is private and therefore not important.
I'm also with @visionmedia on not resolving the local ./components -- in my opinion it can make the application quite messy and doesn't lend itself to using the same sub-components in different larger components very well.
@eldargab yeah that's why originally I was leaning more towards using .local
(or paths in the deps, whatever) to be more explicit about the path relative to that component, getting rid of lookup paths all together. Keep in mind though that even if you have say:
- lib
- my-component
- more-stuff
- foo
- bar
- component.json
- another-component
and my-component/component.json
has paths: ['more-stuff']
only it will check ./more-stuff, not another-component for example, the lookup paths are scoped to the single component. Effectively it would be the same as doing local: ['./more-stuff/foo' ,'./more-stuff/bar']
, just saves a bit of typing so it's not really worth it
the other alternative is implying those .local
s like browserify does via hacky static analysis, but that would only work for non-public components
@visionmedia in that way would you also allow something like paths: [ '../some-other-component']
?
My dir structure looks something like this:
- lib/
- components/
- some-component-a/
- some-component-b/
- pages/
- some-page-a/
- some-page-b/
Where some-page-a
and some-page-b
both might use components from the components/
directory. Pages then basically map out to express routes w/ views that can be rendered either client or server side (and as such package themselves up in some-page-a/component.json
such that an application router such as pages.js
can render them client side too.
Hm, personally I wanted to try nested components some day because it's not always convenient to see lots of foo-*
in a root dir. But for that case it's much better to have
- lib/
- core/
- a
- b-c
- foo/
- foo-bar/
- foo-baz/
- mm/
- mm-a/
- mm-b/
and add to paths all lib/*
Interesting will that solve a problem for others?
@eldargab I absolutely agree that nested components should be possible, but it shouldn't be dependent -- since the whole point of using components is reusable and modular code.
I also use nested components, it makes a lot of sense for my app structure.
I have something like that
- generic-components/
- core/
- a
- b
- app/
- app-components/
- c
- d
- pages/
- main/
- e/
- f/
- other/
- g/
Personally I still prefer a flat list personally, it helps a lot for the asset url rewriting since they need unique names but we can try and tackle that later for nesting.
Been using the flat approach for the past month+ after seeing TJ recommend it everywhere and it's actually been really nice. Our setup is something like @rschmukler's:
site/
components/
component-tip/
component-each/
...
lib/
user-model/
integration-model/
integration-sheet/
integration-metadata/
page-header/
...
pages/
signup/
login/
...
theme/
theme-button/
theme-tooltip/
theme-page-header/
...
components
is you know what.lib
are most of our private componentspages
are the pages inside our app, that can eventually be bundled into single builds (haven't gotten around to this yet, @rschmukler would love to read a blog post about how you guys are doing it btw).theme
is all of the theme files for public components like component/tip
, as well as for our private components like page-header
. I'm still not exactly sure if I'm sold on keeping the private components' themes in here since it's so easy to forget and get the non-base styles mixed up (already happens).@visionmedia Yeah, I definitely have run into the unique name issue. As such folders in lib/models
are usually named like user-model
etc. This solves the naming collision and helps me keep it a bit more organized.
@ianstormtaylor what do you mean by single builds? I am planning on releasing a blog post/screencast shortly showcasing how I am using component and a few other libraries I wrote to do shared views/models/routes client and server side. It's got me really excited and definitely wouldn't be possible without @visionmedia and his awesome work on component.
@rschmukler ah sorry looks like I misunderstood you. I meant that page components would get built separately, so that login
builds into login-build.js
instead of packaging the entire app into one huge build.
Sharing stuff server/client also sounds like something I'd read though :p
@ianstormtaylor Ah yeah, nah nothing like that yet. Interesting idea though.
It would be great if we could some how hit a nice medium between the flexibility of regular node/browserify with non-unique names (though that gets confusing as well..) but with the modularity of components for assets and everything else. Our components are structured around features more than anything else, so if you remove a component from ./lib you're effectively removing an entire feature, all associated model-like things etc are within there
@visionmedia Could you share some insights about how you guys go about re-using model logic in various components under that structure?
Also, initially the unique names constraint felt taxing, but now that I've actually gotten some depth in my project I actually feel it's better regardless of whether it's required or not.
Granted require('models/user')
would also work fine, but require('user-model')
is still good I find.
If they're used anywhere else we make them their own component, but things like say "settings" has its own views/rest stuff/styles, if we remove it other than a single require() in our boot script it's just gone. We dont really use models since it's a bit of an anti-pattern when it comes to modularity.
Yeah I know what you mean, it's two-fold IMO, there's a certain elegance in just having a flat list of canonical modules, no special-casing, no digging around, but it can be annoying I suppose. I dont use the dir tree in my editor at all so it doesn't bother me but it would if I was using that to locate a file.
It would be nice to facilitate node-style when you want it, if they could co-exist that would be great. I'm not 100% satisfied with this approach for those cases but it certainly beats having a mess of ./views / ./models / ./styles ./assets that slowly get less and less mirrored. Need to think on that some more.
Absolutely agree on the mess of ./views, ./models, ./styles, ./assets. Within our components everything is nice and flat, it's just a question of where the components go. I use a dir-tree from time to time in my editor and I think that may be the distinguishing difference. I think that if I didn't, it'd be irrelevant since Cmd+T/Ctrl+P just takes care of it for you; but having a massive list of components in the dir-tree can be a bit claustrophobic.
Will there ever be any other kind of
lookup
except forpaths
?If not, then I guess
lookup
is just fine.