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

Packaging UI components #109

Closed
i-like-robots opened this issue Jan 28, 2019 · 15 comments
Closed

Packaging UI components #109

i-like-robots opened this issue Jan 28, 2019 · 15 comments

Comments

@i-like-robots
Copy link
Contributor

i-like-robots commented Jan 28, 2019

How do expect consumers to integrate UI components provided by Anvil?

We currently have two components: The FT header and the FT footer. They each comprise of templates provided by Anvil with styles and behaviour from Origami.

The footer component has a clear separation of concerns so we are able to provide straightforward instructions to consumers to inform them that they may integrate Origami styles and client-side JS however suits them. We have done this previously done exactly this with x-teaser.

However, there is often no clear separation between the responsibilities of Anvil and Origami. The header component requires additional styles and behaviour to be added to it, including:

  • Search typeahead
  • MyFT notifications
  • Drawer and meganav z-indexes

These additions will need to be packaged along with the template. They require dependencies from both npm and Bower. How should we do this?

This is not a new problem and many Next UI components have needed to solve it previously. The happy path for the end user is that they only need to install and integrate a single dependency. Ideally any solution to this problem should be a repeatable pattern we can apply to Anvil components.

Any proposed solution needs to consider:

  • How Bower dependencies should be specified and installed by both consuming apps and when developing locally within Anvil.
  • How styles should be distributed, should we ship source code or a precompiled CSS file?
  • We want to leverage bundle splitting and hashing to optimise code reuse between apps using Anvil. In this example both the header and footer code should be extractable in order to include it in shared bundles.
  • These challenges also affect non-visible components too such as ads and tracking.
  • Anvil components cannot be published to Bower (because we are working within a monorepo)
  • Installing Origami components installed via Bower are source code and require transpiling

This is a complicated problem and I don't expect any solution to be free of drawbacks, but I'm opening this issue so that trade-offs can be shared in a public place.

CC #55

@i-like-robots
Copy link
Contributor Author

i-like-robots commented Jan 28, 2019

When we tackled this problem as part of the x-dash project we installed Bower as a dependency of each component that needed it. Then, using Rollup we bundled the styles at the same time as the JavaScript source code, enabling x-dash components to ship a CSS file along with the component JS code.

This solution did require some extra code in order to integrate with Storybook and if these styles are to be effectively shared (and cached) between apps using Anvil then we may need to come up with some form of CSS bundle splitting.

We also do not have any x-dash components requiring installation of Origami JS via Bower.

Links:

@i-like-robots
Copy link
Contributor Author

i-like-robots commented Jan 29, 2019

Previously we have double published to both npm and Bower, for example the n-teaser component or n-ui itself. The only reason for double publishing was so that all dependencies could be specified and resolved by consuming apps by running both an npm install and bower install step.

@i-like-robots
Copy link
Contributor Author

i-like-robots commented Jan 29, 2019

Interesting idea... ship bower.json manifest file with our components to npm and enable consumers to find them: https://www.npmjs.com/package/bower-dependencies-resolver

Example end-user bower.json:

{
  "name": "consuming-app",
  "dependencies": {
    "anvil-stuff": "bdr:./packages/anvil-ui-*"
  }
}

And .bowerrc:

{
  "resolvers": [
    "bower-dependencies-resolver"
  ]
}

Please note that I had to make a small modification in order to get the package above to work...

@magsallen
Copy link
Contributor

I keep changing my mind about how we should approach this.

I think each anvil component should probably resolve all its own dependencies from bower, npm and any custom styles and that we should implement some clever deduping to manage combined code after the fact.

There are probably potential performance issues to consider though. And I'm sure there would be challenges around managing alternate semver versions of deps. And it would be a fair amount of abstraction 🤔 🤷‍♀️

@kavanagh
Copy link
Member

Isn't the plan for us to get rid of Bower?

@kavanagh
Copy link
Member

How styles should be distributed, should we ship source code or a precompiled CSS file?

This idea of shipping precompiled CSS is interesting. Or put another way, not exposing SCSS to consumers. Or, not exposing the Scss of the underlying Origami components to consumers and instead of a very deliberate mixin API if consumers need something.

