Skip to content

Commit

Permalink
Merge pull request #4235 from serlo/refactor/remove-unused-packages-f…
Browse files Browse the repository at this point in the history
…rom-webcomponent

refactor(web-component): remove css magic, remove unused packages
  • Loading branch information
elbotho authored Oct 30, 2024
2 parents 7c1d3db + 4a7c3ce commit 9fe9b1f
Show file tree
Hide file tree
Showing 8 changed files with 4 additions and 273 deletions.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
16 changes: 1 addition & 15 deletions packages/editor-web-component/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -46,36 +46,25 @@
"@rollup/plugin-replace": "^5.0.5",
"@serlo/editor": "0.18.0",
"@serlo/typescript-config": "workspace:*",
"@types/escodegen": "^0.0.10",
"@types/estraverse": "^5.1.7",
"@types/react": "^18.0.25",
"@types/react-dom": "^18.0.8",
"@vitejs/plugin-react": "^4.2.1",
"acorn": "^8.11.3",
"dotenv": "^16.4.5",
"escodegen": "^2.1.0",
"eslint": "^8.57.0",
"eslint-config-next": "^14.2.2",
"eslint-config-prettier": "^9.1.0",
"eslint-import-resolver-typescript": "^3.6.1",
"eslint-plugin-import": "^2.29.1",
"eslint-plugin-react": "^7.34.1",
"eslint-plugin-react-hooks": "^4.6.0",
"estraverse": "^5.3.0",
"jest": "^27.5.1",
"npm": "^10.5.2",
"npm-run-all": "^4.1.5",
"nprogress": "^0.2.0",
"prettier": "^3.2.5",
"prettier-plugin-packagejson": "^2.5.0",
"prettier-plugin-tailwindcss": "^0.5.14",
"react": "^18.2.0",
"react-dom": "^18.2.0",
"uuid": "^9.0.1",
"vite": "^5.2.14",
"vite-plugin-css-injected-by-js": "^3.5.1",
"vite-plugin-dts": "3.8.3",
"vite-plugin-svgr": "^4.2.0"
"vite-plugin-dts": "3.8.3"
},
"packageManager": "yarn@3.6.1",
"engines": {
Expand All @@ -88,8 +77,5 @@
"postcss-flexbugs-fixes": {},
"autoprefixer": {}
}
},
"svgr": {
"svgo": false
}
}
2 changes: 1 addition & 1 deletion packages/editor-web-component/src/editor-web-component.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ import {
defaultPlugins,
EditorPluginType,
} from '@serlo/editor'
import styles from '@serlo/editor/style.css?raw'
import styles from '@serlo/editor/dist/style.css?raw'
import React, { Suspense, lazy } from 'react'
import * as ReactDOM from 'react-dom/client'

Expand Down
212 changes: 1 addition & 211 deletions packages/editor-web-component/vite.config.ts
Original file line number Diff line number Diff line change
@@ -1,15 +1,7 @@
import react from '@vitejs/plugin-react'
import { resolve } from 'path'
import { Plugin, defineConfig } from 'vite'
// I wonder how many plugins we can get rid off here. Probably everything but
// the React one?
import cssInjectedByJsPlugin from 'vite-plugin-css-injected-by-js'
import { defineConfig } from 'vite'
import dts from 'vite-plugin-dts'
import svgr from 'vite-plugin-svgr'
import * as acorn from 'acorn'
import * as estraverse from 'estraverse'
import * as escodegen from 'escodegen'
import { type Program, type Node } from 'estree'
import replace from '@rollup/plugin-replace'

// https://vitejs.dev/guide/build.html#library-mode
Expand All @@ -20,205 +12,6 @@ const envReplacements = {
'process.env.NODE_ENV': JSON.stringify('production'),
}

function parseCodeToAST(code: string): Program {
return acorn.parse(code, {
ecmaVersion: 2020,
sourceType: 'module',
}) as Program
}

function generateCodeFromAST(ast: Node) {
return escodegen.generate(ast)
}

type PatternMatcher = (node: Node) => boolean

function removePattern(
ast: Program,
patternMatcher: PatternMatcher,
replacement: Node
) {
let foundMatch = false

estraverse.replace(ast, {
enter(node) {
if (patternMatcher(node)) {
foundMatch = true

// We only want to replace the body of functions, and not the whole
// function!
if (node.type === 'FunctionDeclaration') {
if (replacement.type !== 'BlockStatement') {
throw new Error(
'Replacement for function body must be a BlockStatement'
)
}

// Replace function body, no need to return
node.body = replacement
} else {
// Replace entire node
return replacement
}
}
},
})

return { ast, foundMatch }
}

