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

[webpack Module Integration] Release Candidate Feedback thread #10140

Closed
rafibomb opened this issue Jun 5, 2017 · 30 comments
Closed

[webpack Module Integration] Release Candidate Feedback thread #10140

rafibomb opened this issue Jun 5, 2017 · 30 comments

Comments

@rafibomb
Copy link
Member

rafibomb commented Jun 5, 2017

Good news! Foundation 6.4 has shifted to a new ES2016 module-based architecture powered by webpack. This means speed! Super fast JS compilation on top of being more pluggable into more environments! If you are using webpack or another module bundler, you can now import Foundation modules quick and easy resolving a major pain point of the previous architecture.

It would be awesome if you could update your existing project or start a new one with the new webpack module integration and comment below with suggestions or feedback on how it went for your use case.

You can find the ZURB Template branch with the module integration here: https://github.com/zurb/foundation-zurb-template/tree/v6.4

For previous discussion history see:

@oxyc
Copy link

oxyc commented Jun 7, 2017

It's somewhat ugly that you have to init the plugins, there is no way to do this on import somehow?

Would be awesome if you could do like with lodash-es:

import {
  Foundation,
  Abide,
} from 'foundation-sites/js/entries/foundation-es'

@kball
Copy link
Contributor

kball commented Jun 7, 2017

@oxyc Great question, I've been stewing on this, so maybe you could help me think through it...

One item I'm thinking is to build a couple global entry points that you could import from... the one I'm confident we want is an entry that includes and initialized everything and returns the Foundation (Basically what is in foundation-sites/js/entries/foundation.js except returning the Foundation object). The second would be something similar to what you're describing, one that essentially just creates a "bundled export".

The one thing that I'm a little concerned about with the latter is that if you want to tap into the auto-parsing from $(document).foundation() you'll still need to set up the global Foundation registry with the plugins via Foundation.plugin(Abide), and that's not super obvious and a lot of boilerplate if you're pulling in a lot of plugins.

Do you have any other ideas on good ways to attack this?

@kball
Copy link
Contributor

kball commented Jun 7, 2017

Another way we could take this is create an entrypoint that dynamically requires the modules you want. So you'd do something like:

let Foundation = require(`foundation`);
Foundation.initializePlugins(Abide, Reveal, ...)

