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

feat(tools): scripts and generator updates to enable SWC transpilation for v9 packages #26527

Conversation

TristanWatanabe
Copy link
Member

@TristanWatanabe TristanWatanabe commented Jan 27, 2023

Changes:

Scripts:

  • Adds an swc.ts file which invokes the SWC CLI that allows us to transpile our code based on a browser matrix target. Browser matrix and other necessary configs are provided by the .swcrc file added to each v9 package.
  • Adds swc related just-tasks to preset just-tasks.
  • Adds build:react-components just-task to be used by v9 packages. The key difference here and the normal build task is that this now splits up transpilation and api-generation as two separate tasks. The transpilation portion uses SWC to transpile code down and then makes use of babel to transform griffel related code. Transpiling to cjs or amd will now use the already transpiled esm code output from SWC as a workaround to the issue where our griffel babel-preset is unable to directly parse cjs code transpiled by SWC (tracking PR of ongoing work for this). This method allows for @griffel/babel-preset to still parse code as expected since it has no trouble transforming esm transpiled code from SWC. The api-generation task now generates TS dts files and then runs the api-extractor thus allowing as to do both transpilation and api-generation in PARALLEL, speeding up build time.
    • Example of browser target based transpilation:
      1. After SWC transpiles code with our full browser support matrix which includes nullish coallescing , code is not transformed:

        image

      2. After SWC transpiles files with a browser matrix target that doesn't support nullish coalescing, nullish coalescing syntax is transformed:

        image

Generators:

  • migrate-converged-pkg workspace generator will now add an .swcrc file to all v9 packages pre-populated with our Full Browser Support Matrix
  • migrate-converged-pkg will now update the just.config.ts file of all v9 packages to map new build:react-components just-task to act as the build task. This will essentially "opt-in" these packages to the new browser matrix based transpilation approach with SWC.

PR with migration applied to v9 packages: #26528

Related Issue(s)

@size-auditor
Copy link

size-auditor bot commented Jan 27, 2023

Asset size changes

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

Baseline commit: be936b6edefb8d319b6693eb1f36230b716377da (build)

@codesandbox-ci
Copy link

codesandbox-ci bot commented Jan 27, 2023

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 ca8f5dd:

Sandbox Source
@fluentui/react 8 starter Configuration
@fluentui/react-components 9 starter Configuration

@fabricteam
Copy link
Collaborator

fabricteam commented Jan 27, 2023

Perf Analysis (@fluentui/react-components)

No significant results to display.

All results

Scenario Render type Master Ticks PR Ticks Iterations Status
Avatar mount 1344 1335 5000
Button mount 943 965 5000
Field mount 2048 2071 5000
FluentProvider mount 1691 1697 5000
FluentProviderWithTheme mount 657 646 10
FluentProviderWithTheme virtual-rerender 600 606 10
FluentProviderWithTheme virtual-rerender-with-unmount 630 646 10
InfoButton mount 585 570 5000
MakeStyles mount 1902 1904 50000
Persona mount 3083 3045 5000
SpinButton mount 2476 2459 5000

@fabricteam
Copy link
Collaborator

fabricteam commented Jan 27, 2023

📊 Bundle size report

