Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

main property - showdown #935

Closed
wibblymat opened this issue Oct 21, 2013 · 41 comments · Fixed by bower/spec#43
Closed

main property - showdown #935

wibblymat opened this issue Oct 21, 2013 · 41 comments · Fixed by bower/spec#43

Comments

@wibblymat
Copy link
Member

We need to get this done. It's been more than half a year. Let's leave this for discussion and voting until the Bower meeting on the 3rd November (14 days). Then we should count up votes and pick a winner.

Resources:

Contenders:

  1. single entry point - A relative path to the single file that you include to load the component. Could be any file type.
  2. one-per-type array - An array of file paths, only one per type (e.g. CSS, JS, image, etc.)
  3. array of everything - An array of all of the paths you could possibly want to include to get everything. All types mixed together.
  4. type map - An object with file types as keys. Values are either single paths (4a) or arrays of paths (4b)
  5. feature map - An object with feature names as keys. Values could be any the above (5a - single paths, 5b - array of paths, 5c - type map)

Anywhere that there is an array we can obviously optionally allow a single string instead which means the same as a one-item array without introducing any ambiguity.

If you have a favorite option not listed here, let me know ASAP and I will include it.

//cc: @satazor @addyosmani @sindresorhus @paulirish @benschwarz @wycats @svnlto @desandro @necolas. Please ping anyone else who might have a strong opinion or interesting use-case.

@desandro
Copy link
Member

3️⃣ or 4️⃣

3 is a bit easier to implement from a package producer's standpoint.
4 is easier to consume -- if producers implement it correctly.

I'm leaning towards 3.

@KidkArolis
Copy link

  1. single JavaScript entry point - it's optional (if your module is css only or if you have multiple possible entry points that the user can pick, for example), but if it's provided - it can only be the main JS entry point.

This is so incredibly important to get right, because without this rule, I don't see a simple way of turning bower into a .. package manager. If you think about why npm is so powerful and useful, it is because each package has an entry file and doing something like require('backbone') where backbone is the name of the package in the registry results in a very predictable thing happening - that code is loaded and can then be consumed in a standard way. This allows any package to depend on any other package within the registry in a predictable manner.

If bower's main is a list of js/css/image files, then you cannot build a rich ecosystem of packages with deep dependencies or create tooling that makes require('backbone') or something similar work. Without agreeing on some kind of fundamental rule like the "one main js entry point per package" it's not possible for one package developer to depend on another package and expect it to work for all users out of the box.

This "one main js entry point per package" rule is independent of the module format (it would be a lot easier if we all agreed on one, of course). You can still author your modules in globals/AMD/CJS/ES6, but you should always have just one main entry point file and conversion between formats could be done via tooling (if possible in that particular case, otherwise you basically can't use that module, for example, I see potential problems with things like AMD plugins, or a global script that exports 5 variables to window).

The main entry point doesn't mean there should only be one js file per package. You can do things like require("backbone/model"). In case of globals, for example, if you have multiple global code files you want to include (not sure why that's a good idea), that could be achieved via a separate bower.json key (e.g. "scripts" or "files").

I would love if bower figured out a good way of managing CSS as well, but that's a tricky problem and for that I would also suggest creating a separate bower.json key, e.g. styles. This then won't interfere with the most important purpose of bower (at least in my opinion) - providing a way to share and reuse javascript code. Yet it will still provide tools with a way to auto wire css, etc.

I don't see any point in listing images or other types of files in your bower.json, images can't be automatically "injected" into the page or anything. If that's really needed, then perhaps there should be a separate key in bower.json that lists all files that are relevant to the package (but aren't all files that are published and are not in ignore relevant to the package?? why list them again in main?).

I can see how this "what is a package in frontend world" problem is quite tricky, but my main beef with bower at the moment is that as package developer, I can barely depend on any other package in bower registry due to the spec being so loose. If I make a package with 2 dependencies that each have 2 other dependencies - I can push that to npm and know that it will work for everyone. If I push that to bower - chances are it will be a lot harder for the users to consume that package - the users will need to use a specific set of tooling that can wire those specific packages, or do the wiring manually by inspecting each package. Bower should enforce some kind of minimal set of rules about the structure of the package. Otherwise, I don't see it becoming more useful than manual script management or npm.

Thanks for taking the time to read this ;)

@necolas
Copy link
Contributor

necolas commented Oct 23, 2013