// Traverses the AST and look for the specific try-catch block that
// looks something like this
// try {
// if (typeof document < "u") {
// var t = document.createElement("style");
// t.appendChild(document.createTextNode(`.katex{font:....
// }
// } catch (e) {
// console.error("vite-plugin-css-injected-by-js", e);
// }
function matchGlobalStyleInjection(node: Node): boolean {
return (
node.type === 'TryStatement' &&
node.handler !== undefined &&
node.handler !== null &&
node.handler.body !== undefined &&
node.handler.body.body.some(
(n) =>
n.type === 'ExpressionStatement' &&
n.expression.type === 'CallExpression' &&
n.expression.callee.type === 'MemberExpression' &&
n.expression.callee.object.type === 'Identifier' &&
n.expression.callee.object.name === 'console' &&
n.expression.callee.property.type === 'Identifier' &&
n.expression.callee.property.name === 'error' &&
n.expression.arguments.length > 0 &&
n.expression.arguments[0].type === 'Literal' &&
n.expression.arguments[0].value === 'vite-plugin-css-injected-by-js'
)
)
}

// Traverses the AST and matches the addStyle function body that looks something
// like this (see function l to see why we only replace the function body and
// not the whole function):

// return (() => {
// o.r(a), o.d(a, {
// addStyles: () => l,
// EditableMathField: () => c,
// StaticMathField: () => u,
// default: () => d
// });
// var s = o(527);
// function l() {
// if (document.getElementById('react-mathquill-styles') == null) {
// var m = document.createElement('style');
// m.setAttribute('id', 'react-mathquill-styles'), m.innerHTML = s.Z[0][1], document.getElementsByTagName('head')[0].appendChild(m);
// }
// }
// })
function matchMathQuillStyleInjection(node: Node): boolean {
return (
node.type === 'FunctionDeclaration' &&
node.body.body.some(
(n) =>
n.type === 'IfStatement' &&
n.test.type === 'BinaryExpression' &&
n.test.left.type === 'CallExpression' &&
n.test.left.callee.type === 'MemberExpression' &&
n.test.left.callee.object.type === 'Identifier' &&
n.test.left.callee.object.name === 'document' &&
n.test.left.callee.property.type === 'Identifier' &&
n.test.left.callee.property.name === 'getElementById' &&
n.test.left.arguments.length > 0 &&
n.test.left.arguments[0].type === 'Literal' &&
n.test.left.arguments[0].value === 'react-mathquill-styles' &&
n.test.right.type === 'Literal' &&
n.test.right.value === null
)
)
}

function getNoOpReplacement(identifier: string): Node {
return {
type: 'VariableDeclaration',
declarations: [
{
type: 'VariableDeclarator',
id: {
type: 'Identifier',
name: `NO_OPERATION_INSTEAD_OF_${identifier}`,
},
init: { type: 'Literal', value: null },
},
],
kind: 'const',
}
}

function getNoOpFunctionBodyReplacement(identifier: string): Node {
return {
type: 'BlockStatement',
body: [getNoOpReplacement(identifier)],
} as Node
}

// Custom Rollup plugin to remove global style injection from the @editor
// package. This basically undos the cssInjectedByJsPlugin inside the editor
// package. The reason why we don't remove it from there, is because we want to
// keep the convenience of importing from the @serlo/editor package without
// having to worry about styling.
function removeGlobalStylesPlugin(): Plugin {
return {
name: 'remove-styles-plugin',
generateBundle(_, bundle) {
let globalStyleMatch = false
let mathQuillStyleMatch = false

for (const file of Object.values(bundle)) {
if (file.type === 'chunk') {
const chunk = file
let ast = parseCodeToAST(chunk.code)

const globalEditorStyleResult = removePattern(
ast,
matchGlobalStyleInjection,
getNoOpReplacement('GLOBAL_EDITOR_STYLES')
)

globalStyleMatch =
globalStyleMatch || globalEditorStyleResult.foundMatch
ast = globalEditorStyleResult.ast
const mathQuillStyleResult = removePattern(
ast,
matchMathQuillStyleInjection,
getNoOpFunctionBodyReplacement('MATH_QUILL')
)
mathQuillStyleMatch =
mathQuillStyleMatch || mathQuillStyleResult.foundMatch
ast = mathQuillStyleResult.ast

// Turn AST back into code and write it into the chunk.
chunk.code = generateCodeFromAST(ast)
}
}

if (!globalStyleMatch) {
throw new Error(
'No editor global style injection pattern was found and removed.'
)
}

if (!mathQuillStyleMatch) {
throw new Error(
'No MathQuill style injection pattern was found and removed.'
)
}
},
}
}

