foundation / foundation-sites

The most advanced responsive front-end framework in the world. Quickly create prototypes and production code for sites that work on any kind of device.
https://get.foundation
MIT License
29.65k stars 5.49k forks source link

[webpack Module Integration] Release Candidate Feedback thread #10140

Closed rafibomb closed 6 years ago

rafibomb commented 7 years ago

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 commented 7 years ago

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 commented 7 years ago

@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 commented 7 years ago

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 commented 7 years ago

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 commented 7 years ago

@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 commented 7 years ago

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 commented 7 years ago

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 commented 7 years ago

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 commented 7 years ago

@JeremyEnglert for an example you can check the zurb/foundation-zurb-template repo:

rossb commented 7 years ago

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 commented 7 years ago

@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 commented 7 years ago

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 commented 7 years ago

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

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 commented 7 years ago

@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 commented 7 years ago

@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 commented 7 years ago

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

aaronjpitts commented 7 years ago

@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 commented 7 years ago

@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 commented 7 years ago
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 commented 7 years ago

@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 commented 7 years ago

@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 commented 7 years ago

@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 commented 7 years ago

@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 commented 7 years ago

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 commented 7 years ago

@jacobarriola - Thanks for posting your solution! The MediaQuery reference was a bug fixed here: https://github.com/zurb/foundation-sites/pull/10292

jacobarriola commented 7 years ago

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

Thanks! 👍

kball commented 7 years ago

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

proov commented 7 years ago

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 commented 7 years ago

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 commented 6 years ago

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