Skip to content

Commit bda2d96

Browse files
authored
[add] Wiki pages with Open Source policy files (#27)
1 parent ff061ac commit bda2d96

File tree

16 files changed

+1493
-1290
lines changed

16 files changed

+1493
-1290
lines changed

.github/workflows/main.yml

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,9 @@ jobs:
1616
- uses: actions/checkout@v4
1717
if: ${{ env.VERCEL_TOKEN && env.VERCEL_ORG_ID && env.VERCEL_PROJECT_ID }}
1818

19+
- name: Clone latest submodules
20+
run: git submodule update --init --recursive --remote
21+
1922
- name: Deploy to Vercel
2023
id: vercel-deployment
2124
uses: amondnet/vercel-action@v25

.gitmodules

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
[submodule "public/wiki/policy"]
2+
path = public/wiki/policy
3+
url = https://github.com/fpsig/open-source-policy

components/Layout/MDXLayout.tsx

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
import { FC } from 'react';
2+
import { Container, ContainerProps } from 'react-bootstrap';
3+
4+
import { PageHead } from './PageHead';
5+
6+
export const MDXLayout: FC<ContainerProps> = ({
7+
className = 'mt-5 pt-5 pb-3',
8+
title,
9+
children,
10+
...props
11+
}) => (
12+
<Container as="article" {...props} className={className}>
13+
<PageHead title={title} />
14+
<h1 className="my-4">{title}</h1>
15+
16+
{children}
17+
</Container>
18+
);

components/Navigator/MainNavigator.tsx

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,7 @@ const topNavBarMenu = ({ t }: typeof i18n): MenuItem[] => [
2727
name: t('hackathon'),
2828
},
2929
{ href: '/license-filter', name: t('license_filter') },
30+
{ href: '/wiki', name: t('wiki') },
3031
];
3132

