diff --git a/.stackblitzrc b/.stackblitzrc index cecca9411..90f2b35c7 100644 --- a/.stackblitzrc +++ b/.stackblitzrc @@ -1,9 +1,12 @@ { "installDependencies": true, - "startCommand": "yarn run start", + "startCommand": "yarn run docusaurus start", "env": { "NODE_ENV": "development", "OFFLINE_API": "indexedDB", - "STACKBLITZ": "true" + "STACKBLITZ": "true", + "YARN_IGNORE_ENGINES": "true", + "PACKAGE_SRC": "packages", + "PACKAGE_DEST": "docs/Packages" } } \ No newline at end of file diff --git a/.vscode/extensions.json b/.vscode/extensions.json index 1058a23bc..c60c27832 100644 --- a/.vscode/extensions.json +++ b/.vscode/extensions.json @@ -1,7 +1,7 @@ { "recommendations": [ - "pomdtr.excalidraw-editor", "zhuangtongfa.Material-theme", + "betterplace.betterplace-vscode-vitest-runner", "mhutchie.git-graph", "esbenp.prettier-vscode", "dbaeumer.vscode-eslint", diff --git a/README.md b/README.md index 580ded534..a1c352c45 100644 --- a/README.md +++ b/README.md @@ -1,9 +1,8 @@ -# Website +# Teaching Dev -This website is built using [Docusaurus](https://docusaurus.io/), a modern static website generator. +[![Open in StackBlitz](https://developer.stackblitz.com/img/open_in_stackblitz.svg)](https://stackblitz.com/github/GBSL-Informatik/teaching-dev) -> [!NOTE] -> Compatible with @docusaurus/faster (rspack and swc). +Diese Seite basiert auf [Docusaurus](https://docusaurus.io/), einem modernen statischen Website-Generator. Sie fügt viele hilfreiche Funktionalität für den Einsatz im Unterricht hinzu. ## TDEV-Website diff --git a/docusaurus.config.ts b/docusaurus.config.ts index f82db99f4..158cd2718 100644 --- a/docusaurus.config.ts +++ b/docusaurus.config.ts @@ -29,6 +29,7 @@ import { withSiteConfig } from './src/siteConfig/withSiteConfig'; import { sassPluginConfig, dynamicRouterPluginConfig, + stackblitzRspackTargetPluginConfig, rsDoctorPluginConfig, sentryPluginConfig, socketIoNoDepWarningsPluginConfig, @@ -381,6 +382,7 @@ const docusaurusConfig = withSiteConfig().then(async (siteConfig) => { plugins: [ sassPluginConfig, dynamicRouterPluginConfig(siteConfig), + stackblitzRspackTargetPluginConfig(), rsDoctorPluginConfig, [ aliasConfigurationPlugin, diff --git a/packages/tdev/page-index/remark-plugin/index.ts b/packages/tdev/page-index/remark-plugin/index.ts index 744a09701..195c13d68 100644 --- a/packages/tdev/page-index/remark-plugin/index.ts +++ b/packages/tdev/page-index/remark-plugin/index.ts @@ -2,37 +2,55 @@ import type { Plugin, Transformer } from 'unified'; import type { Code, Root } from 'mdast'; import type { MdxJsxFlowElement, MdxJsxTextElement } from 'mdast-util-mdx'; import path from 'path'; -import db from '../utils/db'; import { exportDB } from '../utils/exportDb'; import { debounce } from 'es-toolkit/function'; import { tdevRoot } from '../utils/options'; import { TypeModelMapping } from '@tdev-api/document'; +import type { Statement } from 'better-sqlite3'; const TdevRoot = `${tdevRoot === '' ? '' : '/'}${tdevRoot}`; const TdevRootRegex = new RegExp(`^${TdevRoot}`); const projectRoot = process.cwd(); const isDev = process.env.NODE_ENV !== 'production'; -const insertDocRoot = db.prepare( - `INSERT INTO document_roots ( - id, - type, - page_id, - path, - position - ) VALUES ( - @id, - @type, - @page_id, - @path, - @position - ) ON CONFLICT(id, path) DO NOTHING` -); +const _cachedImport: Partial<{ + insertDocRoot: Statement; + cleanupPage: Statement; +}> = {}; -const cleanupPage = db.prepare( - `DELETE FROM document_roots - WHERE path = ? AND page_id = ?;` -); +const requireDb = async (): Promise => { + if (process.env.STACKBLITZ === 'true') { + return Promise.resolve({}); + } + if (_cachedImport.cleanupPage && _cachedImport.insertDocRoot) { + return Promise.resolve(_cachedImport); + } + const db = (await import('../utils/db')).default; + + const insertDocRoot = db.prepare( + `INSERT INTO document_roots ( + id, + type, + page_id, + path, + position + ) VALUES ( + @id, + @type, + @page_id, + @path, + @position + ) ON CONFLICT(id, path) DO NOTHING` + ); + + const cleanupPage = db.prepare( + `DELETE FROM document_roots + WHERE path = ? AND page_id = ?;` + ); + _cachedImport.insertDocRoot = insertDocRoot; + _cachedImport.cleanupPage = cleanupPage; + return _cachedImport; +}; interface JsxConfig { /** @@ -67,6 +85,10 @@ const remarkPlugin: Plugin = function plugin( const { components } = options; const mdxJsxComponents = new Map(components.map((c) => [c.name, c])); return async (root, file) => { + const { insertDocRoot, cleanupPage } = await requireDb(); + if (!insertDocRoot || !cleanupPage) { + return; + } const { page_id } = (file.data?.frontMatter || {}) as { page_id?: string }; if (components.length < 1 || !page_id) { return; diff --git a/packages/tdev/page-index/utils/exportDb.ts b/packages/tdev/page-index/utils/exportDb.ts index 985c41aec..f18378dcc 100644 --- a/packages/tdev/page-index/utils/exportDb.ts +++ b/packages/tdev/page-index/utils/exportDb.ts @@ -1,15 +1,31 @@ -import db from './db'; import { promises as fs } from 'fs'; import { pageIndexPath } from './options'; import { PageIndex } from '..'; +import type { Statement } from 'better-sqlite3'; -const getDocumentRoots = db.prepare('SELECT * FROM document_roots ORDER BY path ASC, position ASC'); +const _cachedImport = { + getDocumentRoots: null as Statement | null +}; +const requireDb = async () => { + if (process.env.STACKBLITZ === 'true') { + // Stackblitz does not support better-sqlite3, so we skip the database export + return Promise.resolve(); + } + const db = (await import('./db')).default; + const getDocumentRoots = db.prepare('SELECT * FROM document_roots ORDER BY path ASC, position ASC'); + _cachedImport.getDocumentRoots = getDocumentRoots; +}; -export const getContent = () => { +const getContent = () => { + const { getDocumentRoots } = _cachedImport; + if (!getDocumentRoots) { + return { documentRoots: [] as PageIndex[] }; + } const documentRoots = getDocumentRoots.all() as PageIndex[]; return { documentRoots }; }; export const exportDB = async () => { + await requireDb(); await fs.writeFile(pageIndexPath, JSON.stringify(getContent(), null, 2)); }; diff --git a/src/plugins/plugin-stackblitz-rspack-target/index.ts b/src/plugins/plugin-stackblitz-rspack-target/index.ts new file mode 100644 index 000000000..86c22317d --- /dev/null +++ b/src/plugins/plugin-stackblitz-rspack-target/index.ts @@ -0,0 +1,17 @@ +import { PluginModule } from '@docusaurus/types'; + +const stackblitzRspackTarget: PluginModule = (context, options) => { + if (process.env.STACKBLITZ === 'true') { + return { + name: 'webpack-target-fix-plugin', + configureWebpack() { + return { + target: 'web' // bypass rspack's browserslist-based target inference + }; + } + }; + } + return null; +}; + +export default stackblitzRspackTarget; diff --git a/src/siteConfig/pluginConfigs.ts b/src/siteConfig/pluginConfigs.ts index cb6fc4498..d66cfad9e 100644 --- a/src/siteConfig/pluginConfigs.ts +++ b/src/siteConfig/pluginConfigs.ts @@ -1,4 +1,5 @@ import dynamicRouterPlugin, { Config as DynamicRouteConfig } from '../plugins/plugin-dynamic-routes'; +import stackblitzRspackTarget from '../plugins/plugin-stackblitz-rspack-target'; import aliasConfigurationPlugin from '../plugins/plugin-alias-configuration'; import type { PluginConfig } from '@docusaurus/types'; import { sentryWebpackPlugin } from '@sentry/webpack-plugin'; @@ -21,6 +22,8 @@ export const dynamicRouterPluginConfig: (siteConfig: SiteConfig) => PluginConfig } satisfies DynamicRouteConfig ]; +export const stackblitzRspackTargetPluginConfig: () => PluginConfig = () => stackblitzRspackTarget; + export const rsDoctorPluginConfig: PluginConfig = process.env.RSDOCTOR === 'true' && [ 'rsdoctor', {