diff --git a/.github/workflows/check.yml b/.github/workflows/check.yml index b02838e5c6..0021518955 100644 --- a/.github/workflows/check.yml +++ b/.github/workflows/check.yml @@ -11,13 +11,7 @@ on: jobs: check: - strategy: - fail-fast: false - matrix: - os: [ubuntu-latest, windows-latest, macos-latest] - node: ['18', '20'] - - runs-on: ${{ matrix.os }} + runs-on: ubuntu-latest steps: - uses: actions/checkout@v4 @@ -40,6 +34,11 @@ jobs: - name: Linter Test run: pnpm lint + - name: Docs Test + run: | + pnpm --filter @vuepress/ecosystem-docs docs:build + pnpm --filter @vuepress/ecosystem-docs docs:build-webpack + - name: Unit Test run: pnpm test:unit diff --git a/.github/workflows/docs.yml b/.github/workflows/docs.yml new file mode 100644 index 0000000000..823eb47aae --- /dev/null +++ b/.github/workflows/docs.yml @@ -0,0 +1,44 @@ +name: Deploy Ecosystem Docs + +on: + push: + branches: + - main + +jobs: + deploy-github-docs: + name: Deploy ecosystem docs to Github Pages + runs-on: ubuntu-latest + steps: + - name: Checkout + uses: actions/checkout@v4 + with: + fetch-depth: 0 + + - name: Install pnpm + uses: pnpm/action-setup@v2 + + - name: Setup Node.js + uses: actions/setup-node@v4 + with: + node-version: 20 + cache: pnpm + + - name: Install deps + run: pnpm install --frozen-lockfile + + - name: Build Project + run: pnpm build + + - name: Docs build + env: + BASE: /ecosystem/ + NODE_OPTIONS: --max_old_space_size=8192 + run: pnpm --filter @vuepress/ecosystem-docs docs:build + + - name: Deploy docs + uses: JamesIves/github-pages-deploy-action@v4 + with: + branch: gh-pages + folder: docs/.vuepress/dist + single-commit: true diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index b0574a9b72..4c6e450fbe 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -25,3 +25,39 @@ jobs: tag_name: ${{ github.ref }} body: | Please refer to [CHANGELOG.md](https://github.com/${{ github.repository }}/blob/main/CHANGELOG.md) for details. + + deploy-docs: + name: Deploy ecosystem docs to Netlify + runs-on: ubuntu-latest + steps: + - name: Checkout + uses: actions/checkout@v4 + with: + fetch-depth: 0 + + - name: Install pnpm + uses: pnpm/action-setup@v2 + + - name: Setup Node.js + uses: actions/setup-node@v4 + with: + node-version: 20 + cache: pnpm + + - name: Install deps + run: pnpm install --frozen-lockfile + + - name: Build Project + run: pnpm build + + - name: Docs build + env: + NODE_OPTIONS: --max_old_space_size=8192 + run: pnpm --filter @vuepress/ecosystem-docs docs:build + + - name: Deploy docs + uses: JamesIves/github-pages-deploy-action@v4 + with: + branch: netlify + folder: docs/.vuepress/dist + single-commit: true diff --git a/docs/.vuepress/components/NpmBadge.vue b/docs/.vuepress/components/NpmBadge.vue new file mode 100644 index 0000000000..c9ff7be250 --- /dev/null +++ b/docs/.vuepress/components/NpmBadge.vue @@ -0,0 +1,49 @@ + + + + + + + + + diff --git a/docs/.vuepress/config.ts b/docs/.vuepress/config.ts new file mode 100644 index 0000000000..1a7b841c47 --- /dev/null +++ b/docs/.vuepress/config.ts @@ -0,0 +1,131 @@ +import { createRequire } from 'node:module' +import process from 'node:process' +import { viteBundler } from '@vuepress/bundler-vite' +import { webpackBundler } from '@vuepress/bundler-webpack' +// import { docsearchPlugin } from '@vuepress/plugin-docsearch' +import { registerComponentsPlugin } from '@vuepress/plugin-register-components' +import { shikiPlugin } from '@vuepress/plugin-shiki' +import { defineUserConfig } from 'vuepress' +import type { UserConfig } from 'vuepress' +import { getDirname, path } from 'vuepress/utils' +import { head } from './configs/index.js' +import theme from './theme.js' + +const __dirname = getDirname(import.meta.url) +const require = createRequire(import.meta.url) + +const isProd = process.env.NODE_ENV === 'production' + +export default defineUserConfig({ + // set site base to default value + base: (process.env.BASE as `/${string}/` | '/') || '/', + + // extra tags in `
` + head, + + // site-level locales config + locales: { + '/': { + lang: 'en-US', + title: 'VuePress Ecosystem', + description: 'VuePress official themes plugins', + }, + '/zh/': { + lang: 'zh-CN', + title: 'VuePress 生态系统', + description: 'VuePress 官方主题和插件', + }, + }, + + // specify bundler via environment variable + bundler: + process.env.DOCS_BUNDLER === 'webpack' ? webpackBundler() : viteBundler(), + + // configure markdown + markdown: { + importCode: { + handleImportPath: (importPath) => { + // handle @vuepress packages import path + if (importPath.startsWith('@vuepress/')) { + const packageName = importPath.match(/^(@vuepress\/[^/]*)/)![1] + return importPath + .replace( + packageName, + path.dirname(require.resolve(`${packageName}/package.json`)), + ) + .replace('/src/', '/lib/') + .replace(/hotKey\.ts$/, 'hotKey.d.ts') + } + return importPath + }, + }, + }, + + // configure default theme + theme, + + // use plugins + plugins: [ + // docsearchPlugin({ + // appId: '34YFD9IUQ2', + // apiKey: '9a9058b8655746634e01071411c366b8', + // indexName: 'vuepress', + // searchParameters: { + // facetFilters: ['tags:v2'], + // }, + // locales: { + // '/zh/': { + // placeholder: '搜索文档', + // translations: { + // button: { + // buttonText: '搜索文档', + // buttonAriaLabel: '搜索文档', + // }, + // modal: { + // searchBox: { + // resetButtonTitle: '清除查询条件', + // resetButtonAriaLabel: '清除查询条件', + // cancelButtonText: '取消', + // cancelButtonAriaLabel: '取消', + // }, + // startScreen: { + // recentSearchesTitle: '搜索历史', + // noRecentSearchesText: '没有搜索历史', + // saveRecentSearchButtonTitle: '保存至搜索历史', + // removeRecentSearchButtonTitle: '从搜索历史中移除', + // favoriteSearchesTitle: '收藏', + // removeFavoriteSearchButtonTitle: '从收藏中移除', + // }, + // errorScreen: { + // titleText: '无法获取结果', + // helpText: '你可能需要检查你的网络连接', + // }, + // footer: { + // selectText: '选择', + // navigateText: '切换', + // closeText: '关闭', + // searchByText: '搜索提供者', + // }, + // noResultsScreen: { + // noResultsText: '无法找到相关结果', + // suggestedQueryText: '你可以尝试查询', + // reportMissingResultsText: '你认为该查询应该有结果?', + // reportMissingResultsLinkText: '点击反馈', + // }, + // }, + // }, + // }, + // }, + // }), + registerComponentsPlugin({ + componentsDir: path.resolve(__dirname, './components'), + }), + // only enable shiki plugin in production mode + isProd + ? shikiPlugin({ + langs: ['bash', 'diff', 'json', 'md', 'ts', 'vue'], + theme: 'dark-plus', + }) + : [], + ], +}) as UserConfig diff --git a/docs/.vuepress/configs/head.ts b/docs/.vuepress/configs/head.ts new file mode 100644 index 0000000000..a869976d67 --- /dev/null +++ b/docs/.vuepress/configs/head.ts @@ -0,0 +1,40 @@ +import type { HeadConfig } from 'vuepress/core' + +export const head: HeadConfig[] = [ + [ + 'link', + { + rel: 'icon', + type: 'image/png', + sizes: '16x16', + href: `/images/icons/favicon-16x16.png`, + }, + ], + [ + 'link', + { + rel: 'icon', + type: 'image/png', + sizes: '32x32', + href: `/images/icons/favicon-32x32.png`, + }, + ], + ['link', { rel: 'manifest', href: '/manifest.webmanifest' }], + ['meta', { name: 'application-name', content: 'VuePress' }], + ['meta', { name: 'apple-mobile-web-app-title', content: 'VuePress' }], + ['meta', { name: 'apple-mobile-web-app-status-bar-style', content: 'black' }], + [ + 'link', + { rel: 'apple-touch-icon', href: `/images/icons/apple-touch-icon.png` }, + ], + [ + 'link', + { + rel: 'mask-icon', + href: '/images/icons/safari-pinned-tab.svg', + color: '#3eaf7c', + }, + ], + ['meta', { name: 'msapplication-TileColor', content: '#3eaf7c' }], + ['meta', { name: 'theme-color', content: '#3eaf7c' }], +] diff --git a/docs/.vuepress/configs/index.ts b/docs/.vuepress/configs/index.ts new file mode 100644 index 0000000000..b23dc923d2 --- /dev/null +++ b/docs/.vuepress/configs/index.ts @@ -0,0 +1,3 @@ +export * from './head.js' +export * from './navbar/index.js' +export * from './sidebar/index.js' diff --git a/docs/.vuepress/configs/navbar/en.ts b/docs/.vuepress/configs/navbar/en.ts new file mode 100644 index 0000000000..3c237a7afb --- /dev/null +++ b/docs/.vuepress/configs/navbar/en.ts @@ -0,0 +1,52 @@ +import type { NavbarConfig } from '@vuepress/theme-default' + +export const navbarEn: NavbarConfig = [ + { + text: 'Themes', + children: [ + { + text: 'Default Theme', + link: '/themes/default/', + }, + ], + }, + { + text: 'Plugins', + children: [ + { + text: 'Common Features', + children: [ + '/plugins/back-to-top', + '/plugins/container', + '/plugins/external-link-icon', + '/plugins/google-analytics', + '/plugins/medium-zoom', + '/plugins/nprogress', + '/plugins/register-components', + ], + }, + { + text: 'Content Search', + children: ['/plugins/docsearch', '/plugins/search'], + }, + { + text: 'PWA', + children: ['/plugins/pwa', '/plugins/pwa-popup'], + }, + { + text: 'Syntax Highlighting', + children: ['/plugins/prismjs', '/plugins/shiki'], + }, + { + text: 'Theme Development', + children: [ + '/plugins/active-header-links', + '/plugins/git', + '/plugins/palette', + '/plugins/theme-data', + '/plugins/toc', + ], + }, + ], + }, +] diff --git a/docs/.vuepress/configs/navbar/index.ts b/docs/.vuepress/configs/navbar/index.ts new file mode 100644 index 0000000000..7183393c31 --- /dev/null +++ b/docs/.vuepress/configs/navbar/index.ts @@ -0,0 +1,2 @@ +export * from './en.js' +export * from './zh.js' diff --git a/docs/.vuepress/configs/navbar/zh.ts b/docs/.vuepress/configs/navbar/zh.ts new file mode 100644 index 0000000000..8ed14f4129 --- /dev/null +++ b/docs/.vuepress/configs/navbar/zh.ts @@ -0,0 +1,52 @@ +import type { NavbarConfig } from '@vuepress/theme-default' + +export const navbarZh: NavbarConfig = [ + { + text: '主题', + children: [ + { + text: '默认主题', + link: '/zh/themes/default/', + }, + ], + }, + { + text: '插件', + children: [ + { + text: '常用功能', + children: [ + '/zh/plugins/back-to-top', + '/zh/plugins/container', + '/zh/plugins/external-link-icon', + '/zh/plugins/google-analytics', + '/zh/plugins/medium-zoom', + '/zh/plugins/nprogress', + '/zh/plugins/register-components', + ], + }, + { + text: '内容搜索', + children: ['/zh/plugins/docsearch', '/zh/plugins/search'], + }, + { + text: 'PWA', + children: ['/zh/plugins/pwa', '/zh/plugins/pwa-popup'], + }, + { + text: '语法高亮', + children: ['/zh/plugins/prismjs', '/zh/plugins/shiki'], + }, + { + text: '主题开发', + children: [ + '/zh/plugins/active-header-links', + '/zh/plugins/git', + '/zh/plugins/palette', + '/zh/plugins/theme-data', + '/zh/plugins/toc', + ], + }, + ], + }, +] diff --git a/docs/.vuepress/configs/sidebar/en.ts b/docs/.vuepress/configs/sidebar/en.ts new file mode 100644 index 0000000000..89a9ac0f8e --- /dev/null +++ b/docs/.vuepress/configs/sidebar/en.ts @@ -0,0 +1,54 @@ +import type { SidebarConfig } from '@vuepress/theme-default' + +export const sidebarEn: SidebarConfig = { + '/plugins/': [ + { + text: 'Common Features', + children: [ + '/plugins/back-to-top', + '/plugins/container', + '/plugins/external-link-icon', + '/plugins/google-analytics', + '/plugins/medium-zoom', + '/plugins/nprogress', + '/plugins/register-components', + ], + }, + { + text: 'Content Search', + children: ['/plugins/docsearch', '/plugins/search'], + }, + { + text: 'PWA', + children: ['/plugins/pwa', '/plugins/pwa-popup'], + }, + { + text: 'Syntax Highlighting', + children: ['/plugins/prismjs', '/plugins/shiki'], + }, + { + text: 'Theme Development', + children: [ + '/plugins/active-header-links', + '/plugins/git', + '/plugins/palette', + '/plugins/theme-data', + '/plugins/toc', + ], + }, + ], + '/themes/': [ + { + text: 'Default Theme', + children: [ + '/themes/default/', + '/themes/default/config', + '/themes/default/frontmatter', + '/themes/default/components', + '/themes/default/markdown', + '/themes/default/styles', + '/themes/default/extending', + ], + }, + ], +} diff --git a/docs/.vuepress/configs/sidebar/index.ts b/docs/.vuepress/configs/sidebar/index.ts new file mode 100644 index 0000000000..7183393c31 --- /dev/null +++ b/docs/.vuepress/configs/sidebar/index.ts @@ -0,0 +1,2 @@ +export * from './en.js' +export * from './zh.js' diff --git a/docs/.vuepress/configs/sidebar/zh.ts b/docs/.vuepress/configs/sidebar/zh.ts new file mode 100644 index 0000000000..a3405a7282 --- /dev/null +++ b/docs/.vuepress/configs/sidebar/zh.ts @@ -0,0 +1,54 @@ +import type { SidebarConfig } from '@vuepress/theme-default' + +export const sidebarZh: SidebarConfig = { + '/zh/plugins/': [ + { + text: '常用功能', + children: [ + '/zh/plugins/back-to-top', + '/zh/plugins/container', + '/zh/plugins/external-link-icon', + '/zh/plugins/google-analytics', + '/zh/plugins/medium-zoom', + '/zh/plugins/nprogress', + '/zh/plugins/register-components', + ], + }, + { + text: '内容搜索', + children: ['/zh/plugins/docsearch', '/zh/plugins/search'], + }, + { + text: 'PWA', + children: ['/zh/plugins/pwa', '/zh/plugins/pwa-popup'], + }, + { + text: '语法高亮', + children: ['/zh/plugins/prismjs', '/zh/plugins/shiki'], + }, + { + text: '主题开发', + children: [ + '/zh/plugins/active-header-links', + '/zh/plugins/git', + '/zh/plugins/palette', + '/zh/plugins/theme-data', + '/zh/plugins/toc', + ], + }, + ], + '/zh/themes/': [ + { + text: '默认主题', + children: [ + '/zh/themes/default/', + '/zh/themes/default/config', + '/zh/themes/default/frontmatter', + '/zh/themes/default/components', + '/zh/themes/default/markdown', + '/zh/themes/default/styles', + '/zh/themes/default/extending', + ], + }, + ], +} diff --git a/docs/.vuepress/public/favicon.ico b/docs/.vuepress/public/favicon.ico new file mode 100644 index 0000000000..e481e5dda7 Binary files /dev/null and b/docs/.vuepress/public/favicon.ico differ diff --git a/docs/.vuepress/public/images/cookbook/extending-a-theme-01.png b/docs/.vuepress/public/images/cookbook/extending-a-theme-01.png new file mode 100644 index 0000000000..9ba6d7e812 Binary files /dev/null and b/docs/.vuepress/public/images/cookbook/extending-a-theme-01.png differ diff --git a/docs/.vuepress/public/images/hero.png b/docs/.vuepress/public/images/hero.png new file mode 100644 index 0000000000..ac6beaff06 Binary files /dev/null and b/docs/.vuepress/public/images/hero.png differ diff --git a/docs/.vuepress/public/images/icons/android-chrome-192x192.png b/docs/.vuepress/public/images/icons/android-chrome-192x192.png new file mode 100644 index 0000000000..ddd043910e Binary files /dev/null and b/docs/.vuepress/public/images/icons/android-chrome-192x192.png differ diff --git a/docs/.vuepress/public/images/icons/android-chrome-384x384.png b/docs/.vuepress/public/images/icons/android-chrome-384x384.png new file mode 100644 index 0000000000..86e1fd58b3 Binary files /dev/null and b/docs/.vuepress/public/images/icons/android-chrome-384x384.png differ diff --git a/docs/.vuepress/public/images/icons/apple-touch-icon.png b/docs/.vuepress/public/images/icons/apple-touch-icon.png new file mode 100644 index 0000000000..208915f1de Binary files /dev/null and b/docs/.vuepress/public/images/icons/apple-touch-icon.png differ diff --git a/docs/.vuepress/public/images/icons/favicon-16x16.png b/docs/.vuepress/public/images/icons/favicon-16x16.png new file mode 100644 index 0000000000..ca5047e7b8 Binary files /dev/null and b/docs/.vuepress/public/images/icons/favicon-16x16.png differ diff --git a/docs/.vuepress/public/images/icons/favicon-32x32.png b/docs/.vuepress/public/images/icons/favicon-32x32.png new file mode 100644 index 0000000000..e275ce9ba1 Binary files /dev/null and b/docs/.vuepress/public/images/icons/favicon-32x32.png differ diff --git a/docs/.vuepress/public/images/icons/mstile-150x150.png b/docs/.vuepress/public/images/icons/mstile-150x150.png new file mode 100644 index 0000000000..d0b1439483 Binary files /dev/null and b/docs/.vuepress/public/images/icons/mstile-150x150.png differ diff --git a/docs/.vuepress/public/images/icons/safari-pinned-tab.svg b/docs/.vuepress/public/images/icons/safari-pinned-tab.svg new file mode 100644 index 0000000000..dc0b992c04 --- /dev/null +++ b/docs/.vuepress/public/images/icons/safari-pinned-tab.svg @@ -0,0 +1,23 @@ + + + diff --git a/docs/.vuepress/public/images/logo.png b/docs/.vuepress/public/images/logo.png new file mode 100644 index 0000000000..60e17006ad Binary files /dev/null and b/docs/.vuepress/public/images/logo.png differ diff --git a/docs/.vuepress/public/manifest.webmanifest b/docs/.vuepress/public/manifest.webmanifest new file mode 100644 index 0000000000..d2e935f1ae --- /dev/null +++ b/docs/.vuepress/public/manifest.webmanifest @@ -0,0 +1,21 @@ +{ + "name": "VuePress", + "short_name": "VuePress", + "description": "Vue-powered Static Site Generator", + "start_url": "/index.html", + "display": "standalone", + "background_color": "#fff", + "theme_color": "#3eaf7c", + "icons": [ + { + "src": "/images/icons/android-chrome-192x192.png", + "sizes": "192x192", + "type": "image/png" + }, + { + "src": "/images/icons/android-chrome-384x384.png", + "sizes": "384x384", + "type": "image/png" + } + ] +} diff --git a/docs/.vuepress/theme.ts b/docs/.vuepress/theme.ts new file mode 100644 index 0000000000..907c3aebe8 --- /dev/null +++ b/docs/.vuepress/theme.ts @@ -0,0 +1,68 @@ +import { defaultTheme } from '@vuepress/theme-default' +import { navbarEn, navbarZh, sidebarEn, sidebarZh } from './configs' + +const isProd = process.env.NODE_ENV === 'production' + +export default defaultTheme({ + logo: '/images/hero.png', + repo: 'vuepress/docs', + docsDir: 'docs', + + // theme-level locales config + locales: { + /** + * English locale config + * + * As the default locale of @vuepress/theme-default is English, + * we don't need to set all of the locale fields + */ + '/': { + // navbar + navbar: navbarEn, + // sidebar + sidebar: sidebarEn, + // page meta + editLinkText: 'Edit this page on GitHub', + }, + + /** + * Chinese locale config + */ + '/zh/': { + // navbar + navbar: navbarZh, + selectLanguageName: '简体中文', + selectLanguageText: '选择语言', + selectLanguageAriaLabel: '选择语言', + // sidebar + sidebar: sidebarZh, + // page meta + editLinkText: '在 GitHub 上编辑此页', + lastUpdatedText: '上次更新', + contributorsText: '贡献者', + // custom containers + tip: '提示', + warning: '注意', + danger: '警告', + // 404 page + notFound: [ + '这里什么都没有', + '我们怎么到这来了?', + '这是一个 404 页面', + '看起来我们进入了错误的链接', + ], + backToHome: '返回首页', + // a11y + openInNewWindow: '在新窗口打开', + toggleColorMode: '切换颜色模式', + toggleSidebar: '切换侧边栏', + }, + }, + + themePlugins: { + // only enable git plugin in production mode + git: isProd, + // use shiki plugin in production mode instead + prismjs: !isProd, + }, +}) diff --git a/docs/README.md b/docs/README.md new file mode 100644 index 0000000000..337defa917 --- /dev/null +++ b/docs/README.md @@ -0,0 +1,15 @@ +--- +home: true +title: Home +heroImage: /images/hero.png +# actions: +# - text: Themes +# link: ./themes/ +# type: primary + +# - text: Plugins +# link: ./plugins/ +# type: primary + +footer: MIT Licensed | Copyright © 2018-present VuePress Community +--- diff --git a/docs/package.json b/docs/package.json new file mode 100644 index 0000000000..5ea86d30c6 --- /dev/null +++ b/docs/package.json @@ -0,0 +1,30 @@ +{ + "name": "@vuepress/ecosystem-docs", + "private": true, + "scripts": { + "docs:build": "vuepress build . --clean-cache --clean-temp", + "docs:build-webpack": "DOCS_BUNDLER=webpack pnpm docs:build", + "docs:dev": "vuepress dev . --clean-cache --clean-temp", + "docs:dev-webpack": "DOCS_BUNDLER=webpack pnpm docs:dev", + "docs:serve": "anywhere -s -h localhost -d .vuepress/dist" + }, + "dependencies": { + "@vuepress/bundler-vite": "2.0.0-rc.2", + "@vuepress/bundler-webpack": "2.0.0-rc.2", + "@vuepress/plugin-back-to-top": "workspace:*", + "@vuepress/plugin-docsearch": "workspace:*", + "@vuepress/plugin-external-link-icon": "workspace:*", + "@vuepress/plugin-google-analytics": "workspace:*", + "@vuepress/plugin-medium-zoom": "workspace:*", + "@vuepress/plugin-nprogress": "workspace:*", + "@vuepress/plugin-pwa-popup": "workspace:*", + "@vuepress/plugin-register-components": "workspace:*", + "@vuepress/plugin-search": "workspace:*", + "@vuepress/plugin-shiki": "workspace:*", + "@vuepress/theme-default": "workspace:*", + "anywhere": "^1.6.0", + "sass-loader": "^14.0.0", + "vue": "^3.4.15", + "vuepress": "2.0.0-rc.2" + } +} diff --git a/docs/plugins/active-header-links.md b/docs/plugins/active-header-links.md new file mode 100644 index 0000000000..a4b0ca0b00 --- /dev/null +++ b/docs/plugins/active-header-links.md @@ -0,0 +1,74 @@ +# active-header-links + +${info}
` : ''}\n` +``` + + +- Details: + + A function to render the starting tag of the container. + + The first param is the `info` part of [container syntax](#container-syntax). + + This option will not take effect if you don't specify the [after](#after) option. + +### after + +- Type: `(info: string) => string` + +- Default: + + +```ts +(): string => '