Unchanged fixtures
Package & Exports Size (minified/GZIP)
global-context
createContext
533 B
341 B
global-context
createContextSelector
554 B
348 B
priority-overflow
createOverflowManager
3.194 kB
1.314 kB
react-accordion
Accordion (including children components)
88.789 kB
26.532 kB
react-alert
Alert
90.467 kB
22.106 kB
react-avatar
Avatar
54.149 kB
14.571 kB
react-avatar
AvatarGroup
18.064 kB
6.664 kB
react-avatar
AvatarGroupItem
70.425 kB
19.075 kB
react-badge
Badge
25.746 kB
7.568 kB
react-badge
CounterBadge
26.725 kB
7.874 kB
react-badge
PresenceBadge
28.318 kB
7.779 kB
react-button
Button
39.613 kB
9.935 kB
react-button
CompoundButton
46.795 kB
11.405 kB
react-button
MenuButton
44.296 kB
11.255 kB
react-button
SplitButton
52.846 kB
12.866 kB
react-button
ToggleButton
57.982 kB
11.853 kB
react-card
Card - All
83.548 kB
23.894 kB
react-card
Card
78.317 kB
22.433 kB
react-card
CardFooter
9.045 kB
3.806 kB
react-card
CardHeader
10.984 kB
4.509 kB
react-card
CardPreview
9.852 kB
4.159 kB
react-checkbox
Checkbox
35.895 kB
10.82 kB
react-checkbox
CheckboxField
42.8 kB
12.714 kB
react-combobox
Combobox (including child components)
88.7 kB
28.099 kB
react-combobox
ComboboxField
85.066 kB
27.838 kB
react-combobox
Dropdown (including child components)
87.387 kB
27.842 kB
react-components
react-components: Button, FluentProvider & webLightTheme
67.15 kB
18.121 kB
react-components
react-components: Accordion, Button, FluentProvider, Image, Menu, Popover
206.317 kB
57.147 kB
react-components
react-components: FluentProvider & webLightTheme
37.981 kB
12.053 kB
react-dialog
Dialog (including children components)
92.72 kB
27.216 kB
react-divider
Divider
19.754 kB
6.68 kB
react-field
Field
20.397 kB
7.053 kB
react-image
Image
13.897 kB
4.974 kB
react-infobutton
InfoButton
130.438 kB
39.407 kB
react-input
Input
25.664 kB
7.695 kB
react-input
InputField
35.613 kB
10.592 kB
react-label
Label
12.461 kB
4.57 kB
react-link
Link
12.306 kB
5.073 kB
react-menu
Menu (including children components)
130.333 kB
39.499 kB
react-menu
Menu (including selectable components)
133.509 kB
40.037 kB
react-overflow
hooks only
11.214 kB
4.271 kB
react-persona
Persona
61.21 kB
16.521 kB
react-popover
Popover
117.51 kB
35.799 kB
react-portal
Portal
11.715 kB
4.265 kB
react-portal-compat
PortalCompatProvider
6.324 kB
2.129 kB
react-positioning
usePositioning
24.01 kB
8.793 kB
react-progress
ProgressBar
15.846 kB
5.658 kB
react-progress
ProgressField
26.289 kB
8.819 kB
react-provider
FluentProvider
19.928 kB
6.832 kB
react-radio
Radio
35.392 kB
11.117 kB
react-radio
RadioGroup
17.872 kB
6.523 kB
react-radio
RadioGroupField
28.023 kB
9.681 kB
react-select
Select
26.87 kB
8.761 kB
react-select
SelectField
36.179 kB
11.28 kB
react-slider
Slider
35.816 kB
11.043 kB
react-slider
SliderField
45.717 kB
13.975 kB
react-spinbutton
SpinButton
35.559 kB
10.351 kB
react-spinbutton
SpinButtonField
44.565 kB
12.765 kB
react-spinner
Spinner
23.336 kB
7.18 kB
react-switch
Switch
30.943 kB
9.25 kB
react-switch
SwitchField
37.805 kB
11.148 kB
react-table
DataGrid
149.792 kB
40.502 kB
react-table
Table (Primitives only)
46.798 kB
12.557 kB
react-table
Table as DataGrid
137.684 kB
35.154 kB
react-table
Table (Selection only)
85.511 kB
21.173 kB
react-table
Table (Sort only)
84.832 kB
20.982 kB
react-text
Text - Default
14.898 kB
5.31 kB
react-text
Text - Wrappers
15.571 kB
5.23 kB
react-textarea
Textarea
28.981 kB
9.071 kB
react-textarea
TextareaField
39.334 kB
12.008 kB
react-theme
Single theme token import
69 B
89 B
react-theme
Teams: all themes
31.33 kB
6.764 kB
react-theme
Teams: Light theme
17.895 kB
5.162 kB
react-tooltip
Tooltip
49.351 kB
16.799 kB
react-utilities
SSRProvider
180 B
159 B
🤖 This report was generated against be936b6edefb8d319b6693eb1f36230b716377da

@fabricteam
Copy link
Collaborator

Perf Analysis (@fluentui/react-northstar)

⚠️ No perf measurements available

@fabricteam
Copy link
Collaborator

fabricteam commented Jan 27, 2023

🕵 fluentuiv9 No visual regressions between this PR and main

@fabricteam
Copy link
Collaborator

fabricteam commented Jan 27, 2023

Perf Analysis (@fluentui/react)

No significant results to display.

All results

