Skip to content

Commit

Permalink
Lazy-load the YAML editor
Browse files Browse the repository at this point in the history
Use dynamic import with React.lazy to delay loading of the YAML
editor and CodeMirror dependencies until the user switches the
Create page to YAML mode. This significantly reduces the initial
bundle, improving load times and reducing processing overhead for
the majority of users who do not use this functionality, including
all users running the Dashboard in read-only mode.

Remove the core-js polyfill as it's no longer required, and vite
will warn us if we're using features not supported by the specified
build target (currently es2021).

Enable vendor chunk splitting plugin. The end-result is that the
build now produces 3 chunks:
- one with the bulk of the Dashboard's own code
- one with the YAMLEditor component and its dependenceis (CodeMirror etc.)
- vendor chunk with the remaining 3rd party deps from node_modules

The main chunk and vendor chunk are loaded in parallel so there's no
additional delay introduced by splitting these.
  • Loading branch information
AlanGreene authored and tekton-robot committed Aug 14, 2023
1 parent 4a6b510 commit 18e568c
Show file tree
Hide file tree
Showing 12 changed files with 116 additions and 1,014 deletions.
1 change: 0 additions & 1 deletion index.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,6 @@ import ReactDOM from 'react-dom';
import { QueryClient, QueryClientProvider } from '@tanstack/react-query';
import { ReactQueryDevtools } from '@tanstack/react-query-devtools';

import './src/utils/polyfills';
import { getLocale, setTheme } from './src/utils';

import App from './src/containers/App';
Expand Down
938 changes: 8 additions & 930 deletions package-lock.json

Large diffs are not rendered by default.

