module boilerplate? #833

Closed
bryanlarsen opened this Issue Jul 7, 2015 · 31 comments

Projects

None yet
@bryanlarsen

The material.js file doesn't appear to have any mechanism to export itself when run from within a closure, like when included via webpack or similar.

I would expect to be able to do something like:

npm install material-design-lite --save

and then from my source code do (using webpack and babel):

import componentHandler from 'material-design-lite';

or

var componentHandler = require('material-design-lite');

I'm assuming that componentHandler is the only thing that needs to be exported; that seems to be all I need in my quick experiment.

To be able to do this, one fix is required, and another would be nice.

Add module boilerplate.

There are a ton of different module boilerplates out there, but to support my use case it could be as simple as:

if (typeof module === 'object') {
  module.exports = componentHandler;
}

I'd also be able to work with:

window.componentHandler = componentHandler

Add a 'main' entry to package.json.

optional: without it, we can use 'material-design-lite/material' instead of 'material-design-lite', but that forces people to look in source to figure it out.

"main": "material.js",
@yamalight

would also be good to have a style field for webpack, e.g.:

"style": "material.css",
@surma
Collaborator
surma commented Jul 8, 2015

I am not sure how this will fit with our current setup. @addyosmani @jasonmayes ?

@yamalight

Current workaround for webpack:

var Material = require('exports?componentHandler&MaterialRipple!material-design-lite/material.js');

But it would indeed be nicer to have it as a proper module (require.js, commonjs or UMD).
Will make it much easier to apply it in React apps.

@Garbee
Collaborator
Garbee commented Jul 8, 2015

Issue #873 was opened just now which explains the issue well I believe.

@Garbee Garbee added the Tooling label Jul 15, 2015
@tleunen
Contributor
tleunen commented Jul 23, 2015

@Garbee @addyosmani For a specific project, I need to require just a few components instead of the whole library. So I was thinking about updating the way a component registers itself to the component handler and therefore I'd need to change a bit how the master material.js is written. Because it will be in a nodejs way or ES6 import actually, we'd need browserify or webpack to build the file. Any preferences?

@Swatinem

@tleunen are you working on a PR for this? Otherwise I might take a shot at this.

@tleunen
Contributor
tleunen commented Jul 25, 2015

Yep.. creating a PR now to get feedbacks

@tleunen tleunen referenced this issue Jul 25, 2015
Closed

Modularity #1211

@jjalonso

webpack support !

Uncaught ReferenceError: componentHandler is not defined

Using https://github.com/lpiepiora/bower-webpack-plugin

var BowerWebpackPlugin = require("bower-webpack-plugin");
module.exports = {
  module:  {
    loaders: [
      {
        test:   /\.css$/,
        loader: "style-loader!css-loader"
      }
    ]
  },
  plugins: [new BowerWebpackPlugin()]
};
var Material = require('material-design-lite')

@Garbee
Collaborator
Garbee commented Jul 26, 2015

Whatever we end up doing needs to not break existing workflow. Just because the output remains the same doesn't mean we can change whatever we feel like. Developers have the existing code integrated in their workflow, if we go making major changes to the structure and their stuff breaks, that is a problem.

Does anyone have any recommendations on modifications that could be made that would solve this issue but not change any workflow?

@jasonmayes
Contributor

Personally I feel all these bindings to things like closure, etc, should be outside the core code base which should be vanilla JS. For example we could instead have an exports file for a given lib we wish to support which you can just add to your gulp build (and expose a flag maybe too). This way people who are using one of these libs can just include the appropriate exporter which exposes the ComponentHandler as required, instead of bloating the code for everyone else who doesn't use any of them. This way we can support as many 3rd party libs etc as we like without the worry of if additions are going to break people's workflows who rely on the core code. I am all for keeping things as simple as possible.

@Garbee
Collaborator
Garbee commented Jul 26, 2015

As discussed in issue #1040 in relation to supporting just meteor in-house, we shouldn't do anything in core to directly support external systems. We are then left maintaining those with each update, even if we aren't familiar with the system and could very likely do things in a non-optimal way. Or quite possibly accidentally break things in those systems and then have complaints about unexpected breaking changes.

If there are going to be exports, they should be handled in separate repos by people who use that tooling. That way maintenance and support is on them, not the core team.

Another issue with supporting all these front-end systems, is we currently don't help handle many of the situations that occur with them (like dynamically adding tabs.) So that adds to the scope of what we would need to support in core by showing support for these other engines.

@tleunen
Contributor
tleunen commented Jul 26, 2015

Developers are currently using the output file. So if the output file provide the same functionality, it's fine. But I really dislike that since the output file currently create a lot of global variables. I don't think it's a good way to provide a library.

Because of the way Components are built and registered, I think the best way of doing it is provide the components itself and let the developer register the components he needs in his application.
Kind of what I tried to achieve, but it could be done differently. The developer could have his own "handler" file (just an abstraction of the the mdl handler) which register every components he wants, and then he could use this handler to manually upgrade the dom elements.

@tleunen
Contributor
tleunen commented Jul 26, 2015

