diff --git a/.devcontainer/Dockerfile b/.devcontainer/Dockerfile new file mode 100644 index 00000000..9233dcf1 --- /dev/null +++ b/.devcontainer/Dockerfile @@ -0,0 +1,2 @@ +ARG VARIANT="16-buster" +FROM mcr.microsoft.com/vscode/devcontainers/typescript-node:0-${VARIANT} diff --git a/.devcontainer/devcontainer.json b/.devcontainer/devcontainer.json new file mode 100644 index 00000000..a069f0cc --- /dev/null +++ b/.devcontainer/devcontainer.json @@ -0,0 +1,10 @@ +{ + "name": "Gatsby OI Wiki", + "build": { + "dockerfile": "Dockerfile", + "args": { + "VARIANT": "14" + } + }, + "postStartCommand": "yarn install" +} diff --git a/.gitpod.yml b/.gitpod.yml index 5cb483fb..fecc2780 100644 --- a/.gitpod.yml +++ b/.gitpod.yml @@ -1,3 +1,3 @@ tasks: - - init: npm install - command: npm run develop + - init: yarn install + command: yarn dev diff --git a/README.md b/README.md index 85bfb1da..71b8c267 100644 --- a/README.md +++ b/README.md @@ -1,6 +1,6 @@ # gatsby-oiwiki -![Gitpod Ready-to-Code](https://img.shields.io/badge/Gitpod-Ready--to--Code-blue?logo=gitpod) +[![Gitpod Ready-to-Code](https://img.shields.io/badge/Gitpod-Ready--to--Code-blue?logo=gitpod)](https://gitpod.io/#https://github.com/OI-wiki/gatsby-oi-wiki) [![FOSSA Status](https://app.fossa.com/api/projects/git%2Bgithub.com%2FOI-wiki%2Fgatsby-oi-wiki.svg?type=shield)](https://app.fossa.com/projects/git%2Bgithub.com%2FOI-wiki%2Fgatsby-oi-wiki?ref=badge_shield) 正在开发中的基于 gatsby 的渲染框架。欢迎您点击下方链接入群并参与项目。 diff --git a/gatsby-theme-oi-wiki/.eslintrc.js b/gatsby-theme-oi-wiki/.eslintrc.js index 3f1d19f7..28b84eb6 100644 --- a/gatsby-theme-oi-wiki/.eslintrc.js +++ b/gatsby-theme-oi-wiki/.eslintrc.js @@ -64,7 +64,7 @@ module.exports = { '@typescript-eslint/ban-ts-comment': 'warn', 'no-unused-vars': 'off', '@typescript-eslint/no-unused-vars': [ - 'warn', + 'warn', { 'argsIgnorePattern': '^_' }, ], '@typescript-eslint/explicit-module-boundary-types': 'off', '@typescript-eslint/no-explicit-any': 'off', @@ -73,7 +73,9 @@ module.exports = { { files: [ '.eslintrc.js', - 'gatsby-*.js', + 'gatsby-node.js', + 'gatsby-config.js', + 'esmRequire.js', 'plugins/**/*.js', ], env: { diff --git a/gatsby-theme-oi-wiki/esmRequire.js b/gatsby-theme-oi-wiki/esmRequire.js new file mode 100644 index 00000000..7dd9f2e6 --- /dev/null +++ b/gatsby-theme-oi-wiki/esmRequire.js @@ -0,0 +1,34 @@ +/** + * @file export compiled ES modules as a workaround before Gatsby properly handles it + */ + +const esm = require('esm') +const fs = require('fs') +const Module = require('module') + + +// Node: bypass [ERR_REQUIRE_ESM] +const orig = Module._extensions['.js'] +Module._extensions['.js'] = function (module, filename) { + try { + return orig(module, filename) + } catch (e) { + if (e.code === 'ERR_REQUIRE_ESM') { + const content = fs.readFileSync(filename, 'utf8') + module._compile(content, filename) + } + } +} + +const _esmRequire = esm(module, { + cjs: true, + mode: 'all', +}) + +// don't pollute Module +Module._extensions['.js'] = orig + + +module.exports = function esmRequire(id) { + return _esmRequire(id) +} diff --git a/gatsby-theme-oi-wiki/gatsby-browser.js b/gatsby-theme-oi-wiki/gatsby-browser.js index 67d5868e..549e3626 100644 --- a/gatsby-theme-oi-wiki/gatsby-browser.js +++ b/gatsby-theme-oi-wiki/gatsby-browser.js @@ -1,6 +1,7 @@ -require('./static/extra.css') +import('./static/extra.css') +import WrapRootElement from './src/gatsby/WrapRootElement' -exports.onRouteUpdate = () => { +const onRouteUpdate = () => { if (process.env.GATSBY_IS_DEV) { requestIdleCallback(() => MathJax.typeset()) } @@ -10,3 +11,5 @@ exports.onRouteUpdate = () => { console.error(e) } } + +export { onRouteUpdate, WrapRootElement as wrapRootElement } diff --git a/gatsby-theme-oi-wiki/gatsby-config.esm.js b/gatsby-theme-oi-wiki/gatsby-config.esm.js deleted file mode 100644 index e3a7b624..00000000 --- a/gatsby-theme-oi-wiki/gatsby-config.esm.js +++ /dev/null @@ -1,27 +0,0 @@ -/** - * @file export compiled ES modules as a workaround before Gatsby properly handles it - */ - -const esm = require('esm') -const fs = require('fs') - -// Node: bypass [ERR_REQUIRE_ESM] limitations -const Module = require('module') -const orig = Module._extensions['.js'] -Module._extensions['.js'] = function (module, filename) { - const contents = fs.readFileSync(filename, 'utf8') - module._compile(contents, filename) -} - -// ESM: compile -const esmRequire = esm(module, { - cjs: true, - mode: 'all', -}) - -const imports = { - remarkDetails: esmRequire('remark-details').default, -} - -Module._extensions['.js'] = orig -module.exports = imports diff --git a/gatsby-theme-oi-wiki/gatsby-config.js b/gatsby-theme-oi-wiki/gatsby-config.js index 7addc5c9..a7645867 100644 --- a/gatsby-theme-oi-wiki/gatsby-config.js +++ b/gatsby-theme-oi-wiki/gatsby-config.js @@ -1,4 +1,5 @@ const path = require('path') +const esmRequire = require('./esmRequire') const IS_EXEC_BUILD = process.env.gatsby_executing_command === 'build' const IS_PROD = process.env.PRODUCTION === 'true' || @@ -26,8 +27,6 @@ const mathRehype = IS_EXEC_BUILD ? [require('rehype-mathjax/chtml'), { fontURL }] : [require('rehype-mathjax/browser')] -const { remarkDetails } = require('./gatsby-config.esm') - module.exports = { plugins: [ { @@ -87,7 +86,7 @@ module.exports = { ], remarkPlugins: [ require('remark-math'), - remarkDetails, + esmRequire('remark-details').default, [require('@mgtd/remark-shiki').remarkShiki, { semantic: false, theme: 'css-variables', diff --git a/gatsby-theme-oi-wiki/gatsby-node.js b/gatsby-theme-oi-wiki/gatsby-node.js index 4609ece9..655feea5 100644 --- a/gatsby-theme-oi-wiki/gatsby-node.js +++ b/gatsby-theme-oi-wiki/gatsby-node.js @@ -1,7 +1,8 @@ -const _ = require('lodash') +const kebabCase = require('lodash/kebabCase') const git = require('simple-git') const { createFilePath } = require('gatsby-source-filesystem') const { SitemapManager } = require('sitemap-manager') +const path = require('path') exports.onCreateWebpackConfig = ({ actions }) => { actions.setWebpackConfig({ @@ -16,10 +17,6 @@ exports.onCreateWebpackConfig = ({ actions }) => { }) } -const gitQuery = async function (prop) { - const res = await git().log(['-15', prop]).catch(err => console.log(err)) - return res -} exports.onCreateNode = ({ node, actions, getNode }) => { const { createNodeField } = actions // you only want to operate on `Mdx` nodes. If you had content from a @@ -43,12 +40,10 @@ exports.onCreateNode = ({ node, actions, getNode }) => { } } -exports.createPages = async ({ actions, graphql, reporter }) => { - const { createPage } = actions +const gitQuery = async (prop) => await git().log(['-15', prop]) - const docTemplate = require.resolve('./src/templates/doc.js') - const tagTemplate = require.resolve('./src/templates/tags.js') - const logTemplate = require.resolve('./src/templates/changelog.js') +exports.createPages = async (args) => { + const { graphql, actions, reporter } = args const result = await graphql(` { @@ -78,36 +73,41 @@ exports.createPages = async ({ actions, graphql, reporter }) => { } `) - // handle errors + // return when the result is wrong if (result.errors) { reporter.panicOnBuild('Error while running GraphQL query.') return } - const posts = result.data.postsRemark.edges - // console.log(posts) - // Create post detail pages + const { createPage } = actions + const data = result.data + + // Extract doc data from query + const posts = data.postsRemark.edges + const docTemplate = require.resolve('./src/templates/Doc.tsx') + const logTemplate = require.resolve('./src/templates/Changelog.tsx') for (const index in posts) { const { node } = posts[index] - - const previous = index === posts.length - 1 ? null : posts[index + 1] + const prev = index === posts.length - 1 ? null : posts[index + 1] const next = index === 0 ? null : posts[index - 1] - // /workspace/gatsby-oi-wiki/docs/empty.md -> docs/empty.md const { fileAbsolutePath: path } = node - const relativePath = path.slice(path.indexOf('/docs') + 1) + const relativePath = path?.slice(path.indexOf('/docs') + 1) || '' + + const log = await gitQuery(relativePath).catch(err => console.error(err)) - const log = await gitQuery(relativePath) + // Make doc page createPage({ path: node.fields.slug, component: docTemplate, context: { id: node.id, lastModified: log.latest?.date || new Date().toString(), - previous, + prev, next, }, }) + // Make changelog page createPage({ path: node.fields.slug + 'changelog/', component: logTemplate, @@ -119,81 +119,174 @@ exports.createPages = async ({ actions, graphql, reporter }) => { }) } + // Extract tag data from query const tags = result.data.tagsGroup.group + const tagsTemplate = require.resolve('./src/templates/Tag.tsx') // Make tag pages tags.forEach((tag) => { createPage({ - path: `/tags/${_.kebabCase(tag.fieldValue)}/`, - component: tagTemplate, + path: `/tags/${kebabCase(tag.fieldValue)}/`, + component: tagsTemplate, context: { tag: tag.fieldValue, }, }) }) - - if (result.errors) { - reporter.panic(result.errors) - } } -exports.onPostBuild = async ({ graphql, reporter }) => { - let queryResult = await graphql(`{ - site { - siteMetadata { - siteUrl - } - } - postsQuery:allMarkdownRemark { - edges { - node { - id - fields{ - slug - } - fileAbsolutePath - } - } - } - tagsQuery:allMarkdownRemark(limit: 2000) { - group(field: frontmatter___tags) { - fieldValue - } - } - }`) - if (queryResult.errors) { - reporter.panicOnBuild('Error while running GraphQL query to create sitemaps.', queryResult.errors) - } - queryResult = queryResult.data - const siteUrl = queryResult.site.siteMetadata.siteUrl +// exports.createPages = async ({ actions, graphql, reporter }) => { +// const { createPage } = actions +// +// const docTemplate = require.resolve('./src/templates/doc.js') +// const tagTemplate = require.resolve('./src/templates/tags.js') +// const logTemplate = require.resolve('./src/templates/changelog.js') +// +// const result = await graphql(` +// { +// postsRemark: allMarkdownRemark( +// sort: { order: DESC, fields: [frontmatter___title] } +// limit: 2000 +// ) { +// edges { +// node { +// fields { +// slug +// } +// id +// frontmatter { +// tags +// title +// } +// fileAbsolutePath +// } +// } +// } +// tagsGroup: allMarkdownRemark(limit: 2000) { +// group(field: frontmatter___tags) { +// fieldValue +// } +// } +// } +// `) +// +// // handle errors +// if (result.errors) { +// reporter.panicOnBuild('Error while running GraphQL query.') +// return +// } +// +// const posts = result.data.postsRemark.edges +// // console.log(posts) +// // Create post detail pages +// +// for (const index in posts) { +// const { node } = posts[index] +// +// const previous = index === posts.length - 1 ? null : posts[index + 1] +// const next = index === 0 ? null : posts[index - 1] +// // /workspace/gatsby-oi-wiki/docs/empty.md -> docs/empty.md +// const { fileAbsolutePath: path } = node +// const relativePath = path.slice(path.indexOf('/docs') + 1) +// +// const log = await gitQuery(relativePath) +// createPage({ +// path: node.fields.slug, +// component: docTemplate, +// context: { +// id: node.id, +// lastModified: log.latest?.date || new Date().toString(), +// previous, +// next, +// }, +// }) +// createPage({ +// path: node.fields.slug + 'changelog/', +// component: logTemplate, +// context: { +// title: node.frontmatter.title, +// changelog: log, +// relativePath, +// }, +// }) +// } +// +// // Extract tag data from query +// const tags = result.data.tagsGroup.group +// +// // Make tag pages +// tags.forEach((tag) => { +// createPage({ +// path: `/tags/${_.kebabCase(tag.fieldValue)}/`, +// component: tagTemplate, +// context: { +// tag: tag.fieldValue, +// }, +// }) +// }) +// +// if (result.errors) { +// reporter.panic(result.errors) +// } +// } - const MySitemap = new SitemapManager({ siteURL: siteUrl }) - for (const index in queryResult.postsQuery.edges) { - const { node } = queryResult.postsQuery.edges[index] - const { fileAbsolutePath: path } = node - const relativePath = path.slice(path.indexOf('/docs') + 1) - const lastmod = (await gitQuery(relativePath)).latest?.date || new Date().toString() - - MySitemap.addUrl('articles', [{ - loc: new URL(node.fields.slug, siteUrl).toString(), - lastmod, - }]) - MySitemap.addUrl('logs', [{ - loc: new URL(node.fields.slug + 'changelog/', siteUrl).toString(), - lastmod, - }]) - } - queryResult.tagsQuery.group.forEach(({ fieldValue }) => { - MySitemap.addUrl('tags', [{ - loc: new URL(`/tags/${_.kebabCase(fieldValue)}/`, siteUrl).toString(), - }]) - }) - MySitemap.addUrl('pages', [ - { loc: new URL('/pages/', siteUrl).toString() }, - { loc: new URL('/settings/', siteUrl).toString() }, - ]) - await MySitemap.finish().catch((e) => { - reporter.error(e) - }) -} +// exports.onPostBuild = async ({ graphql, reporter }) => { +// let queryResult = await graphql(`{ +// site { +// siteMetadata { +// siteUrl +// } +// } +// postsQuery:allMarkdownRemark { +// edges { +// node { +// id +// fields{ +// slug +// } +// fileAbsolutePath +// } +// } +// } +// tagsQuery:allMarkdownRemark(limit: 2000) { +// group(field: frontmatter___tags) { +// fieldValue +// } +// } +// }`) +// if (queryResult.errors) { +// reporter.panicOnBuild('Error while running GraphQL query to create sitemaps.', queryResult.errors) +// } +// queryResult = queryResult.data +// const siteUrl = queryResult.site.siteMetadata.siteUrl +// +// const MySitemap = new SitemapManager({ siteURL: siteUrl }) +// for (const index in queryResult.postsQuery.edges) { +// const { node } = queryResult.postsQuery.edges[index] +// const { fileAbsolutePath: path } = node +// const relativePath = path.slice(path.indexOf('/docs') + 1) +// const lastmod = (await gitQuery(relativePath)).latest?.date || new Date().toString() +// +// MySitemap.addUrl('articles', [{ +// loc: new URL(node.fields.slug, siteUrl).toString(), +// lastmod, +// }]) +// MySitemap.addUrl('logs', [{ +// loc: new URL(node.fields.slug + 'changelog/', siteUrl).toString(), +// lastmod, +// }]) +// } +// queryResult.tagsQuery.group.forEach(({ fieldValue }) => { +// MySitemap.addUrl('tags', [{ +// loc: new URL(`/tags/${_.kebabCase(fieldValue)}/`, siteUrl).toString(), +// }]) +// }) +// MySitemap.addUrl('pages', [ +// { loc: new URL('/pages/', siteUrl).toString() }, +// { loc: new URL('/settings/', siteUrl).toString() }, +// ]) +// await MySitemap.finish().catch((e) => { +// reporter.error(e) +// }) +// } diff --git a/gatsby-theme-oi-wiki/gatsby-ssr.js b/gatsby-theme-oi-wiki/gatsby-ssr.js index d4606900..ecf5fff9 100644 --- a/gatsby-theme-oi-wiki/gatsby-ssr.js +++ b/gatsby-theme-oi-wiki/gatsby-ssr.js @@ -1,46 +1,8 @@ -const React = require('react') -const script = require('./static/script') +import WrapRootElement from './src/gatsby/WrapRootElement' +import onRenderBody from './src/gatsby/onRenderBody' +import onPreRenderHTML from './src/gatsby/onPreRenderHTML' +import { enableStaticRendering } from 'mobx-react-lite' -const darkModeSwitch = `(${script.default.toString()})()` +enableStaticRendering(true) -const HtmlAttributes = { - lang: 'zh-cmn-Hans', -} - -const HeadComponents = [ - , - , - , - , -