5 changes: 2 additions & 3 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -25,16 +25,15 @@
"dependencies": {
"@carbon/icons-react": "^10.49.0",
"@carbon/themes": "^10.55.1",
"@codemirror/legacy-modes": "^6.3.3",
"@tanstack/react-query": "^4.29.3",
"@tanstack/react-query-devtools": "^4.32.0",
"@tektoncd/dashboard-components": "*",
"@tektoncd/dashboard-utils": "*",
"@uiw/codemirror-extensions-langs": "^4.21.9",
"@uiw/react-codemirror": "^4.21.9",
"carbon-components": "^10.58.6",
"carbon-components-react": "^7.59.10",
"carbon-icons": "^7.0.7",
"core-js": "^3.30.2",
"git-url-parse": "^13.1.0",
"js-yaml": "^4.1.0",
"lodash.clonedeep": "^4.5.0",
Expand Down Expand Up @@ -84,7 +83,7 @@
"msw": "^1.2.1",
"prettier": "^3.0.0",
"rollup-plugin-visualizer": "^5.9.2",
"sass": "^1.64.1",
"sass": "^1.64.2",
"storybook": "^7.2.2",
"vite": "^4.4.9",
"vite-plugin-html": "^3.2.0",
Expand Down
42 changes: 24 additions & 18 deletions src/containers/CreatePipelineRun/CreatePipelineRun.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ limitations under the License.
*/
/* istanbul ignore file */

import React, { useState } from 'react';
import React, { Suspense, useState } from 'react';
import { useLocation, useNavigate } from 'react-router-dom-v5-compat';
import keyBy from 'lodash.keyby';
import yaml from 'js-yaml';
Expand All @@ -31,13 +31,13 @@ import {
urls,
useTitleSync
} from '@tektoncd/dashboard-utils';
import { KeyValueList } from '@tektoncd/dashboard-components';
import { KeyValueList, Loading } from '@tektoncd/dashboard-components';
import { useIntl } from 'react-intl';

import {
NamespacesDropdown,
PipelinesDropdown,
ServiceAccountsDropdown,
YAMLEditor
ServiceAccountsDropdown
} from '..';
import {
createPipelineRun,
Expand All @@ -50,6 +50,8 @@ import {
} from '../../api';
import { isValidLabel } from '../../utils';

const YAMLEditor = React.lazy(() => import('../YAMLEditor'));

const initialState = {
creating: false,
invalidLabels: {},
Expand Down Expand Up @@ -478,14 +480,16 @@ function CreatePipelineRun() {
);

return (
<YAMLEditor
code={payloadYaml || ''}
handleClose={handleCloseYAMLEditor}
handleCreate={handleCreate}
kind="PipelineRun"
loading={isLoading}
loadingMessage={loadingMessage}
/>
<Suspense fallback={<Loading />}>
<YAMLEditor
code={payloadYaml || ''}
handleClose={handleCloseYAMLEditor}
handleCreate={handleCreate}
kind="PipelineRun"
loading={isLoading}
loadingMessage={loadingMessage}
/>
</Suspense>
);
}
const pipelineRun = getPipelineRunPayload({
Expand All @@ -511,12 +515,14 @@ function CreatePipelineRun() {
});

return (
<YAMLEditor
code={yaml.dump(pipelineRun)}
handleClose={handleCloseYAMLEditor}
handleCreate={handleCreate}
kind="PipelineRun"
/>
<Suspense fallback={<Loading />}>
<YAMLEditor
code={yaml.dump(pipelineRun)}
handleClose={handleCloseYAMLEditor}
handleCreate={handleCreate}
kind="PipelineRun"
/>
</Suspense>
);
}

Expand Down
32 changes: 27 additions & 5 deletions src/containers/CreatePipelineRun/CreatePipelineRun.test.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -168,6 +168,22 @@ describe('CreatePipelineRun yaml mode', () => {
beforeEach(() => {
vi.clearAllMocks();
vi.spyOn(window.history, 'pushState');

// Workaround for codemirror vs jsdom https://github.com/jsdom/jsdom/issues/3002#issuecomment-1118039915
// for textRange(...).getClientRects is not a function
Range.prototype.getBoundingClientRect = () => ({
bottom: 0,
height: 0,
left: 0,
right: 0,
top: 0,
width: 0
});
Range.prototype.getClientRects = () => ({
item: () => null,
length: 0,
[Symbol.iterator]: vi.fn()
});
});

it('renders with namespace', async () => {
Expand All @@ -176,11 +192,14 @@ describe('CreatePipelineRun yaml mode', () => {
vi.spyOn(PipelineRunsAPI, 'usePipelineRun')
.mockImplementation(() => ({ data: pipelineRunRawGenerateName }));

const { getByRole } = renderWithRouter(<CreatePipelineRun />, {
path: '/create',
route: '/create?mode=yaml&namespace=test-namespace'
const { getByRole, queryAllByText } = renderWithRouter(<CreatePipelineRun />, {
path: '/pipelineruns/create',
route: '/pipelineruns/create?mode=yaml&namespace=test-namespace'
});

await waitFor(() => {
expect(queryAllByText(/Loading/).length).toBe(0);
});
await waitFor(() => {
expect(getByRole(/textbox/)).toBeTruthy();
});
Expand All @@ -196,11 +215,14 @@ describe('CreatePipelineRun yaml mode', () => {
.mockImplementation(() => ({ data: pipelineRunRawGenerateName }));

const { queryAllByText } = renderWithRouter(<CreatePipelineRun />, {
path: '/create',
path: '/pipelineruns/create',
route:
'/create?mode=yaml&pipelineRunName=test-pipeline-run-name&namespace=test-namespace'
'/pipelineruns/create?mode=yaml&pipelineRunName=test-pipeline-run-name&namespace=test-namespace'
});

await waitFor(() => {
expect(queryAllByText(/Loading/).length).toBe(0);
});
expect(submitButton(queryAllByText)).toBeTruthy();

fireEvent.click(submitButton(queryAllByText));
Expand Down
42 changes: 24 additions & 18 deletions src/containers/CreateTaskRun/CreateTaskRun.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ limitations under the License.
*/
/* istanbul ignore file */

import React, { useState } from 'react';
import React, { Suspense, useState } from 'react';
import { useLocation, useNavigate } from 'react-router-dom-v5-compat';
import keyBy from 'lodash.keyby';
import yaml from 'js-yaml';
Expand All @@ -32,14 +32,14 @@ import {
urls,
useTitleSync
} from '@tektoncd/dashboard-utils';
import { KeyValueList } from '@tektoncd/dashboard-components';
import { KeyValueList, Loading } from '@tektoncd/dashboard-components';
import { useIntl } from 'react-intl';

import {
ClusterTasksDropdown,
NamespacesDropdown,
ServiceAccountsDropdown,
TasksDropdown,
YAMLEditor
TasksDropdown
} from '..';
import {
createTaskRun,
Expand All @@ -52,6 +52,8 @@ import {
} from '../../api';
import { isValidLabel } from '../../utils';

const YAMLEditor = React.lazy(() => import('../YAMLEditor'));

const clusterTaskItem = { id: 'clustertask', text: 'ClusterTask' };
const taskItem = { id: 'task', text: 'Task' };

Expand Down Expand Up @@ -477,14 +479,16 @@ function CreateTaskRun() {
);

return (
<YAMLEditor
code={payloadYaml || ''}
handleClose={handleCloseYAMLEditor}
handleCreate={handleCreate}
kind="TaskRun"
loading={isLoading}
loadingMessage={loadingMessage}
/>
<Suspense fallback={<Loading />}>
<YAMLEditor
code={payloadYaml || ''}
handleClose={handleCloseYAMLEditor}
handleCreate={handleCreate}
kind="TaskRun"
loading={isLoading}
loadingMessage={loadingMessage}
/>
</Suspense>
);
}

Expand All @@ -509,12 +513,14 @@ function CreateTaskRun() {
});

return (
<YAMLEditor
code={yaml.dump(taskRun)}
handleClose={handleCloseYAMLEditor}
handleCreate={handleCreate}
kind="TaskRun"
/>
<Suspense fallback={<Loading />}>
<YAMLEditor
code={yaml.dump(taskRun)}
handleClose={handleCloseYAMLEditor}
handleCreate={handleCreate}
kind="TaskRun"
/>
</Suspense>
);
}

Expand Down
18 changes: 8 additions & 10 deletions src/containers/YAMLEditor/YAMLEditor.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -15,13 +15,14 @@ import {
Button,
Form,
FormGroup,
InlineNotification,
Loading
InlineNotification
} from 'carbon-components-react';
import yaml from 'js-yaml';
import React, { useEffect, useState } from 'react';
import CodeMirror from '@uiw/react-codemirror';
import { langs } from '@uiw/codemirror-extensions-langs';
import { StreamLanguage } from '@codemirror/language';
import { yaml as yamlMode } from '@codemirror/legacy-modes/mode/yaml';
import { Loading } from '@tektoncd/dashboard-components';

export default function YAMLEditor({
code: initialCode,
Expand Down Expand Up @@ -156,17 +157,14 @@ export default function YAMLEditor({
/>
)}
<FormGroup legendText="" className="tkn--codemirror--form">
{loading ? (
<div className="tkn--data-loading-overlay">
<Loading description={loadingMessage} withOverlay={false} />
<span className="tkn--data-loading-text">{loadingMessage}</span>
</div>
) : (
{loading ? <Loading message={loadingMessage} /> : (
<CodeMirror
value={code}
height="800px"
theme="dark"
extensions={[langs.yaml()]}
// there's an issue with CodeMirror in the unit tests when loading certain extensions
// but they're not relevant for the purposes of the tests so skip adding them
{...(import.meta.env.MODE === 'test' ? null : { extensions: [StreamLanguage.define(yamlMode)]})}
onChange={onChange}
editable={!loading}
/>
Expand Down
2 changes: 1 addition & 1 deletion src/containers/YAMLEditor/YAMLEditor.test.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -12,8 +12,8 @@ limitations under the License.
*/

import React from 'react';

import { fireEvent, waitFor } from '@testing-library/react';

import { renderWithRouter } from '../../utils/test';
import YAMLEditor from './YAMLEditor';

Expand Down
1 change: 0 additions & 1 deletion src/containers/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -53,4 +53,3 @@ export { default as Trigger } from './Trigger';
export { default as Triggers } from './Triggers';
export { default as TriggerTemplate } from './TriggerTemplate';
export { default as TriggerTemplates } from './TriggerTemplates';
export { default as YAMLEditor } from './YAMLEditor';
15 changes: 0 additions & 15 deletions src/utils/polyfills.js

This file was deleted.

21 changes: 12 additions & 9 deletions vite.config.analyze.js
Original file line number Diff line number Diff line change
Expand Up @@ -11,16 +11,19 @@ See the License for the specific language governing permissions and
limitations under the License.
*/

import { mergeConfig } from 'vite';
import { defineConfig, mergeConfig } from 'vite';
import { visualizer } from 'rollup-plugin-visualizer';

import baseConfig from './vite.config';

export default mergeConfig(baseConfig({}), {
plugins: [
visualizer({
open: true,
template: 'treemap'
})
]
});
export default mergeConfig(
baseConfig({}),
defineConfig({
plugins: [
visualizer({
open: true,
template: 'treemap'
})
]
})
);
13 changes: 10 additions & 3 deletions vite.config.js
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ See the License for the specific language governing permissions and
limitations under the License.
*/

import { defineConfig } from 'vite';
import { defineConfig, splitVendorChunkPlugin } from 'vite';
import { createHtmlPlugin } from 'vite-plugin-html';
import react from '@vitejs/plugin-react-swc';
import svgr from 'vite-plugin-svgr';
Expand All @@ -31,15 +31,22 @@ export default defineConfig(({ _mode }) => ({
// Relative to outDir
assetsDir: '.',
// Relative to the root
outDir: 'cmd/dashboard/kodata'
outDir: 'cmd/dashboard/kodata',
target: 'es2021'
},
css: {
devSourcemap: true
},
esbuild: {
target: 'es2021'
},
plugins: [createHtmlPlugin({}), react(), svgr(), yaml()],
plugins: [
createHtmlPlugin({}),
react(),
svgr(),
yaml(),
splitVendorChunkPlugin()
],
server: {
headers: {
// https://github.com/codemirror/codemirror5/issues/6707
Expand Down

0 comments on commit 18e568c

Please sign in to comment.