aplosjs 0.15.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +28 -0
- package/aplos.config.dist.js +30 -0
- package/bin/aplos +60 -0
- package/create-aplos/index.js +95 -0
- package/create-aplos/package.json +29 -0
- package/create-aplos/templates/minimal/README.md +38 -0
- package/create-aplos/templates/minimal/_gitignore +7 -0
- package/create-aplos/templates/minimal/aplos.config.js +13 -0
- package/create-aplos/templates/minimal/package.json +22 -0
- package/create-aplos/templates/minimal/public/favicon.svg +4 -0
- package/create-aplos/templates/minimal/src/pages/_app.tsx +6 -0
- package/create-aplos/templates/minimal/src/pages/about.tsx +24 -0
- package/create-aplos/templates/minimal/src/pages/index.tsx +40 -0
- package/create-aplos/templates/minimal/src/styles/global.css +53 -0
- package/create-aplos/templates/minimal/tsconfig.json +18 -0
- package/package.json +92 -0
- package/postcss.config.js +9 -0
- package/rspack.config.js +306 -0
- package/rspack.ssr.config.js +129 -0
- package/src/build/config.js +42 -0
- package/src/build/css-noop-loader.cjs +3 -0
- package/src/build/router.js +609 -0
- package/src/build/ssg.js +198 -0
- package/src/client/public/index.html +8 -0
- package/src/command/build.js +105 -0
- package/src/command/create.js +91 -0
- package/src/command/devServer.js +198 -0
- package/src/command/router.js +137 -0
- package/src/components/head.jsx +65 -0
- package/src/components/navigation.jsx +11 -0
- package/src/config.js +5 -0
- package/src/pages/_app.tsx +9 -0
- package/src/pages/blog/[slug].tsx +6 -0
- package/src/pages/crash.tsx +6 -0
- package/src/pages/index.tsx +10 -0
- package/src/pages/test.tsx +5 -0
- package/src/runtime/DefaultErrorPage.jsx +76 -0
- package/src/runtime/ErrorBoundary.jsx +40 -0
- package/src/runtime/MiddlewareGate.jsx +149 -0
- package/src/runtime/app-ssr.jsx +42 -0
- package/src/runtime/app.jsx +126 -0
- package/src/runtime/default-middleware.js +10 -0
- package/src/runtime/default-not-found.jsx +3 -0
- package/src/runtime/passthrough-layout.jsx +5 -0
- package/src/runtime/redirect.js +46 -0
- package/src/runtime/ssr-entry.jsx +104 -0
- package/templates/minimal/README.md +38 -0
- package/templates/minimal/_gitignore +7 -0
- package/templates/minimal/aplos.config.js +13 -0
- package/templates/minimal/package.json +22 -0
- package/templates/minimal/public/favicon.svg +4 -0
- package/templates/minimal/src/pages/_app.tsx +6 -0
- package/templates/minimal/src/pages/about.tsx +24 -0
- package/templates/minimal/src/pages/index.tsx +40 -0
- package/templates/minimal/src/styles/global.css +53 -0
- package/templates/minimal/tsconfig.json +18 -0
|
@@ -0,0 +1,126 @@
|
|
|
1
|
+
import React, { createElement, useEffect } from 'react';
|
|
2
|
+
import { createRoot } from 'react-dom/client';
|
|
3
|
+
import { Routes, BrowserRouter, Route } from 'react-router-dom';
|
|
4
|
+
|
|
5
|
+
import { routeTree } from '@aplos_routes';
|
|
6
|
+
import { CustomError, NoMatch } from '@aplos_pages';
|
|
7
|
+
import headConfig, { reactStrictMode } from '@aplos_head';
|
|
8
|
+
|
|
9
|
+
import ErrorBoundary from './ErrorBoundary.jsx';
|
|
10
|
+
import DefaultErrorPage from './DefaultErrorPage.jsx';
|
|
11
|
+
import MiddlewareGate from './MiddlewareGate.jsx';
|
|
12
|
+
|
|
13
|
+
const MANAGED_ATTR = "data-head-default";
|
|
14
|
+
|
|
15
|
+
function renderRoutes(nodes) {
|
|
16
|
+
return nodes.map((node, i) => {
|
|
17
|
+
if (node.children) {
|
|
18
|
+
return (
|
|
19
|
+
<Route key={i} element={createElement(node.element)}>
|
|
20
|
+
{renderRoutes(node.children)}
|
|
21
|
+
</Route>
|
|
22
|
+
);
|
|
23
|
+
}
|
|
24
|
+
return <Route key={i} path={node.path} element={createElement(node.element)} />;
|
|
25
|
+
});
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
function HeadDefaults() {
|
|
29
|
+
useEffect(() => {
|
|
30
|
+
if (!headConfig) return;
|
|
31
|
+
|
|
32
|
+
const { defaultTitle, meta = [], link = [], script = [] } = headConfig;
|
|
33
|
+
|
|
34
|
+
if (defaultTitle) {
|
|
35
|
+
document.title = defaultTitle;
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
const elements = [];
|
|
39
|
+
|
|
40
|
+
meta.forEach((m) => {
|
|
41
|
+
const el = document.createElement('meta');
|
|
42
|
+
Object.entries(m).forEach(([key, value]) => el.setAttribute(key, value));
|
|
43
|
+
el.setAttribute(MANAGED_ATTR, 'true');
|
|
44
|
+
document.head.appendChild(el);
|
|
45
|
+
elements.push(el);
|
|
46
|
+
});
|
|
47
|
+
|
|
48
|
+
link.forEach((l) => {
|
|
49
|
+
const el = document.createElement('link');
|
|
50
|
+
Object.entries(l).forEach(([key, value]) => el.setAttribute(key, value));
|
|
51
|
+
el.setAttribute(MANAGED_ATTR, 'true');
|
|
52
|
+
document.head.appendChild(el);
|
|
53
|
+
elements.push(el);
|
|
54
|
+
});
|
|
55
|
+
|
|
56
|
+
script.forEach((s) => {
|
|
57
|
+
const el = document.createElement('script');
|
|
58
|
+
Object.entries(s).forEach(([key, value]) => {
|
|
59
|
+
if (key === 'innerHTML') {
|
|
60
|
+
el.textContent = value;
|
|
61
|
+
} else if (typeof value === 'boolean') {
|
|
62
|
+
if (value) el.setAttribute(key, '');
|
|
63
|
+
} else {
|
|
64
|
+
el.setAttribute(key, value);
|
|
65
|
+
}
|
|
66
|
+
});
|
|
67
|
+
el.setAttribute(MANAGED_ATTR, 'true');
|
|
68
|
+
document.head.appendChild(el);
|
|
69
|
+
elements.push(el);
|
|
70
|
+
});
|
|
71
|
+
|
|
72
|
+
return () => {
|
|
73
|
+
elements.forEach((el) => {
|
|
74
|
+
if (el.parentNode) el.parentNode.removeChild(el);
|
|
75
|
+
});
|
|
76
|
+
};
|
|
77
|
+
}, []);
|
|
78
|
+
|
|
79
|
+
return null;
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
function App() {
|
|
83
|
+
const ErrorComponent = CustomError || DefaultErrorPage;
|
|
84
|
+
|
|
85
|
+
return (
|
|
86
|
+
<>
|
|
87
|
+
<HeadDefaults />
|
|
88
|
+
<ErrorBoundary errorComponent={ErrorComponent}>
|
|
89
|
+
<BrowserRouter>
|
|
90
|
+
<MiddlewareGate>
|
|
91
|
+
<Routes>
|
|
92
|
+
{renderRoutes(routeTree)}
|
|
93
|
+
<Route path="*" element={createElement(NoMatch)} />
|
|
94
|
+
</Routes>
|
|
95
|
+
</MiddlewareGate>
|
|
96
|
+
</BrowserRouter>
|
|
97
|
+
</ErrorBoundary>
|
|
98
|
+
</>
|
|
99
|
+
);
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
const container = document.getElementById('root');
|
|
103
|
+
|
|
104
|
+
// Reuse the React root across hot updates so HMR re-renders instead of
|
|
105
|
+
// recreating the root (which would force a full reload).
|
|
106
|
+
const root =
|
|
107
|
+
(module.hot && module.hot.data && module.hot.data.root) || createRoot(container);
|
|
108
|
+
|
|
109
|
+
function render() {
|
|
110
|
+
if (reactStrictMode) {
|
|
111
|
+
const { StrictMode } = React;
|
|
112
|
+
root.render(<StrictMode><App /></StrictMode>);
|
|
113
|
+
} else {
|
|
114
|
+
root.render(<App />);
|
|
115
|
+
}
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
render();
|
|
119
|
+
|
|
120
|
+
if (module.hot) {
|
|
121
|
+
// Accept updates here so React Refresh commits them without a full reload.
|
|
122
|
+
module.hot.accept();
|
|
123
|
+
module.hot.dispose((data) => {
|
|
124
|
+
data.root = root;
|
|
125
|
+
});
|
|
126
|
+
}
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Default route middleware used when a project does not define
|
|
3
|
+
* `src/middleware.{ts,tsx,js,jsx}`. It never redirects, so navigation always
|
|
4
|
+
* proceeds unchanged.
|
|
5
|
+
*
|
|
6
|
+
* @returns {undefined}
|
|
7
|
+
*/
|
|
8
|
+
export default function middleware() {
|
|
9
|
+
return undefined;
|
|
10
|
+
}
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Sentinel returned by a route middleware to ask Aplos to navigate elsewhere
|
|
3
|
+
* before the matched route renders.
|
|
4
|
+
*
|
|
5
|
+
* A middleware returns the result of `redirect(...)` to short-circuit
|
|
6
|
+
* navigation. Returning `undefined` (or nothing) lets navigation proceed.
|
|
7
|
+
*
|
|
8
|
+
* @example
|
|
9
|
+
* import { redirect } from 'aplos/redirect';
|
|
10
|
+
*
|
|
11
|
+
* export default function middleware({ pathname }) {
|
|
12
|
+
* const token = localStorage.getItem('token');
|
|
13
|
+
* if (!token && pathname !== '/login') {
|
|
14
|
+
* return redirect('/login');
|
|
15
|
+
* }
|
|
16
|
+
* }
|
|
17
|
+
*
|
|
18
|
+
* @param {string} to - Destination path (e.g. `/login`).
|
|
19
|
+
* @param {{ replace?: boolean }} [options] - `replace: true` (default) swaps
|
|
20
|
+
* the current history entry so the guarded URL is not kept in history.
|
|
21
|
+
* Pass `replace: false` to push a new entry instead.
|
|
22
|
+
* @returns {{ __aplosRedirect: true, to: string, replace: boolean }}
|
|
23
|
+
*/
|
|
24
|
+
export function redirect(to, options = {}) {
|
|
25
|
+
if (typeof to !== 'string' || to.length === 0) {
|
|
26
|
+
throw new TypeError(`redirect(): "to" must be a non-empty string, received ${typeof to}`);
|
|
27
|
+
}
|
|
28
|
+
return {
|
|
29
|
+
__aplosRedirect: true,
|
|
30
|
+
to,
|
|
31
|
+
replace: options.replace !== false,
|
|
32
|
+
};
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
/**
|
|
36
|
+
* @param {unknown} value
|
|
37
|
+
* @returns {value is { __aplosRedirect: true, to: string, replace: boolean }}
|
|
38
|
+
*/
|
|
39
|
+
export function isRedirect(value) {
|
|
40
|
+
return (
|
|
41
|
+
typeof value === 'object' &&
|
|
42
|
+
value !== null &&
|
|
43
|
+
value.__aplosRedirect === true &&
|
|
44
|
+
typeof value.to === 'string'
|
|
45
|
+
);
|
|
46
|
+
}
|
|
@@ -0,0 +1,104 @@
|
|
|
1
|
+
import React from 'react';
|
|
2
|
+
import { renderToString } from 'react-dom/server';
|
|
3
|
+
import AppSSR from './app-ssr.jsx';
|
|
4
|
+
import { routeTree } from '@aplos_routes';
|
|
5
|
+
|
|
6
|
+
export function render(url) {
|
|
7
|
+
return renderToString(<AppSSR url={url} />);
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
function isStaticPath(p) {
|
|
11
|
+
if (!p) {
|
|
12
|
+
return false;
|
|
13
|
+
}
|
|
14
|
+
if (p.includes(':')) {
|
|
15
|
+
return false;
|
|
16
|
+
}
|
|
17
|
+
if (p.includes('*')) {
|
|
18
|
+
return false;
|
|
19
|
+
}
|
|
20
|
+
return true;
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
function walk(nodes, acc, forceAll) {
|
|
24
|
+
for (const node of nodes) {
|
|
25
|
+
if (node.path !== undefined && isStaticPath(node.path)) {
|
|
26
|
+
if (forceAll || node.static === true) {
|
|
27
|
+
acc.push(node.path);
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
if (node.children) {
|
|
31
|
+
walk(node.children, acc, forceAll);
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
export function getStaticRoutes({ forceAll = false } = {}) {
|
|
37
|
+
const acc = [];
|
|
38
|
+
walk(routeTree, acc, forceAll);
|
|
39
|
+
return Array.from(new Set(acc));
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
function findRouteModule(nodes, url) {
|
|
43
|
+
for (const node of nodes) {
|
|
44
|
+
if (node.children) {
|
|
45
|
+
const found = findRouteModule(node.children, url);
|
|
46
|
+
if (found) return found;
|
|
47
|
+
}
|
|
48
|
+
if (node.path !== undefined && node.path === url) {
|
|
49
|
+
return node;
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
return null;
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
function extractParams(sourcePath, url) {
|
|
56
|
+
if (!sourcePath) {
|
|
57
|
+
return {};
|
|
58
|
+
}
|
|
59
|
+
const sourceSegments = sourcePath.split('/').filter(Boolean);
|
|
60
|
+
const urlSegments = url.split('/').filter(Boolean);
|
|
61
|
+
const params = {};
|
|
62
|
+
for (let i = 0; i < sourceSegments.length; i++) {
|
|
63
|
+
const seg = sourceSegments[i];
|
|
64
|
+
const catchAll = seg.match(/^\[\.\.\.(.+)\]$/);
|
|
65
|
+
if (catchAll) {
|
|
66
|
+
params[catchAll[1]] = urlSegments.slice(i).join('/');
|
|
67
|
+
return params;
|
|
68
|
+
}
|
|
69
|
+
const dynamic = seg.match(/^\[(.+)\]$/);
|
|
70
|
+
if (dynamic) {
|
|
71
|
+
params[dynamic[1]] = urlSegments[i];
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
return params;
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
/**
|
|
78
|
+
* Return the `meta` export of the page module matching `url`, if any.
|
|
79
|
+
* Pages opt in by exporting `export const meta = { title, description, ... }`
|
|
80
|
+
* or `export const meta = (url, params) => ({ ... })` for per-instance values.
|
|
81
|
+
* When the matched node carries an inline `meta` (from a `paths` entry), that
|
|
82
|
+
* value wins over the component-level export.
|
|
83
|
+
* @param {string} url
|
|
84
|
+
* @returns {object|null}
|
|
85
|
+
*/
|
|
86
|
+
export function getRouteMeta(url) {
|
|
87
|
+
const node = findRouteModule(routeTree, url);
|
|
88
|
+
if (!node || node.meta === undefined || node.meta === null) {
|
|
89
|
+
return null;
|
|
90
|
+
}
|
|
91
|
+
if (typeof node.meta === 'function') {
|
|
92
|
+
const params = extractParams(node.sourcePath, url);
|
|
93
|
+
try {
|
|
94
|
+
const result = node.meta(url, params);
|
|
95
|
+
return result && typeof result === 'object' ? result : null;
|
|
96
|
+
} catch (err) {
|
|
97
|
+
console.error(`meta() threw for ${url}:`, err.message);
|
|
98
|
+
return null;
|
|
99
|
+
}
|
|
100
|
+
}
|
|
101
|
+
return node.meta;
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
export default { render, getStaticRoutes, getRouteMeta };
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
# {{NAME}}
|
|
2
|
+
|
|
3
|
+
Built with [Aplos](https://aplos.alpacode.io) — a fast, file-based React framework powered by Rspack.
|
|
4
|
+
|
|
5
|
+
## Getting started
|
|
6
|
+
|
|
7
|
+
```bash
|
|
8
|
+
bun install
|
|
9
|
+
bun dev
|
|
10
|
+
```
|
|
11
|
+
|
|
12
|
+
Open [http://localhost:3000](http://localhost:3000) to view the app.
|
|
13
|
+
|
|
14
|
+
## Available scripts
|
|
15
|
+
|
|
16
|
+
- `bun dev` — start the development server with HMR
|
|
17
|
+
- `bun build` — build for production
|
|
18
|
+
- `bun build:static` — build with static pre-rendering for opt-in pages
|
|
19
|
+
|
|
20
|
+
## Project structure
|
|
21
|
+
|
|
22
|
+
```
|
|
23
|
+
src/
|
|
24
|
+
pages/ # File-based routes
|
|
25
|
+
_app.tsx # Root layout
|
|
26
|
+
index.tsx # Home page (/)
|
|
27
|
+
about.tsx # About page (/about)
|
|
28
|
+
styles/
|
|
29
|
+
global.css
|
|
30
|
+
aplos.config.js
|
|
31
|
+
```
|
|
32
|
+
|
|
33
|
+
Add files in `src/pages/` to create new routes automatically.
|
|
34
|
+
|
|
35
|
+
## Learn more
|
|
36
|
+
|
|
37
|
+
- [Aplos documentation](https://aplos.alpacode.io)
|
|
38
|
+
- [GitHub repository](https://github.com/alpac0de/aplos)
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
export default {
|
|
2
|
+
reactStrictMode: true,
|
|
3
|
+
server: {
|
|
4
|
+
port: 3000,
|
|
5
|
+
},
|
|
6
|
+
head: {
|
|
7
|
+
defaultTitle: '{{NAME}}',
|
|
8
|
+
meta: [
|
|
9
|
+
{ name: 'viewport', content: 'width=device-width, initial-scale=1' },
|
|
10
|
+
{ name: 'description', content: 'Built with Aplos' },
|
|
11
|
+
],
|
|
12
|
+
},
|
|
13
|
+
};
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "{{NAME}}",
|
|
3
|
+
"private": true,
|
|
4
|
+
"version": "0.1.0",
|
|
5
|
+
"type": "module",
|
|
6
|
+
"scripts": {
|
|
7
|
+
"dev": "aplos server",
|
|
8
|
+
"build": "aplos build",
|
|
9
|
+
"build:static": "aplos build --static"
|
|
10
|
+
},
|
|
11
|
+
"dependencies": {
|
|
12
|
+
"aplosjs": "^0.14.0",
|
|
13
|
+
"react": "^19.2.0",
|
|
14
|
+
"react-dom": "^19.2.0",
|
|
15
|
+
"react-router-dom": "^7.9.5"
|
|
16
|
+
},
|
|
17
|
+
"devDependencies": {
|
|
18
|
+
"typescript": "^5.9.3",
|
|
19
|
+
"@types/react": "^19.0.0",
|
|
20
|
+
"@types/react-dom": "^19.0.0"
|
|
21
|
+
}
|
|
22
|
+
}
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
import Head from 'aplos/head';
|
|
2
|
+
import { Link } from 'aplos/navigation';
|
|
3
|
+
|
|
4
|
+
export default function About() {
|
|
5
|
+
return (
|
|
6
|
+
<>
|
|
7
|
+
<Head>
|
|
8
|
+
<title>About</title>
|
|
9
|
+
</Head>
|
|
10
|
+
|
|
11
|
+
<main className="container">
|
|
12
|
+
<h1>About</h1>
|
|
13
|
+
<p>
|
|
14
|
+
This is an example page. Aplos uses file-based routing — this page
|
|
15
|
+
lives at <code>src/pages/about.tsx</code> and is served at{' '}
|
|
16
|
+
<code>/about</code>.
|
|
17
|
+
</p>
|
|
18
|
+
<p>
|
|
19
|
+
<Link to="/">← Back home</Link>
|
|
20
|
+
</p>
|
|
21
|
+
</main>
|
|
22
|
+
</>
|
|
23
|
+
);
|
|
24
|
+
}
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
import Head from 'aplos/head';
|
|
2
|
+
import { Link } from 'aplos/navigation';
|
|
3
|
+
|
|
4
|
+
export default function Home() {
|
|
5
|
+
return (
|
|
6
|
+
<>
|
|
7
|
+
<Head>
|
|
8
|
+
<title>Welcome — {{NAME}}</title>
|
|
9
|
+
</Head>
|
|
10
|
+
|
|
11
|
+
<main className="container">
|
|
12
|
+
<h1>Welcome to Aplos</h1>
|
|
13
|
+
<p>
|
|
14
|
+
Your project is up and running. Edit <code>src/pages/index.tsx</code>{' '}
|
|
15
|
+
to start building.
|
|
16
|
+
</p>
|
|
17
|
+
|
|
18
|
+
<ul className="links">
|
|
19
|
+
<li>
|
|
20
|
+
<Link to="/about">About page example</Link>
|
|
21
|
+
</li>
|
|
22
|
+
<li>
|
|
23
|
+
<a href="https://aplos.alpacode.io" target="_blank" rel="noreferrer">
|
|
24
|
+
Documentation
|
|
25
|
+
</a>
|
|
26
|
+
</li>
|
|
27
|
+
<li>
|
|
28
|
+
<a
|
|
29
|
+
href="https://github.com/alpac0de/aplos"
|
|
30
|
+
target="_blank"
|
|
31
|
+
rel="noreferrer"
|
|
32
|
+
>
|
|
33
|
+
GitHub repository
|
|
34
|
+
</a>
|
|
35
|
+
</li>
|
|
36
|
+
</ul>
|
|
37
|
+
</main>
|
|
38
|
+
</>
|
|
39
|
+
);
|
|
40
|
+
}
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
*,
|
|
2
|
+
*::before,
|
|
3
|
+
*::after {
|
|
4
|
+
box-sizing: border-box;
|
|
5
|
+
}
|
|
6
|
+
|
|
7
|
+
html,
|
|
8
|
+
body {
|
|
9
|
+
margin: 0;
|
|
10
|
+
padding: 0;
|
|
11
|
+
font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto,
|
|
12
|
+
"Helvetica Neue", Arial, sans-serif;
|
|
13
|
+
color: #1a1a1a;
|
|
14
|
+
background: #fafafa;
|
|
15
|
+
line-height: 1.6;
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
.container {
|
|
19
|
+
max-width: 720px;
|
|
20
|
+
margin: 0 auto;
|
|
21
|
+
padding: 4rem 1.5rem;
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
h1 {
|
|
25
|
+
font-size: 2.25rem;
|
|
26
|
+
margin-bottom: 1rem;
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
code {
|
|
30
|
+
background: #eee;
|
|
31
|
+
padding: 0.15rem 0.4rem;
|
|
32
|
+
border-radius: 4px;
|
|
33
|
+
font-size: 0.9em;
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
.links {
|
|
37
|
+
list-style: none;
|
|
38
|
+
padding: 0;
|
|
39
|
+
margin-top: 2rem;
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
.links li {
|
|
43
|
+
margin: 0.5rem 0;
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
a {
|
|
47
|
+
color: #0066cc;
|
|
48
|
+
text-decoration: none;
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
a:hover {
|
|
52
|
+
text-decoration: underline;
|
|
53
|
+
}
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
{
|
|
2
|
+
"compilerOptions": {
|
|
3
|
+
"target": "ES2022",
|
|
4
|
+
"module": "ESNext",
|
|
5
|
+
"moduleResolution": "bundler",
|
|
6
|
+
"jsx": "react-jsx",
|
|
7
|
+
"strict": true,
|
|
8
|
+
"esModuleInterop": true,
|
|
9
|
+
"skipLibCheck": true,
|
|
10
|
+
"forceConsistentCasingInFileNames": true,
|
|
11
|
+
"baseUrl": ".",
|
|
12
|
+
"paths": {
|
|
13
|
+
"@/*": ["./src/*"],
|
|
14
|
+
"~/*": ["./src/*"]
|
|
15
|
+
}
|
|
16
|
+
},
|
|
17
|
+
"include": ["src"]
|
|
18
|
+
}
|