mirror of
https://github.com/logto-io/logto.git
synced 2025-02-03 21:48:55 -05:00
refactor(console): support explicit tutorial order (#4404)
This commit is contained in:
parent
2a8a5ede70
commit
c5bf1d7171
21 changed files with 189 additions and 86 deletions
|
@ -55,3 +55,20 @@ Images and other assets (if any) should be placed in the `assets` directory of t
|
|||
Since Parcel doesn't support dynamic import (see [#112](https://github.com/parcel-bundler/parcel/issues/112) [#125](https://github.com/parcel-bundler/parcel/issues/125)), we need to run `node generate-metadata.js` to update the metadata in `index.ts`, thus we can use it in the guide components with React lazy loading.
|
||||
|
||||
This may be fixed by replacing Parcel with something else.
|
||||
|
||||
### Order guides
|
||||
|
||||
The guides are ordered by the following rules in ascending order:
|
||||
|
||||
1. The first segment of the directory name, which should be the target of the guide;
|
||||
2. The `order` property of the guide.
|
||||
|
||||
You can configure the property by creating a `config.json` file in the guide directory. The file should be an object with the following structure:
|
||||
|
||||
```json
|
||||
{
|
||||
"order": 1
|
||||
}
|
||||
```
|
||||
|
||||
If no `config.json` file is found, the guide will be placed at the end of the list.
|
||||
|
|
|
@ -8,8 +8,8 @@ import fs from 'node:fs/promises';
|
|||
const entries = await fs.readdir('.');
|
||||
const directories = entries.filter((entry) => !entry.includes('.'));
|
||||
|
||||
const metadata = directories
|
||||
.map((directory) => {
|
||||
const data = await Promise.all(
|
||||
directories.map(async (directory) => {
|
||||
if (!existsSync(`${directory}/README.mdx`)) {
|
||||
console.warn(`No README.mdx file found in ${directory} directory, skipping.`);
|
||||
return;
|
||||
|
@ -23,12 +23,24 @@ const metadata = directories
|
|||
// Add `.png` later
|
||||
const logo = ['logo.svg'].find((logo) => existsSync(`${directory}/${logo}`));
|
||||
|
||||
const config = existsSync(`${directory}/config.json`)
|
||||
? await import(`./${directory}/config.json`, { assert: { type: 'json' } }).then((module) => module.default)
|
||||
: undefined;
|
||||
|
||||
return {
|
||||
name: directory,
|
||||
logo,
|
||||
order: config?.order ?? Number.POSITIVE_INFINITY,
|
||||
};
|
||||
})
|
||||
.filter(Boolean);
|
||||
);
|
||||
const metadata = data.filter(Boolean).slice().sort((a, b) => {
|
||||
if (a.name.split('-')[0] !== b.name.split('-')[0]) {
|
||||
return a.name.localeCompare(b.name);
|
||||
}
|
||||
|
||||
return a.order - b.order;
|
||||
});
|
||||
|
||||
const camelCase = (value) => value.replaceAll(/-./g, (x) => x[1].toUpperCase());
|
||||
const filename = 'index.ts';
|
||||
|
@ -46,12 +58,13 @@ for (const { name } of metadata) {
|
|||
await fs.appendFile(filename, '\n');
|
||||
await fs.appendFile(filename, 'const guides: Readonly<Guide[]> = Object.freeze([');
|
||||
|
||||
for (const { name, logo } of metadata) {
|
||||
for (const { name, logo, order } of metadata) {
|
||||
// eslint-disable-next-line no-await-in-loop
|
||||
await fs.appendFile(
|
||||
filename,
|
||||
`
|
||||
{
|
||||
order: ${order},
|
||||
id: '${name}',
|
||||
Logo: ${logo ? `lazy(async () => import('./${name}/${logo}'))` : 'undefined'},
|
||||
Component: lazy(async () => import('./${name}/README.mdx')),
|
||||
|
|
|
@ -25,118 +25,137 @@ import webRemix from './web-remix/index';
|
|||
|
||||
const guides: Readonly<Guide[]> = Object.freeze([
|
||||
{
|
||||
id: 'web-next',
|
||||
Logo: lazy(async () => import('./web-next/logo.svg')),
|
||||
Component: lazy(async () => import('./web-next/README.mdx')),
|
||||
metadata: webNext,
|
||||
},
|
||||
{
|
||||
id: 'web-next-app-router',
|
||||
Logo: lazy(async () => import('./web-next-app-router/logo.svg')),
|
||||
Component: lazy(async () => import('./web-next-app-router/README.mdx')),
|
||||
metadata: webNextAppRouter,
|
||||
},
|
||||
{
|
||||
id: 'web-express',
|
||||
Logo: lazy(async () => import('./web-express/logo.svg')),
|
||||
Component: lazy(async () => import('./web-express/README.mdx')),
|
||||
metadata: webExpress,
|
||||
},
|
||||
{
|
||||
id: 'web-go',
|
||||
Logo: lazy(async () => import('./web-go/logo.svg')),
|
||||
Component: lazy(async () => import('./web-go/README.mdx')),
|
||||
metadata: webGo,
|
||||
},
|
||||
{
|
||||
id: 'web-php',
|
||||
Logo: lazy(async () => import('./web-php/logo.svg')),
|
||||
Component: lazy(async () => import('./web-php/README.mdx')),
|
||||
metadata: webPhp,
|
||||
},
|
||||
{
|
||||
id: 'web-python',
|
||||
Logo: lazy(async () => import('./web-python/logo.svg')),
|
||||
Component: lazy(async () => import('./web-python/README.mdx')),
|
||||
metadata: webPython,
|
||||
},
|
||||
{
|
||||
id: 'web-remix',
|
||||
Logo: lazy(async () => import('./web-remix/logo.svg')),
|
||||
Component: lazy(async () => import('./web-remix/README.mdx')),
|
||||
metadata: webRemix,
|
||||
},
|
||||
{
|
||||
id: 'web-asp-net-core',
|
||||
Logo: lazy(async () => import('./web-asp-net-core/logo.svg')),
|
||||
Component: lazy(async () => import('./web-asp-net-core/README.mdx')),
|
||||
metadata: webAspNetCore,
|
||||
},
|
||||
{
|
||||
id: 'web-outline',
|
||||
Logo: lazy(async () => import('./web-outline/logo.svg')),
|
||||
Component: lazy(async () => import('./web-outline/README.mdx')),
|
||||
metadata: webOutline,
|
||||
},
|
||||
{
|
||||
id: 'spa-react',
|
||||
Logo: lazy(async () => import('./spa-react/logo.svg')),
|
||||
Component: lazy(async () => import('./spa-react/README.mdx')),
|
||||
metadata: spaReact,
|
||||
},
|
||||
{
|
||||
order: Number.POSITIVE_INFINITY,
|
||||
id: 'm2m-general',
|
||||
Logo: lazy(async () => import('./m2m-general/logo.svg')),
|
||||
Component: lazy(async () => import('./m2m-general/README.mdx')),
|
||||
metadata: m2mGeneral,
|
||||
},
|
||||
{
|
||||
id: 'web-gpt-plugin',
|
||||
Logo: lazy(async () => import('./web-gpt-plugin/logo.svg')),
|
||||
Component: lazy(async () => import('./web-gpt-plugin/README.mdx')),
|
||||
metadata: webGptPlugin,
|
||||
},
|
||||
{
|
||||
id: 'spa-vue',
|
||||
Logo: lazy(async () => import('./spa-vue/logo.svg')),
|
||||
Component: lazy(async () => import('./spa-vue/README.mdx')),
|
||||
metadata: spaVue,
|
||||
},
|
||||
{
|
||||
id: 'spa-vanilla',
|
||||
Logo: lazy(async () => import('./spa-vanilla/logo.svg')),
|
||||
Component: lazy(async () => import('./spa-vanilla/README.mdx')),
|
||||
metadata: spaVanilla,
|
||||
},
|
||||
{
|
||||
order: 1,
|
||||
id: 'native-ios-swift',
|
||||
Logo: lazy(async () => import('./native-ios-swift/logo.svg')),
|
||||
Component: lazy(async () => import('./native-ios-swift/README.mdx')),
|
||||
metadata: nativeIosSwift,
|
||||
},
|
||||
{
|
||||
order: 2,
|
||||
id: 'native-android-java',
|
||||
Logo: lazy(async () => import('./native-android-java/logo.svg')),
|
||||
Component: lazy(async () => import('./native-android-java/README.mdx')),
|
||||
metadata: nativeAndroidJava,
|
||||
},
|
||||
{
|
||||
order: 2.1,
|
||||
id: 'native-android-kt',
|
||||
Logo: lazy(async () => import('./native-android-kt/logo.svg')),
|
||||
Component: lazy(async () => import('./native-android-kt/README.mdx')),
|
||||
metadata: nativeAndroidKt,
|
||||
},
|
||||
{
|
||||
order: 3,
|
||||
id: 'native-flutter',
|
||||
Logo: lazy(async () => import('./native-flutter/logo.svg')),
|
||||
Component: lazy(async () => import('./native-flutter/README.mdx')),
|
||||
metadata: nativeFlutter,
|
||||
},
|
||||
{
|
||||
order: 4,
|
||||
id: 'native-capacitor',
|
||||
Logo: lazy(async () => import('./native-capacitor/logo.svg')),
|
||||
Component: lazy(async () => import('./native-capacitor/README.mdx')),
|
||||
metadata: nativeCapacitor,
|
||||
},
|
||||
{
|
||||
id: 'native-flutter',
|
||||
Logo: lazy(async () => import('./native-flutter/logo.svg')),
|
||||
Component: lazy(async () => import('./native-flutter/README.mdx')),
|
||||
metadata: nativeFlutter,
|
||||
order: 1,
|
||||
id: 'spa-react',
|
||||
Logo: lazy(async () => import('./spa-react/logo.svg')),
|
||||
Component: lazy(async () => import('./spa-react/README.mdx')),
|
||||
metadata: spaReact,
|
||||
},
|
||||
{
|
||||
order: 2,
|
||||
id: 'spa-vue',
|
||||
Logo: lazy(async () => import('./spa-vue/logo.svg')),
|
||||
Component: lazy(async () => import('./spa-vue/README.mdx')),
|
||||
metadata: spaVue,
|
||||
},
|
||||
{
|
||||
order: 3,
|
||||
id: 'spa-vanilla',
|
||||
Logo: lazy(async () => import('./spa-vanilla/logo.svg')),
|
||||
Component: lazy(async () => import('./spa-vanilla/README.mdx')),
|
||||
metadata: spaVanilla,
|
||||
},
|
||||
{
|
||||
order: 1,
|
||||
id: 'web-next',
|
||||
Logo: lazy(async () => import('./web-next/logo.svg')),
|
||||
Component: lazy(async () => import('./web-next/README.mdx')),
|
||||
metadata: webNext,
|
||||
},
|
||||
{
|
||||
order: 1.1,
|
||||
id: 'web-next-app-router',
|
||||
Logo: lazy(async () => import('./web-next-app-router/logo.svg')),
|
||||
Component: lazy(async () => import('./web-next-app-router/README.mdx')),
|
||||
metadata: webNextAppRouter,
|
||||
},
|
||||
{
|
||||
order: 2,
|
||||
id: 'web-express',
|
||||
Logo: lazy(async () => import('./web-express/logo.svg')),
|
||||
Component: lazy(async () => import('./web-express/README.mdx')),
|
||||
metadata: webExpress,
|
||||
},
|
||||
{
|
||||
order: 3,
|
||||
id: 'web-go',
|
||||
Logo: lazy(async () => import('./web-go/logo.svg')),
|
||||
Component: lazy(async () => import('./web-go/README.mdx')),
|
||||
metadata: webGo,
|
||||
},
|
||||
{
|
||||
order: 4,
|
||||
id: 'web-python',
|
||||
Logo: lazy(async () => import('./web-python/logo.svg')),
|
||||
Component: lazy(async () => import('./web-python/README.mdx')),
|
||||
metadata: webPython,
|
||||
},
|
||||
{
|
||||
order: 5,
|
||||
id: 'web-php',
|
||||
Logo: lazy(async () => import('./web-php/logo.svg')),
|
||||
Component: lazy(async () => import('./web-php/README.mdx')),
|
||||
metadata: webPhp,
|
||||
},
|
||||
{
|
||||
order: 6,
|
||||
id: 'web-remix',
|
||||
Logo: lazy(async () => import('./web-remix/logo.svg')),
|
||||
Component: lazy(async () => import('./web-remix/README.mdx')),
|
||||
metadata: webRemix,
|
||||
},
|
||||
{
|
||||
order: 7,
|
||||
id: 'web-asp-net-core',
|
||||
Logo: lazy(async () => import('./web-asp-net-core/logo.svg')),
|
||||
Component: lazy(async () => import('./web-asp-net-core/README.mdx')),
|
||||
metadata: webAspNetCore,
|
||||
},
|
||||
{
|
||||
order: 8,
|
||||
id: 'web-outline',
|
||||
Logo: lazy(async () => import('./web-outline/logo.svg')),
|
||||
Component: lazy(async () => import('./web-outline/README.mdx')),
|
||||
metadata: webOutline,
|
||||
},
|
||||
{
|
||||
order: 9,
|
||||
id: 'web-gpt-plugin',
|
||||
Logo: lazy(async () => import('./web-gpt-plugin/logo.svg')),
|
||||
Component: lazy(async () => import('./web-gpt-plugin/README.mdx')),
|
||||
metadata: webGptPlugin,
|
||||
},
|
||||
]);
|
||||
|
||||
|
|
|
@ -0,0 +1,3 @@
|
|||
{
|
||||
"order": 2
|
||||
}
|
|
@ -0,0 +1,3 @@
|
|||
{
|
||||
"order": 2.1
|
||||
}
|
|
@ -0,0 +1,3 @@
|
|||
{
|
||||
"order": 4
|
||||
}
|
|
@ -0,0 +1,3 @@
|
|||
{
|
||||
"order": 3
|
||||
}
|
|
@ -0,0 +1,3 @@
|
|||
{
|
||||
"order": 1
|
||||
}
|
|
@ -0,0 +1,3 @@
|
|||
{
|
||||
"order": 1
|
||||
}
|
|
@ -0,0 +1,3 @@
|
|||
{
|
||||
"order": 3
|
||||
}
|
|
@ -0,0 +1,3 @@
|
|||
{
|
||||
"order": 2
|
||||
}
|
|
@ -0,0 +1,3 @@
|
|||
{
|
||||
"order": 7
|
||||
}
|
|
@ -0,0 +1,3 @@
|
|||
{
|
||||
"order": 2
|
||||
}
|
|
@ -0,0 +1,3 @@
|
|||
{
|
||||
"order": 3
|
||||
}
|
|
@ -0,0 +1,3 @@
|
|||
{
|
||||
"order": 9
|
||||
}
|
|
@ -0,0 +1,3 @@
|
|||
{
|
||||
"order": 1.1
|
||||
}
|
|
@ -0,0 +1,3 @@
|
|||
{
|
||||
"order": 1
|
||||
}
|
|
@ -0,0 +1,3 @@
|
|||
{
|
||||
"order": 8
|
||||
}
|
|
@ -0,0 +1,3 @@
|
|||
{
|
||||
"order": 5
|
||||
}
|
|
@ -0,0 +1,3 @@
|
|||
{
|
||||
"order": 4
|
||||
}
|
|
@ -0,0 +1,3 @@
|
|||
{
|
||||
"order": 6
|
||||
}
|
Loading…
Add table
Reference in a new issue