Scenario Render type Master Ticks PR Ticks Iterations Status
BaseButton mount 1196 1181 5000
Breadcrumb mount 2965 2966 1000
Checkbox mount 2658 2635 5000
CheckboxBase mount 2384 2361 5000
ChoiceGroup mount 4418 4461 5000
ComboBox mount 1270 1282 1000
CommandBar mount 9589 9597 1000
ContextualMenu mount 14224 14314 1000
DefaultButton mount 1368 1418 5000
DetailsRow mount 3453 3509 5000
DetailsRowFast mount 3500 3494 5000
DetailsRowNoStyles mount 3307 3357 5000
Dialog mount 3086 3142 1000
DocumentCardTitle mount 577 582 1000
Dropdown mount 3213 3254 5000
FocusTrapZone mount 2055 2068 5000
FocusZone mount 1961 1987 5000
GroupedList mount 52756 59158 2
GroupedList virtual-rerender 24653 24658 2
GroupedList virtual-rerender-with-unmount 93195 93186 2
GroupedListV2 mount 561 561 2
GroupedListV2 virtual-rerender 537 555 2
GroupedListV2 virtual-rerender-with-unmount 548 573 2
IconButton mount 1929 1930 5000
Label mount 734 738 5000
Layer mount 4350 4265 5000
Link mount 833 834 5000
MenuButton mount 1673 1683 5000
MessageBar mount 2257 2257 5000
Nav mount 3267 3591 1000
OverflowSet mount 1342 1379 5000
Panel mount 2525 2546 1000
Persona mount 1303 1341 1000
Pivot mount 1629 1650 1000
PrimaryButton mount 1523 1536 5000
Rating mount 6987 7023 5000
SearchBox mount 1491 1512 5000
Shimmer mount 2882 2838 5000
Slider mount 2073 2104 5000
SpinButton mount 5035 4673 5000
Spinner mount 810 805 5000
SplitButton mount 3114 3111 5000
Stack mount 833 850 5000
StackWithIntrinsicChildren mount 2309 2299 5000
StackWithTextChildren mount 4748 4745 5000
SwatchColorPicker mount 10462 10481 5000
TagPicker mount 2609 2611 5000
TeachingBubble mount 85666 87234 5000
Text mount 799 815 5000
TextField mount 1567 1595 5000
ThemeProvider mount 1525 1510 5000
ThemeProvider virtual-rerender 1095 1113 5000
ThemeProvider virtual-rerender-with-unmount 2142 2160 5000
Toggle mount 1115 1106 5000
buttonNative mount 543 556 5000

@TristanWatanabe TristanWatanabe marked this pull request as ready for review January 27, 2023 08:25
@TristanWatanabe TristanWatanabe requested a review from a team as a code owner January 27, 2023 08:25
@Hotell Hotell self-requested a review January 31, 2023 13:12
@TristanWatanabe
Copy link
Member Author

TristanWatanabe commented Mar 9, 2023

Had a few hiccups with this as it increased bundle size for the v9 packages. Finally got the correct setup for the .swcrc files which decreases (which is what we expect) the bundle size. See results here: #27036 (comment)

@fabricteam
Copy link
Collaborator

fabricteam commented Mar 9, 2023

🕵 fluentuiv8 No visual regressions between this PR and main

@layershifter
Copy link
Member

layershifter commented Mar 9, 2023

Ran npm diff --diff=@fluentui/[email protected] --diff=@fluentui/[email protected] ./lib/ --json > output.json to see any possible build output differences between the last nightly release based on master (@fluentui/[email protected]) and the nightly release based on my test branch with swc transpilation (@fluentui/[email protected]) and the only things that really stood out (amid all the noise) are the difference in helper function imports (from ts to swc), the paths to sources and some more liberated use of modern syntax like nullish coalescing which our browser matrix supports.

@TristanWatanabe there is a difference in the diff that no one noticed:

image

Considering an amount of object spreads it's a bummer that it gets transformed.

