feat(docs): add codebase best practices (#42591)
* feat(docs): add codebase best practices * add anchor to linter setup * add typescript highlighting, and basic idea * apply silly standards Co-authored-by: Oliver Eyton-Williams <ojeytonwilliams@gmail.com> * add stateful class component example Co-authored-by: Tom <20648924+moT01@users.noreply.github.com> * encourage use of functional components * split filename extension Co-authored-by: Nicholas Carrigan (he/him) <nhcarrigan@gmail.com> * add comments for future me * add redux structure * add tsx to prism imports Co-authored-by: Oliver Eyton-Williams <ojeytonwilliams@gmail.com> Co-authored-by: Tom <20648924+moT01@users.noreply.github.com> Co-authored-by: Nicholas Carrigan (he/him) <nhcarrigan@gmail.com>
This commit is contained in:
@ -3,6 +3,7 @@
|
|||||||
- [Frequently Asked Questions](FAQ.md)
|
- [Frequently Asked Questions](FAQ.md)
|
||||||
- **Code Contribution**
|
- **Code Contribution**
|
||||||
- [Set up freeCodeCamp locally](how-to-setup-freecodecamp-locally.md)
|
- [Set up freeCodeCamp locally](how-to-setup-freecodecamp-locally.md)
|
||||||
|
- [Codebase best practices](codebase-best-practices.md)
|
||||||
- [Open a pull request](how-to-open-a-pull-request.md)
|
- [Open a pull request](how-to-open-a-pull-request.md)
|
||||||
- [Work on coding challenges](how-to-work-on-coding-challenges.md)
|
- [Work on coding challenges](how-to-work-on-coding-challenges.md)
|
||||||
- [Work on video challenges](how-to-help-with-video-challenges.md)
|
- [Work on video challenges](how-to-help-with-video-challenges.md)
|
||||||
|
135
docs/codebase-best-practices.md
Normal file
135
docs/codebase-best-practices.md
Normal file
@ -0,0 +1,135 @@
|
|||||||
|
# Codebase Best Practices
|
||||||
|
|
||||||
|
## General JavaScript
|
||||||
|
|
||||||
|
In most cases, our [linter](how-to-setup-freecodecamp-locally?id=follow-these-steps-to-get-your-development-environment-ready) will warn of any formatting which goes against this codebase's preferred practice.
|
||||||
|
|
||||||
|
It is encouraged to use functional components over class-based components.
|
||||||
|
|
||||||
|
## Specific TypeScript
|
||||||
|
|
||||||
|
### Migrating a JavaScript File to TypeScript
|
||||||
|
|
||||||
|
#### Retaining Git File History
|
||||||
|
|
||||||
|
Sometimes changing the file from `<filename>.js` to `<filename>.ts` (or `.tsx`) causes the original file to be deleted, and a new one created, and other times the filename just changes - in terms of Git. Ideally, we want the file history to be preserved.
|
||||||
|
|
||||||
|
The best bet at achieving this is to:
|
||||||
|
|
||||||
|
1. Rename the file
|
||||||
|
2. Commit with the flag `--no-verify` to prevent Husky from complaining about the lint errors
|
||||||
|
3. Refactor to TypeScript for migration, in a separate commit
|
||||||
|
|
||||||
|
> [!NOTE]
|
||||||
|
> Editors like VSCode are still likely to show you the file has been deleted and a new one created. If you use the CLI to `git add .`, then VSCode will show the file as renamed in stage
|
||||||
|
|
||||||
|
### Naming Conventions
|
||||||
|
|
||||||
|
#### Interfaces and Types
|
||||||
|
|
||||||
|
For the most part, it is encouraged to use interface declarations over type declarations.
|
||||||
|
|
||||||
|
React Component Props - suffix with `Props`
|
||||||
|
|
||||||
|
```typescript
|
||||||
|
interface MyComponentProps {}
|
||||||
|
// type MyComponentProps = {};
|
||||||
|
const MyComponent = (props: MyComponentProps) => {};
|
||||||
|
```
|
||||||
|
|
||||||
|
React Stateful Components - suffix with `State`
|
||||||
|
|
||||||
|
```typescript
|
||||||
|
interface MyComponentState {}
|
||||||
|
// type MyComponentState = {};
|
||||||
|
class MyComponent extends Component<MyComponentProps, MyComponentState> {}
|
||||||
|
```
|
||||||
|
|
||||||
|
Default - object name in PascalCase
|
||||||
|
|
||||||
|
```typescript
|
||||||
|
interface MyObject {}
|
||||||
|
// type MyObject = {};
|
||||||
|
const myObject: MyObject = {};
|
||||||
|
```
|
||||||
|
|
||||||
|
<!-- #### Redux Actions -->
|
||||||
|
|
||||||
|
<!-- TODO: Once refactored to TS, showcase naming convention for Reducers/Actions and how to type dispatch funcs -->
|
||||||
|
|
||||||
|
## Redux
|
||||||
|
|
||||||
|
### Action Definitions
|
||||||
|
|
||||||
|
```typescript
|
||||||
|
enum AppActionTypes = {
|
||||||
|
actionFunction = 'actionFunction'
|
||||||
|
}
|
||||||
|
|
||||||
|
export const actionFunction = (
|
||||||
|
arg: Arg
|
||||||
|
): ReducerPayload<AppActionTypes.actionFunction> => ({
|
||||||
|
type: AppActionTypes.actionFunction,
|
||||||
|
payload: arg
|
||||||
|
});
|
||||||
|
```
|
||||||
|
|
||||||
|
### How to Reduce
|
||||||
|
|
||||||
|
```typescript
|
||||||
|
// Base reducer action without payload
|
||||||
|
type ReducerBase<T> = { type: T };
|
||||||
|
// Logic for handling optional payloads
|
||||||
|
type ReducerPayload<T extends AppActionTypes> =
|
||||||
|
T extends AppActionTypes.actionFunction
|
||||||
|
? ReducerBase<T> & {
|
||||||
|
payload: AppState['property'];
|
||||||
|
}
|
||||||
|
: ReducerBase<T>;
|
||||||
|
|
||||||
|
// Switch reducer exported to Redux combineReducers
|
||||||
|
export const reducer = (
|
||||||
|
state: AppState = initialState,
|
||||||
|
action: ReducerPayload<AppActionTypes>
|
||||||
|
): AppState => {
|
||||||
|
switch (action.type) {
|
||||||
|
case AppActionTypes.actionFunction:
|
||||||
|
return { ...state, property: action.payload };
|
||||||
|
default:
|
||||||
|
return state;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
```
|
||||||
|
|
||||||
|
### How to Dispatch
|
||||||
|
|
||||||
|
Within a component, import the actions and selectors needed.
|
||||||
|
|
||||||
|
```tsx
|
||||||
|
// Add type definition
|
||||||
|
interface MyComponentProps {
|
||||||
|
actionFunction: typeof actionFunction;
|
||||||
|
}
|
||||||
|
// Connect to Redux store
|
||||||
|
const mapDispatchToProps = {
|
||||||
|
actionFunction
|
||||||
|
};
|
||||||
|
// Example React Component connected to store
|
||||||
|
const MyComponent = ({ actionFunction }: MyComponentProps): JSX.Element => {
|
||||||
|
const handleClick = () => {
|
||||||
|
// Dispatch function
|
||||||
|
actionFunction();
|
||||||
|
};
|
||||||
|
return <button onClick={handleClick}>freeCodeCamp is awesome!</button>;
|
||||||
|
};
|
||||||
|
|
||||||
|
export default connect(null, mapDispatchToProps)(MyComponent);
|
||||||
|
```
|
||||||
|
|
||||||
|
<!-- ### Redux Types File -->
|
||||||
|
<!-- The types associated with the Redux store state are located in `client/src/redux/types.ts`... -->
|
||||||
|
|
||||||
|
## Further Literature
|
||||||
|
|
||||||
|
- [TypeScript Docs](https://www.typescriptlang.org/docs/)
|
||||||
|
- [TypeScript with React CheatSheet](https://github.com/typescript-cheatsheets/react#readme)
|
@ -19,7 +19,7 @@ We also support Windows 10 via WSL2, which you can prepare by [reading this guid
|
|||||||
|
|
||||||
Some community members also develop on Windows 10 natively with Git for Windows (Git Bash), and other tools installed on Windows. We do not have official support for such a setup at this time, we recommend using WSL2 instead.
|
Some community members also develop on Windows 10 natively with Git for Windows (Git Bash), and other tools installed on Windows. We do not have official support for such a setup at this time, we recommend using WSL2 instead.
|
||||||
|
|
||||||
**Prerequisites:**
|
#### Prerequisites:
|
||||||
|
|
||||||
| Prerequisite | Version | Notes |
|
| Prerequisite | Version | Notes |
|
||||||
| --------------------------------------------------------------------------------------------- | ------- | ------------------------------------------------------------------------------------------- |
|
| --------------------------------------------------------------------------------------------- | ------- | ------------------------------------------------------------------------------------------- |
|
||||||
@ -42,7 +42,7 @@ npm -v
|
|||||||
|
|
||||||
Once you have the prerequisites installed, you need to prepare your development environment. This is common for many development workflows, and you will only need to do this once.
|
Once you have the prerequisites installed, you need to prepare your development environment. This is common for many development workflows, and you will only need to do this once.
|
||||||
|
|
||||||
**Follow these steps to get your development environment ready:**
|
##### Follow these steps to get your development environment ready:
|
||||||
|
|
||||||
1. Install [Git](https://git-scm.com/) or your favorite Git client, if you haven't already. Update to the latest version; the version that came bundled with your OS may be outdated.
|
1. Install [Git](https://git-scm.com/) or your favorite Git client, if you haven't already. Update to the latest version; the version that came bundled with your OS may be outdated.
|
||||||
|
|
||||||
|
323
docs/index.html
323
docs/index.html
@ -1,96 +1,108 @@
|
|||||||
<!DOCTYPE html>
|
<!DOCTYPE html>
|
||||||
<html lang="en">
|
<html lang="en">
|
||||||
|
<head>
|
||||||
|
<meta charset="UTF-8" />
|
||||||
|
<title>Contribution Guidelines | freeCodeCamp.org</title>
|
||||||
|
<link rel="icon" href="images/branding/favicon.ico" />
|
||||||
|
<meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1" />
|
||||||
|
<meta name="description" content="Description" />
|
||||||
|
<meta
|
||||||
|
name="viewport"
|
||||||
|
content="width=device-width, user-scalable=no, initial-scale=1.0, maximum-scale=1.0, minimum-scale=1.0"
|
||||||
|
/>
|
||||||
|
<!--social-->
|
||||||
|
<meta content="freeCodeCamp.org" name="og:title" />
|
||||||
|
<meta content="Learn to code — for free." name="og:description" />
|
||||||
|
<meta
|
||||||
|
content="https://cdn.freecodecamp.org/platform/universal/fcc_meta_1920x1080-indigo.png"
|
||||||
|
property="og:image"
|
||||||
|
/>
|
||||||
|
|
||||||
<head>
|
<meta
|
||||||
<meta charset="UTF-8">
|
content="summary_large_image"
|
||||||
<title>Contribution Guidelines | freeCodeCamp.org</title>
|
key="twitter:card"
|
||||||
<link rel="icon" href="images/branding/favicon.ico">
|
name="twitter:card"
|
||||||
<meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1" />
|
/>
|
||||||
<meta name="description" content="Description">
|
<meta
|
||||||
<meta name="viewport"
|
content="https://cdn.freecodecamp.org/platform/universal/fcc_meta_1920x1080-indigo.png"
|
||||||
content="width=device-width, user-scalable=no, initial-scale=1.0, maximum-scale=1.0, minimum-scale=1.0">
|
name="twitter:image:src"
|
||||||
<!--social-->
|
/>
|
||||||
<meta content='freeCodeCamp.org' name='og:title' />
|
<meta content="freeCodeCamp.org" name="twitter:title" />
|
||||||
<meta content='Learn to code — for free.' name='og:description' />
|
<meta content="Learn to code — for free." name="twitter:description" />
|
||||||
<meta content='https://cdn.freecodecamp.org/platform/universal/fcc_meta_1920x1080-indigo.png' property='og:image' />
|
|
||||||
|
|
||||||
<meta content='summary_large_image' key='twitter:card' name='twitter:card' />
|
<link
|
||||||
<meta content='https://cdn.freecodecamp.org/platform/universal/fcc_meta_1920x1080-indigo.png'
|
rel="stylesheet"
|
||||||
name='twitter:image:src' />
|
href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/5.15.3/css/all.min.css"
|
||||||
<meta content='freeCodeCamp.org' name='twitter:title' />
|
integrity="sha512-iBBXm8fW90+nuLcSKlbmrPcLa0OT92xO1BIsZ+ywDWZCvqsWgccV3gFoRBv0z+8dLJgyAHIhR35VZc2oM/gI1w=="
|
||||||
<meta content='Learn to code — for free.' name='twitter:description' />
|
crossorigin="anonymous"
|
||||||
|
/>
|
||||||
|
|
||||||
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/5.15.3/css/all.min.css"
|
<!-- Theme -->
|
||||||
integrity="sha512-iBBXm8fW90+nuLcSKlbmrPcLa0OT92xO1BIsZ+ywDWZCvqsWgccV3gFoRBv0z+8dLJgyAHIhR35VZc2oM/gI1w=="
|
<!-- <link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/docsify-themeable@0/dist/css/theme-simple.css"> -->
|
||||||
crossorigin="anonymous" />
|
<link
|
||||||
|
rel="stylesheet"
|
||||||
|
href="https://cdn.jsdelivr.net/npm/docsify/themes/vue.css"
|
||||||
|
/>
|
||||||
|
<!-- Custom theme stylesheet -->
|
||||||
|
<link rel="stylesheet" href="_theme.css" />
|
||||||
|
</head>
|
||||||
|
|
||||||
<!-- Theme -->
|
<body class="close">
|
||||||
<!-- <link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/docsify-themeable@0/dist/css/theme-simple.css"> -->
|
<!-- Navigation (we are using a div, instead of nav to avoid conflict with docsify's nav) -->
|
||||||
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/docsify/themes/vue.css">
|
<div class="universal-nav">
|
||||||
<!-- Custom theme stylesheet -->
|
<a class="app-name-link" data-nosearch="" href="/">
|
||||||
<link rel="stylesheet" href="_theme.css">
|
<img alt="freeCodeCamp.org" src="images/branding/primary_logo.svg" />
|
||||||
|
</a>
|
||||||
|
<a class="translations-link" data-nosearch="" href="/#/i18n">
|
||||||
|
Translations
|
||||||
|
</a>
|
||||||
|
</div>
|
||||||
|
|
||||||
</head>
|
<!-- App with its own nav, search and sidebar -->
|
||||||
|
<div id="app"></div>
|
||||||
|
<script>
|
||||||
|
window.$docsify = {
|
||||||
|
homepage: 'index',
|
||||||
|
|
||||||
<body class="close">
|
relativePath: true,
|
||||||
<!-- Navigation (we are using a div, instead of nav to avoid conflict with docsify's nav) -->
|
// common aliases
|
||||||
<div class='universal-nav'>
|
alias: {
|
||||||
<a class="app-name-link" data-nosearch="" href="/">
|
'.*/_navbar.md': '/_navbar.md',
|
||||||
<img alt="freeCodeCamp.org" src="images/branding/primary_logo.svg">
|
'/i18n/?': '/_translations.md'
|
||||||
</a>
|
},
|
||||||
<a class="translations-link" data-nosearch="" href="/#/i18n">
|
|
||||||
Translations
|
|
||||||
</a>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<!-- App with its own nav, search and sidebar -->
|
// break the caching
|
||||||
<div id="app"></div>
|
requestHeaders: {
|
||||||
<script>
|
'cache-control': 'no-cache'
|
||||||
window.$docsify = {
|
},
|
||||||
|
|
||||||
homepage: 'index',
|
// Cover Page
|
||||||
|
coverpage: true,
|
||||||
|
onlyCover: true,
|
||||||
|
|
||||||
relativePath: true,
|
// Navigation
|
||||||
// common aliases
|
autoHeader: true,
|
||||||
alias: {
|
auto2top: true,
|
||||||
'.*/_navbar.md': '/_navbar.md',
|
|
||||||
'/i18n/?': '/_translations.md'
|
|
||||||
},
|
|
||||||
|
|
||||||
|
loadSidebar: true,
|
||||||
|
maxLevel: 2,
|
||||||
|
subMaxLevel: 2,
|
||||||
|
|
||||||
// break the caching
|
topMargin: 90,
|
||||||
requestHeaders: {
|
|
||||||
'cache-control': 'no-cache'
|
|
||||||
},
|
|
||||||
|
|
||||||
// Cover Page
|
// we do not use the built in navbar other then in mobile view
|
||||||
coverpage: true,
|
loadNavbar: true,
|
||||||
onlyCover: true,
|
mergeNavbar: true,
|
||||||
|
|
||||||
|
// Plugins
|
||||||
|
search: {
|
||||||
|
depth: 3,
|
||||||
|
noData: 'No results!',
|
||||||
|
placeholder: 'Search...'
|
||||||
|
},
|
||||||
|
|
||||||
// Navigation
|
// Add languages here for message box translations
|
||||||
autoHeader: true,
|
flexibleAlerts: {
|
||||||
auto2top: true,
|
|
||||||
|
|
||||||
loadSidebar: true,
|
|
||||||
maxLevel: 2,
|
|
||||||
subMaxLevel: 2,
|
|
||||||
|
|
||||||
topMargin: 90,
|
|
||||||
|
|
||||||
// we do not use the built in navbar other then in mobile view
|
|
||||||
loadNavbar: true,
|
|
||||||
mergeNavbar: true,
|
|
||||||
|
|
||||||
// Plugins
|
|
||||||
search: {
|
|
||||||
depth: 3,
|
|
||||||
noData: 'No results!',
|
|
||||||
placeholder: 'Search...'
|
|
||||||
},
|
|
||||||
|
|
||||||
// Add languages here for message box translations
|
|
||||||
'flexibleAlerts': {
|
|
||||||
note: {
|
note: {
|
||||||
label: {
|
label: {
|
||||||
'/i18n/chinese/': '注意',
|
'/i18n/chinese/': '注意',
|
||||||
@ -125,89 +137,104 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
pagination: {
|
pagination: {
|
||||||
crossChapter: true
|
crossChapter: true
|
||||||
},
|
},
|
||||||
|
|
||||||
remoteMarkdown: {
|
remoteMarkdown: {
|
||||||
tag: 'remote-markdown-url',
|
tag: 'remote-markdown-url'
|
||||||
},
|
},
|
||||||
|
|
||||||
plugins: [
|
plugins: [
|
||||||
function (hook, vm) {
|
function (hook, vm) {
|
||||||
|
hook.beforeEach(function (markdown) {
|
||||||
|
// -- ignore the translations list page
|
||||||
|
if (vm.route.file === '_translations.md') return markdown;
|
||||||
|
|
||||||
hook.beforeEach(function (markdown) {
|
// -- add "Update this translation" link for all i18n language pages
|
||||||
|
if (vm.route.path.search('i18n') !== -1) {
|
||||||
// -- ignore the translations list page
|
var dynamicText =
|
||||||
if (vm.route.file === '_translations.md') return markdown;
|
'[<i class="far fa-edit"></i> Update the translation](' +
|
||||||
|
'https://translate.freecodecamp.org/contributing-docs' +
|
||||||
// -- add "Update this translation" link for all i18n language pages
|
') or [visit the English version](' +
|
||||||
if (vm.route.path.search('i18n') !== -1) {
|
'/' +
|
||||||
var dynamicText =
|
vm.route.path.split('/').pop() +
|
||||||
'[<i class="far fa-edit"></i> Update the translation](' +
|
') of this guide to update instructions.';
|
||||||
'https://translate.freecodecamp.org/contributing-docs' +
|
|
||||||
') or [visit the English version](' +
|
|
||||||
'/' + vm.route.path.split('/').pop() +
|
|
||||||
') of this guide to update instructions.';
|
|
||||||
}
|
|
||||||
|
|
||||||
// -- add "Edit this guide on GitHub" link for all English language pages
|
|
||||||
if (vm.route.path.search('i18n') === -1) {
|
|
||||||
var dynamicText =
|
|
||||||
'[<i class="far fa-edit"></i> Edit this guide on GitHub](' +
|
|
||||||
'https://github.com/freeCodeCamp/freeCodeCamp/blob/main/docs/' +
|
|
||||||
vm.route.file +
|
|
||||||
')';
|
|
||||||
}
|
|
||||||
|
|
||||||
// Used from https://github.com/ckoliber/docsify-rtl/blob/master/build/docsify-rtl.js,
|
|
||||||
// Currently this is a hack because can't use the plugin as is.
|
|
||||||
if (vm.route.path.search('Arabic') !== -1 || vm.route.path.search('Hebrew') !== -1) {
|
|
||||||
for (var counter = 0, elements = document.getElementsByClassName("markdown-section"); counter < elements.length; counter++) {
|
|
||||||
var item = elements[counter];
|
|
||||||
item.dir = "rtl";
|
|
||||||
}
|
}
|
||||||
};
|
|
||||||
|
|
||||||
if (vm.route.path.search('Arabic') === -1 && vm.route.path.search('Hebrew') === -1) {
|
// -- add "Edit this guide on GitHub" link for all English language pages
|
||||||
for (var counter = 0, elements = document.getElementsByClassName("markdown-section"); counter < elements.length; counter++) {
|
if (vm.route.path.search('i18n') === -1) {
|
||||||
var item = elements[counter];
|
var dynamicText =
|
||||||
item.dir = "ltr";
|
'[<i class="far fa-edit"></i> Edit this guide on GitHub](' +
|
||||||
|
'https://github.com/freeCodeCamp/freeCodeCamp/blob/main/docs/' +
|
||||||
|
vm.route.file +
|
||||||
|
')';
|
||||||
}
|
}
|
||||||
};
|
|
||||||
|
|
||||||
return (
|
// Used from https://github.com/ckoliber/docsify-rtl/blob/master/build/docsify-rtl.js,
|
||||||
markdown +
|
// Currently this is a hack because can't use the plugin as is.
|
||||||
'\n----\n' +
|
if (
|
||||||
dynamicText
|
vm.route.path.search('Arabic') !== -1 ||
|
||||||
);
|
vm.route.path.search('Hebrew') !== -1
|
||||||
// -- do not add logic below this line --
|
) {
|
||||||
|
for (
|
||||||
|
var counter = 0,
|
||||||
|
elements =
|
||||||
|
document.getElementsByClassName('markdown-section');
|
||||||
|
counter < elements.length;
|
||||||
|
counter++
|
||||||
|
) {
|
||||||
|
var item = elements[counter];
|
||||||
|
item.dir = 'rtl';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
});
|
if (
|
||||||
}
|
vm.route.path.search('Arabic') === -1 &&
|
||||||
]
|
vm.route.path.search('Hebrew') === -1
|
||||||
|
) {
|
||||||
|
for (
|
||||||
|
var counter = 0,
|
||||||
|
elements =
|
||||||
|
document.getElementsByClassName('markdown-section');
|
||||||
|
counter < elements.length;
|
||||||
|
counter++
|
||||||
|
) {
|
||||||
|
var item = elements[counter];
|
||||||
|
item.dir = 'ltr';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
}
|
return markdown + '\n----\n' + dynamicText;
|
||||||
</script>
|
// -- do not add logic below this line --
|
||||||
|
});
|
||||||
|
}
|
||||||
|
]
|
||||||
|
};
|
||||||
|
</script>
|
||||||
|
|
||||||
<script src="https://cdn.jsdelivr.net/npm/docsify/lib/docsify.min.js"></script>
|
<script src="https://cdn.jsdelivr.net/npm/docsify/lib/docsify.min.js"></script>
|
||||||
|
|
||||||
<!-- Theme -->
|
<!-- Theme -->
|
||||||
<!-- <script src="https://cdn.jsdelivr.net/npm/docsify-themeable@0"></script> -->
|
<!-- <script src="https://cdn.jsdelivr.net/npm/docsify-themeable@0"></script> -->
|
||||||
|
|
||||||
<!-- Plugins -->
|
<!-- Plugins -->
|
||||||
<script src="https://cdn.jsdelivr.net/npm/docsify/lib/plugins/emoji.min.js"></script>
|
<script src="https://cdn.jsdelivr.net/npm/docsify/lib/plugins/emoji.min.js"></script>
|
||||||
<script src="https://cdn.jsdelivr.net/npm/docsify/lib/plugins/zoom-image.min.js"></script>
|
<script src="https://cdn.jsdelivr.net/npm/docsify/lib/plugins/zoom-image.min.js"></script>
|
||||||
<script src="https://cdn.jsdelivr.net/npm/docsify-copy-code@2"></script>
|
<script src="https://cdn.jsdelivr.net/npm/docsify-copy-code@2"></script>
|
||||||
<script src="https://cdn.jsdelivr.net/npm/docsify@4/lib/plugins/search.js"></script>
|
<script src="https://cdn.jsdelivr.net/npm/docsify@4/lib/plugins/search.js"></script>
|
||||||
<script src="https://cdn.jsdelivr.net/npm/docsify-plugin-flexible-alerts@1"></script>
|
<script src="https://cdn.jsdelivr.net/npm/docsify-plugin-flexible-alerts@1"></script>
|
||||||
|
<script src="https://cdn.jsdelivr.net/npm/prismjs@1.23.0/components/prism-jsx.min.js"></script>
|
||||||
|
<script src="https://cdn.jsdelivr.net/npm/prismjs@1.23.0/components/prism-typescript.min.js"></script>
|
||||||
|
<script src="https://cdn.jsdelivr.net/npm/prismjs@1.23.0/components/prism-tsx.min.js"></script>
|
||||||
|
|
||||||
<script src="https://unpkg.com/docsify-pagination/dist/docsify-pagination.min.js"></script>
|
<script src="https://unpkg.com/docsify-pagination/dist/docsify-pagination.min.js"></script>
|
||||||
<script src="https://unpkg.com/docsify-remote-markdown/dist/docsify-remote-markdown.min.js"></script>
|
<script src="https://unpkg.com/docsify-remote-markdown/dist/docsify-remote-markdown.min.js"></script>
|
||||||
|
|
||||||
<script src="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/5.15.3/js/all.min.js"
|
|
||||||
integrity="sha512-RXf+QSDCUQs5uwRKaDoXt55jygZZm2V++WUZduaU/Ui/9EGp3f/2KZVahFZBKGH0s774sd3HmrhUy+SgOFQLVQ=="
|
|
||||||
crossorigin="anonymous"></script>
|
|
||||||
</body>
|
|
||||||
|
|
||||||
|
<script
|
||||||
|
src="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/5.15.3/js/all.min.js"
|
||||||
|
integrity="sha512-RXf+QSDCUQs5uwRKaDoXt55jygZZm2V++WUZduaU/Ui/9EGp3f/2KZVahFZBKGH0s774sd3HmrhUy+SgOFQLVQ=="
|
||||||
|
crossorigin="anonymous"
|
||||||
|
></script>
|
||||||
|
</body>
|
||||||
</html>
|
</html>
|
||||||
|
Reference in New Issue
Block a user