Page MenuHomePhabricator

Publish mediawiki.base components as standalone libraries
Open, LowPublic

Description

After T133462 is resolved, we can split some of the mediawiki.base utilities into their own file and publish them as their own versioned libraries.

Ideas:

  • mediawiki.html (mw.html.element, mw.html.escape)
  • mediawiki.message (mw.Message, mw.msg)

Use cases:

  • ..
Original task description:

When working on a side project (written in node) I found myself keen to use the i18n JavaScript code in MediaWiki. To my frustration it's buried inside mediawiki.js with multiple other things and unnecessarily tied to the mediawiki implementation of Map.

I'd like to rectify this with the following proposal:

Benefits:

  • Removal of Map code should save us a few bytes (browser support is pretty good for what we use it for)
  • More generic reusable code for other people's projects.

Event Timeline

I'm not sure how the mediawiki implementation differs from the standard, but I just wanted to point out that there are multiple widely used es2015 shims with polyfills for Map:

If the mediawiki implementation has some additional methods then I'd suggest subclassing the standard type Map and extending it as necessary instead of monkeypatching a native type.

Change 309698 had a related patch set uploaded (by Jdlrobson):
Map should have has and entries methods

https://gerrit.wikimedia.org/r/309698

I made a start to this.
First step is to try and get the Map inside MediaWiki more in line with the native Map class (https://gerrit.wikimedia.org/r/309698)

After that I'd like to us formally remove the non-standard compatible uses of it and see this code pulled out into a shim.

mediawiki.js as I see it can be split into several components:
map.js, mediawiki.js, messages.js, resourceLoader.js and htmlHelpers.js

@Jdlrobson wrote:

When working on a side project (written in node) I found myself keen to use the i18n JavaScript code in MediaWiki.

I agree, I've run into the same problem in Tool Labs. I re-created some of it as Intuition. However, I don't see how any of these sub-components are relevant in this quest. mw.Message only deals with simple $1 formatting (the 4-line function exposed as mw.format()). The rest is just key/value retrieval.

The real benefit would be handling of plural rules and language fallback handling. Most of which is maintained separately as mediawiki.jqueryMsg - and already independently available as jQuery.i18n. The fallback handling is not yet there though, that's currently done server-side only.

I think we should focus on a tangible objective here, such as allowing a certain kind of re-use of our logic end-to-end (e.g. being able to write code that runs on Node.js that is able to produce localised text from just a directory localisation json files).

And most of the sub-components mentioned here wouldn't actually be a prerequisite for that.

Drop mw.Map in favour of native Map
[..]
Removal of Map code should save us a few bytes (browser support is pretty good for what we use it for)

Seems fine in the long term (2+ years ahead). However at this point the native Map is significantly more complex and in my opinion not worth shimming inside the base module. Remember we'll have to ship it unconditionally to all users. Map is new in ES6, we're still busy on dropping ES3 support at the moment in order to require ES5, never mind ES6. Our current implementation is minimal, well-tested and does what we need. The added features would bloat the API with unused code for little benefit.

I'd support starting to phase out (discourage) public use of mw.Map. To my knowledge there no users of it anyway. If there is non-internal use of mw.Map, I'd support migrating that to an es6-map polyfill, then we can deprecate and remove mw.Map as public interface (and keep private for mw.config/mw.user only).

Introduce mediawiki-map shim npm library

I don't think our map interface is worth publishing. Anyone interested in a minimal map can use a plain object, or write their own abstraction, or support newer browsers only and use native Map. And yet, there are various modules like this on npm already. For more elaborate cases, there are well-maintained open-source alternatives that we need not compete with.

Introduce mediawiki-messages module

The only non-trivial code in there would be the 4-line function mw.format(). The rest isn't in mediawiki.js, but in mediawiki.language/ files, pluralRuleParser and mediawiki.jqueryMsg - which was already forked and published as jQuery.i18n.

Introduce mediawiki-html-construction-helper

This could be useful to factor out. Though for non-mediawiki environments there are similar micro libraries, too.

I think in general even if we don't share these with the wider world it would be good to split up mediawiki.js as there's a lot going on it - and by this exercise we might find opportunities!

The problem as I see it, is mediawiki.js provides require and define functionality. If we pulled out things like a module called mediawiki-map it would be nice if we could require( 'mediawiki-map' ) rather than have a library that adds it to the mediawiki object.

I guess what I'm getting at is the startup module could be a lot more modular and this exercise would be beneficial regardless of whether it gets pulled out into npm modules.

@Krinkle speaking to @Catrope it sounds like you are writing code for mediawiki.js to use native maps (he shared with me that apparently V8 does a better job at caching objects if you use the same keys and if you use one like a map we lose out on performance benefits).

I don't see any reference to that discussion here, or you activity to removing the need for the map code. Could you please link those conversations?

Thanks @Krinkle that's super helpful/interesting.

Change 309698 abandoned by Krinkle:
Map should have has and entries methods

Reason:
mw.Map is deprecated. Existing public interface through mw.config and others suffices all use cases and wouldn't benefit much from the methods matching names in ES6 Map. See task for details. We can ship es6 modules for use elsewhere if needed.

https://gerrit.wikimedia.org/r/309698

Just to check - it's not actually deprecated right now (but will be)?

@Jdlrobson It doesn't yet emit a deprecation warning, but direct instantiation of mw.Map has never been promoted and should be avoided - especially in new code. If you find otherwise, I'd like to record use cases here (after having considered a plain object, ad-hoc wrapper or es6-map module).

Volker_E triaged this task as High priority.Feb 8 2017, 7:32 PM
Volker_E moved this task from Inbox to Backlog on the Front-end-Standards-Group board.
Volker_E moved this task from Backlog to Inbox on the Front-end-Standards-Group board.

Would using webpack make sense here? This wouldn't address publishing npm libraries but it would make it easier if we wanted to go down that road.
It would also make the mediawiki.js file more manageable while still leaving a single output at the cost of adding a build step to core... but I think this might be useful to other modules.
Does that sound like an interesting idea? I could throw up a POC patch to show what that looks like.

Would using webpack make sense here? This wouldn't address publishing npm libraries but it would make it easier if we wanted to go down that road.
It would also make the mediawiki.js file more manageable while still leaving a single output at the cost of adding a build step to core... but I think this might be useful to other modules.
Does that sound like an interesting idea? I could throw up a POC patch to show what that looks like.

I am 100% in favor of splitting mediawiki.js into several manageable modules and using a Javascript module bundler to bundle them up into a single file.

I am a big fan of webpack. However, for our use case, I think rollup may be a better fit. I encourage people to read this great comment from the author of rollup.

When should you use Webpack?

Webpack really shines if you're developing a complex application. I don't personally use it, because I'm not particularly keen on the way it encourages Webpack-specific idioms (such as overloading require() to sometimes mean 'load this module', sometimes mean 'have these side-effects', and sometimes mean 'generate this URL'), but that's just me – a huge number of developers are in love with it, and we've seen some incredibly cool stuff (such as hot module replacement) come from the Webpack camp. I'm glad that it exists.

It makes less sense if you're developing a library. The idea that a library should include a module loader (i.e. webpack_require) just so it can load its own modules is, if you think about it, rather bizarre. But that's not Webpack's fault – developers flocked to it in the absence of an alternative. Rollup aims to be one such alternative. (I say 'one such' and not 'the' because the great thing about standards is that annoying details such as which module bundler you use fade into the background – we can stop writing Browserify code and Webpack code and RequireJS code, and just write JavaScript.)

Webpack is more than a Javascript module bundler, I look at it as an application bundler, capable of bundling Javascript modules as well as bundling all static assets together. More like ResourceLoader.

Provided we use commonjs (just like we support that in RL) I'm agnostic to the tool we'd use - I just suggested webpack as I use it, know it and love it, but any type of bundler would be great and I think it would be easy to migrate as we leant on it more.

Will write a poc patch to capture my thinking...

Change 337043 had a related patch set uploaded (by Jdlrobson):
POC: What if we built mediawiki.js using a bundler?

https://gerrit.wikimedia.org/r/337043

Provided we use commonjs (just like we support that sortof in RL) I'm agnostic to the tool we'd use - I just suggested webpack as I use it, know it and love it, but any type of bundler would be great and I think it would be easy to migrate as we leant on it more.

Change 337043 abandoned by Jdlrobson:
POC: What if we built mediawiki.js using a bundler?

Reason:
Was just a POC. I'd be interested in any views about this approach however - this exercise really helped me understand MediaWiki's inner workings a lot more and surely that's a good thing - right now I am very scared to touch all of this code... until then I will dream of a better world.

https://gerrit.wikimedia.org/r/337043

Krinkle renamed this task from Split mediawiki module into multiple components to Publish mediawiki.base components as standalone libraries.Jun 20 2018, 1:55 PM
Krinkle updated the task description. (Show Details)

Yes yes yes! This is all my dreams and more.