Skip to content

Commit 201e5fc

Browse files
authoredMar 14, 2024··
Lazy loading less common languages for syntax highlighting (#2388)
* Use fewer syntax highlighter languages. Reduces client.js size by about 250kB (800kB uncompressed) Common languages: bash, c, cpp, csharp, css, diff, go, graphql, ini, java, javascript, json, kotlin, less, lua, makefile, markdown, objectivec, perl, php-template, php, plaintext, python-repl, python, r, ruby, rust, scss, shell, sql, swift, typescript, vbnet, wasm, xml, yaml Additionally enabled languages: dockerfile, pgsql * Configurable syntax highlighter languages Allows to individually enable languages. * Lazy load syntax highlighter languages Allows to enable additional languages that will not be autodetected. * Include highlight.js in dynamic import check
1 parent e832cd2 commit 201e5fc

23 files changed

+291
-28
lines changed
 

‎.eslintignore

+1
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
generate_translations.js
22
webpack.config.js
3+
src/shared/build-config.js
34
src/api_tests
45
**/*.png
56
**/*.css

‎package.json

+1
Original file line numberDiff line numberDiff line change
@@ -48,6 +48,7 @@
4848
"emoji-mart": "^5.5.2",
4949
"emoji-short-name": "^2.0.0",
5050
"express": "~4.18.2",
51+
"highlight.js": "^11.9.0",
5152
"history": "^5.3.0",
5253
"html-to-text": "^9.0.5",
5354
"i18next": "^23.10.0",

‎pnpm-lock.yaml

+3
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

‎src/client/index.tsx

+3
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ import { initializeSite } from "@utils/app";
22
import { hydrate } from "inferno-hydrate";
33
import { BrowserRouter } from "inferno-router";
44
import { App } from "../shared/components/app/app";
5+
import { lazyHighlightjs } from "../shared/lazy-highlightjs";
56
import { loadUserLanguage } from "../shared/services/I18NextService";
67
import { verifyDynamicImports } from "../shared/dynamic-imports";
78

@@ -17,6 +18,8 @@ async function startClient() {
1718

1819
initializeSite(window.isoData.site_res);
1920

21+
lazyHighlightjs.enableLazyLoading();
22+
2023
await loadUserLanguage();
2124

2225
const wrapper = (

‎src/shared/build-config.d.ts

+2
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
export const bundledSyntaxHighlighters: ["plaintext", ...string[]];
2+
export const lazySyntaxHighlighters: string[] | "*";

‎src/shared/build-config.js

+26
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
// Don't import/require things here. This file is also imported in
2+
// webpack.config.js. Needs dev server restart to apply changes.
3+
4+
/** Bundled highlighters can be autodetected in markdown.
5+
* @type ["plaintext", ...string[]] **/
6+
// prettier-ignore
7+
const bundledSyntaxHighlighters = [
8+
"plaintext",
9+
// The 'Common' set of highlight.js languages.
10+
"bash", "c", "cpp", "csharp", "css", "diff", "go", "graphql", "ini", "java",
11+
"javascript", "json", "kotlin", "less", "lua", "makefile", "markdown",
12+
"objectivec", "perl", "php-template", "php", "python-repl", "python", "r",
13+
"ruby", "rust", "scss", "shell", "sql", "swift", "typescript", "vbnet",
14+
"wasm", "xml", "yaml",
15+
];
16+
17+
/** Lazy highlighters can't be autodetected, they have to be explicitly specified
18+
* as the language. (e.g. ```dockerfile ...)
19+
* "*" enables all non-bundled languages
20+
* @type string[] | "*" **/
21+
const lazySyntaxHighlighters = "*";
22+
23+
module.exports = {
24+
bundledSyntaxHighlighters,
25+
lazySyntaxHighlighters,
26+
};

‎src/shared/components/comment/comment-node.tsx

+6-2
Original file line numberDiff line numberDiff line change
@@ -305,8 +305,12 @@ export class CommentNode extends Component<CommentNodeProps, CommentNodeState> {
305305
className="md-div"
306306
dangerouslySetInnerHTML={
307307
this.props.hideImages
308-
? mdToHtmlNoImages(this.commentUnlessRemoved)
309-
: mdToHtml(this.commentUnlessRemoved)
308+
? mdToHtmlNoImages(this.commentUnlessRemoved, () =>
309+
this.forceUpdate(),
310+
)
311+
: mdToHtml(this.commentUnlessRemoved, () =>
312+
this.forceUpdate(),
313+
)
310314
}
311315
/>
312316
)}

‎src/shared/components/common/markdown-textarea.tsx

+3-1
Original file line numberDiff line numberDiff line change
@@ -250,7 +250,9 @@ export class MarkdownTextArea extends Component<
250250
{this.state.previewMode && this.state.content && (
251251
<div
252252
className="card border-secondary card-body md-div"
253-
dangerouslySetInnerHTML={mdToHtml(this.state.content)}
253+
dangerouslySetInnerHTML={mdToHtml(this.state.content, () =>
254+
this.forceUpdate(),
255+
)}
254256
/>
255257
)}
256258
{this.state.imageUploadStatus &&

‎src/shared/components/common/registration-application.tsx

+9-2
Original file line numberDiff line numberDiff line change
@@ -68,7 +68,12 @@ export class RegistrationApplication extends Component<
6868
<MomentTime showAgo published={ra.published} />
6969
</div>
7070
<div>{I18NextService.i18n.t("answer")}:</div>
71-
<div className="md-div" dangerouslySetInnerHTML={mdToHtml(ra.answer)} />
71+
<div
72+
className="md-div"
73+
dangerouslySetInnerHTML={mdToHtml(ra.answer, () =>
74+
this.forceUpdate(),
75+
)}
76+
/>
7277

7378
{a.admin && (
7479
<div>
@@ -88,7 +93,9 @@ export class RegistrationApplication extends Component<
8893
{I18NextService.i18n.t("deny_reason")}:{" "}
8994
<div
9095
className="md-div d-inline-flex"
91-
dangerouslySetInnerHTML={mdToHtml(ra.deny_reason)}
96+
dangerouslySetInnerHTML={mdToHtml(ra.deny_reason, () =>
97+
this.forceUpdate(),
98+
)}
9299
/>
93100
</div>
94101
)}

‎src/shared/components/community/sidebar.tsx

+4-1
Original file line numberDiff line numberDiff line change
@@ -271,7 +271,10 @@ export class Sidebar extends Component<SidebarProps, SidebarState> {
271271
const desc = this.props.community_view.community.description;
272272
return (
273273
desc && (
274-
<div className="md-div" dangerouslySetInnerHTML={mdToHtml(desc)} />
274+
<div
275+
className="md-div"
276+
dangerouslySetInnerHTML={mdToHtml(desc, () => this.forceUpdate())}
277+
/>
275278
)
276279
);
277280
}

‎src/shared/components/home/home.tsx

+3-1
Original file line numberDiff line numberDiff line change
@@ -401,7 +401,9 @@ export class Home extends Component<any, HomeState> {
401401
{tagline && (
402402
<div
403403
id="tagline"
404-
dangerouslySetInnerHTML={mdToHtml(tagline)}
404+
dangerouslySetInnerHTML={mdToHtml(tagline, () =>
405+
this.forceUpdate(),
406+
)}
405407
></div>
406408
)}
407409
<div className="d-block d-md-none">{this.mobileView}</div>

‎src/shared/components/home/legal.tsx

+4-1
Original file line numberDiff line numberDiff line change
@@ -32,7 +32,10 @@ export class Legal extends Component<any, LegalState> {
3232
path={this.context.router.route.match.url}
3333
/>
3434
{legal && (
35-
<div className="md-div" dangerouslySetInnerHTML={mdToHtml(legal)} />
35+
<div
36+
className="md-div"
37+
dangerouslySetInnerHTML={mdToHtml(legal, () => this.forceUpdate())}
38+
/>
3639
)}
3740
</div>
3841
);

‎src/shared/components/home/signup.tsx

+1
Original file line numberDiff line numberDiff line change
@@ -221,6 +221,7 @@ export class Signup extends Component<any, State> {
221221
className="md-div"
222222
dangerouslySetInnerHTML={mdToHtml(
223223
siteView.local_site.application_question,
224+
() => this.forceUpdate(),
224225
)}
225226
/>
226227
)}

‎src/shared/components/home/site-sidebar.tsx

+4-1
Original file line numberDiff line numberDiff line change
@@ -99,7 +99,10 @@ export class SiteSidebar extends Component<SiteSidebarProps, SiteSidebarState> {
9999

100100
siteSidebar(sidebar: string) {
101101
return (
102-
<div className="md-div" dangerouslySetInnerHTML={mdToHtml(sidebar)} />
102+
<div
103+
className="md-div"
104+
dangerouslySetInnerHTML={mdToHtml(sidebar, () => this.forceUpdate())}
105+
/>
103106
);
104107
}
105108

‎src/shared/components/person/profile.tsx

+3-1
Original file line numberDiff line numberDiff line change
@@ -586,7 +586,9 @@ export class Profile extends Component<
586586
<div className="d-flex align-items-center mb-2">
587587
<div
588588
className="md-div"
589-
dangerouslySetInnerHTML={mdToHtml(pv.person.bio)}
589+
dangerouslySetInnerHTML={mdToHtml(pv.person.bio, () =>
590+
this.forceUpdate(),
591+
)}
590592
/>
591593
</div>
592594
)}

‎src/shared/components/post/post-listing.tsx

+4-1
Original file line numberDiff line numberDiff line change
@@ -177,7 +177,10 @@ export class PostListing extends Component<PostListingProps, PostListingState> {
177177
{this.state.viewSource ? (
178178
<pre>{body}</pre>
179179
) : (
180-
<div className="md-div" dangerouslySetInnerHTML={mdToHtml(body)} />
180+
<div
181+
className="md-div"
182+
dangerouslySetInnerHTML={mdToHtml(body, () => this.forceUpdate())}
183+
/>
181184
)}
182185
</article>
183186
) : (

‎src/shared/components/private_message/private-message-report.tsx

+3-1
Original file line numberDiff line numberDiff line change
@@ -52,7 +52,9 @@ export class PrivateMessageReport extends Component<Props, State> {
5252
{I18NextService.i18n.t("message")}:
5353
<div
5454
className="md-div"
55-
dangerouslySetInnerHTML={mdToHtml(pmr.original_pm_text)}
55+
dangerouslySetInnerHTML={mdToHtml(pmr.original_pm_text, () =>
56+
this.forceUpdate(),
57+
)}
5658
/>
5759
</div>
5860
<div>

‎src/shared/components/private_message/private-message.tsx

+4-1
Original file line numberDiff line numberDiff line change
@@ -135,7 +135,10 @@ export class PrivateMessage extends Component<
135135
) : (
136136
<div
137137
className="md-div"
138-
dangerouslySetInnerHTML={mdToHtml(this.messageUnlessRemoved)}
138+
dangerouslySetInnerHTML={mdToHtml(
139+
this.messageUnlessRemoved,
140+
() => this.forceUpdate(),
141+
)}
139142
/>
140143
)}
141144
<ul className="list-inline mb-0 text-muted fw-bold">

‎src/shared/dynamic-imports.ts

+11-2
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,17 @@
11
import { verifyTranslationImports } from "./services/I18NextService";
22
import { verifyDateFnsImports } from "@utils/app/setup-date-fns";
3+
import { verifyHighlighjsImports } from "./lazy-highlightjs";
34

45
export class ImportReport {
56
error: Array<{ id: string; error: Error | string | undefined }> = [];
67
success: string[] = [];
8+
message?: string;
79
}
810

911
export type ImportReportCollection = {
1012
translation?: ImportReport;
1113
"date-fns"?: ImportReport;
14+
"highlight.js"?: ImportReport;
1215
};
1316

1417
function collect(
@@ -22,12 +25,15 @@ function collect(
2225
for (const { id, error } of report.error) {
2326
console.warn(`${kind} "${id}" failed: ${error}`);
2427
}
28+
const message = report.message ? ` (${report.message})` : "";
2529
const good = report.success.length;
2630
const bad = report.error.length;
2731
if (bad) {
28-
console.error(`${bad} out of ${bad + good} ${kind} imports failed.`);
32+
console.error(
33+
`${bad} out of ${bad + good} ${kind} imports failed.` + message,
34+
);
2935
} else {
30-
console.log(`${good} ${kind} imports verified.`);
36+
console.log(`${good} ${kind} imports verified.` + message);
3137
}
3238
}
3339
}
@@ -45,5 +51,8 @@ export async function verifyDynamicImports(
4551
await verifyDateFnsImports().then(report =>
4652
collect(verbose, "date-fns", collection, report),
4753
);
54+
await verifyHighlighjsImports().then(report =>
55+
collect(verbose, "highlight.js", collection, report),
56+
);
4857
return collection;
4958
}

0 commit comments

Comments
 (0)
Please sign in to comment.