Skip to content

RFC: Standard package output folders#17298

Closed
dzearing wants to merge 8 commits intomicrosoft:masterfrom
dzearing:rfc/standard-folders
Closed

RFC: Standard package output folders#17298
dzearing wants to merge 8 commits intomicrosoft:masterfrom
dzearing:rfc/standard-folders

Conversation

@dzearing
Copy link
Copy Markdown
Member

@dzearing dzearing commented Mar 5, 2021

Standardize javascript package output folders for better predictability and clarity over what folder contains what format.

  • lib/* - ESM
  • lib-commonjs/* - CommonJS
  • lib-amd/* - AMD
  • dist/* - bundles and static content

This isn't really a change from what we have, except for projects built with the build:commonJSOnly task (wasn't being used until recently) which drops commonjs in lib (probably by accident, because we didn't document the convention.) For everything else this has been the convention with the Just tooling, and with the web-build-tools tooling prior to that. The last time we changed it was when ESM became officially supported.

I did include an alternative that seems a little more inline with the output folder naming of a few OSS libraries, but I don't think the work involved and potential for impact on customers merits major deviation. It creates unexpected confusion when we shift the package structure.

Recent conversation with @ecraig12345 and @JustSlone led to formalizing this detail as an RFC.

@JustSlone JustSlone added the Type: RFC Request for Feedback label Mar 5, 2021
@codesandbox-ci
Copy link
Copy Markdown

codesandbox-ci Bot commented Mar 5, 2021

This pull request is automatically built and testable in CodeSandbox.

To see build info of the built libraries, click here or the icon next to each commit SHA.

Latest deployment of this branch, based on commit f0a62f4:

Sandbox Source
Fluent UI Button Configuration
codesandbox-react-template Configuration
codesandbox-react-northstar-template Configuration

Comment thread rfcs/build-system/02_standard_package_output_folders.md Outdated
Copy link
Copy Markdown
Member

@ecraig12345 ecraig12345 left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Definitely agree that documented standards are good, but I'm less certain about the importance of being able to tell just from the folder name what the output type is (though not totally opposed to the idea). So I'm curious what others think.

Would also be good to clarify if this is just for v8 packages, or also for converged.

@size-auditor
Copy link
Copy Markdown

size-auditor Bot commented Mar 5, 2021

Asset size changes

Size Auditor did not detect a change in bundle size for any component!

Baseline commit: d9fb5e3ce8dfdf0f6fdb5dee945a29243ec85dc9 (build)

@fabricteam
Copy link
Copy Markdown
Collaborator

fabricteam commented Mar 5, 2021

Perf Analysis

No significant results to display.

All results

Scenario Render type Master Ticks PR Ticks Iterations Status
Avatar mount 1160 1180 5000
BaseButton mount 906 891 5000
Breadcrumb mount 42874 44085 5000
ButtonNext mount 1228 1238 5000
Checkbox mount 1499 1474 5000
CheckboxBase mount 1213 1227 5000
ChoiceGroup mount 4675 4557 5000
ComboBox mount 948 982 1000
CommandBar mount 9950 10007 1000
ContextualMenu mount 5958 6153 1000
DefaultButton mount 1106 1138 5000
DetailsRow mount 3570 3494 5000
DetailsRowFast mount 3593 3593 5000
DetailsRowNoStyles mount 3439 3314 5000
Dialog mount 1376 1453 1000
DocumentCardTitle mount 1783 1777 1000
Dropdown mount 3211 3262 5000
FocusTrapZone mount 1772 1788 5000
FocusZone mount 1833 1824 5000
IconButton mount 1714 1702 5000
Label mount 329 331 5000
Layer mount 1755 1763 5000
Link mount 459 457 5000
MakeStyles mount 1996 2099 50000
MenuButton mount 1426 1454 5000
MessageBar mount 1976 1978 5000
Nav mount 3283 3180 1000
OverflowSet mount 1027 1039 5000
Panel mount 1383 1438 1000
Persona mount 812 798 1000
Pivot mount 1350 1396 1000
PrimaryButton mount 1276 1278 5000
Rating mount 7330 7304 5000
SearchBox mount 1304 1323 5000
Shimmer mount 2439 2509 5000
Slider mount 1877 1969 5000
SpinButton mount 4800 4964 5000
Spinner mount 402 419 5000
SplitButton mount 3170 3120 5000
Stack mount 525 496 5000
StackWithIntrinsicChildren mount 1524 1517 5000
StackWithTextChildren mount 4436 4477 5000
SwatchColorPicker mount 9923 10081 5000
Tabs mount 1417 1397 1000
TagPicker mount 2782 2983 5000
TeachingBubble mount 11405 11547 5000
Text mount 549 425 5000
TextField mount 1365 1323 5000
ThemeProvider mount 1179 1182 5000
ThemeProvider virtual-rerender 586 626 5000
ThemeProviderNext mount 15615 15569 5000
Toggle mount 780 784 5000
buttonNative mount 117 117 5000

Perf Analysis (Fluent)

Perf comparison
Status Scenario Fluent TPI Fabric TPI Ratio Iterations Ticks
🦄 Avatar.Fluent 0.17 0.46 0.37:1 2000 346
🦄 Button.Fluent 0.12 0.19 0.63:1 5000 576
🔧 Checkbox.Fluent 0.65 0.35 1.86:1 1000 652
🎯 Dialog.Fluent 0.16 0.21 0.76:1 5000 823
🔧 Dropdown.Fluent 3.08 0.42 7.33:1 1000 3075
🔧 Icon.Fluent 0.14 0.06 2.33:1 5000 685
🦄 Image.Fluent 0.08 0.13 0.62:1 5000 405
🔧 Slider.Fluent 1.62 0.46 3.52:1 1000 1621
🔧 Text.Fluent 0.07 0.03 2.33:1 5000 374
🦄 Tooltip.Fluent 0.12 0.88 0.14:1 5000 592

🔧 Needs work     🎯 On target     🦄 Amazing

Perf tests with no regressions
Scenario Current PR Ticks Baseline Ticks Ratio
GridMinimalPerf.default 380 349 1.09:1
ListNestedPerf.default 614 566 1.08:1
TreeWith60ListItems.default 190 176 1.08:1
ListWith60ListItems.default 663 625 1.06:1
Avatar.Fluent 346 325 1.06:1
ChatWithPopoverPerf.default 450 430 1.05:1
PopupMinimalPerf.default 731 695 1.05:1
SkeletonMinimalPerf.default 387 369 1.05:1
VideoMinimalPerf.default 671 638 1.05:1
ButtonSlotsPerf.default 606 583 1.04:1
LayoutMinimalPerf.default 422 406 1.04:1
MenuButtonMinimalPerf.default 1655 1595 1.04:1
SegmentMinimalPerf.default 368 355 1.04:1
TableMinimalPerf.default 455 439 1.04:1
ButtonUseCssPerf.default 821 795 1.03:1
CardMinimalPerf.default 568 552 1.03:1
LabelMinimalPerf.default 438 424 1.03:1
PortalMinimalPerf.default 178 172 1.03:1
SliderMinimalPerf.default 1616 1569 1.03:1
TextMinimalPerf.default 375 365 1.03:1
Image.Fluent 405 392 1.03:1
Text.Fluent 374 364 1.03:1
DropdownManyItemsPerf.default 748 731 1.02:1
HeaderSlotsPerf.default 816 797 1.02:1
InputMinimalPerf.default 1302 1271 1.02:1
ListMinimalPerf.default 530 518 1.02:1
LoaderMinimalPerf.default 748 731 1.02:1
ProviderMinimalPerf.default 953 937 1.02:1
ReactionMinimalPerf.default 415 408 1.02:1
RefMinimalPerf.default 242 237 1.02:1
TreeMinimalPerf.default 809 797 1.02:1
Tooltip.Fluent 592 583 1.02:1
AnimationMinimalPerf.default 434 431 1.01:1
AttachmentSlotsPerf.default 1220 1212 1.01:1
ButtonOverridesMissPerf.default 1708 1693 1.01:1
DropdownMinimalPerf.default 3099 3067 1.01:1
EmbedMinimalPerf.default 4264 4205 1.01:1
SplitButtonMinimalPerf.default 3746 3696 1.01:1
TableManyItemsPerf.default 2036 2007 1.01:1
CustomToolbarPrototype.default 3816 3783 1.01:1
Dialog.Fluent 823 816 1.01:1
Dropdown.Fluent 3075 3053 1.01:1
Slider.Fluent 1621 1610 1.01:1
AccordionMinimalPerf.default 159 159 1:1
AvatarMinimalPerf.default 205 204 1:1
CarouselMinimalPerf.default 488 489 1:1
ChatDuplicateMessagesPerf.default 375 374 1:1
DatepickerMinimalPerf.default 46598 46641 1:1
DialogMinimalPerf.default 804 806 1:1
Checkbox.Fluent 652 652 1:1
BoxMinimalPerf.default 381 384 0.99:1
ButtonMinimalPerf.default 189 191 0.99:1
CheckboxMinimalPerf.default 2737 2765 0.99:1
HeaderMinimalPerf.default 393 395 0.99:1
ImageMinimalPerf.default 389 394 0.99:1
ProviderMergeThemesPerf.default 1607 1630 0.99:1
RadioGroupMinimalPerf.default 457 462 0.99:1
StatusMinimalPerf.default 707 711 0.99:1
TextAreaMinimalPerf.default 502 505 0.99:1
ToolbarMinimalPerf.default 970 978 0.99:1
TooltipMinimalPerf.default 816 826 0.99:1
Button.Fluent 576 580 0.99:1
AlertMinimalPerf.default 292 299 0.98:1
FlexMinimalPerf.default 308 315 0.98:1
MenuMinimalPerf.default 905 919 0.98:1
ButtonUseCssNestingPerf.default 1087 1126 0.97:1
IconMinimalPerf.default 667 689 0.97:1
Icon.Fluent 685 704 0.97:1
DividerMinimalPerf.default 392 408 0.96:1
FormMinimalPerf.default 420 439 0.96:1
ChatMinimalPerf.default 618 653 0.95:1
ItemLayoutMinimalPerf.default 1171 1236 0.95:1
AttachmentMinimalPerf.default 173 185 0.94:1
ListCommonPerf.default 626 664 0.94:1
RosterPerf.default 1111 1223 0.91:1

Comment on lines +23 to +26
- `/lib` - esm (as we've had for a long time)
- `/lib-commonjs` - commonjs (only needed while Node <= 13.2.0 is supported)
- `/lib-amd` - amd (hopefully we can drop someday)
- `/dist` - bundles and static content
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

My personal preference is to have lib-esm or lib-es as it obviously says what is in a directory.


Another thing that we discussed some time ago to have a single dist folder. For example:

  • /dist/esm - esm (as we've had for a long time)
  • /dist/commonjs - commonjs (only needed while Node <= 13.2.0 is supported)
  • /dist/amd - amd (hopefully we can drop someday)
  • /dist/assets - bundles and static content

It will have the same props&cons as an alternative proposal, but it will be easier to ignore a single entry in .gitignore/IDE 🐱

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If we want to change things, I agree this would be even cleaner

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Also I could behind the lib-esm approach. It's more explicit, and doesn't favor a particular output. This means we can add more output flavors in the future (the react-native output formats would be the first which come to mind) without the akwardness of picking one as the "true" output flavor.

My primary concern changing now is to avoid destabilizing partners, as Elizabeth mentioned below. My recommendation would be to stick with the current output folders in the writeup, fix the node packages only to stay inline with other packages (since this was a recent change.)

Then in the converged packages, we could start adopting the dist/* layout now. Later in v9+, change everything to be inline with the dist approach.

Is this reasonable/ideal? Concerns?

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

My primary concern changing now is to avoid destabilizing partners, as Elizabeth mentioned below. My recommendation would be to stick with the current output folders in the writeup, fix the node packages only to stay inline with other packages (since this was a recent change.)

Then in the converged packages, we could start adopting the dist/* layout now. Later in v9+, change everything to be inline with the dist approach.

Is this reasonable/ideal? Concerns?

No concerns, make sense for me 👍

Should we update a proposed solution to include v8 (as it stays currently) and converged proposal (for example, /dist layout, or another proposal) in this RFC? Or have an another one?

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think it's simple enough that we could cover both v8 and converged in this RFC.

Though the part we didn't totally decide on for v8 is whether lib must be ESM for all packages, even ones where in reality we only need commonjs.

@ecraig12345
Copy link
Copy Markdown
Member

Just remembered another thing to consider/make clear, for our main version 8 packages (the ones that run in the browser) we probably shouldn't change the output structure at this point. We've tried to make output structure changes within a major release in the past, and it caused problems for enough people that it had to be reverted (@jdhuntington just reminded me of this).

So any changes resulting from this proposal would need to be limited to:

  • Node utility packages. Some of these sort of "go with" v8, but they have much lower usage and consumers are less impacted by output structure changes as long as the main/module entries are correct.
  • Converged packages

* immer - Uses only `dist` and rolls up a separate `esm` bundle.
* react - Commonjs is in `cjs`, there's also a `umd` folder for bundles.
* redux - Uses `es` for esm, `lib` for commonjs.
* styled-components - Only `dist` with `s-c.esm.js` and `s-c.cjs.js`. They also emit a react-native `s-c.native.js` flavor.
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

important thing to answer - is this RFC for both legacy and converged?

I'd follow google/preact crew way of shipping libraries. They have plenty of experience in both building smallest possible libraries and way of shipping those to consumers.

For inspiration -> https://github.com/developit/microbundle

Convergence output:

  • one file rollup bundles (3 types only)
  • shipping source is discouraged as you're allowing consumers to use private API's (I understand this was allowed in v0/v7,8 - I recommend to fix this in v9)
"main": "./dist/foo.umd.js", // legacy UMD output (for Node & CDN use)
"module": "./dist/foo.module.js", // legacy ES Modules output (for bundlers)
"exports": "./dist/foo.modern.js", // modern ES2017 output

Pros:

  • smallest possible JS stored in registry === faster installs, less "garbage" in node modules
  • rollup-ed files faster to parse by bundlers instead of parsing and bundling N modules
  • esbuild can be enabled without any further modification
  • no exposure of private APIs
  • only 1 output instead of 2 for "legacy consumers" (cjs/amd -> umd)

Cons:

  • might not work for legacy (v0,7,8) as they consume private apis/nested paths

Copy link
Copy Markdown
Contributor

@Hotell Hotell Mar 12, 2021

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

regarding dist/, I definitely agree with following suggestion/change:

I briefly mentioned in on of my PR's regarding better DX - TS path aliases, but it got lost within changes...

I'd recommend to have only one dist, coverage(out of topic) folder in root of monorepo

/coverage
  /packages/
     /react-button
/dist
   /packages/
     /react-button
       | - index.umd.js
       | - index.module.js
       | - index.modern.js
       | - index.d.ts
       | - Readme.md
       | - License.md
       | - package.json  // normalized package.json - contains only important data (deps/peerDeps/license,etc)- no scripts/devDeps and other "baggage"
/packages
   /react-button
/apps
  /todo-app

Pros:

  • source code is not being mismatched with generated code
  • all generated assets live under one roof
  • faster cleanup
  • ease of processing

Cons:

  • might not work for v0,7,8 as they are not leveraging TS path aliases - additional work would be needed

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

important thing to answer - is this RFC for both legacy and converged?

Any nontrivial changes will have to be for converged only (the RFC needs to be updated to mention this). #17298 (comment)

Copy link
Copy Markdown
Member Author

@dzearing dzearing Apr 12, 2021

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@Hotell I think 1 dist folder with rollup bundles makes tons of sense for small libraries. With converged packages where they're more single purposed, for example. It is also a very good tool for avoiding deeply nested imports; and as you mentioned, we would probably discourage including build output completely. (There are a few scenarios where it may be useful, like documentation.) Would be nice to roll up the typings as well.

It breaks down in monolithic suite packages that re-export a lot of things, because of a variety of reasons. More to parse unnecessarily, more prone to side effect problems, bundle graph doesn't show enough detail.

So I could get behind this for the converged smaller-purpose packages.

Copy link
Copy Markdown
Member Author

@dzearing dzearing Apr 12, 2021

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What about this:

Legacy: we keep the same. lib (esm), lib-commonjs (commonjs), amd (amd)

Converged:

dist/
   react-button.esm.js
   react-button.d.ts
   react-button.commonjs.js
   react-button.umd.js

@layershifter @ecraig12345 @JustSlone thoughts?

@JustSlone
Copy link
Copy Markdown
Collaborator

So, given the open conversations. I'd like to suggest "Let's Do Both".

For v8 - take the easy no-break fix
(unless it's more complicated than we expected and not worth it)

For vNext: - follow the single /dist folder
(out of scope of this RFC?)

@msft-fluent-ui-bot
Copy link
Copy Markdown
Collaborator

Because this pull request has not had activity for over 150 days, we're automatically closing it for house-keeping purposes.

The pull request will still be available for reference. If it's still relevant to merge at some point, you can reopen or make a new version based on the latest code.

@msft-fluent-ui-bot msft-fluent-ui-bot added the Resolution: Soft Close Soft closing inactive issues over a certain period label Sep 28, 2021
@ecraig12345
Copy link
Copy Markdown
Member

Huh it says "The repository that submitted this pull request has been deleted"?? So I guess if we want this back someone will have to make a new copy.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Resolution: Soft Close Soft closing inactive issues over a certain period Type: RFC Request for Feedback

Projects

None yet

Development

Successfully merging this pull request may close these issues.

8 participants