0
0
mirror of https://github.com/PostHog/posthog.git synced 2024-11-21 21:49:51 +01:00

fix: reduce toolbar bundle size by 60% (#20122)

* output analyzable build info for the toolbar

* don't use code snippet it adds half a meg

* Update UI snapshots for `chromium` (2)

* use esbuild visualizer instead

* fix

* allow treeshaking and remove circular dependency from imports toolbar uses

* fix

* lint the mjs files at the root of frontend folder

* no need to mention lemonui at all

* no ned to specify metafile

* don't allow posthog-js to sneak into the toolbar

* simpler date picker so fewer dependencies

* maybe this

* like this?

* Update UI snapshots for `webkit` (2)

* Update UI snapshots for `chromium` (2)

* Update UI snapshots for `webkit` (2)

* Update UI snapshots for `chromium` (2)

* Update UI snapshots for `webkit` (2)

* Update UI snapshots for `chromium` (2)

* Update UI snapshots for `webkit` (2)

* Update UI snapshots for `chromium` (2)

* Update UI snapshots for `webkit` (2)

* Update UI snapshots for `chromium` (2)

* Update UI snapshots for `webkit` (2)

* Update UI snapshots for `chromium` (2)

* Update UI snapshots for `webkit` (2)

* Update UI snapshots for `chromium` (2)

* Update UI snapshots for `webkit` (2)

* Update UI snapshots for `chromium` (2)

* Update UI snapshots for `webkit` (2)

* Update UI snapshots for `chromium` (2)

* Update UI snapshots for `webkit` (2)

* Update UI snapshots for `chromium` (2)

* Update UI snapshots for `webkit` (2)

* Update UI snapshots for `chromium` (2)

* Update UI snapshots for `webkit` (2)

* Update UI snapshots for `chromium` (2)

* Update UI snapshots for `webkit` (2)

* Update UI snapshots for `chromium` (2)

* Update UI snapshots for `webkit` (2)

* Update UI snapshots for `chromium` (2)

* Update UI snapshots for `webkit` (2)

* Update UI snapshots for `chromium` (2)

* ragE

* Update UI snapshots for `webkit` (2)

* Update UI snapshots for `chromium` (2)

* too easy to break things this way

* Update UI snapshots for `webkit` (2)

* Update UI snapshots for `chromium` (1)

* Update UI snapshots for `chromium` (2)

* Update UI snapshots for `webkit` (2)

* around the houses

* Reset snapshots to master

* explain why there's a plugin

* Update UI snapshots for `webkit` (2)

* Update UI snapshots for `chromium` (1)

* Update UI snapshots for `chromium` (2)

* Update UI snapshots for `webkit` (2)

* Update UI snapshots for `chromium` (2)

* fix

* fix

* Update UI snapshots for `webkit` (2)

* fix

* Update UI snapshots for `webkit` (2)

---------

Co-authored-by: github-actions <41898282+github-actions[bot]@users.noreply.github.com>
This commit is contained in:
Paul D'Ambra 2024-02-19 11:39:15 +00:00 committed by GitHub
parent 61006d8a77
commit d5df038f16
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
19 changed files with 191 additions and 112 deletions

4
.gitignore vendored
View File

@ -60,4 +60,6 @@ upgrade/
hogvm/typescript/dist
.wokeignore
plugin-transpiler/dist
.dlt
*-esbuild-meta.json
*-esbuild-bundle-visualization.html
.dlt

Binary file not shown.

Before

Width:  |  Height:  |  Size: 8.0 KiB

After

Width:  |  Height:  |  Size: 7.9 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 64 KiB

After

Width:  |  Height:  |  Size: 64 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 20 KiB

After

Width:  |  Height:  |  Size: 20 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 75 KiB

After

Width:  |  Height:  |  Size: 75 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 13 KiB

After

Width:  |  Height:  |  Size: 13 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 68 KiB

After

Width:  |  Height:  |  Size: 68 KiB

View File

@ -51,9 +51,43 @@ await buildInParallel(
// make sure we don't link to a global window.define
banner: { js: 'var posthogToolbar = (function () { var define = undefined;' },
footer: { js: 'return posthogToolbar })();' },
// This isn't great but we load some static assets at runtime for the toolbar and we can't sub in
// This isn't great, but we load some static assets at runtime for the toolbar, and we can't sub in
// a variable at runtime it seems...
publicPath: isDev ? '/static/' : 'https://app.posthog.com/static/',
alias: {
'posthog-js': 'posthog-js-lite',
},
writeMetaFile: true,
extraPlugins: [
{
name: 'no-side-effects',
setup(build) {
// sideEffects in package.json lists files that _have_ side effects,
// but we only want to mark lemon-ui as having no side effects,
// so we'd have to list every other file and keep that up to date
// no thanks!
// a glob that negates the path doesn't seem to work
// so based off a comment from the esbuild author here
// https://github.com/evanw/esbuild/issues/1895#issuecomment-1003404929
// we can add a plugin just for the toolbar build to mark lemon-ui as having no side effects
// that will allow tree-shaking and reduce the toolbar bundle size
// by over 40% at implementation time
build.onResolve({ filter: /^(lib|@posthog)\/lemon-ui/ }, async (args) => {
if (args.pluginData) {
return
} // Ignore this if we called ourselves
const { path, ...rest } = args
rest.pluginData = true // Avoid infinite recursion
const result = await build.resolve(path, rest)
result.sideEffects = false
return result
})
},
},
],
...common,
},
],

