componentjs / component

frontend package manager and build tool for modular web applications
https://github.com/componentjs/guide
MIT License
4.55k stars 306 forks source link

remove lookup paths (used to be rename "paths" to ....) #152

Closed tj closed 10 years ago

avetisk commented 11 years ago

Will there ever be any other kind of lookup except for paths?

If not, then I guess lookup is just fine.

tj commented 11 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

gjohnson commented 11 years ago

+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.

tj commented 11 years ago

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

tj commented 11 years ago

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 ./

gjohnson commented 11 years ago

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?

tj commented 11 years ago

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

gjohnson commented 11 years ago

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.

tj commented 11 years ago

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

matthewmueller commented 11 years ago

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.

tj commented 11 years ago

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

karlbohlmark commented 11 years ago

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.

It would be very nice to reach some kind of stability in the basic component json format.

vendethiel commented 11 years ago

"some/foo": "*", They're not versioned. I'd vote for "useName": "/path/to/component/",

karlbohlmark commented 11 years ago

@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

gjohnson commented 11 years ago

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"
  }
}
gjohnson commented 11 years ago

@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.

karlbohlmark commented 11 years ago

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?

tj commented 11 years ago

@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

yields commented 11 years ago

@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.

tj commented 11 years ago

yeah we do that with a lookup path for ./lib

yields commented 11 years ago

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.. ?

tj commented 11 years ago

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

rschmukler commented 11 years ago

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...

yields commented 11 years ago
...
"paths": ["lib"]
"local": [
    "models/user"
    "pages/user"
  ]

@rschmukler Is this the root component.json ?

rschmukler commented 11 years ago

@yields Correct and then lib/models/user would have it's own component.json etc...

yields commented 11 years ago

maybe you can do that with builder, i'm not sure though (didn't play with it yet) addLookup

tj commented 11 years ago

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

rschmukler commented 11 years ago

@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?

tj commented 11 years ago

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

rschmukler commented 11 years ago

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

rschmukler commented 11 years ago

@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.

eldargab commented 11 years ago

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!

tj commented 11 years ago

@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

eldargab commented 11 years ago

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.

rschmukler commented 11 years ago

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.

tj commented 11 years ago

@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

tj commented 11 years ago

the other alternative is implying those .locals like browserify does via hacky static analysis, but that would only work for non-public components

rschmukler commented 11 years ago

@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.

eldargab commented 11 years ago

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?

rschmukler commented 11 years ago

@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.

micky2be commented 11 years ago

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/
tj commented 11 years ago

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.

ianstormtaylor commented 11 years ago

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/
    ...
rschmukler commented 11 years ago

@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.

ianstormtaylor commented 11 years ago

@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

rschmukler commented 11 years ago

@ianstormtaylor Ah yeah, nah nothing like that yet. Interesting idea though.

tj commented 11 years ago

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

rschmukler commented 11 years ago

@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.

tj commented 11 years ago

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.

rschmukler commented 11 years ago

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.