@kavanagh
Copy link
Member

If you were to put Origami and Bower aside what would be the best way of doing this? What do other people/projects do? Can we try to work backwards from those ideas?

@kavanagh
Copy link
Member

Also are there any migration/adoption considerations that you haven't mentioned?

@i-like-robots
Copy link
Contributor Author

i-like-robots commented Jan 29, 2019

Isn't the plan for us to get rid of Bower?

It will be with us for a while yet I suspect. Migrations are never straightforward! We're not going to be publishing any Anvil components to the Bower registry so that at least means we can't treat Bower as a first-class thing anyway.

This idea of shipping precompiled CSS is interesting

It's pretty tempting. The downsides that I can foresee are that updates from upstream dependencies will not naturally flow through and consuming apps will need to concern themselves with copying the files around/re-hashing them somehow.

What do other people/projects do?

I suspect we're in a fairly unique position, at least I can't think of anybody else who has components which are so distributed. I certainly haven't ever seen anybody ship components which require multiple dependencies to be installed in order to use them (e.g. x-teaser + o-teaser)!

Also are there any migration/adoption considerations that you haven't mentioned?

We intend for apps to build all of their assets themselves (unlike n-ui which built and deployed some shared CSS and JS bundles itself). We believe this will simplify things - especially local development - but it does somewhat change the dynamics of this issue.

And although we intend to ship many individual packages we also aim to wrap up related packages into "presets" (cc #98). I reckon presets should encompass and document as many of the interoperability workarounds and hacks as possible to avoid them being spread around our apps and keep the responsibility of the individual parts small.

@i-like-robots
Copy link
Contributor Author

So the current strategy on this is:

The intention here is that this tooling should enable the migration of Bower dependencies without extra work and be straightforward to remove when the time comes.

@i-like-robots
Copy link
Contributor Author

i-like-robots commented Mar 12, 2019

Next up... what are pkg.browser, pkg.main, and pkg.module?

Answer: Nobody knows!

Some related discussions:

Does pkg.main = ES2018 code in CommonJS format? Does pkg.browser = ES5 code in UMD format? Discuss!

@i-like-robots
Copy link
Contributor Author

i-like-robots commented Apr 24, 2019

So my proposal for this is as follows:

pkg.browser = Transpiled ES5 code using ESM
pkg.main = ES2018 code using CommonJS
pkg.module = not in scope

Example package.json for a module which can be used on both the server and client:

{
  "main": "node.js",
  "browser": "browser.js",
  "scripts": {
    "tsc": "../../node_modules/.bin/tsc",
    "build:node": "npm run tsc -- --outDir ./dist/node",
    "build:browser": "npm run tsc -- --outDir ./dist/browser --module es2015 --target ES5",
    "build": "npm run build:node && npm run build:browser"
  }
}

Packages must make both their Node.js and browser code accessible using a deep import:

require('@financial-times/anvil-package/node')
require('@financial-times/anvil-package/browser')

Packages with styles

  • The Sass styles must be specified by pkg.sass
  • The Sass styles must be accessible using a deep import named "styles" @import "anvil-package/styles";

Packages with server-side only components

  • The components must be accessible via the main entry point specified by pkg.main
  • The components must be accessible using a deep import named "component" require('@financial-times/anvil-package/component')
  • A blank browser entry point must be added if there is no corresponding client-side code

Packages with universal components

  • The component must be accessible via the main entry point specified by pkg.main and pkg.browser.
  • The component must be accessible using a deep import named "component" require('@financial-times/anvil-package/component')

@oliverturner
Copy link
Contributor

This looks very good.

Is there a way of linting components to ensure they meet this spec?

@i-like-robots i-like-robots added UI and removed Help wanted Extra attention is needed labels Jun 11, 2019
@i-like-robots
Copy link
Contributor Author

Because we now process the contents of node_modules with Babel (#366) we have switched pkg.browser to point to modern ES2018 code (#409).

@i-like-robots
Copy link
Contributor Author

I'm going to close this as I believe we have not had any problems with the implemented strategy for delivering the UI components specified in this repo.

apaleslimghost pushed a commit that referenced this issue Sep 16, 2024
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

4 participants