View File

@ -4,7 +4,7 @@ import { textCardModalLogic } from 'lib/components/Cards/TextCard/textCardModalL
import { PayGateMini } from 'lib/components/PayGateMini/PayGateMini'
import { LemonButton } from 'lib/lemon-ui/LemonButton'
import { LemonModal } from 'lib/lemon-ui/LemonModal'
import { LemonTextAreaMarkdown } from 'lib/lemon-ui/LemonTextArea/LemonTextArea'
import { LemonTextAreaMarkdown } from 'lib/lemon-ui/LemonTextArea'
import { userLogic } from 'scenes/userLogic'
import { AvailableFeature, DashboardType } from '~/types'

View File

@ -1,5 +1,4 @@
import { useActions, useValues } from 'kea'
import { CodeSnippet } from 'lib/components/CodeSnippet'
import { htmlElementsDisplayLogic } from 'lib/components/HTMLElementsDisplay/htmlElementsDisplayLogic'
import { ParsedCSSSelector } from 'lib/components/HTMLElementsDisplay/preselectWithCSS'
import { LemonBanner } from 'lib/lemon-ui/LemonBanner'
@ -130,8 +129,11 @@ export function HTMLElementsDisplay({
return (
<div className="flex flex-col gap-1">
{editable && !!parsedElements.length && (
<div>
Selector: <CodeSnippet thing="chosen selector">{chosenSelector}</CodeSnippet>
<div className="flex flex-col gap-2 mb-2">
<div>Selector:</div>
<div className="w-full border rounded bg-bg-3000 px-4 py-2 select-text">
<pre className="m-0">{chosenSelector}</pre>
</div>
</div>
)}
{checkUniqueness && (

View File

@ -1,7 +1,8 @@
import { Meta, StoryFn, StoryObj } from '@storybook/react'
import { LemonTextAreaMarkdown as _LemonTextMarkdown } from 'lib/lemon-ui/LemonTextArea/LemonTextAreaMarkdown'
import { useState } from 'react'
import { LemonTextArea, LemonTextAreaMarkdown as _LemonTextMarkdown, LemonTextAreaProps } from './LemonTextArea'
import { LemonTextArea, LemonTextAreaProps } from './LemonTextArea'
type Story = StoryObj<typeof LemonTextArea>
const meta: Meta<typeof LemonTextArea> = {

View File

@ -1,20 +1,8 @@
import './LemonTextArea.scss'
import clsx from 'clsx'
import { useValues } from 'kea'
import { TextContent } from 'lib/components/Cards/TextCard/TextCard'
import { useUploadFiles } from 'lib/hooks/useUploadFiles'
import { IconMarkdown, IconTools } from 'lib/lemon-ui/icons'
import { LemonFileInput } from 'lib/lemon-ui/LemonFileInput/LemonFileInput'
import { lemonToast } from 'lib/lemon-ui/LemonToast/LemonToast'
import { Link } from 'lib/lemon-ui/Link'
import { Tooltip } from 'lib/lemon-ui/Tooltip'
import posthog from 'posthog-js'
import React, { useRef, useState } from 'react'
import React, { useRef } from 'react'
import TextareaAutosize from 'react-textarea-autosize'
import { preflightLogic } from 'scenes/PreflightCheck/preflightLogic'
import { LemonTabs } from '../LemonTabs'
export interface LemonTextAreaProps
extends Pick<
@ -74,84 +62,3 @@ export const LemonTextArea = React.forwardRef<HTMLTextAreaElement, LemonTextArea
/>
)
})
export const LemonTextAreaMarkdown = React.forwardRef<HTMLTextAreaElement, LemonTextAreaProps>(
function _LemonTextAreaMarkdown({ value, onChange, ...editAreaProps }, ref): JSX.Element {
const { objectStorageAvailable } = useValues(preflightLogic)
const [isPreviewShown, setIsPreviewShown] = useState(false)
const dropRef = useRef<HTMLDivElement>(null)
const { setFilesToUpload, filesToUpload, uploading } = useUploadFiles({
onUpload: (url, fileName) => {
onChange?.(value + `\n\n![${fileName}](${url})`)
posthog.capture('markdown image uploaded', { name: fileName })
},
onError: (detail) => {
posthog.capture('markdown image upload failed', { error: detail })
lemonToast.error(`Error uploading image: ${detail}`)
},
})
return (
<LemonTabs
activeKey={isPreviewShown ? 'preview' : 'write'}
onChange={(key) => setIsPreviewShown(key === 'preview')}
tabs={[
{
key: 'write',
label: 'Write',
content: (
<div ref={dropRef} className="LemonTextMarkdown flex flex-col space-y-1 rounded">
<LemonTextArea
ref={ref}
{...editAreaProps}
autoFocus
value={value}
onChange={onChange}
/>
<div className="text-muted inline-flex items-center space-x-1">
<IconMarkdown className="text-2xl" />
<span>Markdown formatting support</span>
</div>
{objectStorageAvailable ? (
<LemonFileInput
accept={'image/*'}
multiple={false}
alternativeDropTargetRef={dropRef}
onChange={setFilesToUpload}
loading={uploading}
value={filesToUpload}
/>
) : (
<div className="text-muted inline-flex items-center space-x-1">
<Tooltip title="Enable object storage to add images by dragging and dropping.">
<IconTools className="text-xl mr-1" />
</Tooltip>
<span>
Add external images using{' '}
<Link to="https://www.markdownguide.org/basic-syntax/#images-1">
{' '}
Markdown image links
</Link>
.
</span>
</div>
)}
</div>
),
},
{
key: 'preview',
label: 'Preview',
content: value ? (
<TextContent text={value} className="LemonTextArea--preview" />
) : (
<i>Nothing to preview</i>
),
},
]}
/>
)
}
)

View File

@ -0,0 +1,94 @@
import { useValues } from 'kea'
import { TextContent } from 'lib/components/Cards/TextCard/TextCard'
import { useUploadFiles } from 'lib/hooks/useUploadFiles'
import { IconMarkdown, IconTools } from 'lib/lemon-ui/icons'
import { LemonFileInput } from 'lib/lemon-ui/LemonFileInput'
import { LemonTabs } from 'lib/lemon-ui/LemonTabs'
import { LemonTextArea, LemonTextAreaProps } from 'lib/lemon-ui/LemonTextArea/LemonTextArea'
import { lemonToast } from 'lib/lemon-ui/LemonToast'
import { Link } from 'lib/lemon-ui/Link'
import { Tooltip } from 'lib/lemon-ui/Tooltip'
import posthog from 'posthog-js'
import React, { useRef, useState } from 'react'
import { preflightLogic } from 'scenes/PreflightCheck/preflightLogic'
export const LemonTextAreaMarkdown = React.forwardRef<HTMLTextAreaElement, LemonTextAreaProps>(
function _LemonTextAreaMarkdown({ value, onChange, ...editAreaProps }, ref): JSX.Element {
const { objectStorageAvailable } = useValues(preflightLogic)
const [isPreviewShown, setIsPreviewShown] = useState(false)
const dropRef = useRef<HTMLDivElement>(null)
const { setFilesToUpload, filesToUpload, uploading } = useUploadFiles({
onUpload: (url, fileName) => {
onChange?.(value + `\n\n![${fileName}](${url})`)
posthog.capture('markdown image uploaded', { name: fileName })
},
onError: (detail) => {
posthog.capture('markdown image upload failed', { error: detail })
lemonToast.error(`Error uploading image: ${detail}`)
},
})
return (
<LemonTabs
activeKey={isPreviewShown ? 'preview' : 'write'}
onChange={(key) => setIsPreviewShown(key === 'preview')}
tabs={[
{
key: 'write',
label: 'Write',
content: (
<div ref={dropRef} className="LemonTextMarkdown flex flex-col space-y-1 rounded">
<LemonTextArea
ref={ref}
{...editAreaProps}
autoFocus
value={value}
onChange={onChange}
/>
<div className="text-muted inline-flex items-center space-x-1">
<IconMarkdown className="text-2xl" />
<span>Markdown formatting support</span>
</div>
{objectStorageAvailable ? (
<LemonFileInput
accept={'image/*'}
multiple={false}
alternativeDropTargetRef={dropRef}
onChange={setFilesToUpload}
loading={uploading}
value={filesToUpload}
/>
) : (
<div className="text-muted inline-flex items-center space-x-1">
<Tooltip title="Enable object storage to add images by dragging and dropping.">
<IconTools className="text-xl mr-1" />
</Tooltip>
<span>
Add external images using{' '}
<Link to="https://www.markdownguide.org/basic-syntax/#images-1">
{' '}
Markdown image links
</Link>
.
</span>
</div>
)}
</div>
),
},
{
key: 'preview',
label: 'Preview',
content: value ? (
<TextContent text={value} className="LemonTextArea--preview" />
) : (
<i>Nothing to preview</i>
),
},
]}
/>
)
}
)

View File

@ -1,2 +1,3 @@
export type { LemonTextAreaProps } from './LemonTextArea'
export { LemonTextArea, LemonTextAreaMarkdown } from './LemonTextArea'
export { LemonTextArea } from './LemonTextArea'
export { LemonTextAreaMarkdown } from 'lib/lemon-ui/LemonTextArea/LemonTextAreaMarkdown'

View File

@ -1,7 +1,7 @@
import { LemonButton, LemonModal } from '@posthog/lemon-ui'
import { useActions, useValues } from 'kea'
import { capitalizeFirstLetter } from 'lib/utils'
import { posthog } from 'posthog-js'
import posthog from 'posthog-js'
import { sceneLogic } from './sceneLogic'
import { urls } from './urls'

View File

@ -1,11 +1,13 @@
import { useActions, useValues } from 'kea'
import { DateFilter } from 'lib/components/DateFilter/DateFilter'
import { CUSTOM_OPTION_KEY } from 'lib/components/DateFilter/types'
import { IconSync } from 'lib/lemon-ui/icons'
import { LemonButton } from 'lib/lemon-ui/LemonButton'
import { LemonInput } from 'lib/lemon-ui/LemonInput'
import { LemonMenu } from 'lib/lemon-ui/LemonMenu'
import { LemonSwitch } from 'lib/lemon-ui/LemonSwitch'
import { Spinner } from 'lib/lemon-ui/Spinner'
import { Tooltip } from 'lib/lemon-ui/Tooltip'
import { dateFilterToText, dateMapping } from 'lib/utils'
import { ToolbarMenu } from '~/toolbar/bar/ToolbarMenu'
import { elementsLogic } from '~/toolbar/elements/elementsLogic'
@ -21,6 +23,13 @@ export const HeatmapToolbarMenu = (): JSX.Element => {
const { setHeatmapFilter, loadMoreElementStats, setMatchLinksByHref } = useActions(heatmapLogic)
const { setHighlightElement, setSelectedElement } = useActions(elementsLogic)
const dateItems = dateMapping
.filter((dm) => dm.key !== CUSTOM_OPTION_KEY)
.map((dateOption) => ({
label: dateOption.key,
onClick: () => setHeatmapFilter({ date_from: dateOption.values[0], date_to: dateOption.values[1] }),
}))
return (
<ToolbarMenu>
<ToolbarMenu.Header>
@ -28,11 +37,11 @@ export const HeatmapToolbarMenu = (): JSX.Element => {
<div className="space-y-1 border-b px-1 pb-2">
<div className="text-muted p-1">Use * as a wildcard</div>
<div className="flex flex-row items-center space-x-2">
<DateFilter
dateFrom={heatmapFilter.date_from ?? '-7d'}
dateTo={heatmapFilter.date_to}
onChange={(date_from, date_to) => setHeatmapFilter({ date_from, date_to })}
/>
<LemonMenu items={dateItems}>
<LemonButton size="small" type="secondary">
{dateFilterToText(heatmapFilter.date_from, heatmapFilter.date_to, 'Last 7 days')}
</LemonButton>
</LemonMenu>
<LemonButton
icon={<IconSync />}

View File

@ -247,7 +247,7 @@ function getBuiltEntryPoints(config, result) {
let buildsInProgress = 0
export async function buildOrWatch(config) {
const { absWorkingDir, name, onBuildStart, onBuildComplete, ..._config } = config
const { absWorkingDir, name, onBuildStart, onBuildComplete, writeMetaFile, extraPlugins, ..._config } = config
let buildPromise = null
let buildAgain = false
@ -311,7 +311,9 @@ export async function buildOrWatch(config) {
async function runBuild() {
if (!esbuildContext) {
esbuildContext = await context({ ...commonConfig, ..._config })
const combinedConfig = { ...commonConfig, ..._config }
combinedConfig.plugins = [...commonConfig.plugins, ...(extraPlugins || [])]
esbuildContext = await context(combinedConfig)
}
buildCount++
@ -319,6 +321,14 @@ export async function buildOrWatch(config) {
log({ name })
try {
const buildResult = await esbuildContext.rebuild()
if (writeMetaFile) {
await fs.writeFile(
`${config.name.toLowerCase().replace(' ', '-')}-esbuild-meta.json`,
JSON.stringify(buildResult.metafile)
)
}
inputFiles = getInputFiles(buildResult)
log({ success: true, name, time })

View File

@ -61,7 +61,8 @@
"prepare": "husky install",
"mobile-replay:web:schema:build:json": "ts-json-schema-generator -f tsconfig.json --path 'node_modules/@rrweb/types/dist/index.d.ts' --type 'eventWithTime' --expose all --no-top-ref --out ee/frontend/mobile-replay/schema/web/rr-web-schema.json && prettier --write ee/frontend/mobile-replay/schema/web/rr-web-schema.json",
"mobile-replay:mobile:schema:build:json": "ts-json-schema-generator -f tsconfig.json --path 'ee/frontend/mobile-replay/mobile.types.ts' --type 'mobileEventWithTime' --expose all --no-top-ref --out ee/frontend/mobile-replay/schema/mobile/rr-mobile-schema.json && prettier --write ee/frontend/mobile-replay/schema/mobile/rr-mobile-schema.json",
"mobile-replay:schema:build:json": "pnpm mobile-replay:web:schema:build:json && pnpm mobile-replay:mobile:schema:build:json"
"mobile-replay:schema:build:json": "pnpm mobile-replay:web:schema:build:json && pnpm mobile-replay:mobile:schema:build:json",
"visualize-toolbar-bundle": "pnpm exec esbuild-visualizer --metadata ./toolbar-esbuild-meta.json --filename=toolbar-esbuild-bundle-visualization.html"
},
"dependencies": {
"@ant-design/icons": "^4.7.0",
@ -244,6 +245,7 @@
"cypress": "^13.3.0",
"cypress-axe": "^1.5.0",
"cypress-terminal-report": "^5.3.7",
"esbuild-visualizer": "^0.6.0",
"eslint": "^8.52.0",
"eslint-config-prettier": "^9.0.0",
"eslint-plugin-compat": "^4.2.0",
@ -319,6 +321,10 @@
"eslint -c .eslintrc.js --fix",
"prettier --write"
],
"frontend/*.mjs": [
"eslint -c .eslintrc.js --fix",
"prettier --write"
],
"ee/frontend/**/*.{js,jsx,mjs,ts,tsx}": [
"eslint -c .eslintrc.js --fix",
"prettier --write"

View File

@ -545,6 +545,9 @@ devDependencies:
cypress-terminal-report:
specifier: ^5.3.7
version: 5.3.7(cypress@13.3.0)
esbuild-visualizer:
specifier: ^0.6.0
version: 0.6.0
eslint:
specifier: ^8.52.0
version: 8.52.0
@ -11627,6 +11630,16 @@ packages:
sass-embedded: 1.70.0
dev: false
/esbuild-visualizer@0.6.0:
resolution: {integrity: sha512-oNK3JAhC7+re93VTtUdWJKTDVnA2qXPAjCAoaw9OxEFUXztszw3kcaK46u1U790T8FdUBAWv6F9Xt59P8nJCVA==}
engines: {node: '>=18'}
hasBin: true
dependencies:
open: 8.4.2
picomatch: 2.3.1
yargs: 17.6.2
dev: true
/esbuild@0.18.20:
resolution: {integrity: sha512-ceqxoedUrcayh7Y7ZX6NdbbDzGROiyVBgC4PriJThBKSVPWnnFHZAkfI1lJT8QFkOwH4qOS2SJkS4wvpGl8BpA==}
engines: {node: '>=12'}