The core issue/question here is that we want including the modules themselves to be side effect free, but we also want to make it super simple to get started with the currently expected side effects (such as setting up the jquery helper with the set of plugins you've chosen)

@oxyc
Copy link

oxyc commented Jun 7, 2017

The one thing that I'm a little concerned about with the latter is that if you want to tap into the auto-parsing from $(document).foundation() you'll still need to set up the global Foundation registry with the plugins via Foundation.plugin(Abide), and that's not super obvious and a lot of boilerplate if you're pulling in a lot of plugins.

Yes this is not ideal. Would be great if you could have a single import/init list where you just comment out the modules you dont need. If you need to maintain that list in two places you will for sure forget something and end up having to manually count the modules you have active.

import {
  Foundation,
  // Abide,
  Accordion,
  // AccordionMenu,
  Drilldown,
  ...
} from 'foundation-sites/js/entries/foundation-es'

Not sure how to fix it properly, I was trying to make a POC but my webpack setup kept including all plugins in the build.

@kball
Copy link
Contributor

kball commented Jun 7, 2017

@oxyc I think we could get it to work if we did the 2nd approach where we imported a global Foundation object and then called our imports within the actual function of the initialization... should let webpack statically analyze our imports and only pull in the ones we want. I'll take a stab at this later today or tomorrow

@kball
Copy link
Contributor

kball commented Jun 7, 2017

Hmm... taking a pass at this @oxyc it looks like I was wrong. Imports are hoisted so we can't do the import inside the function.

I also did a pass on wrapping up all of the plugins and re-exporting them to see how webpack treats it... it looks like webpack is properly identifying which ones are not needed (labeling them in the generated JS as unused harmony reexport, but there's some configuration needed to make it actually drop them from the outputted JS.

I'll dig into this further and see what I can figure out. We still end up with the initialization problem if you want to initialize... and part of the premise of this type of modularization is don't necessarily have a global object to put things on to.

That said, since everything is depending on jQuery, and I suspect most folks will use a single global jquery instance, we do in fact have a de facto global object for most environments... we could hang things off of that object and use that as a way to bootstrap implicitly when you run $.fn.foundation()...

Curious what folks like @gakimball @DaSchTour @Owlbertz think of that option

@oxyc
Copy link

oxyc commented Jun 8, 2017

it looks like webpack is properly identifying which ones are not needed (labeling them in the generated JS as unused harmony reexport, but there's some configuration needed to make it actually drop them from the outputted JS.

I believe they're dropped in a production build. https://webpack.js.org/guides/tree-shaking/

@JeremyEnglert
Copy link

JeremyEnglert commented Jun 11, 2017

I'm slightly confused by which files should be imported - however, this may just be because I'm not super familiar with Webpack yet.

There are a lot of similar JS files.

foundation-sites/dist/js/plugins - I'm assuming these are the non-webpack JS files.
foundation-sites/js/entries/plugins - These look like the files to import into Webpack.
foundation-sites/js/ - Not sure what these are. Can someone clarify?

@oxyc
Copy link

oxyc commented Jun 11, 2017

@rossb
Copy link

rossb commented Jun 12, 2017

I'm also having trouble figuring out how to import -- the "Import it all" example above is straightforward, but when it comes to importing and initialising a single plugin I'm at a loss.

I'm thinking something like:

import $ from 'jquery';

window.$ = $;

import { Foundation } from 'js/entries/plugins/foundation.core';
import { ResponsiveMenu, ResponsiveToggle, OffCanvas } from 'js/entries/foundation-plugins';

Foundation.plugin(ResponsiveMenu, 'ResponsiveMenu');
Foundation.plugin(ResponsiveToggle, 'ResponsiveToggle');
Foundation.plugin(OffCanvas, 'OffCanvas');

$(document).foundation();

Where am I going wrong? This is throwing the error plugins/foundation.core doesn't export Foundation, which I can see is the case by looking at that file -- however I also see that each plugin file e.g. "foundation.offcanvas.js" does actually include Foundation from plugins/foundation.core.

I'm using roll-up to bundle, in case that's pertinent.

Basically I'm just trying to work out the most minimal example code to import (and initialise) a single module.

@kball
Copy link
Contributor

kball commented Jun 12, 2017

@rossb Definitely confusing there, using entries for 2 different things at the moment, which is confusing and my bad. Other than the plugins file (which is kind of an experiment at a unified entry point, and probably should include Foundation core) the other files in entries are being used by our internal process to build compiled files.

In your case, if you change import { Foundation } from 'js/entries/plugins/foundation.core'; to import { Foundation } from 'js/plugins/foundation.core'; it should work.

@JeremyEnglert
Copy link

The compiled files in /dist/js/plugins seem to conflict with other jQuery.

In 6.3.x, almost all JS functions started with function($) - which caused them to not to conflict with other jQuery plugins. This doesn't appear to be the case in 6.4.

@aaronjpitts
Copy link

Hi guys, so the following plugins don't seem to work at all with the ES6 imports and using through webpack:

  • Tooltip
  • Interchange
  • Responsive Accordion Tabs

An example would be the below, setting them as directives in Vue.js. Only the toggler works, the other two set some aria attributes correctly but just don't seem to function, i.e. nothing happens.

The syntax is all correct for Vue and I'm using some of the other plugins (tabs, reveal, accordion) as components in a similar way which function well. I have still to test some of the other plugins like equalizer etc.

import { Foundation } from '../../node_modules/foundation-sites/js/foundation.core';
import { Toggler } from '../../node_modules/foundation-sites/js/foundation.toggler';
import { Tooltip } from '../../node_modules/foundation-sites/js/foundation.tooltip';
import { Interchange } from '../../node_modules/foundation-sites/js/foundation.interchange';

Foundation.plugin(Toggler, 'Toggler');
Foundation.plugin(Tooltip, 'Tooltip');
Foundation.plugin(Interchange, 'Interchange');

const FoundationDirectives = {
  install(Vue) {
    Vue.directive('f-toggler', {
      inserted(el) {
        /* eslint-disable no-param-reassign */
        el.fToggler = new Foundation.Toggler($(el));
      },
      unbind(el) {
        el.fToggler.destroy();
      },
    });

    Vue.directive('f-interchange', {
      inserted(el) {
        /* eslint-disable no-param-reassign */
        el.fInterchange = new Foundation.Interchange($(el));
      },
      unbind(el) {
        el.fInterchange.destroy();
      },
    });

    Vue.directive('f-tooltip', {
      inserted(el) {
        /* eslint-disable no-param-reassign */
        el.fTooltip = new Foundation.Tooltip($(el));
      },
      unbind(el) {
        el.fTooltip.destroy();
      },
    });
  },
};

export default FoundationDirectives;

What is the latest on the progress with this?

Many thanks!

@kball
Copy link
Contributor

kball commented Jun 19, 2017

@aaronjpitts thanks for the bug report... this is helpful, and the common thread through these plugins is they all use the MediaQuery utility which may not be initializing properly in the new world... I'll look into a fix, but to make sure can you try importing MediaQuery and running Foundation.MediaQuery._init(); ?

@aaronjpitts
Copy link

aaronjpitts commented Jun 19, 2017

@kball I just tried, imported MediaQuery and tried running Foundation.MediaQuery._init();, it gives undefined. So hopefully this will help you track down the problem :) I hope this helps with the older github issues I logged and that you just replied to. It seems they may be related to MediaQuery, yes. I'm very excited to get all this working and I'm currently working on a Vue.js integration that I'd like to release as a starter template for Vue.js apps.

@kball
Copy link
Contributor

kball commented Jun 19, 2017

aaronjpitts Would it be possible for you to pull down the media-query-init-problem branch and see if it resolves your issue?

@aaronjpitts
Copy link

aaronjpitts commented Jun 19, 2017

@kball I haven't tested all the plugins yet, but from what I can see, yes they all seem to function fine now with this media-query-init-problem branch :) thank you very much!

@aaronjpitts
Copy link

@kball also a question, for a framework like Vue.js, to integrate Foundation, do you think it's best to set all Foundation javascript plugins as global directives in Vue.js or to use them as components? I am using components for things such as Reveal because I think modal windows will often need to be opened programmatically. Just wondering your thoughts.

@dgrammatiko
Copy link

import $ from 'jquery';

This is hideous, either use ES6 or ES5 or jquery. By the way why is jquery a dependency in 2017? Are you supporting IE8?

@kball
Copy link
Contributor

kball commented Jun 20, 2017

@dgt41 100% agree we shouldn't need to have jquery as a dependency moving forward, that is the plan for F7, however F6 is deeply entangled with jQuery (partially due to support back to IE9 and old Android).

The change to use true ESmodern dependency management is the first step to stripping out the jQuery dependency by giving us a framework to separate out that entanglement and create a clean dom manipulation API.

You'll see this go much further in the 6.5 release, and for F7 (targeted Nov 7 of this year) we'll have fully moved to a pluggable architecture that both has a pure ES backend but also makes native implementations in frameworks like Angular, React, Ember, and Vue.

@dgrammatiko
Copy link

@kball are you planning to use web components or custom elements for the components that require javascript?
I've played a bit with that concept and seems the right way to implement such components.
you can check it out here

@kball
Copy link
Contributor

kball commented Jun 20, 2017

@dgt41 plans are still getting finalized... we have some experiments underway... With the custom elements, what's the browser support on the polyfill look like?

@dgrammatiko
Copy link

dgrammatiko commented Jun 20, 2017

@kball Browser support is very good and depending on the polyfiill used for the unsupported browsers can be either > IE11 or > IE9.
The 2 polyffils are:
from Google https://github.com/webcomponents/custom-elements
or https://github.com/WebReflection/document-register-element (used by Google in AMP)

The only thing with custom elements is that they're natively ES6 and if you need to target <IE11 then you need to transpile the code to ES5, but since you're already utilising webpack that will be an easy task.

Anyways eagerly awaiting to see your implementation, the framework support is a game changer IMHO

@jacobarriola
Copy link

Hi all,

I got this working for me. Simple example to cherry pick Reveal only.

One issue I had was having to add the Foundation.MediaQuery = MediaQuery declaration. the addToJquery() method calls for it in the file but it is not imported.

I'm def still learning webpack, so feel free to make any corrections/suggestions.

// foundation.js

import { Foundation } from 'foundation-sites/js/foundation.core';
import { Reveal } from 'foundation-sites/js/foundation.reveal';
import { MediaQuery } from 'foundation-sites/js/foundation.util.mediaQuery';

Foundation.plugin(Reveal, 'Reveal');

// MediaQuery is oddly needed for the addToJquery Method
Foundation.MediaQuery = MediaQuery;
Foundation.addToJquery(jQuery);
jQuery(document).foundation();

// index.js

import './foundation'

@kball
Copy link
Contributor

kball commented Jun 30, 2017

@jacobarriola - Thanks for posting your solution! The MediaQuery reference was a bug fixed here: #10292

@jacobarriola
Copy link

Ahh! Sweet @kball - I'll keep an eye out to see when that fix gets merged in.

Thanks! 👍

@kball
Copy link
Contributor

kball commented Jun 30, 2017

@jacobarriola it is! It was included in the 6.4.1 patch release we pushed out about an hour ago

@proov
Copy link

proov commented Jul 16, 2017

Really nice !! I was looking for this kind of post: load only some plugins, i did this :

/**
* Foundation 6.4 Framework
*/
// ### Core
import { Foundation } from 'foundation-sites/js/foundation.core';

// ### Plugins
import { MediaQuery } from 'foundation-sites/js/foundation.util.mediaQuery';
Foundation.plugin(MediaQuery, 'MediaQuery');

// ### Init
Foundation.addToJquery(jQuery);
$(document).foundation();

It's perfectly working, thanks @jacobarriola and @kball 😘 😀

@JarvisH
Copy link

JarvisH commented Sep 7, 2017

Hi!

The files in js/entries/plugins seem to provide an easy way to import foundation as well as plugins without having to initialize them. The only thing which is missing is an export of the main Foundation object at the end of js/entries/plugins/foundation.core.js. Adding the missing "export {Foundation}" then allows you to do this:

import 'foundation-sites/js/entries/plugins/foundation.core';
import 'foundation-sites/js/entries/plugins/foundation.util.triggers';
import 'foundation-sites/js/entries/plugins/foundation.util.mediaQuery';
import 'foundation-sites/js/entries/plugins/foundation.util.keyboard';
import 'foundation-sites/js/entries/plugins/foundation.interchange';
import 'foundation-sites/js/entries/plugins/foundation.offcanvas';
import 'foundation-sites/js/entries/plugins/foundation.drilldown';

...or importing any other needed plugin.

@DanielRuf
Copy link
Contributor

Do we need more feedback or is the current discussion happening in another issue?

@ncoden ncoden closed this as completed Aug 3, 2018
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests