Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

support prerender #95

Merged
merged 16 commits into from Mar 1, 2021
1 change: 1 addition & 0 deletions .eslintrc.js
Expand Up @@ -79,6 +79,7 @@ const eslintrc = {
"no-use-before-define": 0,
"semi": ["error", "never"],
"quotes": 0,
"no-plusplus": 0
}
}

Expand Down
1 change: 1 addition & 0 deletions .github/workflows/gh-pages.yml
Expand Up @@ -3,6 +3,7 @@ on:
push:
branches:
- main
- feature/seo

jobs:
deploy:
Expand Down
5 changes: 3 additions & 2 deletions README.md
Expand Up @@ -27,7 +27,8 @@

* 建站理念: 文件即站点 (Files as a Site)。
* 开箱即用: 通过指定目录或文档, 一键生成文档、博客站点, 无需关心站点环境配置信息。
* 性能: 文档支持懒加载提升站点加载速度。
* 性能: 通过预渲染、懒加载大幅提升站点加载速度。
* SEO: 支持 SEO, 让文档更易被搜索。
* 个性化: 支持[自定义主题](http://muyunyun.cn/create-react-doc/#/%E8%87%AA%E5%AE%9A%E4%B9%89%E4%B8%BB%E9%A2%98)。
* 工作流: 集成 Github action, 支持自动化打包、发布站点。

Expand All @@ -39,7 +40,7 @@ create-react-doc 提供了官方默认主题 [crd-seed](https://github.com/MuYun

* 适配移动、PC 多端展示。
* 支持暗黑模式。
* 文档支持内嵌 codepen、codesansbox
* 文档支持内嵌 codepen、codesandbox
* GitHub 联动。

使用该主题搭建的站点有:
Expand Down
11 changes: 10 additions & 1 deletion config.yml
Expand Up @@ -5,7 +5,16 @@ title: Create React Doc

# Menu dir
## you can also set detailed dir, such as BasicSkill/css
menu: docs/快速上手.md,docs/默认主题.md,docs/站点发布.md,docs/高阶用法.md,docs/自定义主题.md,docs/其它工具.md,docs/更新日志.md
## todo: auto menu
menu: [
docs/快速上手.md,
docs/默认主题.md,
docs/站点发布.md,
docs/高阶用法.md,
docs/自定义主题.md,
docs/其它工具.md,
docs/更新日志.md,
]
## set init open menu keys
# menuOpenKeys:

Expand Down
9 changes: 9 additions & 0 deletions docs/更新日志.md
Expand Up @@ -2,6 +2,15 @@

`create-react-doc` 严格遵循 [Semantic Versioning 2.0.0](http://semver.org/lang/zh-CN/) 语义化版本规范。

### 1.0.0

- **Feature**

- 🚀 文档支持预渲染。[pr](https://github.com/MuYunyun/create-react-doc/pull/95/files)
- 🚀 路由由 hash 路由调整为 browser 路由。
🎈 站点 SEO 优化。[doc](https://github.com/MuYunyun/blog/issues/84#issuecomment-786418891)
- 🚀 配置文件中 menu 字段类型从 string 调整为 array。

### 0.3.30

- **Feature**
Expand Down
4 changes: 2 additions & 2 deletions docs/默认主题.md
Expand Up @@ -4,7 +4,7 @@ create-react-doc 的默认主题为 [crd-seed](https://github.com/MuYunyun/creat

* 适配移动、PC 多端展示。
* 支持暗黑模式。
* 文档支持内嵌 codepen、codesansbox
* 文档支持内嵌 codepen、codesandbox
* GitHub 联动。

使用该主题搭建的站点有:
Expand All @@ -25,7 +25,7 @@ create-react-doc 的默认主题为 [crd-seed](https://github.com/MuYunyun/creat
| 属性名 | 作用 | 类型 | 默认 |
| :------------: | :-------------------------------: | :---------: | :------: |
| title | 站点名 | string | |
| menu | 作为站点菜单的文件/文件夹路径 | string | |
| menu | 作为站点菜单的文件/文件夹路径 | string[] | |
| menuOpenKeys | 默认展开菜单的文件夹路径 | string | |
| user | Github 用户名 | string | |
| repo | Github 项目名 | string | |
Expand Down
16 changes: 16 additions & 0 deletions gh-pages.js
@@ -0,0 +1,16 @@
const ghPages = require('gh-pages')

ghPages.publish(
'.crd-dist',
{
branch: 'gh-pages',
repo: 'https://github.com/MuYunyun/create-react-doc.git',
},
(error) => {
if (error) {
console.error(error)
} else {
console.log('docs sync success')
}
}
)
5 changes: 4 additions & 1 deletion package.json
Expand Up @@ -17,7 +17,9 @@
"build": "node packages/create-react-doc/index.js build",
"deploy": "node packages/create-react-doc/index.js deploy",
"release": "lerna publish",
"release-qa": "lerna publish --npm-tag=beta"
"release-qa": "lerna publish --npm-tag=beta",
"cleanup": "rm -rf node_modules/gh-pages/.cache",
"deploy:site": "npm run cleanup && node gh-pages"
},
"repository": {
"type": "git",
Expand All @@ -37,6 +39,7 @@
"eslint-plugin-import": "^2.11.0",
"eslint-plugin-jsx-a11y": "^6.0.3",
"eslint-plugin-react": "^7.7.0",
"gh-pages": "^3.1.0",
"lerna": "^3.22.1"
},
"dependencies": {
Expand Down
2 changes: 1 addition & 1 deletion packages/crd-seed/component/Menu/MenuItem.js
Expand Up @@ -19,7 +19,7 @@ function MenuItem({
return (
<li
className={cx(styles['menu-item'], styles[`menu-${theme}`], {
[styles['menu-item-selected']]: selectedKey === keyValue,
[styles['menu-item-selected']]: keyValue.indexOf(selectedKey) > -1,
})}
onMouseEnter={() => {
onHoverKey(keyValue)
Expand Down
5 changes: 3 additions & 2 deletions packages/crd-seed/component/NoMatch/index.js
@@ -1,15 +1,16 @@
import { Link } from 'react-router-dom'
import styles from './index.less'

const Footer = () => {
// eslint-disable-next-line no-undef
const { user, repo } = DOCSCONFIG || {}
return (
<table className={styles.noMatch}>
<tbody>
<tr>
<td>
<h1>404</h1>
<div>杯具啊!页面不存在 </div>
<Link className={styles.button} to="/">返回首页</Link>
<section>在 github 访问<a href={`https://github.com/${user}/${repo}`}>该项目</a></section>
</td>
</tr>
</tbody>
Expand Down
63 changes: 32 additions & 31 deletions packages/crd-seed/layout/index.js
Expand Up @@ -7,7 +7,7 @@ import Affix from '../component/Affix'
import Header from '../component/Header'
import Footer from '../component/Footer'
import languageMap from '../language'
import { isMobile } from '../utils'
import { isMobile, ifAddPrefix, ifProd, ifPrerender } from '../utils'
import { getOpenSubMenuKeys } from './utils'
import logo from '../crd.logo.svg'
import styles from './index.less'
Expand All @@ -29,13 +29,11 @@ function BasicLayout({
const curOpenKeys = getOpenSubMenuKeys(pathname, menuOpenKeys)

useEffect(() => {
// eslint-disable-next-line no-use-before-define
scrollToTop()
}, [])

useEffect(() => {
// eslint-disable-next-line no-undef
INJECT?.inject?.()
if (ifPrerender) {
scrollToTop()
// eslint-disable-next-line no-undef
INJECT?.inject?.()
}
}, [])

useEffect(() => {
Expand Down Expand Up @@ -78,8 +76,8 @@ function BasicLayout({
</span>
) : (
<Link
to={item.routePath}
replace={pathname === item.routePath}
to={ifProd ? `/${repo}${item.routePath}` : item.routePath}
replace={pathname.indexOf(item.routePath) > -1}
>
{item && item.mdconf && item.mdconf.title
? item.mdconf.title
Expand Down Expand Up @@ -141,14 +139,15 @@ function BasicLayout({
</a>
) : null}
</div>
);
)
}
/**
* this section is to show article's relevant information
* such as edit in created time、edited time and so on.
*/
const renderPageFooter = () => {
const matchData = routeData.find((data) => data.path === pathname)
// in local env, data.path is to be /READEME, however pathname may be /Users/mac/.../.crd-dist/READEME/index.html
const matchData = routeData.find((data) => pathname.indexOf(data.path) > -1)
const matchProps = matchData && matchData.props
return (
<div className={cx(styles.pageFooter)}>
Expand All @@ -164,23 +163,23 @@ function BasicLayout({
<Icon className={cx(styles.icon)} type="update-time" size={13} />
{languageMap[language].modify_tm}:
<span>
{routeData.find((data) => data.path === pathname).props.mtime}
{routeData.find((data) => pathname.indexOf(data.path) > -1).props.mtime}
</span>
</span>
): null}
</div>
);
)
}
const isCurentChildren = () => {
const getRoute = routeData.filter((data) => pathname === data.path);
const article = getRoute.length > 0 ? getRoute[0].article : null;
const getRoute = routeData.filter((data) => pathname.indexOf(data.path) > -1)
const article = getRoute.length > 0 ? getRoute[0].article : null
const childs = menuSource.filter(
(data) =>
article === data.article && data.children && data.children.length > 1
);
return childs.length > 0;
};
const isChild = isCurentChildren();
)
return childs.length > 0
}
const isChild = isCurentChildren()
const renderMenuContainer = () => {
return (
<>
Expand All @@ -196,13 +195,15 @@ function BasicLayout({
[`${styles.menuMask}`]: isMobile && !inlineCollapsed,
})}
onClick={(e) => {
e.stopPropagation();
setInlineCollapsed(true);
e.stopPropagation()
setInlineCollapsed(true)
}}
/>
</>
);
)
}

const carryRepoInProd = ifProd && repo
const renderContent = () => {
return (
<div
Expand All @@ -212,31 +213,31 @@ function BasicLayout({
>
<Switch>
{/* see https://reacttraining.com/react-router/web/api/Redirect/exact-bool */}
<Redirect exact from="/" to="/README" />
<Redirect exact from={ifAddPrefix ? `/${repo}` : `/`} to={ifAddPrefix ? `/${repo}/README` : `/README`} />
{routeData.map((item) => {
return (
<Route
key={item.path}
exact
path={item.path}
path={ifAddPrefix ? `/${repo}${item.path}` : item.path}
render={() => {
const Comp = item.component;
return <Comp {...item} />;
const Comp = item.component
return <Comp {...item} />
}}
/>
);
)
})}
<Redirect to="/404" />
<Redirect to={ifAddPrefix ? `/${repo}/404` : `/404`} />
</Switch>
{renderPageFooter()}
</div>
);
)
}
return (
<div className={styles.wrapper}>
<Header
logo={logo}
href="/"
href={ifAddPrefix ? `/${repo}` : `/`}
location={location}
indexProps={indexProps}
menuSource={menuSource}
Expand Down
8 changes: 7 additions & 1 deletion packages/crd-seed/utils/index.js
Expand Up @@ -3,4 +3,10 @@ import isClient from 'diana/lib/isClient'
/** judge if is in mobile */
const isMobile = isClient() ? 'ontouchend' in window : false

export { isMobile }
// eslint-disable-next-line no-undef
const ifProd = env === 'prod'
const ifPrerender = window.__PRERENDER_INJECTED && window.__PRERENDER_INJECTED.prerender
// decide to if add prefix for path, eg: '/' or '/${repo}'
const ifAddPrefix = ifProd && !ifPrerender

export { isMobile, ifPrerender, ifAddPrefix, ifProd }
1 change: 0 additions & 1 deletion packages/create-react-doc/index.js
Expand Up @@ -69,7 +69,6 @@ if (start || build) {

docsConfig &&
docsConfig.menu
.split(',')
.forEach(itemPath =>
program.markdownPaths.push(path.join(process.cwd(), itemPath))
)
Expand Down
1 change: 1 addition & 0 deletions packages/scripts/package.json
Expand Up @@ -15,6 +15,7 @@
"@babel/preset-env": "^7.3.4",
"@babel/preset-react": "^7.0.0",
"@babel/runtime": "^7.3.4",
"crd-prerender-spa-plugin": "^0.1.0",
"@nuxtjs/friendly-errors-webpack-plugin": "^2.0.2",
"babel-eslint": "^8.0.1",
"babel-loader": "^8.0.5",
Expand Down
41 changes: 41 additions & 0 deletions packages/scripts/src/conf/getPrerenderRoutes.js
@@ -0,0 +1,41 @@
const fs = require('fs')
const { getDocsConfig } = require('../utils')

// eg: [docs/quick_start.md, a]
// output: [/quick_start, /a/b, /a/b/c]
const getPrerenderRoutes = () => {
const docsConfig = getDocsConfig()
const menu = docsConfig && Array.isArray(docsConfig.menu)
? docsConfig.menu
: []
const result = ['/README', '/404']
dfs(menu, result, '', true)
return result
}

const dfs = (arr, result, prefix, isRoot) => {
for (let i = 0; i < arr.length; i++) {
const source = `${prefix}${arr[i]}`
const stats = fs.statSync(source)
const isFile = stats.isFile()
const isDirectory = stats.isDirectory()

if (isFile && arr[i].indexOf('.md') > -1) {
if (isRoot) {
// eg: 'a/b.md'
const splitArr = arr[i].split('/')
const lastSplitValue = splitArr[splitArr.length - 1]
result.push(`/${lastSplitValue.split('.md')[0]}`)
} else {
result.push(`/${prefix}${arr[i].split('.md')[0]}`)
}
}

if (isDirectory) {
const dirArr = fs.readdirSync(source)
dfs(dirArr, result, `${source}/`, false)
}
}
}

module.exports = getPrerenderRoutes
2 changes: 1 addition & 1 deletion packages/scripts/src/conf/path.js
Expand Up @@ -131,7 +131,7 @@ module.exports = {
// markdown dir
crdConf: getCrdConf(),
docsGitIgnore: resolveApp('.gitignore'),
docsNodeModules: resolveApp(''),
docsBase: resolveApp(''),
docsConfig: docsConfigPath,
docsReadme: resolveApp('README.md'),
docsBuildDist: resolveApp('.crd-dist'),
Expand Down