IMO, Component(1) gets this right: scripts, styles, templates, images, fonts…that kind of thing. Makes it really easy for the tool to build a bundle of your script dependencies, styles, templates (as JS strings), and so on. One problem is does have is that it doesn't seem to provide control over how to order your style dependencies, which is sometimes important.

@unscriptable
Copy link

I can't see how this can be decided until the purpose of main can be decided. I've been trying to follow the dozens of threads, and it seems there's no consensus, yet. The spec is currently very vague about it.

The purpose of main in CommonJS and AMD environments has already been standardized. The smart folks of those efforts toiled over this for years and finally settled on a simple definition for main: http://wiki.commonjs.org/wiki/Packages/1.1#Package_Descriptor_File It just describes the default entry point of the package.

Breaking the meaning of main would break thousands of existing AMD and CommonJS packages.

Fwiw, the term "single entry point" is misleading. "default entry point" is more accurate if we're following CJS/AMD.

var when = require('when'); // default module == 'when/when'
var sequence = require('when/sequence'); // another, explicit module

In cujoJS, we allow developers to specify CSS dependencies in JavaScript (require("myWidget/foo.css");) as well as in CSS (@import url(baseWidget.css);) so having a default CSS entry point would make this even nicer for tools and developers. The developer doesn't have to know which CSS file is the default one, and the package author can restructure files without breaking the core API as often. e.g. require("myWidget.css"); imports the default CSS file which might be "myWidget/main.css"

Options 2️⃣ and 4️⃣ seem like interesting extensions to AMD/CJS main. They would allow us to provide the same conveniences to CSS, etc. Although, 2️⃣ seems far less friendly to tools. I can see 4️⃣ working well as long as the values are a single string, not an array (i.e. 4️⃣a). There really should only be one default entry point per type. The ignore option can and should handle the things that should be ignored by tools.

Option 5️⃣ seems intractable. The definition of "feature" is way too loose. Let's take baby steps.

My vote: 4️⃣a with a fallback to 1️⃣ if a string is provided (AMD/CJS).

