Page MenuHomePhabricator

Spike: Experiment with creating GrowthExperiments components in external library
Closed, ResolvedPublic

Description

This is a follow-up from @egardner's suggestion in T326233:

Growth could consider developing its Vue components in an external library (outside of MW) similar to how Codex is developed. This would allow a couple of benefits: components could be written in a standard way, you could use Typescript and other front-end build tools like Vite; you could compile your components for use inside of MW but also deploy a stand-alone documentation site using something like Vitepress, and you could split out your CSS styles into a separate file that you could load independently. If Growth has a large-enough amount of custom components, this approach might be worth exploring. In this situation a lot of the existing Codex Vite configuration could be re-used, and DST could help you with initial setup.

Those benefits sound nice to me. And it is relevant to the proposed work for Growth's engineering intern, where their work would be centered in building Vue/Codex applications independent of MediaWiki.

I would like to propose that we do a spike of e.g. 8 hours to see about setting this up in our repository.

Event Timeline

Some recommendations if you move forward with this:

  • Use Vite as a build tool. In Codex, we use a Vite feature called Library Mode which makes it easy to automatically bundle our code into two different formats: UMD (which supports CommonJS usage aka require inside of ResourceLoader) and ES modules (for use outside of MediaWiki). Your styles can be built as a stand-alone CSS file which will make it easy to load styles on the server before JS gets initalized.
  • Library Mode also makes it easy to "externalize" certain runtime dependencies – for example, Vue.js itself (which you'll load from MW during production; you don't want to re-bundle it). If your components build on top of Codex then you can externalize Codex as well.
  • For development, you can add a simple index.html to your project that Vite can use as an entry point; when you build the library you'll use some kind of master "lib" file that imports/exports all components in one place. The index.html file can act as a sandbox or a demo page for all your various components. During dev mode you'll get nice features like hot module reloading and a lot of things should "just work".
  • You can also build this page as a static site and wire it up to something like Netlify if you want a persistent staging environment. If you want a more robust set of documentation pages for each component, consider using Vitepress; we use Vitepress to build the Codex docs.
  • Write your code in standard ES6 style: let and const, import and export, use modern array methods, fetch, etc. Avoid jQuery if possible, but if you need it then be sure to externalize it as you would with Vue or Codex (you'll be using the MW-supplied version of jQuery in production so you don't want to bundle it in with your UI components).
  • Similarly you are going to want to avoid hard-coding dependencies on the global mw object inside your components. A better approach would be to write them to use props or slots so that in production, the caller can talk to MW and then just pass in the data (or make calls to mw.message inside of a slot, etc). You can also consider writing a plugin like we did for the i18n methods. We have managed to make Codex MediaWiki-agnostic by following this approach (with the exception of some places in CSS where we need to override styles that are in core...)
  • Don't feel like you have to use TypeScript if you don't want to; it can be really nice but there is some learning curve.

I'm happy to review any initial "scaffolding" work as you set up this repo, so feel free to ping me on a patch.

We'll prioritize exploring this in the next week or two. Thanks for the recommendations and offers to help, @egardner!

Would storybook make sense to explore? They have vite support for building so it also aligns with using it for bundling source components.

Would storybook make sense to explore? They have vite support for building so it also aligns with using it for bundling source components.

