diff --git a/html/index.html b/html/index.html index 1d6f2e9..8486ac2 100644 --- a/html/index.html +++ b/html/index.html @@ -7,8 +7,6 @@ -
- <%= ROOT_CONTENT %> -
+
{% ROOT_CONTENT %}
diff --git a/package.json b/package.json index 9e0c6e6..de54994 100644 --- a/package.json +++ b/package.json @@ -10,9 +10,9 @@ "reset": "rm -rf node_modules", "setup": "yarn reset && yarn", "clean": "rm -rf dist", - "dev": "yarn build:ssr && rspack serve --watch", - "build:ssr": "yarn clean && rspack build --config=rspack.ssr.config.ts", - "build": "yarn build:ssr && rspack build && rm -rf dist/ssr", + "dev": "rspack serve --config=tools/rspack.config.ts --watch", + "build:ssr": "rspack build --config=tools/rspack.ssr.config.ts", + "build": "yarn clean && yarn build:ssr && rspack build --config=tools/rspack.config.ts && rm -rf dist/ssr", "new": "ts-node tools/addNewProblem.ts", "lint": "eslint --fix --color --cache --quiet .", "prepare": "husky install" diff --git a/src/modules/Editor/monaco-editor.tsx b/src/modules/Editor/monaco-editor.tsx index 2330eb1..6e728c9 100644 --- a/src/modules/Editor/monaco-editor.tsx +++ b/src/modules/Editor/monaco-editor.tsx @@ -147,7 +147,7 @@ const MonacoEditor = withAutoResize( } for (const filename of Object.keys(this.props.raw)) { this.models[filename]?.updateOptions(this.props.setting); - if (!filename.includes('/node_modules/')) { + if (!filename.includes('node_modules')) { validateMonacoModel(this.models[filename]); } } diff --git a/tools/RspackSSRPlugin.ts b/tools/RspackSSRPlugin.ts new file mode 100644 index 0000000..17c4cb2 --- /dev/null +++ b/tools/RspackSSRPlugin.ts @@ -0,0 +1,83 @@ +import path from 'path'; +import childProcess from 'child_process'; +import { readFileSync, writeFileSync } from 'fs'; +import { RspackPluginInstance, Compiler } from '@rspack/core'; + +type RspackSSRPluginOptions = { + token: string; + template: string; +}; + +class RspackSSRPlugin implements RspackPluginInstance { + private readonly templateContent: string; + private readonly options: RspackSSRPluginOptions; + constructor(options: RspackSSRPluginOptions) { + this.options = options; + const { template } = options; + this.templateContent = readFileSync(template, { encoding: 'utf-8' }); + } + apply(compiler: Compiler) { + const mode = compiler.options.mode; + const pluginName = RspackSSRPlugin.name; + const tabFunc = + mode === 'development' + ? compiler.hooks.watchRun + : compiler.hooks.beforeRun; + tabFunc.tapAsync(pluginName, (compiler, callback) => { + this.runSSRBuild(compiler, callback); + }); + tabFunc.tap(pluginName, compiler => { + this.replaceTemplateFile(compiler); + }); + compiler.hooks.done.tap(pluginName, () => + this.recoverTemplateFile(compiler), + ); + } + runSSRBuild(compiler: Compiler, callback: () => void) { + const mode = compiler.options.mode; + if (mode !== 'development') { + callback(); + return; + } + const buildProcess = childProcess.spawn('yarn', [ + 'build:ssr', + `--mode=${mode}`, + ]); + buildProcess.stdout.on('data', process.stdout.write); + buildProcess.stderr.on('data', process.stderr.write); + buildProcess.on('close', function (code) { + if (code === 0) { + callback(); + } else { + throw new Error(`Generate SSR content failed with code ${code}`); + } + }); + } + replaceTemplateFile(compiler: Compiler) { + const token = this.options.token; + const context = compiler.context; + const templateContent = this.templateContent; + const ssrFilePath = path.resolve(context, './dist/ssr/ssr.bundle.js'); + // eslint-disable-next-line @typescript-eslint/no-var-requires + const getSSRContent = require(ssrFilePath).default; + delete require.cache[require.resolve(ssrFilePath)]; + const ssrContent = getSSRContent(); + const newTemplateContent = templateContent.replace(token, ssrContent); + writeFileSync( + path.resolve(context, './html/index.html'), + newTemplateContent, + { + encoding: 'utf-8', + }, + ); + } + recoverTemplateFile(compiler: Compiler) { + const context = compiler.context; + const templateContent = this.templateContent; + writeFileSync(path.resolve(context, './html/index.html'), templateContent, { + encoding: 'utf-8', + }); + } +} + +export default RspackSSRPlugin; diff --git a/rspack.base.config.ts b/tools/rspack.base.config.ts similarity index 83% rename from rspack.base.config.ts rename to tools/rspack.base.config.ts index 92e9fa8..2ed677c 100644 --- a/rspack.base.config.ts +++ b/tools/rspack.base.config.ts @@ -3,21 +3,18 @@ import type { Configuration } from '@rspack/cli'; export default function createBaseRspackConfig(): Configuration { return { - context: __dirname, - entry: { - main: './src/main.tsx', - }, + context: path.resolve(__dirname, '..'), output: { - path: path.resolve(__dirname, 'dist'), + path: './dist', filename: '[name].[contenthash:8].bundle.js', chunkFilename: '[name].[contenthash:8].bundle.js', cssChunkFilename: '[name].[contenthash:8].bundle.js', }, resolve: { alias: { - '@config': path.resolve(__dirname, './config'), - '@problems': path.resolve(__dirname, './problems'), - '@src': path.resolve(__dirname, './src'), + '@config': './config', + '@problems': './problems', + '@src': './src', }, }, builtins: { diff --git a/rspack.config.ts b/tools/rspack.config.ts similarity index 85% rename from rspack.config.ts rename to tools/rspack.config.ts index 9fa4609..25343fe 100644 --- a/rspack.config.ts +++ b/tools/rspack.config.ts @@ -1,18 +1,14 @@ -import path from 'path'; import type { Configuration } from '@rspack/cli'; -import { ArcoDesignPlugin } from '@arco-plugins/unplugin-react'; -import { CopyRspackPlugin, DefinePlugin } from '@rspack/core'; import HtmlRspackPlugin from '@rspack/plugin-html'; +import { CopyRspackPlugin, DefinePlugin } from '@rspack/core'; +import { ArcoDesignPlugin } from '@arco-plugins/unplugin-react'; +import RspackSSRPlugin from './RspackSSRPlugin'; import createBaseRspackConfig from './rspack.base.config'; export default function createRspackConfig(): Configuration { const baseConfig = createBaseRspackConfig(); const mode = process.env.NODE_ENV as Configuration['mode']; - // eslint-disable-next-line @typescript-eslint/no-var-requires - const getSSRContent = require( - path.resolve(__dirname, 'dist/ssr/ssr.bundle.js'), - ).default; - const ssrContent = getSSRContent(); + const template = './html/index.html'; return { ...baseConfig, mode, @@ -21,17 +17,21 @@ export default function createRspackConfig(): Configuration { main: './src/main.tsx', }, devtool: mode === 'production' ? false : 'source-map', + watchOptions: { + ignored: template, + }, plugins: [ + new RspackSSRPlugin({ + template, + token: '{% ROOT_CONTENT %}', + }), new HtmlRspackPlugin({ + template, minify: true, sri: 'sha256', inject: 'body', scriptLoading: 'defer', favicon: './assets/favicon.png', - template: './html/index.html', - templateParameters: { - ROOT_CONTENT: ssrContent, - }, }), new CopyRspackPlugin({ patterns: [ diff --git a/rspack.ssr.config.ts b/tools/rspack.ssr.config.ts similarity index 91% rename from rspack.ssr.config.ts rename to tools/rspack.ssr.config.ts index 0feb030..00127f0 100644 --- a/rspack.ssr.config.ts +++ b/tools/rspack.ssr.config.ts @@ -1,4 +1,3 @@ -import path from 'path'; import type { Configuration } from '@rspack/cli'; import { HtmlRspackPlugin, DefinePlugin } from '@rspack/core'; import createBaseRspackConfig from './rspack.base.config'; @@ -14,7 +13,7 @@ export default function createSSRRspackConfig(): Configuration { ssr: './src/ssr.tsx', }, output: { - path: path.resolve(__dirname, 'dist/ssr'), + path: './dist/ssr', filename: 'ssr.bundle.js', library: { type: 'commonjs',