aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorLibravatar Kristóf Marussy <marussy@mit.bme.hu>2021-10-02 02:11:31 +0200
committerLibravatar Kristóf Marussy <marussy@mit.bme.hu>2021-10-02 02:11:31 +0200
commitb834db0fd424e7ab02fcd5e509d855f2d97863bd (patch)
treeb56ce9b8f752d8ca98e1d9082c63542e5dd993c1
parentfeat: skeleton for language to store mapping (diff)
downloadrefinery-b834db0fd424e7ab02fcd5e509d855f2d97863bd.tar.gz
refinery-b834db0fd424e7ab02fcd5e509d855f2d97863bd.tar.zst
refinery-b834db0fd424e7ab02fcd5e509d855f2d97863bd.zip
perf(web): split off CodeMirror chunks
Also optimizes statis asset caching.
-rw-r--r--language-web/package.json5
-rw-r--r--language-web/src/main/css/index.scss29
-rw-r--r--language-web/src/main/java/org/eclipse/viatra/solver/language/web/CacheControlFilter.java55
-rw-r--r--language-web/src/main/java/org/eclipse/viatra/solver/language/web/ServerLauncher.java3
-rw-r--r--language-web/src/main/js/App.tsx6
-rw-r--r--language-web/src/main/js/editor/Editor.tsx20
-rw-r--r--language-web/src/main/js/editor/EditorArea.tsx42
-rw-r--r--language-web/src/main/js/editor/EditorStore.ts75
-rw-r--r--language-web/src/main/js/editor/editor.ts18
-rw-r--r--language-web/src/main/js/theme/ThemeStore.ts4
-rw-r--r--language-web/webpack.config.js135
-rw-r--r--language-web/yarn.lock33
12 files changed, 328 insertions, 97 deletions
diff --git a/language-web/package.json b/language-web/package.json
index ec54f7fd..ddb00f57 100644
--- a/language-web/package.json
+++ b/language-web/package.json
@@ -26,8 +26,8 @@
26 "@babel/preset-env": "^7.15.0", 26 "@babel/preset-env": "^7.15.0",
27 "@babel/preset-react": "^7.14.5", 27 "@babel/preset-react": "^7.14.5",
28 "@babel/preset-typescript": "^7.15.0", 28 "@babel/preset-typescript": "^7.15.0",
29 "@babel/plugin-transform-runtime": "^7.15.0",
29 "babel-loader": "^8.2.2", 30 "babel-loader": "^8.2.2",
30 "before-build-webpack": "^0.2.11",
31 "css-loader": "^6.2.0", 31 "css-loader": "^6.2.0",
32 "eslint": "^7.32.0", 32 "eslint": "^7.32.0",
33 "eslint-config-airbnb": "^18.2.1", 33 "eslint-config-airbnb": "^18.2.1",
@@ -38,7 +38,9 @@
38 "eslint-plugin-jsx-a11y": "^6.4.1", 38 "eslint-plugin-jsx-a11y": "^6.4.1",
39 "html-webpack-plugin": "^5.3.2", 39 "html-webpack-plugin": "^5.3.2",
40 "image-webpack-loader": "^7.0.1", 40 "image-webpack-loader": "^7.0.1",
41 "magic-comments-loader": "^1.4.1",
41 "mini-css-extract-plugin": "^2.2.0", 42 "mini-css-extract-plugin": "^2.2.0",
43 "@principalstudio/html-webpack-inject-preload": "^1.2.7",
42 "sass": "^1.38.0", 44 "sass": "^1.38.0",
43 "sass-loader": "^12.1.0", 45 "sass-loader": "^12.1.0",
44 "style-loader": "^3.2.1", 46 "style-loader": "^3.2.1",
@@ -56,6 +58,7 @@
56 "webpack-subresource-integrity": "^5.0.0-rc.1" 58 "webpack-subresource-integrity": "^5.0.0-rc.1"
57 }, 59 },
58 "dependencies": { 60 "dependencies": {
61 "@babel/runtime": "^7.15.0",
59 "@emotion/react": "^11.4.1", 62 "@emotion/react": "^11.4.1",
60 "@emotion/styled": "^11.3.0", 63 "@emotion/styled": "^11.3.0",
61 "@fontsource/jetbrains-mono": "^4.5.0", 64 "@fontsource/jetbrains-mono": "^4.5.0",
diff --git a/language-web/src/main/css/index.scss b/language-web/src/main/css/index.scss
index 9d6e0f6a..54f3a654 100644
--- a/language-web/src/main/css/index.scss
+++ b/language-web/src/main/css/index.scss
@@ -30,12 +30,27 @@ body {
30 height: 100%; 30 height: 100%;
31} 31}
32 32
33.CodeMirror, .CodeMirror-hints { 33.problem-fallback-editor {
34 display: block;
35 height: 100%;
36 width: 100%;
37 resize: none;
38 border: none;
39 outline: none;
40 padding: 4px 4px 4px 16px;
41 white-space: pre;
42 overflow-wrap: normal;
43 overflow: auto;
44}
45
46.CodeMirror, .CodeMirror-hints, .problem-fallback-editor {
34 font-size: 16px; 47 font-size: 16px;
35 font-family: 'JetBrains MonoVariable', 'JetBrains Mono', monospace; 48 font-family: 'JetBrains MonoVariable', 'JetBrains Mono', monospace;
36 font-feature-settings: 'liga', 'calt'; 49 font-feature-settings: 'liga', 'calt';
37 font-weight: 400; 50 font-weight: 400;
38 text-rendering: optimizeLegibility; 51 text-rendering: optimizeLegibility;
52 line-height: 1.35;
53 letter-spacing: 0;
39} 54}
40 55
41@each $themeName, $theme in $themes { 56@each $themeName, $theme in $themes {
@@ -45,6 +60,16 @@ body {
45 color: map.get($theme, 'foreground'); 60 color: map.get($theme, 'foreground');
46 } 61 }
47 62
63 &.problem-fallback-editor {
64 background: map.get($theme, 'background');
65 color: map.get($theme, 'foreground');
66 caret-color: map.get($theme, 'cursor');
67
68 &::selection {
69 background: map.get($theme, 'selection');
70 }
71 }
72
48 .CodeMirror-gutters { 73 .CodeMirror-gutters {
49 background: map.get($theme, 'background'); 74 background: map.get($theme, 'background');
50 border: none; 75 border: none;
@@ -183,11 +208,13 @@ li.CodeMirror-hint-active {
183 208
184.xtext-marker_read { 209.xtext-marker_read {
185 background: rgba(128, 203, 196, 0.2); 210 background: rgba(128, 203, 196, 0.2);
211 display: inline-block;
186} 212}
187 213
188 214
189.xtext-marker_write { 215.xtext-marker_write {
190 background: rgba(255, 229, 100, 0.2); 216 background: rgba(255, 229, 100, 0.2);
217 display: inline-block;
191} 218}
192 219
193.problem-abstract { 220.problem-abstract {
diff --git a/language-web/src/main/java/org/eclipse/viatra/solver/language/web/CacheControlFilter.java b/language-web/src/main/java/org/eclipse/viatra/solver/language/web/CacheControlFilter.java
new file mode 100644
index 00000000..41b8e5bf
--- /dev/null
+++ b/language-web/src/main/java/org/eclipse/viatra/solver/language/web/CacheControlFilter.java
@@ -0,0 +1,55 @@
1package org.eclipse.viatra.solver.language.web;
2
3import java.io.IOException;
4import java.util.regex.Pattern;
5
6import javax.servlet.Filter;
7import javax.servlet.FilterChain;
8import javax.servlet.FilterConfig;
9import javax.servlet.ServletException;
10import javax.servlet.ServletRequest;
11import javax.servlet.ServletResponse;
12import javax.servlet.http.HttpServletRequest;
13import javax.servlet.http.HttpServletResponse;
14
15public class CacheControlFilter implements Filter {
16
17 private static final String CACHE_CONTROL_HEADER = "Cache-Control";
18
19 private static final String EXPIRES_HEADER = "Expires";
20
21 private static final Pattern CACHE_URI_PATTERN = Pattern.compile(".*\\.(css|gif|js|map|png|svg|woff2)");
22
23 private static final long EXPIRY = 31536000;
24
25 private static final String CACHE_CONTROL_CACHE_VALUE = "public, max-age: " + EXPIRY + ", immutable";
26
27 private static final String CACHE_CONTROL_NO_CACHE_VALUE = "no-cache, no-store, max-age: 0, must-revalidate";
28
29 @Override
30 public void init(FilterConfig filterConfig) throws ServletException {
31 // Nothing to initialize.
32 }
33
34 @Override
35 public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain)
36 throws IOException, ServletException {
37 if (request instanceof HttpServletRequest && response instanceof HttpServletResponse) {
38 var httpRequest = (HttpServletRequest) request;
39 var httpResponse = (HttpServletResponse) response;
40 if (CACHE_URI_PATTERN.matcher(httpRequest.getRequestURI()).matches()) {
41 httpResponse.setHeader(CACHE_CONTROL_HEADER, CACHE_CONTROL_CACHE_VALUE);
42 httpResponse.setDateHeader(EXPIRES_HEADER, System.currentTimeMillis() + EXPIRY * 1000L);
43 } else {
44 httpResponse.setHeader(CACHE_CONTROL_HEADER, CACHE_CONTROL_NO_CACHE_VALUE);
45 httpResponse.setDateHeader(EXPIRES_HEADER, 0);
46 }
47 }
48 chain.doFilter(request, response);
49 }
50
51 @Override
52 public void destroy() {
53 // Nothing to dispose.
54 }
55}
diff --git a/language-web/src/main/java/org/eclipse/viatra/solver/language/web/ServerLauncher.java b/language-web/src/main/java/org/eclipse/viatra/solver/language/web/ServerLauncher.java
index d92c7735..a6d58f95 100644
--- a/language-web/src/main/java/org/eclipse/viatra/solver/language/web/ServerLauncher.java
+++ b/language-web/src/main/java/org/eclipse/viatra/solver/language/web/ServerLauncher.java
@@ -8,8 +8,10 @@ import java.io.IOException;
8import java.net.InetSocketAddress; 8import java.net.InetSocketAddress;
9import java.net.URI; 9import java.net.URI;
10import java.net.URISyntaxException; 10import java.net.URISyntaxException;
11import java.util.EnumSet;
11import java.util.Set; 12import java.util.Set;
12 13
14import javax.servlet.DispatcherType;
13import javax.servlet.SessionTrackingMode; 15import javax.servlet.SessionTrackingMode;
14 16
15import org.eclipse.jetty.server.Server; 17import org.eclipse.jetty.server.Server;
@@ -42,6 +44,7 @@ public class ServerLauncher {
42 handler.setWelcomeFiles(new String[] { "index.html" }); 44 handler.setWelcomeFiles(new String[] { "index.html" });
43 addDefaultServlet(handler); 45 addDefaultServlet(handler);
44 } 46 }
47 handler.addFilter(CacheControlFilter.class, "/*", EnumSet.of(DispatcherType.REQUEST));
45 server.setHandler(handler); 48 server.setHandler(handler);
46 } 49 }
47 50
diff --git a/language-web/src/main/js/App.tsx b/language-web/src/main/js/App.tsx
index 17d4f339..5cd157fa 100644
--- a/language-web/src/main/js/App.tsx
+++ b/language-web/src/main/js/App.tsx
@@ -9,12 +9,12 @@ import MenuIcon from '@material-ui/icons/Menu';
9import PlayArrowIcon from '@material-ui/icons/PlayArrow'; 9import PlayArrowIcon from '@material-ui/icons/PlayArrow';
10 10
11import { makeStyles } from './makeStyles'; 11import { makeStyles } from './makeStyles';
12import { Editor } from './editor/Editor'; 12import { EditorArea } from './editor/EditorArea';
13import { EditorButtons } from './editor/EditorButtons'; 13import { EditorButtons } from './editor/EditorButtons';
14 14
15const useStyles = makeStyles()((theme) => ({ 15const useStyles = makeStyles()((theme) => ({
16 container: { 16 container: {
17 maxHeight: '100vh', 17 height: '100vh',
18 }, 18 },
19 menuButton: { 19 menuButton: {
20 marginRight: theme.spacing(2), 20 marginRight: theme.spacing(2),
@@ -85,7 +85,7 @@ export const App = (): JSX.Element => {
85 flexShrink={1} 85 flexShrink={1}
86 className={cx(classes.editorBox)} 86 className={cx(classes.editorBox)}
87 > 87 >
88 <Editor /> 88 <EditorArea />
89 </Box> 89 </Box>
90 </Box> 90 </Box>
91 ); 91 );
diff --git a/language-web/src/main/js/editor/Editor.tsx b/language-web/src/main/js/editor/Editor.tsx
deleted file mode 100644
index 9badb6a3..00000000
--- a/language-web/src/main/js/editor/Editor.tsx
+++ /dev/null
@@ -1,20 +0,0 @@
1import { observer } from 'mobx-react-lite';
2import React from 'react';
3import { Controlled as CodeMirror } from 'react-codemirror2';
4
5import { useRootStore } from '../RootStore';
6
7export const Editor = observer(() => {
8 const { editorStore } = useRootStore();
9
10 return (
11 <CodeMirror
12 value={editorStore.value}
13 options={editorStore.codeMirrorOptions}
14 editorDidMount={(editor) => editorStore.editorDidMount(editor)}
15 editorWillUnmount={() => editorStore.editorWillUnmount()}
16 onBeforeChange={(_editor, _data, value) => editorStore.updateValue(value)}
17 onChange={() => editorStore.reportChanged()}
18 />
19 );
20});
diff --git a/language-web/src/main/js/editor/EditorArea.tsx b/language-web/src/main/js/editor/EditorArea.tsx
new file mode 100644
index 00000000..f07a0ad8
--- /dev/null
+++ b/language-web/src/main/js/editor/EditorArea.tsx
@@ -0,0 +1,42 @@
1import { observer } from 'mobx-react-lite';
2import React, { useRef } from 'react';
3
4import { useRootStore } from '../RootStore';
5
6export const EditorArea = observer(() => {
7 const { editorStore } = useRootStore();
8 const { CodeMirror } = editorStore.chunk || {};
9 const fallbackTextarea = useRef<HTMLTextAreaElement>(null);
10
11 if (!CodeMirror) {
12 return (
13 <textarea
14 value={editorStore.value}
15 onChange={(e) => editorStore.updateValue(e.target.value)}
16 ref={fallbackTextarea}
17 className={`problem-fallback-editor cm-s-${editorStore.codeMirrorTheme}`}
18 >
19 </textarea>
20 );
21 }
22
23 const textarea = fallbackTextarea.current;
24 if (textarea) {
25 editorStore.setInitialSelection(
26 textarea.selectionStart,
27 textarea.selectionEnd,
28 document.activeElement === textarea,
29 );
30 }
31
32 return (
33 <CodeMirror
34 value={editorStore.value}
35 options={editorStore.codeMirrorOptions}
36 editorDidMount={(editor) => editorStore.editorDidMount(editor)}
37 editorWillUnmount={() => editorStore.editorWillUnmount()}
38 onBeforeChange={(_editor, _data, value) => editorStore.updateValue(value)}
39 onChange={() => editorStore.reportChanged()}
40 />
41 );
42});
diff --git a/language-web/src/main/js/editor/EditorStore.ts b/language-web/src/main/js/editor/EditorStore.ts
index 5da45ac1..1ac2e79f 100644
--- a/language-web/src/main/js/editor/EditorStore.ts
+++ b/language-web/src/main/js/editor/EditorStore.ts
@@ -1,19 +1,14 @@
1import { Editor, EditorConfiguration } from 'codemirror'; 1import type { Editor, EditorConfiguration } from 'codemirror';
2import 'codemirror/addon/selection/active-line';
3import { 2import {
4 createAtom, 3 createAtom,
5 makeAutoObservable, 4 makeAutoObservable,
6 observable, 5 observable,
6 runInAction,
7} from 'mobx'; 7} from 'mobx';
8import 'mode-problem'; 8import type { IXtextOptions, IXtextServices } from 'xtext/xtext-codemirror';
9import {
10 IXtextOptions,
11 IXtextServices,
12 createServices,
13 removeServices,
14} from 'xtext/xtext-codemirror';
15 9
16import { ThemeStore } from '../theme/ThemeStore'; 10import type { IEditorChunk } from './editor';
11import type { ThemeStore } from '../theme/ThemeStore';
17 12
18const xtextLang = 'problem'; 13const xtextLang = 'problem';
19 14
@@ -33,6 +28,8 @@ export class EditorStore {
33 28
34 atom; 29 atom;
35 30
31 chunk?: IEditorChunk;
32
36 editor?: Editor; 33 editor?: Editor;
37 34
38 xtextServices?: IXtextServices; 35 xtextServices?: IXtextServices;
@@ -41,15 +38,56 @@ export class EditorStore {
41 38
42 showLineNumbers = false; 39 showLineNumbers = false;
43 40
41 initialSelection!: { start: number, end: number, focused: boolean };
42
44 constructor(themeStore: ThemeStore) { 43 constructor(themeStore: ThemeStore) {
45 this.themeStore = themeStore; 44 this.themeStore = themeStore;
46 this.atom = createAtom('EditorStore'); 45 this.atom = createAtom('EditorStore');
46 this.resetInitialSelection();
47 makeAutoObservable(this, { 47 makeAutoObservable(this, {
48 themeStore: false, 48 themeStore: false,
49 atom: false, 49 atom: false,
50 chunk: observable.ref,
50 editor: observable.ref, 51 editor: observable.ref,
51 xtextServices: observable.ref, 52 xtextServices: observable.ref,
53 initialSelection: false,
52 }); 54 });
55 import('./editor').then(({ editorChunk }) => {
56 runInAction(() => {
57 this.chunk = editorChunk;
58 });
59 }).catch((error) => {
60 console.warn('Error while loading editor', error);
61 });
62 }
63
64 setInitialSelection(start: number, end: number, focused: boolean): void {
65 this.initialSelection = { start, end, focused };
66 this.applyInitialSelectionToEditor();
67 }
68
69 private resetInitialSelection(): void {
70 this.initialSelection = {
71 start: 0,
72 end: 0,
73 focused: false,
74 };
75 }
76
77 private applyInitialSelectionToEditor(): void {
78 if (this.editor) {
79 const { start, end, focused } = this.initialSelection;
80 const doc = this.editor.getDoc();
81 const startPos = doc.posFromIndex(start);
82 const endPos = doc.posFromIndex(end);
83 doc.setSelection(startPos, endPos, {
84 scroll: true,
85 });
86 if (focused) {
87 this.editor.focus();
88 }
89 this.resetInitialSelection();
90 }
53 } 91 }
54 92
55 /** 93 /**
@@ -61,16 +99,23 @@ export class EditorStore {
61 * @param newEditor The new CodeMirror instance 99 * @param newEditor The new CodeMirror instance
62 */ 100 */
63 editorDidMount(newEditor: Editor): void { 101 editorDidMount(newEditor: Editor): void {
102 if (!this.chunk) {
103 throw new Error('Editor not loaded yet');
104 }
64 if (this.editor) { 105 if (this.editor) {
65 throw new Error('CoreMirror editor mounted before unmounting'); 106 throw new Error('CoreMirror editor mounted before unmounting');
66 } 107 }
67 this.editor = newEditor; 108 this.editor = newEditor;
68 this.xtextServices = createServices(newEditor, xtextOptions); 109 this.xtextServices = this.chunk.createServices(newEditor, xtextOptions);
110 this.applyInitialSelectionToEditor();
69 } 111 }
70 112
71 editorWillUnmount(): void { 113 editorWillUnmount(): void {
114 if (!this.chunk) {
115 throw new Error('Editor not loaded yet');
116 }
72 if (this.editor) { 117 if (this.editor) {
73 removeServices(this.editor); 118 this.chunk.removeServices(this.editor);
74 } 119 }
75 delete this.editor; 120 delete this.editor;
76 delete this.xtextServices; 121 delete this.xtextServices;
@@ -93,10 +138,14 @@ export class EditorStore {
93 this.atom.reportObserved(); 138 this.atom.reportObserved();
94 } 139 }
95 140
141 get codeMirrorTheme(): string {
142 return `problem-${this.themeStore.className}`;
143 }
144
96 get codeMirrorOptions(): EditorConfiguration { 145 get codeMirrorOptions(): EditorConfiguration {
97 return { 146 return {
98 ...codeMirrorGlobalOptions, 147 ...codeMirrorGlobalOptions,
99 theme: this.themeStore.codeMirrorTheme, 148 theme: this.codeMirrorTheme,
100 lineNumbers: this.showLineNumbers, 149 lineNumbers: this.showLineNumbers,
101 }; 150 };
102 } 151 }
diff --git a/language-web/src/main/js/editor/editor.ts b/language-web/src/main/js/editor/editor.ts
new file mode 100644
index 00000000..fbf8796b
--- /dev/null
+++ b/language-web/src/main/js/editor/editor.ts
@@ -0,0 +1,18 @@
1import 'codemirror/addon/selection/active-line';
2import 'mode-problem';
3import { Controlled } from 'react-codemirror2';
4import { createServices, removeServices } from 'xtext/xtext-codemirror';
5
6export interface IEditorChunk {
7 CodeMirror: typeof Controlled;
8
9 createServices: typeof createServices;
10
11 removeServices: typeof removeServices;
12}
13
14export const editorChunk: IEditorChunk = {
15 CodeMirror: Controlled,
16 createServices,
17 removeServices,
18};
diff --git a/language-web/src/main/js/theme/ThemeStore.ts b/language-web/src/main/js/theme/ThemeStore.ts
index 0e4aeb23..2644a96a 100644
--- a/language-web/src/main/js/theme/ThemeStore.ts
+++ b/language-web/src/main/js/theme/ThemeStore.ts
@@ -51,7 +51,7 @@ export class ThemeStore {
51 return responsiveFontSizes(materialUiTheme); 51 return responsiveFontSizes(materialUiTheme);
52 } 52 }
53 53
54 get codeMirrorTheme(): string { 54 get className(): string {
55 return `problem-${this.currentThemeData.className}`; 55 return this.currentThemeData.className;
56 } 56 }
57} 57}
diff --git a/language-web/webpack.config.js b/language-web/webpack.config.js
index ae2f2386..1bd0edb2 100644
--- a/language-web/webpack.config.js
+++ b/language-web/webpack.config.js
@@ -1,8 +1,8 @@
1const fs = require('fs'); 1const fs = require('fs');
2const path = require('path'); 2const path = require('path');
3 3
4const WebpackBeforeBuildPlugin = require('before-build-webpack');
5const HtmlWebpackPlugin = require('html-webpack-plugin'); 4const HtmlWebpackPlugin = require('html-webpack-plugin');
5const HtmlWebpackInjectPreload = require('@principalstudio/html-webpack-inject-preload');
6const MiniCssExtractPlugin = require('mini-css-extract-plugin'); 6const MiniCssExtractPlugin = require('mini-css-extract-plugin');
7const { SubresourceIntegrityPlugin } = require('webpack-subresource-integrity'); 7const { SubresourceIntegrityPlugin } = require('webpack-subresource-integrity');
8 8
@@ -37,6 +37,15 @@ const babelPresets = [
37 ], 37 ],
38 '@babel/preset-react', 38 '@babel/preset-react',
39]; 39];
40const babelPlugins = [
41 '@babel/plugin-transform-runtime',
42]
43const magicCommentsLoader = {
44 loader: 'magic-comments-loader',
45 options: {
46 webpackChunkName: true,
47 }
48};
40 49
41module.exports = { 50module.exports = {
42 mode: devMode ? 'development' : 'production', 51 mode: devMode ? 'development' : 'production',
@@ -44,8 +53,10 @@ module.exports = {
44 output: { 53 output: {
45 path: outputPath, 54 path: outputPath,
46 publicPath: '/', 55 publicPath: '/',
47 filename: devMode ? '[name].js' : '[contenthash].js', 56 filename: devMode ? '[name].js' : '[name].[contenthash].js',
48 chunkFilename: devMode ? '[id].js' : '[contenthash].js', 57 chunkFilename: devMode ? '[name].js' : '[name].[contenthash].js',
58 assetModuleFilename: devMode ? '[name].js' : '[name].[contenthash][ext]',
59 clean: true,
49 crossOriginLoading: 'anonymous', 60 crossOriginLoading: 'anonymous',
50 }, 61 },
51 module: { 62 module: {
@@ -53,41 +64,53 @@ module.exports = {
53 { 64 {
54 test: /\.jsx?$/i, 65 test: /\.jsx?$/i,
55 ...babelLoaderFilters, 66 ...babelLoaderFilters,
56 loader: 'babel-loader', 67 use: [
57 options: { 68 {
58 presets: babelPresets, 69 loader: 'babel-loader',
59 plugins: [ 70 options: {
60 [ 71 presets: babelPresets,
61 '@babel/plugin-proposal-class-properties', 72 plugins: [
62 { 73 [
63 loose: false, 74 '@babel/plugin-proposal-class-properties',
75 {
76 loose: false,
77 },
78 ...babelPlugins,
79 ],
80 ],
81 assumptions: {
82 'setPublicClassFields': false,
64 }, 83 },
65 ], 84 },
66 ],
67 assumptions: {
68 'setPublicClassFields': false,
69 }, 85 },
70 }, 86 magicCommentsLoader,
87 ],
71 }, 88 },
72 { 89 {
73 test: /.tsx?$/i, 90 test: /.tsx?$/i,
74 ...babelLoaderFilters, 91 ...babelLoaderFilters,
75 loader: 'babel-loader', 92 use: [
76 options: { 93 {
77 presets: [ 94 loader: 'babel-loader',
78 ...babelPresets, 95 options: {
79 [ 96 presets: [
80 '@babel/preset-typescript', 97 ...babelPresets,
81 { 98 [
82 isTSX: true, 99 '@babel/preset-typescript',
83 allExtensions: true, 100 {
84 allowDeclareFields: true, 101 isTSX: true,
85 onlyRemoveTypeImports: true, 102 allExtensions: true,
86 optimizeConstEnums: true, 103 allowDeclareFields: true,
87 }, 104 onlyRemoveTypeImports: true,
88 ] 105 optimizeConstEnums: true,
89 ], 106 },
90 }, 107 ]
108 ],
109 plugins: babelPlugins,
110 },
111 },
112 magicCommentsLoader,
113 ],
91 }, 114 },
92 { 115 {
93 test: /\.scss$/i, 116 test: /\.scss$/i,
@@ -133,8 +156,23 @@ module.exports = {
133 }, 156 },
134 devtool: devMode ? 'inline-source-map' : 'source-map', 157 devtool: devMode ? 'inline-source-map' : 'source-map',
135 optimization: { 158 optimization: {
159 providedExports: !devMode,
160 sideEffects: devMode ? 'flag' : true,
136 splitChunks: { 161 splitChunks: {
137 chunks: 'all', 162 chunks: 'all',
163 cacheGroups: {
164 defaultVendors: {
165 test: /[\\/]node_modules[\\/]/,
166 priority: -10,
167 reuseExistingChunk: true,
168 filename: devMode ? 'vendor.[id].js' : 'vendor.[contenthash].js',
169 },
170 default: {
171 minChunks: 2,
172 priority: -20,
173 reuseExistingChunk: true,
174 },
175 },
138 }, 176 },
139 }, 177 },
140 devServer: { 178 devServer: {
@@ -157,8 +195,8 @@ module.exports = {
157 }, 195 },
158 plugins: [ 196 plugins: [
159 new MiniCssExtractPlugin({ 197 new MiniCssExtractPlugin({
160 filename: '[contenthash].css', 198 filename: '[name].[contenthash].css',
161 chunkFilename: '[contenthash].css', 199 chunkFilename: '[name].[contenthash].css',
162 }), 200 }),
163 new SubresourceIntegrityPlugin(), 201 new SubresourceIntegrityPlugin(),
164 new HtmlWebpackPlugin({ 202 new HtmlWebpackPlugin({
@@ -173,22 +211,17 @@ module.exports = {
173 useShortDoctype: true, 211 useShortDoctype: true,
174 }, 212 },
175 }), 213 }),
176 new WebpackBeforeBuildPlugin((stats, callback) => { 214 new HtmlWebpackInjectPreload({
177 // https://stackoverflow.com/a/40370750 215 files: [
178 const newlyCreatedAssets = stats.compilation.assets; 216 {
179 const unlinked = []; 217 match: /(roboto-latin-(400|500)-normal|jetbrains-mono-latin-variable).*\.woff2/,
180 fs.readdir(outputPath, (err, files) => { 218 attributes: {
181 files.forEach(file => { 219 as: 'font',
182 if (!newlyCreatedAssets[file]) { 220 type: 'font/woff2',
183 fs.unlinkSync(path.resolve(outputPath, file)); 221 crossorigin: 'anonymous',
184 unlinked.push(file); 222 },
185 } 223 },
186 }); 224 ],
187 if (unlinked.length > 0) { 225 }),
188 console.log('Removed old assets: ', unlinked);
189 }
190 });
191 callback();
192 }, ['done']),
193 ], 226 ],
194}; 227};
diff --git a/language-web/yarn.lock b/language-web/yarn.lock
index 15cabc7c..3aac4633 100644
--- a/language-web/yarn.lock
+++ b/language-web/yarn.lock
@@ -742,6 +742,18 @@
742 dependencies: 742 dependencies:
743 "@babel/helper-plugin-utils" "^7.14.5" 743 "@babel/helper-plugin-utils" "^7.14.5"
744 744
745"@babel/plugin-transform-runtime@^7.15.0":
746 version "7.15.0"
747 resolved "https://registry.yarnpkg.com/@babel/plugin-transform-runtime/-/plugin-transform-runtime-7.15.0.tgz#d3aa650d11678ca76ce294071fda53d7804183b3"
748 integrity sha512-sfHYkLGjhzWTq6xsuQ01oEsUYjkHRux9fW1iUA68dC7Qd8BS1Unq4aZ8itmQp95zUzIcyR2EbNMTzAicFj+guw==
749 dependencies:
750 "@babel/helper-module-imports" "^7.14.5"
751 "@babel/helper-plugin-utils" "^7.14.5"
752 babel-plugin-polyfill-corejs2 "^0.2.2"
753 babel-plugin-polyfill-corejs3 "^0.2.2"
754 babel-plugin-polyfill-regenerator "^0.2.2"
755 semver "^6.3.0"
756
745"@babel/plugin-transform-shorthand-properties@^7.14.5": 757"@babel/plugin-transform-shorthand-properties@^7.14.5":
746 version "7.14.5" 758 version "7.14.5"
747 resolved "https://registry.yarnpkg.com/@babel/plugin-transform-shorthand-properties/-/plugin-transform-shorthand-properties-7.14.5.tgz#97f13855f1409338d8cadcbaca670ad79e091a58" 759 resolved "https://registry.yarnpkg.com/@babel/plugin-transform-shorthand-properties/-/plugin-transform-shorthand-properties-7.14.5.tgz#97f13855f1409338d8cadcbaca670ad79e091a58"
@@ -921,7 +933,7 @@
921 core-js-pure "^3.16.0" 933 core-js-pure "^3.16.0"
922 regenerator-runtime "^0.13.4" 934 regenerator-runtime "^0.13.4"
923 935
924"@babel/runtime@^7.10.2", "@babel/runtime@^7.11.2", "@babel/runtime@^7.13.10", "@babel/runtime@^7.14.8", "@babel/runtime@^7.5.5", "@babel/runtime@^7.7.2", "@babel/runtime@^7.8.4", "@babel/runtime@^7.8.7": 936"@babel/runtime@^7.10.2", "@babel/runtime@^7.11.2", "@babel/runtime@^7.13.10", "@babel/runtime@^7.14.8", "@babel/runtime@^7.15.0", "@babel/runtime@^7.5.5", "@babel/runtime@^7.7.2", "@babel/runtime@^7.8.4", "@babel/runtime@^7.8.7":
925 version "7.15.4" 937 version "7.15.4"
926 resolved "https://registry.yarnpkg.com/@babel/runtime/-/runtime-7.15.4.tgz#fd17d16bfdf878e6dd02d19753a39fa8a8d9c84a" 938 resolved "https://registry.yarnpkg.com/@babel/runtime/-/runtime-7.15.4.tgz#fd17d16bfdf878e6dd02d19753a39fa8a8d9c84a"
927 integrity sha512-99catp6bHCaxr4sJ/DbTGgHS4+Rs2RVd2g7iOap6SLGPDknRK9ztKNsE/Fg6QhSeh1FGE5f6gHGQmvvn3I3xhw== 939 integrity sha512-99catp6bHCaxr4sJ/DbTGgHS4+Rs2RVd2g7iOap6SLGPDknRK9ztKNsE/Fg6QhSeh1FGE5f6gHGQmvvn3I3xhw==
@@ -1227,6 +1239,11 @@
1227 resolved "https://registry.yarnpkg.com/@popperjs/core/-/core-2.10.1.tgz#728ecd95ab207aab8a9a4e421f0422db329232be" 1239 resolved "https://registry.yarnpkg.com/@popperjs/core/-/core-2.10.1.tgz#728ecd95ab207aab8a9a4e421f0422db329232be"
1228 integrity sha512-HnUhk1Sy9IuKrxEMdIRCxpIqPw6BFsbYSEUO9p/hNw5sMld/+3OLMWQP80F8/db9qsv3qUjs7ZR5bS/R+iinXw== 1240 integrity sha512-HnUhk1Sy9IuKrxEMdIRCxpIqPw6BFsbYSEUO9p/hNw5sMld/+3OLMWQP80F8/db9qsv3qUjs7ZR5bS/R+iinXw==
1229 1241
1242"@principalstudio/html-webpack-inject-preload@^1.2.7":
1243 version "1.2.7"
1244 resolved "https://registry.yarnpkg.com/@principalstudio/html-webpack-inject-preload/-/html-webpack-inject-preload-1.2.7.tgz#0c1f0b32a34d814b36ce84111f89990441cc64e8"
1245 integrity sha512-KJKkiKG63ugBjf8U0e9jUcI9CLPTFIsxXplEDE0oi3mPpxd90X9SJovo3W2l7yh/ARKIYXhQq8fSXUN7M29TzQ==
1246
1230"@sindresorhus/is@^0.7.0": 1247"@sindresorhus/is@^0.7.0":
1231 version "0.7.0" 1248 version "0.7.0"
1232 resolved "https://registry.yarnpkg.com/@sindresorhus/is/-/is-0.7.0.tgz#9a06f4f137ee84d7df0460c1fdb1135ffa6c50fd" 1249 resolved "https://registry.yarnpkg.com/@sindresorhus/is/-/is-0.7.0.tgz#9a06f4f137ee84d7df0460c1fdb1135ffa6c50fd"
@@ -1927,11 +1944,6 @@ batch@0.6.1:
1927 resolved "https://registry.yarnpkg.com/batch/-/batch-0.6.1.tgz#dc34314f4e679318093fc760272525f94bf25c16" 1944 resolved "https://registry.yarnpkg.com/batch/-/batch-0.6.1.tgz#dc34314f4e679318093fc760272525f94bf25c16"
1928 integrity sha1-3DQxT05nkxgJP8dgJyUl+UvyXBY= 1945 integrity sha1-3DQxT05nkxgJP8dgJyUl+UvyXBY=
1929 1946
1930before-build-webpack@^0.2.11:
1931 version "0.2.11"
1932 resolved "https://registry.yarnpkg.com/before-build-webpack/-/before-build-webpack-0.2.11.tgz#ce508c92e42dfb8d398bce2eba40d211b85439da"
1933 integrity sha512-xigRuKoJmla3cO/BP76CDlmkXmQFrjlHv6oS16RxmbckYTfi5I3ZBp7MnoKv+C05DqDT1pSl+znLf7pv1Vv4ew==
1934
1935big.js@^5.2.2: 1947big.js@^5.2.2:
1936 version "5.2.2" 1948 version "5.2.2"
1937 resolved "https://registry.yarnpkg.com/big.js/-/big.js-5.2.2.tgz#65f0af382f578bcdc742bd9c281e9cb2d7768328" 1949 resolved "https://registry.yarnpkg.com/big.js/-/big.js-5.2.2.tgz#65f0af382f578bcdc742bd9c281e9cb2d7768328"
@@ -5022,6 +5034,15 @@ lru-cache@^6.0.0:
5022 dependencies: 5034 dependencies:
5023 yallist "^4.0.0" 5035 yallist "^4.0.0"
5024 5036
5037magic-comments-loader@^1.4.1:
5038 version "1.4.1"
5039 resolved "https://registry.yarnpkg.com/magic-comments-loader/-/magic-comments-loader-1.4.1.tgz#d54c88f0b96418e19a7695c978960c3b8be5a142"
5040 integrity sha512-5Kh0NkWO40o35sCNJ3NqlDBVop449giCaLJwBZmX32UJADKCcDBnI7MjmlBfjtbBhhnRlMFU5CebqWDfFYyg8Q==
5041 dependencies:
5042 loader-utils "^2.0.0"
5043 micromatch "^4.0.4"
5044 schema-utils "^3.1.1"
5045
5025make-dir@^1.0.0, make-dir@^1.2.0: 5046make-dir@^1.0.0, make-dir@^1.2.0:
5026 version "1.3.0" 5047 version "1.3.0"
5027 resolved "https://registry.yarnpkg.com/make-dir/-/make-dir-1.3.0.tgz#79c1033b80515bd6d24ec9933e860ca75ee27f0c" 5048 resolved "https://registry.yarnpkg.com/make-dir/-/make-dir-1.3.0.tgz#79c1033b80515bd6d24ec9933e860ca75ee27f0c"