We chose not to use Storybook in Codex for a couple of reasons:

  • Support in Vite was still pretty experimental a year or so ago (I'm not sure what it looks like now)
  • We wanted to make a more narrative documentation page that could contain guidelines and more explanatory text in addition to the interactive component demos. Building the demo tools ourselves instead of relying on Storybook made it a little easier to customize things to suit this.

But for a smaller Growth-specific component library Storybook could be a great fit. I would definitely encourage you to take a look – let us know what the Storybook + Vite experience looks like these days.

I would only recommend *against* using Storybook if you have to keep using Webpack in order to make it work; I think we should avoid Webpack if at all humanly possible at this point.

Change 885339 had a related patch set uploaded (by Sergio Gimeno; author: Sergio Gimeno):

[mediawiki/extensions/GrowthExperiments@master] WIP setup storybook with vite builder

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

We chose not to use Storybook in Codex for a couple of reasons:

  • Support in Vite was still pretty experimental a year or so ago (I'm not sure what it looks like now)

Storybook 7 is in beta but aims to have first class support for vite. Testing a local setup with storybook v7.0.0-beta.33 required installing NodeJS 16+. Using vite for building seems to work as documented aside from this config issue I stumbled and the performance concerns brought by the same storybook team (storybook-performance-from-webpack-to-vite).

  • We wanted to make a more narrative documentation page that could contain guidelines and more explanatory text in addition to the interactive component demos. Building the demo tools ourselves instead of relying on Storybook made it a little easier to customize things to suit this.

I think making the narrative aside from the interactive component could be achieved with .md, .mdx files similarly on how you are doing it currently for the Codex docs with vitepress but it's true storybook seems less flexible on this.

But for a smaller Growth-specific component library Storybook could be a great fit. I would definitely encourage you to take a look – let us know what the Storybook + Vite experience looks like these days.

I spent some time on a local setup that would work to document some current existing components in the GrowthExperiments extension and I was disappointed about the support for Vue slots (args API). Based on a github discussion I suceeded to render slots using custom templates but the code preview does not work as expected. There are some (incomplete) hacks to fix the preview storybook-code-preview-doesnt-show-the-usage-of-slots-in-vuejs but having to create a custom template for each component with slots seems a lot of tedious boilerplate. I think storybook (with vite) seems a nice combination to research further but IMO using it as code sandbox for interns to work or as a documentation tool for the GrowthExperiments components has too much overhead right now.

I would only recommend *against* using Storybook if you have to keep using Webpack in order to make it work; I think we should avoid Webpack if at all humanly possible at this point.

I get the point of vite being a preferred build tool for Vue projects (is this documented somewhere?) but the build tool doesn't seem to play such an important role for a sandbox / docs generator since it will only be run by developers locally.

Creating a bundle of GrowthExperiments components can be achieved with vite. For doing so the components need to be translated from commonjs to es module type (Change const dep = require(...) staements by import dep from ...).

To create a bundle follow the next steps:

  1. Create and index.js re-exporting each component you want to add to the bundle.
import CText from './CText.vue';

export default {
    CText
};
  1. Create a vite configuration file using vite's library mode.
import vue from '@vitejs/plugin-vue';
import { resolve } from 'path'
import { defineConfig } from 'vite'

export default defineConfig({
	plugins: [
		vue(),
	],
	build: {
		lib: {
			// Could also be a dictionary or array of multiple entry points
			entry: resolve(__dirname, '../modules/index.js'),
			name: 'ext.growthExperiments.ui',
			// the proper extensions will be added
			fileName: 'growthexperiments-ui',
			formats: [ 'umd' ],
		},
		rollupOptions: {
			// make sure to externalize deps that shouldn't be bundled
			// into your library
			external: ['vue'],
			output: {
				// Provide global variables to use in the UMD build
				// for externalized deps
				globals: {
					vue: 'Vue',
				},
			},
		},
	},
})
  1. Run vite build pointing to the config file
./node_modules/.bin/vite build --config vite.config.js --outDir dist

Use --emptyOutDir to flush the output directory before the build.

gerrit 885339 shows a working usage of the resulting bundle in GrowthExperiments modules.

My conclusion is that this process is interesting to allow writing components in a standard way and documenting them and allow to later reuse them in the GrowthExperiments extension. On the other hand there's no clear benefit on creating a bundle of reusable components in GrowthExperiments at the moment because these are very few (only 5 under vue-components and they are sparsely reused. So probably a per-component bundle would be more appropriate. Since many reusable GrowthExperiments components might be replaced by Codex's when done this doesn't seem very impactful right now.

Change 885850 had a related patch set uploaded (by Sergio Gimeno; author: Sergio Gimeno):

[mediawiki/extensions/GrowthExperiments@master] Setup vitepress frontend documentation

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

Change 886335 had a related patch set uploaded (by Sergio Gimeno; author: Sergio Gimeno):

[mediawiki/extensions/GrowthExperiments@master] DNM Proof of concept of standalone Vue component demo

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

Sgs changed the task status from Open to In Progress.Feb 7 2023, 11:52 AM
Sgs moved this task from In Progress to Code Review on the Growth-Team (Sprint 0 (Growth Team)) board.

Change 885339 abandoned by Sergio Gimeno:

[mediawiki/extensions/GrowthExperiments@master] WIP setup storybook with vite builder

Reason:

Reworked to use VitePress instead of Storybook in If9b03aa00f2dd638b5fb35f273fd48a2e0b5de22

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

Change 885850 merged by jenkins-bot:

[mediawiki/extensions/GrowthExperiments@master] Setup VitePress frontend documentation

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

I don't think this task requires testing conducted by QA engineers since the code we've written is not going to production any time soon. Once we resolve T329034: Publish frontend docs to doc.wikimedia.org/ the docs will be available in doc.wikimedia.org as a central point for testing, reviewing design, etc.

For those interested to test this locally here are the basic steps to get the docs project working. Install NodeJS 14+ and then from the GrowthExperiments extension root directory run:

  1. cd docs/frontend
  2. npm install
  3. npm run docs:dev
> growthexperiments-docs@1.0.0 docs:dev
> vitepress dev docs

vitepress v1.0.0-alpha.45

  ➜  Local:   http://localhost:5173/
  ➜  Network: use --host to expose

You can browse the docs in your browser in http://localhost:5173

Change 891090 had a related patch set uploaded (by Kosta Harlan; author: Kosta Harlan):

[mediawiki/extensions/GrowthExperiments@master] build: Update Codex to 0.6.1

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

Change 891091 had a related patch set uploaded (by Kosta Harlan; author: Kosta Harlan):

[mediawiki/extensions/GrowthExperiments@master] build: Re-enable npm run docs

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

Change 891090 merged by jenkins-bot:

[mediawiki/extensions/GrowthExperiments@master] build: Update Codex to 0.6.1

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

Change 891091 merged by jenkins-bot:

[mediawiki/extensions/GrowthExperiments@master] build: Re-enable npm run doc

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

gerrit 886335 shows a proof of concept of how to reuse a component compiled as a lib in the MW environment, the process is documented in /documentation/frontend/README.md#standalone-demos and T328125#8579382. I think we can conclude this experiment and move forward to use compiled components when we have a use case for this, for instance when T329037 and T329038 are done. Depending on the outcome of T328699: Consider including a JS runtime as part of MediaWiki we could even avoid having to commit compiled scripts to the repository.

Change 891262 had a related patch set uploaded (by Kosta Harlan; author: Kosta Harlan):

[mediawiki/extensions/GrowthExperiments@master] docs: Update content for Vitepress site

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

gerrit 886335 shows a proof of concept of how to reuse a component compiled as a lib in the MW environment, the process is documented in /documentation/frontend/README.md#standalone-demos and T328125#8579382. I think we can conclude this experiment and move forward to use compiled components when we have a use case for this, for instance when T329037 and T329038 are done. Depending on the outcome of T328699: Consider including a JS runtime as part of MediaWiki we could even avoid having to commit compiled scripts to the repository.

Sounds good to me!

Change 891262 merged by jenkins-bot:

[mediawiki/extensions/GrowthExperiments@master] docs: Update content for Vitepress site

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

Change 923563 had a related patch set uploaded (by Sergio Gimeno; author: Sergio Gimeno):

[mediawiki/extensions/GrowthExperiments@master] Frontend documentation: prepare library bundle with AddLinkDialog component

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

Change 923565 had a related patch set uploaded (by Sergio Gimeno; author: Sergio Gimeno):

[mediawiki/extensions/GrowthExperiments@master] DNM Proof of concept of standalone Vue component demo

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

Change 886335 abandoned by Sergio Gimeno:

[mediawiki/extensions/GrowthExperiments@master] DNM Proof of concept of standalone Vue component demo

Reason:

reworked in I6d2754592c6c181dd96b2f06863620a687f76a93

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

Change 923565 had a related patch set uploaded (by Sergio Gimeno; author: Sergio Gimeno):

[mediawiki/extensions/GrowthExperiments@master] DNM Proof of concept of standalone Vue component demo

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

Change 923565 abandoned by Sergio Gimeno:

[mediawiki/extensions/GrowthExperiments@master] DNM Proof of concept of standalone Vue component demo

Reason:

Reworked in I492d3bbb9ec24bbf81cdf2e3c7a9c79f8546a1ba

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

Change 929362 had a related patch set uploaded (by Sergio Gimeno; author: Sergio Gimeno):

[mediawiki/extensions/GrowthExperiments@master] DNM Add a link: use Vue dialog based on query param

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