// JavaScript-only package:
{ "name": wire", "main": "./wire" }

// Mixed package
{
    "name": "wijmo",
    "main": {
        "js": "core",
        "css": "theme/light/all"
    }
}

@KidkArolis
Copy link

Very much agree with @unscriptable !

@briancavalier
Copy link

@unscriptable's suggestion sounds right to me, too. Obviously, bower and npm (and other package.json-based tools) have different goals, but the established precedent of package.json's main is pretty strong. If main can mean roughly (or exactly, in the JS-only case) the same thing, that seems like a win. It also doesn't prevent adding something like component's per-type approach later if other tools would benefit. Why try to smash those two concepts (runtime entry point vs. build-tool-friendly lists/hints) into a single main?

@necolas
Copy link
Contributor

necolas commented Oct 24, 2013

You don't need main if you do what Component(1) does.

Breaking the meaning of main would break thousands of existing AMD and CommonJS packages.

Not sure what npm's use of main has got to do with it. Are you trying to suggest that removing main in a bower.json spec would somehow break existing Node packages?

@unscriptable
Copy link

Not sure what npm's use of main has got to do with it. Are you trying to suggest that removing main in a bower.json spec would somehow break existing Node packages?

Changing the meaning of main would break any tools that try to work with multiple package managers. For instance, we've got code that sniffs package.json for the main property. In CommonJS/node and AMD, this property is used to perform default id translation. When a developer refers to the package name, rather than a module name within a package, the id is translated to the actual module id. (For better or worse, the package descriptor file has run-time specific information in it.)

Since bower.json hasn't specified the purpose of main, we've been hoping that it would do something similar. In the meantime, devs are forced to configure default id translation manually, typically in an AMD config, which is not ideal.

I guess this technically isn't "breaking" the libraries. :) My bad. It's really just breaking convention and forcing us to continue to use a work-around (manual config).

As long as the proposals still allow default id translation (without manual configuration), then I guess I'm cool with whatever is chosen. Can somebody help me understand how this might work if there are multiple values (i.e. in an array)?

Just curious: have any of the browserify folks given their opinion?

@josh
Copy link
Contributor

josh commented Oct 28, 2013

I'm the maintainer of sprockets, which is an asset build tool that has already implemented main. I helped author the original intent of "main" a long time ago which is basically your 2️⃣. The name was basically cargo culted from the package.json spec where it meant one file. Supporting an array was for other file types, not for all your files. Its call "main", not "all the files" for a reason.

I really, really do not want to see this changed since we have existing implementations and Bower is supposed to be 1.0 at this point.

However, I am in favor of introducing a new mapping property. I'm willing to implement such as thing for Sprockets once its figured out.

@benschwarz
Copy link
Member

main is a convention used elsewhere.

I agree with @josh, we should be looking at introducing a new property to address the use case of multiple components.

2️⃣

@necolas
Copy link
Contributor

necolas commented Oct 29, 2013

I don't think there's much point to keeping a convention from another context if it doesn't fix this one. In browser-land not everything can be require-ed and a single entry point delivered. Web Components looks like it would provide something like, but for now we need to wire up different asset types. That's part of the reason why I think Component(1)'s approach of using multiple properties is pretty nice, and Bower's is still confusing and ambiguous.

@sindresorhus
Copy link
Contributor

IMO, Component(1) gets this right: scripts, styles, templates, images, fonts…that kind of thing. Makes it really easy for the tool to build a bundle of your script dependencies, styles, templates (as JS strings), and so on.

I agree. Though do note that Component(1) still has a main field for an JS entry point. It really only makes sense for JS to have an entry point. Maybe CSS, but usually not.

I propose we keep main as a string with a JS file, just like Node.js/Component.js/CommonJS.

We could then have a files object with the following keys: scripts, styles, images, fonts, templates, and other for files not fitting any of the classifications. And only the files in this object will be included in the package. That would solve one of our biggest painpoints of huge packages with lots of junk, and make it easy for tools to consume packages.

@wibblymat
Copy link
Member Author

For the record, I agree with @sindresorhus.

@desandro
Copy link
Member

^^ @sindresorhus Yes. I like it.

@necolas
Copy link
Contributor

necolas commented Oct 29, 2013

Yeah, I think this is the way to go. It would be doing what Component(1) does but nesting in a files object, and avoids confusing the semantics of main by trying to roll non-JS into it.

@benschwarz
Copy link
Member

@sindresorhus how do you see that proposal working with a library like gallery-css?

@wibblymat
Copy link
Member Author

@benschwarz gallery-css wouldn't have a main at all as it contains no JS.
Instead you have a styles object that references all of the CSS files.

On 31 October 2013 13:58, Ben Schwarz notifications@github.com wrote:

@sindresorhus https://github.com/sindresorhus how do you see that
proposal working with a library like gallery-csshttps://github.com/benschwarz/gallery-css/blob/master/bower.json?


Reply to this email directly or view it on GitHubhttps://github.com//issues/935#issuecomment-27487483
.

@benschwarz
Copy link
Member

@wibblymat sgtm! component style 😏

@donaldpipowitch
Copy link

We could then have a files object with the following keys: scripts, styles, images, fonts, templates, and other for files not fitting any of the classifications.

I don't like that, as I think it doesn't scale well. Why is images a key, but not videos or audio? These are all valid media files for websites.
Sometimes I want to use .css with bower, but sometimes I need .less. I would place them both behind styles as it is the best fit, but conceptionally they are both very different.

I also don't like the idea to use files and main. I think they are so similar, that main is enough. I personally tend to solution 3. If too many files are included or they behave to differently I think this is a good code smell to refactor your bower package into two or more different, but maybe related packages.

E.g. I think Bootstrap should place and develop .js and .less of each widget (modal, tabs, collapse, etc.) into its own repo and bower package, which all have a dependency on a Bootstrap common package containing stuff like variables.less and scaffolding.less.

@benschwarz
Copy link
Member

Looks like we've reached quorum on the main attribute.


Should we standardise it in the spec and come up with the design that we're most happy with for citing package resources?

@josh
Copy link
Contributor

josh commented Nov 11, 2013

Trying to turn this into something actionable in #963

@eddiemonge
Copy link
Contributor

I highly disagree with

I propose we keep main as a string with a JS file, just like Node.js/Component.js/CommonJS.

as that essentially turns bower into "Another implementation of NPM" instead of "A package manager for the web". Packages for the web aren't always JS. In fact they could be things that have no JS at all like a css package (normalizecss) or an images pack or a font pack.

I know this will get shot down pretty quick, but I propose dropping main entirely and just going with something like components or packages or even files with it being an object of arrays. The main or entry point for JS or CSS could be the first in the array for that type. It would look something like this:

{
  "components": {
    "javascript": ["js/bootstrap.js"],
    "css": [
      "css/bootstrap.css",
      "css/bootstrap-theme.css"
    ],
    "font": [
      "fonts/glyphicons-halflings-regular.eot",
      "fonts/glyphicons-halflings-regular.svg",
      "fonts/glyphicons-halflings-regular.ttf",
      "fonts/glyphicons-halflings-regular.woff"
    ],
    "image": [],
    "audio": [],
    "video": []
  }
}

The spec would have the supported types and if some other type wanted to be added, package authors could add their own custom (although build tools probably wouldn't support those).

Build tools could still reference the main entry point as bower.components.js[0].

I'm hoping that this makes things clear for package developers, package consumers and build tools as you don't have to figure out what goes in main vs what goes in files and also slightly answers the question "Why Bower and not npm?" because it makes it clear that all web files are supported and treated equally instead of just JavaScript as npm does now.

@necolas
Copy link
Contributor

necolas commented Nov 11, 2013

that essentially turns bower into "Another implementation of NPM"

There's nothing to back up that conclusion.

Component(1) also keeps main to indicate which JS file is the main entry point for the scripts array.

components and packages don't make sense as property names within a component-package.

@benschwarz
Copy link
Member

I was opting for components myself, until I read @fat's xzibit comment… I saw the error in my files.

files would be better, but I think we should be perspective about the keys that it can contain.
The list above (javascript, css, font, image, audio, video) seems sane to me.

@ProLoser
Copy link

Why not

{
 "main": {
   "javascript": ["bootstrap.js"]
 },
 "files": {
   "css": ["bootstrap.css"]
 }
}

Personally I concur with @eddiemonge's general thought:

I highly disagree with

I propose we keep main as a string with a JS file, just like Node.js/Component.js/CommonJS.

as that essentially turns bower into "Another implementation of NPM" instead of "A package manager for the web". > Packages for the web aren't always JS

I realize that this all may seem verbose but I just kind of like the idea of thinking from the ground up vs trying to emulate all these backend-only solutions.

@danielchatfield
Copy link

^agree, file extension is sufficient information and makes bower much more flexible as it can support any file as long as the builder knows what do to with it.

@josh
Copy link
Contributor

josh commented Nov 12, 2013

Trying to push this forward with some actual PRs to our spec.

Please see the following and voice your opinions.

bower/spec#8
bower/spec#6
bower/spec#7
bower/spec#9

@mquandalle
Copy link

I just like to link a use case of the main field to auto-bundle resources from a bower package in a Meteor application. Here is the code https://github.com/mquandalle/meteor-bower/blob/master/plugin/handler.js#L78-L105. This code assumes that the main field is either a string or an array of string where each string is a path to a client resource.

@kflorence
Copy link

Nowhere in this debate do I see a discussion about ingesting packages in production vs. development and which files are applicable to either of those environments. For example, this is why projects like bower-installer exist. Should we really have to use external tools for that? This seems like an extremely common use-case and something that could be specified in bower.json.

@sheerun
Copy link
Contributor

sheerun commented Jun 9, 2014

Scheduling this on 2.0.0 since this should be resolved one or other way in this release.

@sheerun sheerun added this to the 2.0.0 milestone Jun 9, 2014
@eddiemonge
Copy link
Contributor

I've changed my mind slightly after using wiredep for awhile. I think main should be an array of all files your module provides. Let the build tools figure out how to use those files.

"main": [
  "dist/jquery-ui.js",
  "dist/jquery-ui.css",
  "dist/jquery-ui.html"
]

If a build tool wants to do something with a certain file type, it can parse the array and find (or not) what its looking for.

@benschwarz
Copy link
Member

@eddiemonge I think its safe to say that no one can agree to how this should work, nor what the syntax should be.

I think the core team should get together and choose the proposal with the 'best' / 'most' merits and make main our own… OR, we should exactly mirror the NPM specification.

… Then lock this issue forever.


@bower/bower?

@unscriptable
Copy link

👍 Ben. That's a great idea. I recommend something simple.

@blai
Copy link

blai commented Oct 3, 2014

Has supporting globs ever been part of this discussion? bower/spec#30

@cvrebert
Copy link
Contributor

Happy 1 year 1 month anniversary of this important issue!
Looks like indecision is continuing to rule the day...

@sheerun
Copy link
Contributor

sheerun commented Nov 21, 2014

I think we need to accept what main is de facto. It's one-per-module-system array that points to entry points of these module systems. Or string if only one module system is used.

@desandro
Copy link
Member

I've opened bower/spec#43 to implement sheerun's proposal and resolve this once and for all.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Development

Successfully merging a pull request may close this issue.