3233
export interface MainNavigatorProps {

eslint.config.ts

Lines changed: 5 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,5 @@
11
import cspellPlugin from '@cspell/eslint-plugin';
22
import eslint from '@eslint/js';
3-
// @ts-expect-error eslint-plugin-next doesn't come with TypeScript definitions
43
import nextPlugin from '@next/eslint-plugin-next';
54
import stylistic from '@stylistic/eslint-plugin';
65
import eslintConfigPrettier from 'eslint-config-prettier';
@@ -31,12 +30,7 @@ export default tsEslint.config(
3130
},
3231
{
3332
// config with just ignores is the replacement for `.eslintignore`
34-
ignores: [
35-
'**/node_modules/**',
36-
'**/public/**',
37-
'**/.next/**',
38-
'.github/scripts/**',
39-
],
33+
ignores: ['**/node_modules/**', '**/public/**', '**/.next/**', '.github/scripts/**'],
4034
},
4135

4236
// extends ...
@@ -53,22 +47,15 @@ export default tsEslint.config(
5347
warnOnUnsupportedTypeScriptVersion: false,
5448
},
5549
},
50+
// @ts-expect-error https://github.com/vercel/next.js/issues/81695
5651
rules: {
5752
// spellchecker
5853
'@cspell/spellchecker': [
5954
'warn',
6055
{
6156
cspell: {
6257
language: 'en',
63-
dictionaries: [
64-
'typescript',
65-
'node',
66-
'html',
67-
'css',
68-
'bash',
69-
'npm',
70-
'pnpm',
71-
],
58+
dictionaries: ['typescript', 'node', 'html', 'css', 'bash', 'npm', 'pnpm'],
7259
},
7360
},
7461
],
@@ -91,8 +78,7 @@ export default tsEslint.config(
9178
'error',
9279
{
9380
selector: "TSPropertySignature[key.name='children']",
94-
message:
95-
'Please use PropsWithChildren<T> instead of defining children manually',
81+
message: 'Please use PropsWithChildren<T> instead of defining children manually',
9682
},
9783
],
9884
'consistent-return': 'warn',
@@ -109,10 +95,7 @@ export default tsEslint.config(
10995
// React
11096
'react/no-unescaped-entities': 'off',
11197
'react/self-closing-comp': ['error', { component: true, html: true }],
112-
'react/jsx-curly-brace-presence': [
113-
'error',
114-
{ props: 'never', children: 'never' },
115-
],
98+
'react/jsx-curly-brace-presence': ['error', { props: 'never', children: 'never' }],
11699
'react/jsx-no-target-blank': 'warn',
117100
'react/jsx-sort-props': [
118101
'error',

next-env.d.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
/// <reference types="next" />
22
/// <reference types="next/image-types/global" />
3+
/// <reference path="./.next/types/routes.d.ts" />
34

45
// NOTE: This file should not be edited
56
// see https://nextjs.org/docs/pages/api-reference/config/typescript for more information.

package.json

Lines changed: 31 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -12,68 +12,70 @@
1212
"test": "lint-staged && npm run lint"
1313
},
1414
"dependencies": {
15-
"@koa/router": "^13.1.1",
15+
"@koa/router": "^14.0.0",
1616
"@mdx-js/loader": "^3.1.0",
1717
"@mdx-js/react": "^3.1.0",
18-
"@next/mdx": "^15.3.5",
19-
"core-js": "^3.44.0",
18+
"@next/mdx": "^15.5.0",
19+
"core-js": "^3.45.1",
2020
"file-type": "^21.0.0",
2121
"idea-react": "^2.0.0-rc.13",
22-
"koa": "^2.16.1",
22+
"koa": "^3.0.1",
2323
"koajax": "^3.1.2",
2424
"license-filter": "^0.2.5",
25-
"marked": "^16.0.0",
25+
"marked": "^16.2.0",
2626
"mime": "^4.0.7",
2727
"mobx": "^6.13.7",
2828
"mobx-github": "^0.3.11",
2929
"mobx-i18n": "^0.7.1",
30-
"mobx-lark": "^2.2.0",
30+
"mobx-lark": "^2.4.0",
3131
"mobx-react": "^9.2.0",
3232
"mobx-react-helper": "^0.5.1",
3333
"mobx-restful": "^2.1.0",
34-
"mobx-restful-table": "^2.5.2",
35-
"next": "^15.3.5",
34+
"mobx-restful-table": "^2.5.3",
35+
"next": "^15.5.0",
3636
"next-pwa": "^5.6.0",
37-
"next-ssr-middleware": "^1.0.1",
38-
"react": "^19.1.0",
37+
"next-ssr-middleware": "^1.0.2",
38+
"react": "^19.1.1",
3939
"react-bootstrap": "^2.10.10",
40-
"react-dom": "^19.1.0",
40+
"react-dom": "^19.1.1",
4141
"react-typed-component": "^1.0.6",
42-
"undici": "^7.11.0",
43-
"web-utility": "^4.4.3"
42+
"undici": "^7.15.0",
43+
"web-utility": "^4.5.1",
44+
"yaml": "^2.8.1"
4445
},
4546
"devDependencies": {
4647
"@babel/plugin-proposal-decorators": "^7.28.0",
4748
"@babel/plugin-transform-typescript": "^7.28.0",
4849
"@babel/preset-react": "^7.27.1",
49-
"@cspell/eslint-plugin": "^9.1.5",
50-
"@eslint/js": "^9.31.0",
50+
"@cspell/eslint-plugin": "^9.2.0",
51+
"@eslint/js": "^9.34.0",
52+
"@next/eslint-plugin-next": "^15.5.0",
5153
"@softonus/prettier-plugin-duplicate-remover": "^1.1.2",
52-
"@stylistic/eslint-plugin": "^5.1.0",
54+
"@stylistic/eslint-plugin": "^5.2.3",
5355
"@types/eslint-config-prettier": "^6.11.3",
54-
"@types/koa": "^2.15.0",
56+
"@types/koa": "^3.0.0",
5557
"@types/koa__router": "^12.0.4",
5658
"@types/next-pwa": "^5.6.9",
57-
"@types/node": "^22.16.3",
58-
"@types/react": "^19.1.8",
59-
"@types/react-dom": "^19.1.6",
60-
"eslint": "^9.31.0",
61-
"eslint-config-next": "^15.3.5",
62-
"eslint-config-prettier": "^10.1.5",
59+
"@types/node": "^22.17.2",
60+
"@types/react": "^19.1.11",
61+
"@types/react-dom": "^19.1.7",
62+
"eslint": "^9.34.0",
63+
"eslint-config-next": "^15.5.0",
64+
"eslint-config-prettier": "^10.1.8",
6365
"eslint-plugin-react": "^7.37.5",
6466
"eslint-plugin-simple-import-sort": "^12.1.1",
6567
"globals": "^16.3.0",
6668
"husky": "^9.1.7",
67-
"jiti": "^2.4.2",
68-
"less": "^4.3.0",
69+
"jiti": "^2.5.1",
70+
"less": "^4.4.1",
6971
"less-loader": "^12.3.0",
70-
"lint-staged": "^16.1.2",
72+
"lint-staged": "^16.1.5",
7173
"next-with-less": "^3.0.1",
7274
"prettier": "^3.6.2",
7375
"prettier-plugin-css-order": "^2.1.2",
74-
"sass": "^1.89.2",
75-
"typescript": "~5.8.3",
76-
"typescript-eslint": "^8.36.0"
76+
"sass": "^1.90.0",
77+
"typescript": "~5.9.2",
78+
"typescript-eslint": "^8.40.0"
7779
},
7880
"resolutions": {
7981
"next": "$next"

pages/_app.tsx

Lines changed: 6 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -10,12 +10,7 @@ import { Image } from 'react-bootstrap';
1010
import { MainNavigator } from '../components/Navigator/MainNavigator';
1111
import { PageContent } from '../components/PageContent';
1212
import { isServer } from '../models/configuration';
13-
import {
14-
createI18nStore,
15-
I18nContext,
16-
I18nProps,
17-
loadSSRLanguage,
18-
} from '../models/Translation';
13+
import { createI18nStore, I18nContext, I18nProps, loadSSRLanguage } from '../models/Translation';
1914

2015
configure({ enforceActions: 'never' });
2116

@@ -46,7 +41,8 @@ export default class CustomApp extends App<I18nProps> {
4641
render() {
4742
const { Component, pageProps, router } = this.props,
4843
{ t } = this.i18nStore;
49-
const thisFullYear = new Date().getFullYear();
44+
const thisFullYear = new Date().getFullYear(),
45+
{ asPath } = router;
5046

5147
return (
5248
<I18nContext.Provider value={this.i18nStore}>
@@ -59,7 +55,7 @@ export default class CustomApp extends App<I18nProps> {
5955
<MainNavigator />
6056

6157
<div className="mt-5 pt-2">
62-
{router.route.startsWith('/article/') ? (
58+
{asPath.startsWith('/article/') || asPath.startsWith('/wiki/') ? (
6359
<PageContent>
6460
<Component {...pageProps} />
6561
</PageContent>
@@ -71,8 +67,7 @@ export default class CustomApp extends App<I18nProps> {
7167
<footer className="mw-100 bg-dark text-white">
7268
<p className="text-center my-0 py-3">
7369
<span className="pr-3">
74-
© 2021{thisFullYear === 2021 ? '' : `-${thisFullYear}`}{' '}
75-
{t('open_source_bazaar')}
70+
© 2021{thisFullYear === 2021 ? '' : `-${thisFullYear}`} {t('open_source_bazaar')}
7671
</span>
7772
<a
7873
className="flex-fill d-flex justify-content-center align-items-center"
@@ -82,12 +77,7 @@ export default class CustomApp extends App<I18nProps> {
8277
>
8378
Powered by
8479
<span className="mx-2">
85-
<Image
86-
src="/vercel.svg"
87-
alt="Vercel Logo"
88-
width={72}
89-
height={16}
90-
/>
80+
<Image src="/vercel.svg" alt="Vercel Logo" width={72} height={16} />
9181
</span>
9282
</a>
9383
</p>

pages/api/core.ts

Lines changed: 80 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,12 @@
1+
import 'core-js/full/array/from-async';
2+
13
import Router, { RouterParamContext } from '@koa/router';
24
import { Context, Middleware } from 'koa';
35
import { HTTPError } from 'koajax';
6+
import { DataObject } from 'mobx-restful';
47
import { KoaOption, withKoa, withKoaRouter } from 'next-ssr-middleware';
58
import { ProxyAgent, setGlobalDispatcher } from 'undici';
9+
import { parse } from 'yaml';
610

711
const { HTTP_PROXY } = process.env;
812

@@ -46,3 +50,79 @@ export const withSafeKoaRouter = <S, C extends RouterParamContext<S>>(
4650
router: Router<S, C>,
4751
...middlewares: Middleware<S, C>[]
4852
) => withKoaRouter<S, C>({} as KoaOption, router, safeAPI, ...middlewares);
53+
54+
export interface ArticleMeta {
55+
name: string;
56+
path?: string;
57+
meta?: DataObject;
58+
subs: ArticleMeta[];
59+
}
60+
61+
const MDX_pattern = /\.mdx?$/;
62+
63+
export async function splitFrontMatter(path: string) {
64+
const { readFile } = await import('fs/promises');
65+
66+
const file = await readFile(path, 'utf-8');
67+
68+
const [, frontMatter, markdown] =
69+
file.trim().match(/^---[\r\n]([\s\S]+?[\r\n])---[\r\n]([\s\S]*)/) || [];
70+
71+
if (!frontMatter) return { markdown: file };
72+
73+
try {
74+
const meta = parse(frontMatter) as DataObject;
75+
76+
return { markdown, meta };
77+
} catch (error) {
78+
console.error(`Error reading front matter for ${path}:`, error);
79+
80+
return { markdown };
81+
}
82+
}
83+
84+
export async function* pageListOf(path: string, prefix = 'pages'): AsyncGenerator<ArticleMeta> {
85+
const { readdir } = await import('fs/promises');
86+
87+
const list = await readdir(prefix + path, { withFileTypes: true });
88+
89+
for (const node of list) {
90+
let { name, path } = node;
91+
92+
if (name.startsWith('.')) continue;
93+
94+
const isMDX = MDX_pattern.test(name);
95+
96+
name = name.replace(MDX_pattern, '');
97+
path = `${path}/${name}`.replace(new RegExp(`^${prefix}`), '');
98+
99+
if (node.isFile() && isMDX) {
100+
const article: ArticleMeta = { name, path, subs: [] };
101+
102+
const { meta } = await splitFrontMatter(`${node.path}/${node.name}`);
103+
104+
if (meta) article.meta = meta;
105+
106+
yield article;
107+
}
108+
if (!node.isDirectory()) continue;
109+
110+
const subs = await Array.fromAsync(pageListOf(path, prefix));
111+
112+
if (subs.length) yield { name, subs };
113+
}
114+
}
115+
116+
export type TreeNode<K extends string> = {
117+
[key in K]: TreeNode<K>[];
118+
};
119+
120+
export function* traverseTree<K extends string, N extends TreeNode<K>>(
121+
tree: N,
122+
key: K,
123+
): Generator<N> {
124+
for (const node of tree[key] || []) {
125+
yield node as N;
126+
yield* traverseTree(node as N, key);
127+
}
128+
}

0 commit comments

Comments
 (0)