diff --git a/.devcontainer/devcontainer.json b/.devcontainer/devcontainer.json index b034619be6124d..4c97e1a30d85b5 100644 --- a/.devcontainer/devcontainer.json +++ b/.devcontainer/devcontainer.json @@ -18,6 +18,7 @@ "deepscan.vscode-deepscan", "rangav.vscode-thunder-client", "SonarSource.sonarlint-vscode", + "unifiedjs.vscode-mdx", "ZihanLi.at-helper" ] } diff --git a/.github/dependabot.yml b/.github/dependabot.yml index 1cf69281883d3d..af18fe5ac50b63 100644 --- a/.github/dependabot.yml +++ b/.github/dependabot.yml @@ -45,22 +45,6 @@ updates: open-pull-requests-limit: 10 labels: - dependencies - ignore: - # ESM only packages - - dependency-name: remark - versions: ['>=14.0.0'] - - dependency-name: remark-frontmatter - versions: ['>=4.0.0'] - - dependency-name: remark-gfm - versions: ['>=2.0.0'] - - dependency-name: remark-parse - versions: ['>=10.0.0'] - - dependency-name: remark-preset-prettier - versions: ['>=1.0.0'] - - dependency-name: remark-stringify - versions: ['>=10.0.0'] - - dependency-name: string-width - versions: ['>=5.0.0'] groups: docusaurus: patterns: diff --git a/.gitpod.yml b/.gitpod.yml index 71551a1e2c53bf..039f632e3ccf25 100644 --- a/.gitpod.yml +++ b/.gitpod.yml @@ -34,4 +34,5 @@ vscode: - deepscan.vscode-deepscan - rangav.vscode-thunder-client - sonarsource.sonarlint-vscode + - unifiedjs.vscode-mdx # - ZihanLi.at-helper not available on Open VSX diff --git a/package.json b/package.json index 2e87198e7e61d1..c1baab7aae6cf7 100644 --- a/package.json +++ b/package.json @@ -24,7 +24,7 @@ "build:maintainer": "node scripts/workflow/build-maintainer.js", "build:radar": "node scripts/workflow/build-radar.js", "dev": "cross-env NODE_ENV=dev nodemon --inspect lib/index.js", - "format": "eslint --cache --fix \"**/*.{js,yml}\" && node website/docs/.format/format.js && prettier \"**/*.{js,json}\" --write", + "format": "eslint --cache --fix \"**/*.{js,yml}\" && node website/docs/.format/format.mjs && prettier \"**/*.{js,json}\" --write", "format:check": "eslint --cache \"**/*.{js,yml}\" && prettier \"**/*.{js,json}\" --check", "format:staged": "lint-staged", "jest": "cross-env NODE_ENV=test jest --runInBand --forceExit --detectOpenHandles", @@ -41,7 +41,7 @@ "eslint --cache --fix", "prettier --ignore-unknown --ignore-path ./.gitignore --write" ], - "*.md": "node website/docs/.format/format.js --staged", + "*.md": "node website/docs/.format/format.mjs --staged", "*.yml": "eslint --cache --fix" }, "nodemonConfig": { diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 0105394473597f..5414d35b067176 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -1636,8 +1636,8 @@ packages: '@types/mdurl': 1.0.2 dev: true - /@types/mdast@3.0.12: - resolution: {integrity: sha512-DT+iNIRNX884cx0/Q1ja7NyUPpZuv0KPyL5rGNxm1WC1OtHstl7n4Jb7nk+xacNShQMbczJjt8uFzznpp6kYBg==} + /@types/mdast@3.0.14: + resolution: {integrity: sha512-gVZ04PGgw1qLZKsnWnyFv4ORnaJ+DXLdHTVSFbU8yX6xZ34Bjg4Q32yPkmveUP1yItXReKfB0Aknlh/3zxTKAw==} dependencies: '@types/unist': 2.0.7 dev: true @@ -5596,7 +5596,7 @@ packages: /mdast-util-from-markdown@0.8.5: resolution: {integrity: sha512-2hkTXtYYnr+NubD/g6KGBS/0mFmBcifAsI0yIWRiRo0PjVs6SSOSOdtzbp6kSGnShDN6G5aWZpKQ2lWRy27mWQ==} dependencies: - '@types/mdast': 3.0.12 + '@types/mdast': 3.0.14 mdast-util-to-string: 2.0.0 micromark: 2.11.4 parse-entities: 2.0.0 diff --git a/website/babel.config.js b/website/babel.config.cjs similarity index 100% rename from website/babel.config.js rename to website/babel.config.cjs diff --git a/website/docs/.format/chineseFormat.js b/website/docs/.format/chineseFormat.js deleted file mode 100644 index c054b5b3bb2bae..00000000000000 --- a/website/docs/.format/chineseFormat.js +++ /dev/null @@ -1,30 +0,0 @@ -const file = require('./file'); -const width = require('string-width'); -const remark = require('remark'); -const pangu = require('remark-pangu'); -const frontmatter = require('remark-frontmatter'); -const stringify = require('remark-stringify'); -const gfm = require('remark-gfm'); -const prettier = require('remark-preset-prettier'); - -module.exports = { - rules: (list) => list.filter((e) => e.lang === file.LANG_CN), - handler: async (doc) => { - let result = await remark() - .use(frontmatter) - .use(pangu, { - inlineCode: false, - link: false, - }) - .use(stringify, { - bullet: '-', - ruleSpaces: true, - }) - .use(prettier) - .use(gfm, { - stringLength: width, - }) - .process(doc); - return typeof result === 'string' ? result : typeof result.contents === 'string' ? result.contents : result.result; - }, -}; diff --git a/website/docs/.format/chineseFormat.mjs b/website/docs/.format/chineseFormat.mjs new file mode 100644 index 00000000000000..6a0db68b50e762 --- /dev/null +++ b/website/docs/.format/chineseFormat.mjs @@ -0,0 +1,34 @@ +import file from './file.mjs'; +import width from 'string-width'; +import { remark } from 'remark'; +import pangu from 'remark-pangu'; +import frontmatter from 'remark-frontmatter'; +import remarkDirective from 'remark-directive'; +import stringify from 'remark-stringify'; +import gfm from 'remark-gfm'; +import prettier from 'remark-preset-prettier'; +import remarkMdx from 'remark-mdx'; + +export default { + rules: (list) => list.filter((e) => e.lang === file.LANG_EN), + handler: async (doc) => { + const result = await remark() + .use(remarkMdx) + .use(frontmatter) + .use(remarkDirective) + .use(pangu, { + inlineCode: false, + link: false, + }) + .use(stringify, { + bullet: '-', + ruleSpaces: true, + }) + .use(prettier) + .use(gfm, { + stringLength: width, + }) + .process(doc); + return String(result); + }, +}; diff --git a/website/docs/.format/file.js b/website/docs/.format/file.js deleted file mode 100644 index 8e5516561b7d6b..00000000000000 --- a/website/docs/.format/file.js +++ /dev/null @@ -1,11 +0,0 @@ -const fs = require('fs'); - -module.exports = { - ROUTE_TYPE: 'route', - GUIDE_TYPE: 'guide', - NAV_TYPE: 'nav', - LANG_CN: 'zh-CN', - LANG_EN: 'en-US', - readFile: async (filePath) => fs.promises.readFile(filePath, { encoding: 'utf8' }), - writeFile: async (filePath, data) => fs.promises.writeFile(filePath, data, { encoding: 'utf8' }), -}; diff --git a/website/docs/.format/file.mjs b/website/docs/.format/file.mjs new file mode 100644 index 00000000000000..f271d9bed4620e --- /dev/null +++ b/website/docs/.format/file.mjs @@ -0,0 +1,11 @@ +import fs from 'fs/promises'; + +export default { + ROUTE_TYPE: 'route', + GUIDE_TYPE: 'guide', + NAV_TYPE: 'nav', + LANG_CN: 'zh-CN', + LANG_EN: 'en-US', + readFile: async (filePath) => await fs.readFile(filePath, { encoding: 'utf8' }), + writeFile: async (filePath, data) => await fs.writeFile(filePath, data, { encoding: 'utf8' }), +}; diff --git a/website/docs/.format/format.js b/website/docs/.format/format.mjs similarity index 86% rename from website/docs/.format/format.js rename to website/docs/.format/format.mjs index dec724d7bf8278..f171877d0201ed 100644 --- a/website/docs/.format/format.js +++ b/website/docs/.format/format.mjs @@ -1,11 +1,11 @@ -const file = require('./file'); -const sgf = require('staged-git-files'); -const path = require('path'); -const sortByHeading = require('./sortByHeading'); -const slugId = require('./slugId'); -// const chineseFormat = require('./chineseFormat'); -const util = require('util'); -const exec = util.promisify(require('child_process').exec); +import file from './file.mjs'; +import sgf from 'staged-git-files'; +import sortByHeading from './sortByHeading.mjs'; +import slugId from './slugId.mjs'; +// import chineseFormat from './chineseFormat.mjs'; +import { exec } from 'child_process'; +import { fileURLToPath } from 'url'; +import sidebars from '../../sidebars.mjs'; /** * Processors are objects contains two methods: @@ -55,10 +55,9 @@ const processors = [sortByHeading, slugId]; } */ const buildFileList = async () => { - const config = require(`../../sidebars.js`); - const fileList = config.guideSidebar[2].items.map(({ id }) => ({ + const fileList = sidebars.guideSidebar[2].items.map(({ id }) => ({ type: file.ROUTE_TYPE, - path: path.resolve(__dirname, '..', `./${id}.md`), + path: fileURLToPath(new URL(`../${id}.mdx`, import.meta.url)), lang: file.LANG_EN, })); // let fileList = []; @@ -88,7 +87,7 @@ const buildStagedList = async () => { // stagedFileList.push(e.filename); // } // }); - const stagedFileList = stagedFiles.filter((e) => e.filename.endsWith('.md')).map((e) => e.filename); + const stagedFileList = stagedFiles.filter((e) => e.filename.endsWith('.md') || e.filename.endsWith('.mdx')).map((e) => e.filename); const fullFileList = await buildFileList(); // const result = []; // stagedFileList.forEach((e) => { @@ -103,7 +102,7 @@ const buildStagedList = async () => { }; /** Entry - * Usage: node format.js --full/--staged + * Usage: node format.mjs --full/--staged */ (async () => { // Mode diff --git a/website/docs/.format/md/hierarchySlug.js b/website/docs/.format/md/hierarchySlug.js deleted file mode 100644 index fdbbd9f2a6e084..00000000000000 --- a/website/docs/.format/md/hierarchySlug.js +++ /dev/null @@ -1,126 +0,0 @@ -// A fork of https://github.com/valeriangalliat/markdown-it-anchor -const slugify = (s) => encodeURIComponent(String(s).trim().toLowerCase().replace(/\s+/g, '-')); - -const position = { - false: 'push', - true: 'unshift', -}; - -const hasProp = Object.prototype.hasOwnProperty; - -const permalinkHref = (slug) => `#${slug}`; -const permalinkAttrs = (slug) => ({}); - -const renderPermalink = (slug, opts, state, idx) => { - const space = () => Object.assign(new state.Token('text', '', 0), { content: ' ' }); - - const linkTokens = [ - Object.assign(new state.Token('link_open', 'a', 1), { - attrs: [['class', opts.permalinkClass], ['href', opts.permalinkHref(slug, state)], ...Object.entries(opts.permalinkAttrs(slug, state))], - }), - Object.assign(new state.Token('html_block', '', 0), { - content: opts.permalinkSymbol, - }), - new state.Token('link_close', 'a', -1), - ]; - - // `push` or `unshift` according to position option. - // Space is at the opposite side. - if (opts.permalinkSpace) { - linkTokens[position[!opts.permalinkBefore]](space()); - } - state.tokens[idx + 1].children[position[opts.permalinkBefore]](...linkTokens); -}; - -const uniqueSlug = (slug, slugs, failOnNonUnique) => { - let uniq = slug; - let i = 2; - if (failOnNonUnique && hasProp.call(slugs, uniq)) { - throw Error(`User defined id attribute '${slug}' is NOT unique. Please fix it in your markdown to continue.`); - } else { - while (hasProp.call(slugs, uniq)) uniq = `${slug}-${i++}`; - } - slugs[uniq] = true; - return uniq; -}; - -const isLevelSelectedNumber = (selection) => (level) => level >= selection; -const isLevelSelectedArray = (selection) => (level) => selection.includes(level); - -const getTitle = (children) => children.filter((token) => token.type === 'text' || token.type === 'code_inline').reduce((acc, t) => acc + t.content, ''); - -const anchor = (md, opts) => { - opts = Object.assign({}, anchor.defaults, opts); - - md.core.ruler.push('anchor', (state) => { - const slugs = {}; - const tokens = state.tokens; - - const isLevelSelected = Array.isArray(opts.level) ? isLevelSelectedArray(opts.level) : isLevelSelectedNumber(opts.level); - - for (let i = 0; i < tokens.length; i++) { - const currentLevel = tokens[i].markup.length; - if (tokens[i].type === 'heading_open' && isLevelSelected(currentLevel)) { - const titleIndex = i + 1; - const currentTitle = getTitle(tokens[titleIndex].children); - let prevTitle = tokens[i].attrGet('id'); - let constructedTitle = prevTitle; - - // Skip if user has defined a id - if (typeof prevTitle !== 'string') { - if (prevTitle === null) { - prevTitle = []; - } - - let recurIndex = i + 2; - while (recurIndex < tokens.length && (tokens[recurIndex].type !== 'heading_open' || tokens[recurIndex].markup.length > currentLevel)) { - if (tokens[recurIndex].type === 'heading_open') { - let recurPrevTitle = tokens[recurIndex].attrGet('id'); - if (typeof recurPrevTitle !== 'string') { - if (recurPrevTitle === null) { - recurPrevTitle = []; - } - recurPrevTitle = [...recurPrevTitle, currentTitle]; - tokens[recurIndex].attrSet('id', recurPrevTitle); - } - } - recurIndex += 1; - } - - prevTitle.push(currentTitle); - //console.log(prevTitle); - - // @TODO maybe use previous slugified title so we don't need to calculate that much? - constructedTitle = prevTitle.join('-'); - //console.log(constructedTitle); - } - - const slug = uniqueSlug(opts.slugify(constructedTitle), slugs, true); - tokens[i].attrSet('id', slug); - - if (opts.permalink) { - opts.renderPermalink(slug, opts, state, i); - } - - if (opts.callback) { - opts.callback(tokens[i], { slug, constructedTitle }); - } - } - } - }); -}; - -anchor.defaults = { - level: 1, - slugify, - permalink: false, - renderPermalink, - permalinkClass: 'header-anchor', - permalinkSpace: true, - permalinkSymbol: '¶', - permalinkBefore: false, - permalinkHref, - permalinkAttrs, -}; - -module.exports = anchor; diff --git a/website/docs/.format/removeHeadingId.mjs b/website/docs/.format/removeHeadingId.mjs new file mode 100644 index 00000000000000..8adff5a465fc22 --- /dev/null +++ b/website/docs/.format/removeHeadingId.mjs @@ -0,0 +1,26 @@ +export default { + rules: (list) => list.filter((e) => e), + handler: async (data) => { + const content = data.split('\n'); + let lastH2 = ''; + let lastH3 = ''; + + for (let i = 0; i < content.length; i++) { + if (content[i].startsWith('## ')) { + lastH2 = content[i].match(`## (([^{])*)`)?.[1].trim(); + + content[i] = `## ${lastH2}`; + } else if (content[i].startsWith('### ')) { + lastH3 = content[i].match(`### (([^{])*)`)?.[1].trim(); + + content[i] = `### ${lastH3}`; + } else if (content[i].startsWith('#### ')) { + const title = content[i].match(`#### (([^{])*)`)?.[1].trim(); + + content[i] = `#### ${title}`; + } + } + + return Promise.resolve(content.join('\n')); + }, +}; diff --git a/website/docs/.format/slugId.js b/website/docs/.format/slugId.mjs similarity index 95% rename from website/docs/.format/slugId.js rename to website/docs/.format/slugId.mjs index 482affb5f10acc..7ab74b8e941652 100644 --- a/website/docs/.format/slugId.js +++ b/website/docs/.format/slugId.mjs @@ -1,4 +1,4 @@ -const { pinyin } = require('pinyin-pro'); +import { pinyin } from 'pinyin-pro'; const slugify = (s) => { s = s?.replace(/[#&'()+,/:[\]|’“”、「」・().:|]/g, '-'); @@ -18,7 +18,7 @@ const slugify = (s) => { ); }; -module.exports = { +export default { rules: (list) => list.filter((e) => e), handler: async (data) => { const content = data.split('\n'); diff --git a/website/docs/.format/sortByHeading.js b/website/docs/.format/sortByHeading.mjs similarity index 96% rename from website/docs/.format/sortByHeading.js rename to website/docs/.format/sortByHeading.mjs index 49ace2df8f3fe9..18fa95be6c2c3e 100644 --- a/website/docs/.format/sortByHeading.js +++ b/website/docs/.format/sortByHeading.mjs @@ -1,8 +1,8 @@ -const file = require('./file'); +import file from './file.mjs'; const pinyinCompare = new Intl.Collator('zh-Hans-CN-u-co-pinyin').compare; const isASCII = (str) => /^[\x00-\x7F]*$/.test(str); -module.exports = { +export default { rules: (list) => list.filter((e) => e.type === file.ROUTE_TYPE), handler: async (data) => { const content = data.split('\n'); diff --git a/website/docs/joinus/new-rss/add-docs.md b/website/docs/joinus/new-rss/add-docs.md index d6877cbcd8e092..0232ae5c7f05f7 100644 --- a/website/docs/joinus/new-rss/add-docs.md +++ b/website/docs/joinus/new-rss/add-docs.md @@ -4,7 +4,7 @@ sidebar_position: 4 # Add documentation -Now that we have completed the code, it's time to add the documentation for your route. Open the appropriate file in the [document (/website/docs)](https://github.com/DIYgod/RSSHub/blob/master/website/docs), which in this example is `website/docs/routes/programming.md`. +Now that we have completed the code, it's time to add the documentation for your route. Open the appropriate file in the [document (/website/docs)](https://github.com/DIYgod/RSSHub/blob/master/website/docs), which in this example is `website/docs/routes/programming.mdx`. In order to preview the documentation in real-time, you need to install the dependencies for the documentation. Run the following command in the **`website` directory**: @@ -64,7 +64,7 @@ You cannot switch to other languages in development mode. This is a [technical l ::: -The documentation is written in MDX and rendered with [Docusaurus v2](https://docusaurus.io/docs). +The documentation is written in MDX v3 and rendered with [Docusaurus v3](https://docusaurus.io/docs). To add documentation to your route, use the `Route` React component. It works like HTML tag. The following are the most commonly used component properties: diff --git a/website/docs/routes/anime.md b/website/docs/routes/anime.mdx similarity index 100% rename from website/docs/routes/anime.md rename to website/docs/routes/anime.mdx diff --git a/website/docs/routes/bbs.md b/website/docs/routes/bbs.mdx similarity index 100% rename from website/docs/routes/bbs.md rename to website/docs/routes/bbs.mdx diff --git a/website/docs/routes/blog.md b/website/docs/routes/blog.mdx similarity index 100% rename from website/docs/routes/blog.md rename to website/docs/routes/blog.mdx diff --git a/website/docs/routes/design.md b/website/docs/routes/design.mdx similarity index 100% rename from website/docs/routes/design.md rename to website/docs/routes/design.mdx diff --git a/website/docs/routes/finance.md b/website/docs/routes/finance.mdx similarity index 100% rename from website/docs/routes/finance.md rename to website/docs/routes/finance.mdx diff --git a/website/docs/routes/forecast.md b/website/docs/routes/forecast.mdx similarity index 100% rename from website/docs/routes/forecast.md rename to website/docs/routes/forecast.mdx diff --git a/website/docs/routes/game.md b/website/docs/routes/game.mdx similarity index 100% rename from website/docs/routes/game.md rename to website/docs/routes/game.mdx diff --git a/website/docs/routes/government.md b/website/docs/routes/government.mdx similarity index 100% rename from website/docs/routes/government.md rename to website/docs/routes/government.mdx diff --git a/website/docs/routes/journal.md b/website/docs/routes/journal.mdx similarity index 100% rename from website/docs/routes/journal.md rename to website/docs/routes/journal.mdx diff --git a/website/docs/routes/live.md b/website/docs/routes/live.mdx similarity index 100% rename from website/docs/routes/live.md rename to website/docs/routes/live.mdx diff --git a/website/docs/routes/multimedia.md b/website/docs/routes/multimedia.mdx similarity index 100% rename from website/docs/routes/multimedia.md rename to website/docs/routes/multimedia.mdx diff --git a/website/docs/routes/new-media.md b/website/docs/routes/new-media.mdx similarity index 100% rename from website/docs/routes/new-media.md rename to website/docs/routes/new-media.mdx diff --git a/website/docs/routes/other.md b/website/docs/routes/other.mdx similarity index 100% rename from website/docs/routes/other.md rename to website/docs/routes/other.mdx diff --git a/website/docs/routes/picture.md b/website/docs/routes/picture.mdx similarity index 100% rename from website/docs/routes/picture.md rename to website/docs/routes/picture.mdx diff --git a/website/docs/routes/program-update.md b/website/docs/routes/program-update.mdx similarity index 100% rename from website/docs/routes/program-update.md rename to website/docs/routes/program-update.mdx diff --git a/website/docs/routes/programming.md b/website/docs/routes/programming.mdx similarity index 100% rename from website/docs/routes/programming.md rename to website/docs/routes/programming.mdx diff --git a/website/docs/routes/reading.md b/website/docs/routes/reading.mdx similarity index 100% rename from website/docs/routes/reading.md rename to website/docs/routes/reading.mdx diff --git a/website/docs/routes/shopping.md b/website/docs/routes/shopping.mdx similarity index 100% rename from website/docs/routes/shopping.md rename to website/docs/routes/shopping.mdx diff --git a/website/docs/routes/social-media.md b/website/docs/routes/social-media.mdx similarity index 100% rename from website/docs/routes/social-media.md rename to website/docs/routes/social-media.mdx diff --git a/website/docs/routes/study.md b/website/docs/routes/study.mdx similarity index 100% rename from website/docs/routes/study.md rename to website/docs/routes/study.mdx diff --git a/website/docs/routes/traditional-media.md b/website/docs/routes/traditional-media.mdx similarity index 100% rename from website/docs/routes/traditional-media.md rename to website/docs/routes/traditional-media.mdx diff --git a/website/docs/routes/travel.md b/website/docs/routes/travel.mdx similarity index 100% rename from website/docs/routes/travel.md rename to website/docs/routes/travel.mdx diff --git a/website/docs/routes/university.md b/website/docs/routes/university.mdx similarity index 100% rename from website/docs/routes/university.md rename to website/docs/routes/university.mdx diff --git a/website/docusaurus.config.js b/website/docusaurus.config.js deleted file mode 100644 index 309ece3d3446b7..00000000000000 --- a/website/docusaurus.config.js +++ /dev/null @@ -1,243 +0,0 @@ -// @ts-check -// Note: type annotations allow type checking and IDEs autocompletion - -const { themes } = require('prism-react-renderer'); -const lightCodeTheme = themes.github; -const darkCodeTheme = themes.dracula; - -/** @type {import('@docusaurus/types').Config} */ -const config = { - title: 'RSSHub', - tagline: '🍰 Everything is RSSible', - favicon: 'img/logo.png', - - // Set the production url of your site here - url: 'https://docs.rsshub.app', - // Set the // pathname under which your site is served - // For GitHub pages deployment, it is often '//' - baseUrl: '/', - - // GitHub pages deployment config. - // If you aren't using GitHub pages, you don't need these. - organizationName: 'DIYgod', // Usually your GitHub org/user name. - projectName: 'RSSHub', // Usually your repo name. - - onBrokenLinks: 'throw', - onBrokenMarkdownLinks: 'warn', - - // Even if you don't use internalization, you can use this field to set useful - // metadata like html lang. For example, if your site is Chinese, you may want - // to replace "en" with "zh-Hans". - i18n: { - defaultLocale: 'en', - locales: ['zh', 'en'], - }, - - plugins: [ - [ - '@dipakparmar/docusaurus-plugin-umami', - /** @type {import('@dipakparmar/docusaurus-plugin-umami').Options} */ - ({ - websiteID: 'be1761be-7547-49d5-91b8-5c97c8f7cec7', // Required - analyticsDomain: 'umami.diygod.dev', // Required - }), - ], - [ - '@docusaurus/plugin-client-redirects', - /** @type {import('@docusaurus/plugin-client-redirects').Options} */ - ({ - fromExtensions: ['html'], - redirects: [ - { from: '/joinus', to: '/joinus/quick-start' }, - { from: '/joinus/script-standard', to: '/joinus/advanced/script-standard' }, - { from: '/joinus/pub-date', to: '/joinus/advanced/pub-date' }, - { from: '/joinus/use-cache', to: '/joinus/advanced/use-cache' }, - ...Object.values(require('./sidebars').guideSidebar) - .find((s) => s.label === 'Routes') - .items.map(({ id }) => ({ - from: [`/${id.split('/')[1]}`, `/en/${id.split('/')[1]}`], - to: `/routes/${id.split('/')[1]}`, - })), - ], - }), - ], - [ - '@docusaurus/plugin-pwa', - /** @type {import('@docusaurus/plugin-pwa').Options} */ - ({ - pwaHead: [ - { tagName: 'link', rel: 'icon', href: '/img/logo.png' }, - { tagName: 'link', rel: 'manifest', href: '/manifest.json' }, - { tagName: 'meta', name: 'theme-color', content: '#ffffff' }, - { tagName: 'meta', name: 'apple-mobile-web-app-capable', content: 'yes' }, - { tagName: 'meta', name: 'apple-mobile-web-app-status-bar-style', content: 'black-translucent' }, - { tagName: 'link', rel: 'apple-touch-icon', href: '/img/apple-touch-icon.png' }, - { tagName: 'link', rel: 'mask-icon', href: '/img/safari-pinned-tab.svg', color: '#F5712C' }, - { tagName: 'meta', name: 'msapplication-TileImage', content: '/img/logo.png' }, - { tagName: 'meta', name: 'msapplication-TileColor', content: '#ffffff' }, - ], - }), - ], - ], - - presets: [ - [ - '@docusaurus/preset-classic', - /** @type {import('@docusaurus/preset-classic').Options} */ - ({ - docs: { - // path: '../docs', - routeBasePath: '/', - sidebarPath: require.resolve('./sidebars.js'), - // Please change this to your repo. - // Remove this to remove the "edit this page" links. - editUrl: 'https://github.com/DIYgod/RSSHub/blob/master/website/', - showLastUpdateAuthor: true, - showLastUpdateTime: true, - }, - blog: false, - theme: { - customCss: require.resolve('./src/css/custom.css'), - }, - gtag: { - trackingID: 'G-322PG1X4EL', - }, - }), - ], - ], - - customFields: { - 'meilisearch-docsearch': { - host: 'https://meilisearch.rsshub.app', - apiKey: '375c36cd9573a2c1d1e536214158c37120fdd0ba6cd8829f7a848e940cc22245', - indexUid: 'rsshub', - container: '#docsearch', - }, - }, - - themeConfig: - /** @type {import('@docusaurus/preset-classic').ThemeConfig} */ - ({ - // Replace with your project's social card - image: 'img/logo.png', - metadata: [{ name: 'description', content: '🍰 Everything is RSSible' }], - navbar: { - title: 'RSSHub', - logo: { - alt: 'RSSHub', - src: 'img/logo.png', - }, - items: [ - { - to: '/', - position: 'left', - label: 'Guide', - activeBaseRegex: '^/(usage|faq|parameter|api)?$', - }, - { - to: '/routes', - position: 'left', - label: 'Routes', - activeBaseRegex: '^/routes/', - }, - { - to: '/joinus/quick-start', - label: 'Join Us', - position: 'left', - activeBaseRegex: '^/joinus/', - }, - { - to: '/install', - position: 'left', - label: 'Deploy', - }, - { - to: '/support', - position: 'left', - label: 'Support RSSHub', - }, - { - type: 'search', - position: 'right', - }, - { - type: 'localeDropdown', - position: 'right', - }, - { - href: 'https://github.com/DIYgod/RSSHub', - label: 'GitHub', - position: 'right', - }, - ], - }, - footer: { - style: 'dark', - links: [ - { - title: 'Docs', - items: [ - { - label: 'Guide', - to: '/', - }, - { - to: '/joinus/quick-start', - label: 'Join Us', - }, - { - to: '/install', - label: 'Deploy', - }, - { - to: '/support', - label: 'Support RSSHub', - }, - ], - }, - { - title: 'Community', - items: [ - { - label: 'GitHub', - href: 'https://github.com/DIYgod/RSSHub', - }, - { - label: 'Telegram Group', - href: 'https://t.me/rsshub', - }, - { - label: 'Telegram Channel', - href: 'https://t.me/awesomeRSSHub', - }, - ], - }, - { - title: 'More', - items: [ - { - label: 'About DIYgod', - to: 'https://diygod.cc', - }, - { - label: 'RSSHub Radar - Discover and subscribe to RSS quickly', - href: 'https://github.com/DIYgod/RSSHub-Radar', - }, - { - label: 'xLog - Open source creation community written on the blockchain', - href: 'https://xlog.app', - }, - ], - }, - ], - copyright: `Copyright © ${new Date().getFullYear()} RSSHub.`, - }, - prism: { - theme: lightCodeTheme, - darkTheme: darkCodeTheme, - additionalLanguages: ['bash'], - }, - }), -}; - -module.exports = config; diff --git a/website/docusaurus.config.ts b/website/docusaurus.config.ts new file mode 100644 index 00000000000000..6adf1e1f10e167 --- /dev/null +++ b/website/docusaurus.config.ts @@ -0,0 +1,247 @@ +import { themes as prismThemes } from 'prism-react-renderer'; +import sidebars from './sidebars.mjs'; + +import type { Config } from '@docusaurus/types'; +import type { Options as PresetOptions, ThemeConfig } from '@docusaurus/preset-classic'; +import type { Options as ClientRedirectsOptions } from '@docusaurus/plugin-client-redirects'; +import type { Options as UmaimiOptions } from '@dipakparmar/docusaurus-plugin-umami'; +import type { DocSearchOptions } from 'meilisearch-docsearch'; + +const config: Config = { + title: 'RSSHub', + tagline: '🍰 Everything is RSSible', + favicon: 'img/logo.png', + + // Set the production url of your site here + url: 'https://docs.rsshub.app', + // Set the // pathname under which your site is served + // For GitHub pages deployment, it is often '//' + baseUrl: '/', + + // GitHub pages deployment config. + // If you aren't using GitHub pages, you don't need these. + organizationName: 'DIYgod', // Usually your GitHub org/user name. + projectName: 'RSSHub', // Usually your repo name. + + onBrokenLinks: 'throw', + onBrokenMarkdownLinks: 'warn', + + // Even if you don't use internalization, you can use this field to set useful + // metadata like html lang. For example, if your site is Chinese, you may want + // to replace "en" with "zh-Hans". + i18n: { + defaultLocale: 'en', + locales: ['zh', 'en'], + }, + + markdown: { + format: 'mdx', + mdx1Compat: { + // comments: false, + }, + }, + + plugins: [ + [ + '@dipakparmar/docusaurus-plugin-umami', + { + websiteID: 'be1761be-7547-49d5-91b8-5c97c8f7cec7', // Required + analyticsDomain: 'umami.diygod.dev', // Required + } satisfies UmaimiOptions, + ], + [ + '@docusaurus/plugin-client-redirects', + // This plugin only works in production builds. + { + fromExtensions: ['html'], + redirects: [ + { from: '/joinus', to: '/joinus/quick-start' }, + { from: '/joinus/script-standard', to: '/joinus/advanced/script-standard' }, + { from: '/joinus/pub-date', to: '/joinus/advanced/pub-date' }, + { from: '/joinus/use-cache', to: '/joinus/advanced/use-cache' }, + ...Object.values(sidebars.guideSidebar) + .find((s) => s.label === 'Routes') + .items.map(({ id }) => ({ + from: [`/${id.split('/')[1]}`, `/en/${id.split('/')[1]}`], + to: `/routes/${id.split('/')[1]}`, + })), + ], + } satisfies ClientRedirectsOptions, + ], + [ + '@docusaurus/plugin-pwa', + { + pwaHead: [ + { tagName: 'link', rel: 'icon', href: '/img/logo.png' }, + { tagName: 'link', rel: 'manifest', href: '/manifest.json' }, + { tagName: 'meta', name: 'theme-color', content: '#ffffff' }, + { tagName: 'meta', name: 'apple-mobile-web-app-capable', content: 'yes' }, + { tagName: 'meta', name: 'apple-mobile-web-app-status-bar-style', content: 'black-translucent' }, + { tagName: 'link', rel: 'apple-touch-icon', href: '/img/apple-touch-icon.png' }, + { tagName: 'link', rel: 'mask-icon', href: '/img/safari-pinned-tab.svg', color: '#F5712C' }, + { tagName: 'meta', name: 'msapplication-TileImage', content: '/img/logo.png' }, + { tagName: 'meta', name: 'msapplication-TileColor', content: '#ffffff' }, + ], + }, + ], + ], + + presets: [ + [ + 'classic', + { + docs: { + // path: '../docs', + routeBasePath: '/', + sidebarPath: './sidebars.mjs', + // Please change this to your repo. + // Remove this to remove the "edit this page" links. + editUrl: 'https://github.com/DIYgod/RSSHub/blob/master/website/', + showLastUpdateAuthor: true, + showLastUpdateTime: true, + }, + blog: false, + theme: { + customCss: './src/css/custom.css', + }, + gtag: { + trackingID: 'G-322PG1X4EL', + }, + } satisfies PresetOptions, + ], + ], + + customFields: { + 'meilisearch-docsearch': { + host: 'https://meilisearch.rsshub.app', + apiKey: '375c36cd9573a2c1d1e536214158c37120fdd0ba6cd8829f7a848e940cc22245', + indexUid: 'rsshub', + container: '#docsearch', + } satisfies DocSearchOptions, + }, + + themeConfig: { + // Replace with your project's social card + image: 'img/logo.png', + metadata: [{ name: 'description', content: '🍰 Everything is RSSible' }], + navbar: { + title: 'RSSHub', + logo: { + alt: 'RSSHub', + src: 'img/logo.png', + }, + items: [ + { + to: '/', + position: 'left', + label: 'Guide', + activeBaseRegex: '^/(usage|faq|parameter|api)?$', + }, + { + to: '/routes', + position: 'left', + label: 'Routes', + activeBaseRegex: '^/routes/', + }, + { + to: '/joinus/quick-start', + label: 'Join Us', + position: 'left', + activeBaseRegex: '^/joinus/', + }, + { + to: '/install', + position: 'left', + label: 'Deploy', + }, + { + to: '/support', + position: 'left', + label: 'Support RSSHub', + }, + { + type: 'search', + position: 'right', + }, + { + type: 'localeDropdown', + position: 'right', + }, + { + href: 'https://github.com/DIYgod/RSSHub', + position: 'right', + className: 'header-github-link', + 'aria-label': 'GitHub repository', + }, + ], + }, + footer: { + style: 'dark', + links: [ + { + title: 'Docs', + items: [ + { + label: 'Guide', + to: '/', + }, + { + to: '/joinus/quick-start', + label: 'Join Us', + }, + { + to: '/install', + label: 'Deploy', + }, + { + to: '/support', + label: 'Support RSSHub', + }, + ], + }, + { + title: 'Community', + items: [ + { + label: 'GitHub', + href: 'https://github.com/DIYgod/RSSHub', + }, + { + label: 'Telegram Group', + href: 'https://t.me/rsshub', + }, + { + label: 'Telegram Channel', + href: 'https://t.me/awesomeRSSHub', + }, + ], + }, + { + title: 'More', + items: [ + { + label: 'About DIYgod', + to: 'https://diygod.cc', + }, + { + label: 'RSSHub Radar - Discover and subscribe to RSS quickly', + href: 'https://github.com/DIYgod/RSSHub-Radar', + }, + { + label: 'xLog - Open source creation community written on the blockchain', + href: 'https://xlog.app', + }, + ], + }, + ], + copyright: `Copyright © ${new Date().getFullYear()} RSSHub.`, + }, + prism: { + theme: prismThemes.github, + darkTheme: prismThemes.dracula, + additionalLanguages: ['bash'], + }, + } satisfies ThemeConfig, +}; + +export default config; diff --git a/website/i18n/zh/docusaurus-plugin-content-docs/current/joinus/new-rss/add-docs.md b/website/i18n/zh/docusaurus-plugin-content-docs/current/joinus/new-rss/add-docs.md index d4cbac3b486a09..6fad306da336ca 100644 --- a/website/i18n/zh/docusaurus-plugin-content-docs/current/joinus/new-rss/add-docs.md +++ b/website/i18n/zh/docusaurus-plugin-content-docs/current/joinus/new-rss/add-docs.md @@ -4,7 +4,7 @@ sidebar_position: 4 # 添加文档 -现在我们完成了代码,是时候为您的路由添加文档了。在 [文档 (/website/docs)](https://github.com/DIYgod/RSSHub/blob/master/website/docs) 中打开相应的文件,本例中是 `website/docs/routes/programming.md`。 +现在我们完成了代码,是时候为您的路由添加文档了。在 [文档 (/website/docs)](https://github.com/DIYgod/RSSHub/blob/master/website/docs) 中打开相应的文件,本例中是 `website/docs/routes/programming.mdx`。 为了实时预览文档,您需要在 **`website` 目录** 下安装文档的依赖项。在终端中输入以下命令: @@ -74,7 +74,7 @@ pnpm run start ::: -文档使用 MDX 编写,并使用 [Docusaurus v2](https://docusaurus.io/docs) 渲染。 +文档使用 MDX v3 编写,并使用 [Docusaurus v3](https://docusaurus.io/docs) 渲染。 要为您的路由添加文档,请使用 `Route` React 组件。它类似于 HTML 标签。以下是最常用的组件属性: diff --git a/website/sidebars.js b/website/sidebars.mjs similarity index 99% rename from website/sidebars.js rename to website/sidebars.mjs index e38ac1f7e79533..eb534498cde559 100644 --- a/website/sidebars.js +++ b/website/sidebars.mjs @@ -196,4 +196,4 @@ const sidebars = { ], }; -module.exports = sidebars; +export default sidebars; diff --git a/website/src/components/Badge.tsx b/website/src/components/Badge.tsx index 871940c70b27fa..da82480e75f75f 100644 --- a/website/src/components/Badge.tsx +++ b/website/src/components/Badge.tsx @@ -1,5 +1,3 @@ -import React from 'react'; - export default function Badge({ type = 'tip', children = '', diff --git a/website/src/components/CarbonAds/index.tsx b/website/src/components/CarbonAds/index.tsx index 1fc7deb45d43cd..592eb2fc17ec3c 100644 --- a/website/src/components/CarbonAds/index.tsx +++ b/website/src/components/CarbonAds/index.tsx @@ -1,46 +1,46 @@ -import React from 'react' -import styles from './styles.module.css' +import React from 'react'; +import styles from './styles.module.css'; function buildScript(src, attrs = {}) { - const script = document.createElement('script') - script.async = true - Object.keys(attrs).forEach((attr) => script.setAttribute(attr, attrs[attr])) - script.src = src + const script = document.createElement('script'); + script.async = true; + Object.keys(attrs).forEach((attr) => script.setAttribute(attr, attrs[attr])); + script.src = src; - return script + return script; } export default function CarbonAds(props) { - const ref = React.useRef() + const ref = React.useRef(); React.useEffect(() => { const script = buildScript('//cdn.carbonads.com/carbon.js?serve=CEAI653E&placement=docsrsshubapp', { type: 'text/javascript', id: '_carbonads_js', - }) - const sidebarWrapper = ref.current + }); + const sidebarWrapper = ref.current; if (!sidebarWrapper) { - return + return; } - sidebarWrapper.classList.remove(styles.roomForCarbon) + sidebarWrapper.classList.remove(styles.roomForCarbon); - const carbonWrapper = document.createElement('div') - carbonWrapper.classList.add(styles.carbonWrapper) + const carbonWrapper = document.createElement('div'); + carbonWrapper.classList.add(styles.carbonWrapper); - carbonWrapper.appendChild(script) + carbonWrapper.appendChild(script); // append at the end - sidebarWrapper.firstChild.insertBefore(carbonWrapper, null) + sidebarWrapper.firstChild.insertBefore(carbonWrapper, null); return () => { - sidebarWrapper.classList.add(styles.roomForCarbon) - carbonWrapper.parentElement.removeChild(carbonWrapper) + sidebarWrapper.classList.add(styles.roomForCarbon); + carbonWrapper.parentElement.removeChild(carbonWrapper); } - }, []) + }, []); // use span to avoid creating containing block so that OriginalDocSidebar keeps being sticky positioned to the existing block ancestor return ( - ) + ); } diff --git a/website/src/components/Route.tsx b/website/src/components/Route.tsx index 2b959241f6c45e..bb7afc5bc7a111 100644 --- a/website/src/components/Route.tsx +++ b/website/src/components/Route.tsx @@ -1,4 +1,3 @@ -import React from 'react'; import MarkdownIt from 'markdown-it'; import Badge from './Badge'; import Translate from '@docusaurus/Translate'; diff --git a/website/src/css/custom.css b/website/src/css/custom.css index 63632388f1ca5b..0d91fa55dbc663 100644 --- a/website/src/css/custom.css +++ b/website/src/css/custom.css @@ -53,3 +53,21 @@ .table-of-contents > li > a { font-weight: 700; } + +.header-github-link:hover { + opacity: 0.6; +} + +.header-github-link::before { + content: ''; + width: 24px; + height: 24px; + display: flex; + background: url("data:image/svg+xml,%3Csvg viewBox='0 0 24 24' xmlns='http://www.w3.org/2000/svg'%3E%3Cpath d='M12 .297c-6.63 0-12 5.373-12 12 0 5.303 3.438 9.8 8.205 11.385.6.113.82-.258.82-.577 0-.285-.01-1.04-.015-2.04-3.338.724-4.042-1.61-4.042-1.61C4.422 18.07 3.633 17.7 3.633 17.7c-1.087-.744.084-.729.084-.729 1.205.084 1.838 1.236 1.838 1.236 1.07 1.835 2.809 1.305 3.495.998.108-.776.417-1.305.76-1.605-2.665-.3-5.466-1.332-5.466-5.93 0-1.31.465-2.38 1.235-3.22-.135-.303-.54-1.523.105-3.176 0 0 1.005-.322 3.3 1.23.96-.267 1.98-.399 3-.405 1.02.006 2.04.138 3 .405 2.28-1.552 3.285-1.23 3.285-1.23.645 1.653.24 2.873.12 3.176.765.84 1.23 1.91 1.23 3.22 0 4.61-2.805 5.625-5.475 5.92.42.36.81 1.096.81 2.22 0 1.606-.015 2.896-.015 3.286 0 .315.21.69.825.57C20.565 22.092 24 17.592 24 12.297c0-6.627-5.373-12-12-12'/%3E%3C/svg%3E") + no-repeat; +} + +[data-theme='dark'] .header-github-link::before { + background: url("data:image/svg+xml,%3Csvg viewBox='0 0 24 24' xmlns='http://www.w3.org/2000/svg'%3E%3Cpath fill='white' d='M12 .297c-6.63 0-12 5.373-12 12 0 5.303 3.438 9.8 8.205 11.385.6.113.82-.258.82-.577 0-.285-.01-1.04-.015-2.04-3.338.724-4.042-1.61-4.042-1.61C4.422 18.07 3.633 17.7 3.633 17.7c-1.087-.744.084-.729.084-.729 1.205.084 1.838 1.236 1.838 1.236 1.07 1.835 2.809 1.305 3.495.998.108-.776.417-1.305.76-1.605-2.665-.3-5.466-1.332-5.466-5.93 0-1.31.465-2.38 1.235-3.22-.135-.303-.54-1.523.105-3.176 0 0 1.005-.322 3.3 1.23.96-.267 1.98-.399 3-.405 1.02.006 2.04.138 3 .405 2.28-1.552 3.285-1.23 3.285-1.23.645 1.653.24 2.873.12 3.176.765.84 1.23 1.91 1.23 3.22 0 4.61-2.805 5.625-5.475 5.92.42.36.81 1.096.81 2.22 0 1.606-.015 2.896-.015 3.286 0 .315.21.69.825.57C20.565 22.092 24 17.592 24 12.297c0-6.627-5.373-12-12-12'/%3E%3C/svg%3E") + no-repeat; +} diff --git a/website/src/theme/DocSidebarItem/index.js b/website/src/theme/DocSidebarItem/index.jsx similarity index 93% rename from website/src/theme/DocSidebarItem/index.js rename to website/src/theme/DocSidebarItem/index.jsx index 72c84e3fdab252..cd8492646df3cf 100644 --- a/website/src/theme/DocSidebarItem/index.js +++ b/website/src/theme/DocSidebarItem/index.jsx @@ -1,4 +1,3 @@ -import React from 'react'; import DocSidebarItem from '@theme-original/DocSidebarItem'; import CarbonAds from '@site/src/components/CarbonAds'; diff --git a/website/src/theme/MDXComponents.js b/website/src/theme/MDXComponents.ts similarity index 100% rename from website/src/theme/MDXComponents.js rename to website/src/theme/MDXComponents.ts diff --git a/website/src/theme/SearchBar/index.jsx b/website/src/theme/SearchBar/index.jsx index db4ca8cb357941..2e478fd8b882ba 100644 --- a/website/src/theme/SearchBar/index.jsx +++ b/website/src/theme/SearchBar/index.jsx @@ -1,8 +1,8 @@ -import React, { useEffect } from 'react'; +import { useEffect } from 'react'; import useDocusaurusContext from '@docusaurus/useDocusaurusContext'; import 'meilisearch-docsearch/css'; -import './variables.css'; +import './style.css'; export default function SearchBarWrapper() { const { siteConfig } = useDocusaurusContext(); @@ -10,7 +10,7 @@ export default function SearchBarWrapper() { const { docsearch } = require('meilisearch-docsearch'); const docSearchOptions = siteConfig.customFields['meilisearch-docsearch']; - return docsearch(docSearchOptions) + return () => docsearch(docSearchOptions) }, []) return ( diff --git a/website/src/theme/SearchBar/variables.css b/website/src/theme/SearchBar/style.css similarity index 100% rename from website/src/theme/SearchBar/variables.css rename to website/src/theme/SearchBar/style.css diff --git a/website/tsconfig.json b/website/tsconfig.json index 314eab8a418b9f..0184702270752e 100644 --- a/website/tsconfig.json +++ b/website/tsconfig.json @@ -2,6 +2,7 @@ // This file is not used in compilation. It is here just for a nice editor experience. "extends": "@docusaurus/tsconfig", "compilerOptions": { - "baseUrl": "." + "baseUrl": ".", + "allowJs": true } }