Just played a bit more with the implementation I did and I notice one major issue.
Because I was registering the component on the fly, when needed. The order was not good.
For example, I could end up with this order: Button, Ripple, Switch, ...

The issue here is when I try to upgrade the switch. Because the order says to init the Ripple first, it will fail because the ripple doesn't create the ripple element. That's the responsibility of the component to create it (in this case, the Switch).

Anyway, without major changes, I think the best we can do is exporting the component handler and let the user/developer register the component he wants, in the right order. And only then he can use it to upgrade the dom.

@Swatinem

I think there are different use cases at work here.

  • One is to make the code extremely modular and give developers the choice of which components to register.
  • The other is to simply expose the functionality we have now as an importable module (for your favorite module system).

I think you are doing the first, and honestly, when you said in the right order it kind of scared me, since developers will surely get it wrong.

What I basically want is to import "material-design-lite" for side effects, on page load to hook up static markup. And a import {upgradeElements} from "material-design-lite" that I call when I manipulate the dom.

@tleunen
Contributor
tleunen commented Jul 26, 2015

What you describe is easy to do.
The MDL team just have to provide the main file in package.json so you can import it, and exporting upgradeElements is easy to do as well.

The issue comes when you want a custom build of MDL. What if my project doesn't want the layout component? How can I just import (and register) what I need?

@Swatinem

As I understand this issue is just about exposing the module in npm and make it require-able (or import if you use ES6).

Customization should be a different issue altogether I believe.

@withinsea

Agree with the module.exports= and window= way.

I'm building an application with AngularJS and MDL.
Since angular is an async-load framework, lot of elements need to be rendered dynamically after MDL initialized (load a view, for example)

Exports componentHandler perfectly solved my problem:

material.js:

if (typeof module === 'object') {
  module.exports = componentHandler;
}

app.coffee

mdl = require 'material-design-lite/material'
ng = require 'angular'

ng.module 'app', [ 'ngRoutes' ]
  .config '$routeProvider', ->
    $routeProvider.when '/mdlpage',
      template: '''
        <button class="mdl-button mdl-js-button mdl-button--raised">
          Button
        </button>
      '''
      controller: ($scope) ->
        $scope.$on '$viewContentLoaded' ->
          mdl.upgradeAllRegistered()

As you see, mdl is just a local variable, nothing leaked to global scope.

p.s.

Webpack's exports-loader could archive to same effect without changing anything....
But i don't know about other packing tools like Browserify, Require.js or Meteor JS.
Append exporting code will still be a common solution.

webpack.config.coffee

  ...
  module: loader: [
    { test: require.resolve("material-design-lite/material"), loader: "exports?componentHandler" }
  ]
@jjalonso

What about this thread?

@genadis
genadis commented Oct 27, 2015

I've created a Fork, and implemented a patch that might be relevant.

It implements the module.exports approach and falls back to window[vendor].mdl encapsulation for script tag.
More info in the readme.

@drastick

+1

@turban
turban commented Feb 15, 2016

+1

@bart
bart commented Apr 8, 2016

+1

@gukennedy

Nice!

@richburdon

Hey Google guys (@surma, @jasonmayes) are you happy about folks forking the repo in order to use it effectively with commonly used tools (webpack, etc?) No real movement on this thread in 9 months -- what's the plan?

@jasonmayes wrote:

Personally I feel all these bindings to things like closure, etc, should be outside the core code base which should be vanilla JS.

I think that although "technically correct" this creates a huge barrier for adoption; AND encourages forks (so a) you lose control; b) users are further confused; c) your updates will be lost to forked users). You could keep your repo "pure" and provide the bindings as well. Example, CSS is a TINY part of the project I'm working on (100s of things on my task list); now MDL becomes a problem on the list, rather than something that can just be used -- unlike most other npm projects.

@Garbee
Collaborator
Garbee commented Apr 30, 2016

We are slowly assessing various methods to tackle this. There are numerous ways each with their own pros and cons. Once we have a working prototype of the component style we want ( PR #4153 ) then we can dig deeper into seeing the exact output the various methods would produce. From there we can compare the build processes, management requirements, etc. and decide on which method to use.

@sgomes
Contributor
sgomes commented Jul 1, 2016

Our architecture is being rebuilt from the ground up for v2, using standard ES5 loading practices, removing IIFEs, and providing different ways of consuming the JS (single-file vs per-component). In the meantime (v1), it's not feasible to fix this.

@sgomes sgomes closed this Jul 1, 2016
@ralexand56

When is v2 expected?

@sgomes
Contributor
sgomes commented Aug 3, 2016

No dates yet; we're still too early in development to be able to provide an estimate. We're currently working towards an alpha release, which we'll use to gather feedback before moving forward.

@mbifulco
mbifulco commented Sep 1, 2016

Running into the same issues here. Where's the best place to keep an eye on v2 alpha release info, @sgomes?

@sgomes
Contributor
sgomes commented Sep 1, 2016

@mbifulco If you look in master, you can get an idea of what we're working towards. Still a lot in flux, but it's starting to solidify.

As for alpha releases, just follow the repo! :) We'll be sure to make a GitHub release with announcement for our alpha.

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