// eslint-disable-next-line import/no-default-export
export default defineConfig({
build: {
Expand All @@ -239,8 +32,5 @@ export default defineConfig({
strictOutput: true,
rollupTypes: true,
}),
svgr({ include: '**/*.svg' }),
cssInjectedByJsPlugin(),
removeGlobalStylesPlugin(),
],
})
47 changes: 1 addition & 46 deletions yarn.lock
Original file line number Diff line number Diff line change
Expand Up @@ -5615,36 +5615,25 @@ __metadata:
"@rollup/plugin-replace": ^5.0.5
"@serlo/editor": 0.18.0
"@serlo/typescript-config": "workspace:*"
"@types/escodegen": ^0.0.10
"@types/estraverse": ^5.1.7
"@types/react": ^18.0.25
"@types/react-dom": ^18.0.8
"@vitejs/plugin-react": ^4.2.1
acorn: ^8.11.3
dotenv: ^16.4.5
escodegen: ^2.1.0
eslint: ^8.57.0
eslint-config-next: ^14.2.2
eslint-config-prettier: ^9.1.0
eslint-import-resolver-typescript: ^3.6.1
eslint-plugin-import: ^2.29.1
eslint-plugin-react: ^7.34.1
eslint-plugin-react-hooks: ^4.6.0
estraverse: ^5.3.0
jest: ^27.5.1
npm: ^10.5.2
npm-run-all: ^4.1.5
nprogress: ^0.2.0
prettier: ^3.2.5
prettier-plugin-packagejson: ^2.5.0
prettier-plugin-tailwindcss: ^0.5.14
react: ^18.2.0
react-dom: ^18.2.0
uuid: ^9.0.1
vite: ^5.2.14
vite-plugin-css-injected-by-js: ^3.5.1
vite-plugin-dts: 3.8.3
vite-plugin-svgr: ^4.2.0
languageName: unknown
linkType: soft

Expand Down Expand Up @@ -6381,22 +6370,6 @@ __metadata:
languageName: node
linkType: hard

"@types/escodegen@npm:^0.0.10":
version: 0.0.10
resolution: "@types/escodegen@npm:0.0.10"
checksum: 270fb286cc2a901963ecab0273435ca30dbd1cbd69c9562be9826342888a938e30fd8446af0cd1c0a679420f5923d1ef10696c6fbad2a1e249d5f04a5ee43729
languageName: node
linkType: hard

"@types/estraverse@npm:^5.1.7":
version: 5.1.7
resolution: "@types/estraverse@npm:5.1.7"
dependencies:
"@types/estree": "*"
checksum: e598a73ec90c0b1989b55d55271742a7abfaa873ebe5041bfb1fbfe44fa5e64e5d759a60f4cb0a3f24b90c4ba98d87955aa836a8eea796ad4d880e4def92c23d
languageName: node
linkType: hard

"@types/estree@npm:*, @types/estree@npm:1.0.5, @types/estree@npm:^1.0.0":
version: 1.0.5
resolution: "@types/estree@npm:1.0.5"
Expand Down Expand Up @@ -7255,15 +7228,6 @@ __metadata:
languageName: node
linkType: hard

"acorn@npm:^8.11.3":
version: 8.11.3
resolution: "acorn@npm:8.11.3"
bin:
acorn: bin/acorn
checksum: 76d8e7d559512566b43ab4aadc374f11f563f0a9e21626dd59cb2888444e9445923ae9f3699972767f18af61df89cd89f5eaaf772d1327b055b45cb829b4a88c
languageName: node
linkType: hard

"agent-base@npm:6":
version: 6.0.2
resolution: "agent-base@npm:6.0.2"
Expand Down Expand Up @@ -10112,7 +10076,7 @@ __metadata:
languageName: node
linkType: hard

"escodegen@npm:^2.0.0, escodegen@npm:^2.1.0":
"escodegen@npm:^2.0.0":
version: 2.1.0
resolution: "escodegen@npm:2.1.0"
dependencies:
Expand Down Expand Up @@ -19817,15 +19781,6 @@ react-mathquill@Entkenntnis/tmp-react-mathquill:
languageName: node
linkType: hard

"vite-plugin-css-injected-by-js@npm:^3.5.1":
version: 3.5.1
resolution: "vite-plugin-css-injected-by-js@npm:3.5.1"
peerDependencies:
vite: ">2.0.0-0"
checksum: 9ace1b773e5756d585301a642a8a601e541f0828c3102577d7280599a5e226f21e41dcc21fe15d492a58b6d88eef7f658eaae832dc7ebc790be450c7ab470878
languageName: node
linkType: hard

"vite-plugin-dts@npm:3.8.3":
version: 3.8.3
resolution: "vite-plugin-dts@npm:3.8.3"
Expand Down

0 comments on commit 9fe9b1f

Please sign in to comment.