I checked that we should always fall into the path with Object.assign (https://github.com/swc-project/swc/blob/e0d4220264ec3926f0a5d6b774ea782f2e511a08/packages/swc-helpers/src/_extends.mjs#L2), but additional function calls are newer free. Especially, it's strange for this feature as object spread is few years older than nullish coalescing and should work natively everywhere in our browser matrix.


EDIT: Looks like bf390c0 fixed that, so ignore it 🐱

@TristanWatanabe
Copy link
Member Author

Ran npm diff --diff=@fluentui/[email protected] --diff=@fluentui/[email protected] ./lib/ --json > output.json to see any possible build output differences between the last nightly release based on master (@fluentui/[email protected]) and the nightly release based on my test branch with swc transpilation (@fluentui/[email protected]) and the only things that really stood out (amid all the noise) are the difference in helper function imports (from ts to swc), the paths to sources and some more liberated use of modern syntax like nullish coalescing which our browser matrix supports.

@TristanWatanabe there is a difference in the diff that no one noticed:

image

Considering an amount of object spreads it's a bummer that it gets transformed.

I checked that we should always fall into the path with Object.assign (swc-project/swc@e0d4220/packages/swc-helpers/src/_extends.mjs#L2), but additional function calls are newer free. Especially, it's strange for this feature as object spread is few years older than nullish coalescing and should work natively everywhere in our browser matrix.

EDIT: Looks like bf390c0 fixed that, so ignore it 🐱

@layershifter Yup we caught that when the bundle size ballooned due to the helper function being used for the spread. @spmonahan actually filed an issue to swc regarding the object spread: swc-project/swc#7044

@fabricteam
Copy link
Collaborator

fabricteam commented Mar 10, 2023

🕵 FluentUI-v0 No visual regressions between this PR and main

@spmonahan
Copy link
Contributor

@layershifter Yup we caught that when the bundle size ballooned due to the helper function being used for the spread. @spmonahan actually filed an issue to swc regarding the object spread: swc-project/swc#7044

@TristanWatanabe Looks like the PR for the issue was closed so I'm not sure if a fix is imminent. However, in looking at the PR it seems like is a (undocumented?) useSpread option. Can you give that a try?

https://github.com/swc-project/swc/pull/7054/files#diff-8abacd70370fd7f85bb3452458bd542918f7b0a9dc6074dce36d726593c4efe0R833

@TristanWatanabe
Copy link
Member Author

@layershifter Yup we caught that when the bundle size ballooned due to the helper function being used for the spread. @spmonahan actually filed an issue to swc regarding the object spread: swc-project/swc#7044

@TristanWatanabe Looks like the PR for the issue was closed so I'm not sure if a fix is imminent. However, in looking at the PR it seems like is a (undocumented?) useSpread option. Can you give that a try?

swc-project/swc#7054 (files)

@spmonahan I just tried adding useSpread:true to the .swcrc file of react-text and now instead of using Object.assign or spreading, it just flat out passes slotProps.root 😬

 * runtime code filtering props in this function.
 *
 * @param state - State including slot definitions
 * @returns An object containing the `slots` map and `slotProps` map.
 */
        function(state) {
            const slots = {}, slotProps = {}, slotNames = Object.keys(state.components);
            for (const slotName of slotNames) {
                const [slot, props] = getSlot(state, slotName);
                slots[slotName] = slot, slotProps[slotName] = props;
            }
            return {
                slots: slots,
                slotProps: slotProps
            };
        }(state);
        /*#__PURE__*/
        return external_React_namespaceObject.createElement(slots.root, slotProps.root);
    }, __GLOBAL__ = "undefined" == typeof window ? __webpack_require__.g : window;

@spmonahan
Copy link
Contributor

@spmonahan I just tried adding useSpread:true to the .swcrc file of react-text and now instead of using Object.assign or spreading, it just flat out passes slotProps.root 😬

Interesting. I wonder if maybe that's even more desirable behavior than directly using spread? Just passing the props seems to be the "default" Babel behavior.

@Hotell / @layershifter do you know why we used spread with the old/current build tooling?

@layershifter
Copy link
Member

@Hotell / @layershifter do you know why we used spread with the old/current build tooling?

That's interesting:

// INPUT
import * as React from 'react'

function App(props) {
    return <div {...props} />
}
function App2(props) {
    return <div {...props} aria-label="foo" />
}

⬇️⬇️⬇️

// OUTPUT BY TS
import * as React from 'react';
function App(props) {
    return React.createElement("div", { ...props });
}
function App2(props) {
    return React.createElement("div", { ...props, "aria-label": "foo" });
}
// OUTPUT BY BABEL
import * as React from 'react';
function App(props) {
  return /*#__PURE__*/React.createElement("div", props);
}
function App2(props) {
  return /*#__PURE__*/React.createElement("div", _extends({}, props, {
    "aria-label": "foo"
  }));
}

AFAIR it's okay to avoid spread there as React does a loop through props anyway.

@TristanWatanabe
Copy link
Member Author

@Hotell / @layershifter do you know why we used spread with the old/current build tooling?

That's interesting:

// INPUT
import * as React from 'react'

function App(props) {
    return <div {...props} />
}
function App2(props) {
    return <div {...props} aria-label="foo" />
}

⬇️⬇️⬇️

// OUTPUT BY TS
import * as React from 'react';
function App(props) {
    return React.createElement("div", { ...props });
}
function App2(props) {
    return React.createElement("div", { ...props, "aria-label": "foo" });
}
// OUTPUT BY BABEL
import * as React from 'react';
function App(props) {
  return /*#__PURE__*/React.createElement("div", props);
}
function App2(props) {
  return /*#__PURE__*/React.createElement("div", _extends({}, props, {
    "aria-label": "foo"
  }));
}

AFAIR it's okay to avoid spread there as React does a loop through props anyway.

To add on to this, the swc output with useSpread:true is basically almost identical to the TS output:

// SWC output via https://swc.rs/playground
import * as React from 'react';
function App(props) {
    return /*#__PURE__*/ React.createElement("div", props);
}
function App2(props) {
    return /*#__PURE__*/ React.createElement("div", {
        ...props,
        "aria-label": "foo"
    });
}

@spmonahan
Copy link
Contributor

To add on to this, the swc output with useSpread:true is basically almost identical to the TS output:

Just so I'm clear, useSpread would work for us then?

@TristanWatanabe
Copy link
Member Author

To add on to this, the swc output with useSpread:true is basically almost identical to the TS output:

Just so I'm clear, useSpread would work for us then?

Haven't investigated it yet but it's now consistently failing for ssr-tests when I fully tested it with all v9 packages using useSpread so maybe not? https://uifabric.visualstudio.com/fabricpublic/_build/results?buildId=292722&view=logs&j=258ec178-2d8b-5611-7b9b-60c5c95dae55&t=254ee48e-1503-524f-f75e-e2c69d0380f5

@TristanWatanabe
Copy link
Member Author

TristanWatanabe commented Mar 15, 2023

To add on to this, the swc output with useSpread:true is basically almost identical to the TS output:

Just so I'm clear, useSpread would work for us then?

Haven't investigated it yet but it's now consistently failing for ssr-tests when I fully tested it with all v9 packages using useSpread so maybe not? uifabric.visualstudio.com/fabricpublic/_build/results?buildId=292722&view=logs&j=258ec178-2d8b-5611-7b9b-60c5c95dae55&t=254ee48e-1503-524f-f75e-e2c69d0380f5

Hmm I was able to get a good build this time around. I wasn't able to repro the puppeteer navigation timeout error that was popping up in CI on my local machine so it was hard to debug. The ssr-tests-v9 timeout errors may be completely unrelated to the swc transpilation change anyway, that's been a known intermittent issue for a while now that pops up on the build failures channel quite frequently 🤔. With what @layershifter said regarding not needing a spread and the swc output with useSpread:true being almost identical with TS, I'm fine with replacing useBuiltIns with useSpread instead

@spmonahan
Copy link
Contributor

I'm fine with replacing useBuiltIns with useSpread instead

I posted a follow up question on my thread for the SWC folks to make sure useSpread is something safe for us to use since I'm not able to find it in the SWC docs. But otherwise, sounds good to me.

@spmonahan
Copy link
Contributor

I posted a follow up question on my thread for the SWC folks to make sure useSpread is something safe for us to use since I'm not able to find it in the SWC docs.

Looks like we should use it.

@TristanWatanabe
Copy link
Member Author

TristanWatanabe commented Mar 17, 2023

Released a new nightly version (0.0.0-nightly-20230317-1454.1) to check and components worked and loaded as expected on a CRA. Will merge this in now and make PR applying these updates to all v9 packages ready for review.

@TristanWatanabe TristanWatanabe merged commit ea12408 into microsoft:master Mar 17, 2023
@TristanWatanabe TristanWatanabe deleted the use-swc-transpilation-tooling-updates branch March 17, 2023 15:26
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging this pull request may close these issues.

7 participants