diff --git a/.editorconfig b/.editorconfig deleted file mode 100644 index a43b112..0000000 --- a/.editorconfig +++ /dev/null @@ -1,9 +0,0 @@ -# EditorConfig is awesome: https://EditorConfig.org - -root = true - -[*.{css,ts,tsx}] -end_of_line = lf -insert_final_newline = true -indent_style = space -indent_size = 2 diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml new file mode 100644 index 0000000..1188223 --- /dev/null +++ b/.github/workflows/ci.yml @@ -0,0 +1,17 @@ +name: Lint +on: [push] + +jobs: + lint: + name: Lint + runs-on: ubuntu-latest + + steps: + - name: Clone repository + uses: actions/checkout@v2 + + - name: Install Deno + uses: denoland/setup-deno@v1 + + - name: Deno Lint + run: deno lint diff --git a/.gitignore b/.gitignore index 1d18737..e43b0f9 100644 --- a/.gitignore +++ b/.gitignore @@ -1,4 +1 @@ .DS_Store -Thumbs.db -.aleph/ -dist/ diff --git a/.vscode/extensions.json b/.vscode/extensions.json index c3f1e9f..c4eb3fe 100644 --- a/.vscode/extensions.json +++ b/.vscode/extensions.json @@ -1,7 +1,5 @@ { "recommendations": [ - "csstools.postcss", - "editorconfig.editorconfig", "denoland.vscode-deno" ] } \ No newline at end of file diff --git a/.vscode/settings.json b/.vscode/settings.json index ea9c047..3993daa 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -1,28 +1,9 @@ { - "files.eol": "\n", - "files.trimTrailingWhitespace": true, - "typescript.format.semicolons": "remove", - "typescript.preferences.quoteStyle": "single", - "[javascript]": { - "editor.defaultFormatter": "vscode.typescript-language-features", - "editor.formatOnSave": false, - }, - "[typescript]": { - "editor.defaultFormatter": "vscode.typescript-language-features", - "editor.formatOnSave": true, - "editor.codeActionsOnSave": { - "source.organizeImports": true, - } - }, - "[typescriptreact]": { - "editor.defaultFormatter": "vscode.typescript-language-features", - "editor.formatOnSave": true, - "editor.codeActionsOnSave": { - "source.organizeImports": true, - } - }, "deno.enable": true, - "deno.unstable": true, - "deno.config": "./tsconfig.json", - "deno.importMap": "./import_map.json" -} + "deno.lint": true, + "deno.config": "./deno.json", + "[typescript][typescriptreact]": { + "editor.defaultFormatter": "denoland.vscode-deno", + "editor.formatOnSave": true + } +} \ No newline at end of file diff --git a/LICENSE b/LICENSE deleted file mode 100644 index 448e308..0000000 --- a/LICENSE +++ /dev/null @@ -1,21 +0,0 @@ -The MIT License (MIT) - -Copyright (c) 2020 postUI Lab. - -Permission is hereby granted, free of charge, to any person obtaining a copy -of this software and associated documentation files (the "Software"), to deal -in the Software without restriction, including without limitation the rights -to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -copies of the Software, and to permit persons to whom the Software is -furnished to do so, subject to the following conditions: - -The above copyright notice and this permission notice shall be included in -all copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN -THE SOFTWARE. \ No newline at end of file diff --git a/README.md b/README.md deleted file mode 100644 index 1ef265c..0000000 --- a/README.md +++ /dev/null @@ -1,23 +0,0 @@ -![Aleph.js](https://raw.githubusercontent.com/alephjs/aleph.js/master/.github/poster.svg) - -# Alephjs.org -The website of **Aleph.js**, this website is built with Aleph.js and automatically deploys to [Vercel](https://vercel.com). - -> https://alephjs.org - -## System Requirements -- [Deno](https://deno.land/) 1.13+ -- [Aleph.js](https://deno.land/x/aleph) 0.3.0+ -- [VS Code](https://code.visualstudio.com/) with [deno extension](https://marketplace.visualstudio.com/items?itemName=denoland.vscode-deno) (recommended) - -## Development Setup -```bash -# start the website in `development` mode -aleph dev - -# start the website in `production` mode -aleph start - -# build the website to a stact site -aleph build -``` diff --git a/aleph.config.ts b/aleph.config.ts deleted file mode 100644 index 159306d..0000000 --- a/aleph.config.ts +++ /dev/null @@ -1,35 +0,0 @@ -import type { Config } from 'aleph/types' -import markdown from 'aleph/plugins/markdown.ts' - -export default { - plugins: [ - markdown(), - { - name: 'google-analytics-plugin', - setup: aleph => { - const id = Deno.env.get('GTAGID') - if (id && aleph.mode === 'production') { - aleph.onRender(({ html }) => { - html.scripts.push( - { - src: `https://www.googletagmanager.com/gtag/js?id=${encodeURIComponent(id)}`, - async: true - }, - `window.dataLayer = window.dataLayer || []; - function gtag() { - dataLayer.push(arguments); - } - gtag('js', new Date()); - gtag('config', ${JSON.stringify(id)});` - ) - }) - } - } - } - ], - css: { - postcss: { - plugins: ['postcss-nested', 'autoprefixer'] - } - } -} diff --git a/api/hello.ts b/api/hello.ts deleted file mode 100644 index d967f45..0000000 --- a/api/hello.ts +++ /dev/null @@ -1,5 +0,0 @@ -import type { APIHandler } from 'aleph/types.d.ts' - -export const handler: APIHandler = ({ response }) => { - response.json({ name: 'Aleph' }) -} diff --git a/app.tsx b/app.tsx deleted file mode 100644 index e1d1925..0000000 --- a/app.tsx +++ /dev/null @@ -1,15 +0,0 @@ -import React, { ComponentType } from 'react' -import Header from './components/Header.tsx' -import './style/app.css' - -export default function App({ Page, pageProps }: { Page: ComponentType, pageProps: any }) { - return ( -
-
- -
- ) -} - -// set scrollFixer with offset to avoid the fixed header override the hash scroll element -(window as any).scrollFixer = { offset: { top: 80 } } diff --git a/public/fast-refresh.mp4 b/assets/fast-refresh.mp4 similarity index 100% rename from public/fast-refresh.mp4 rename to assets/fast-refresh.mp4 diff --git a/public/favicon.ico b/assets/favicon.ico similarity index 100% rename from public/favicon.ico rename to assets/favicon.ico diff --git a/public/grid.svg b/assets/grid.svg similarity index 100% rename from public/grid.svg rename to assets/grid.svg diff --git a/public/logo.svg b/assets/logo.svg similarity index 83% rename from public/logo.svg rename to assets/logo.svg index 9ff6364..e4a82e0 100644 --- a/public/logo.svg +++ b/assets/logo.svg @@ -1,5 +1,5 @@ - + Aleph.js - + Aleph.js @@ -9,4 +9,4 @@ - \ No newline at end of file + diff --git a/public/play_circle.svg b/assets/play_circle.svg similarity index 100% rename from public/play_circle.svg rename to assets/play_circle.svg diff --git a/public/twitter_card.jpg b/assets/twitter_card.jpg similarity index 100% rename from public/twitter_card.jpg rename to assets/twitter_card.jpg diff --git a/components/Button.tsx b/components/Button.tsx index 854839e..23c005b 100755 --- a/components/Button.tsx +++ b/components/Button.tsx @@ -1,4 +1,9 @@ -import React, { CSSProperties, PropsWithChildren, useMemo, useState } from 'react' +import React, { + CSSProperties, + PropsWithChildren, + useMemo, + useState, +} from "react"; export default function Button({ className, @@ -13,68 +18,68 @@ export default function Button({ icon, iconPosition, onClick, - children + children, }: PropsWithChildren<{ - className?: string, - width?: number | string, - height?: number, - color?: string, - compact?: boolean, - strong?: boolean, - disabled?: boolean, - activated?: boolean, - round?: boolean, - icon?: React.ReactNode, - iconPosition?: { top?: number | string, left?: number | string }, - onClick?(e: React.MouseEvent): void + className?: string; + width?: number | string; + height?: number; + color?: string; + compact?: boolean; + strong?: boolean; + disabled?: boolean; + activated?: boolean; + round?: boolean; + icon?: React.ReactNode; + iconPosition?: { top?: number | string; left?: number | string }; + onClick?(e: React.MouseEvent): void; }>) { - const [hover, setHover] = useState(false) + const [hover, setHover] = useState(false); const style = useMemo(() => { const css: CSSProperties = { - display: 'inline-flex', - alignItems: 'center', - justifyContent: 'center', + display: "inline-flex", + alignItems: "center", + justifyContent: "center", height, - border: '1px solid #000', - borderRadius: round === false ? '5px' : height / 2 + 'px', + border: "1px solid #000", + borderRadius: round === false ? "5px" : height / 2 + "px", lineHeight: 1, fontSize: 16, fontWeight: 500, - padding: '0 36px', - transition: 'all 0.21s ease-in-out', - cursor: 'pointer' - } + padding: "0 36px", + transition: "all 0.21s ease-in-out", + cursor: "pointer", + }; if (color) { - css.color = color - css.borderColor = color + css.color = color; + css.borderColor = color; } if (hover || activated) { - if (color === 'white') { - css.color = "black" - css.background = "white" + if (color === "white") { + css.color = "black"; + css.background = "white"; } else { - css.color = "white" - css.background = "black" + css.color = "white"; + css.background = "black"; } } if (compact) { - css.padding = '0 16px' + css.padding = "0 16px"; } if (strong) { - css.borderWidth = '2px' - css.fontWeight = 600 + css.borderWidth = "2px"; + css.fontWeight = 600; } if (width) { - css.width = width - css.padding = 0 + css.width = width; + css.padding = 0; } if (disabled) { - css.color = '#999' - css.borderColor = '#ccc' - css.pointerEvents = 'none' + css.color = "#999"; + css.borderColor = "#ccc"; + css.pointerEvents = "none"; } - return css - }, [height, color, hover, activated]) + return css; + }, [height, color, hover, activated, strong]); return ( {icon})} + {icon && {icon}} {children} - ) + ); } diff --git a/components/GreyTriangle.tsx b/components/GreyTriangle.tsx deleted file mode 100644 index 218f420..0000000 --- a/components/GreyTriangle.tsx +++ /dev/null @@ -1,46 +0,0 @@ -import React, { useMemo } from 'react' - -interface Props { - size: number -} - -export default function GreyTriangle({ size }: Props) { - const y = useMemo(() => size - Math.sin(54 * Math.PI / 180) * size, [size]) - - return ( -
- - - - - - - - - - - - - - -
- ) -} - -GreyTriangle.defaultProps = { - size: 200, -} - diff --git a/components/Header.tsx b/components/Header.tsx index 2987fdf..2bf7b57 100644 --- a/components/Header.tsx +++ b/components/Header.tsx @@ -1,72 +1,84 @@ -import { useRouter } from 'aleph/react' -import React from 'react' -import '~/style/header.css' +import { Link } from "aleph/react"; export default function Header() { - const { pathname } = useRouter() - return ( -
-
+
+
- ) + ); } diff --git a/components/Logo.tsx b/components/Logo.tsx index 43b2d24..6a3a887 100644 --- a/components/Logo.tsx +++ b/components/Logo.tsx @@ -1,15 +1,23 @@ -import React from 'react' - export default function Logo() { return ( - + Aleph.js - + - ) + ); } diff --git a/components/UniverseTriangle.tsx b/components/UniverseTriangle.tsx deleted file mode 100644 index ffd598b..0000000 --- a/components/UniverseTriangle.tsx +++ /dev/null @@ -1,215 +0,0 @@ -import { GlitchFilter } from '@pixi/filter-glitch' -import { ZoomBlurFilter } from '@pixi/filter-zoom-blur' -import * as PIXI from 'pixi' -import React, { useEffect, useRef } from 'react' - -const starImage = '/star.png' -const defaultSpeed = 0.15 -const travel = { speed: defaultSpeed } - -interface Star { - sprite: PIXI.Sprite - x: number - y: number - z: number -} - -class Canvas { - private _app: PIXI.Application - private _zoomBlurFilter: ZoomBlurFilter - private _glitchFilter: GlitchFilter | null - private _cameraZ: number - private _size: number - private _fov: number - private _starBaseSize: number - private _stars: Star[] - - constructor(props: Props) { - const { - size = 200, - fov = 50, - starBaseSize = 8, - glitch = false - } = props - this._size = size * window.devicePixelRatio - this._app = new PIXI.Application({ - width: this._size, - height: this._size, - antialias: true, - backgroundColor: 0xffffff, - backgroundAlpha: 0 - }) - this._zoomBlurFilter = new ZoomBlurFilter({ strength: defaultSpeed / 10, center: [this._size / 2, this._size / 2] }) - this._glitchFilter = glitch ? new GlitchFilter({ fillMode: 4, direction: 90 }) : null - this._cameraZ = 0 - this._fov = fov - this._starBaseSize = starBaseSize - this._stars = [] - this._init() - } - - private _init() { - const { stage, view } = this._app - const { devicePixelRatio } = window - - view.style.width = this._size / devicePixelRatio + 'px' - view.style.height = this._size / devicePixelRatio + 'px' - - const triangle = new PIXI.Graphics() - const y = this._size - Math.sin(54 * Math.PI / 180) * this._size - triangle.beginFill(0x111111) - triangle.drawPolygon([ - 0, this._size, - this._size, this._size, - this._size / 2, y - ]) - triangle.endFill() - triangle.x = 0 - triangle.y = -y - stage.addChild(triangle) - stage.filters = [this._zoomBlurFilter, this._glitchFilter].filter(Boolean) - - const starTexture = PIXI.Texture.from(starImage) - for (let i = 0; i < Math.round(this._size * this._size / 600); i++) { - const star = { - sprite: new PIXI.Sprite(starTexture), - z: 0, - x: 0, - y: 0 - } - star.sprite.anchor.x = 0.5 - star.sprite.anchor.y = 0.7 - this._randomizeStar(star) // 0 ~ 2000 - stage.addChild(star.sprite) - this._stars.push(star) - } - this._renderStars() - this._travel = this._travel.bind(this) - } - - mount(el: HTMLElement) { - el.appendChild(this._app.view) - this.play() - } - - unmount(el: HTMLElement) { - this.stop() - el.removeChild(this._app.view) - } - - play() { - this._app.ticker.add(this._travel) - } - - stop() { - this._app.ticker.remove(this._travel) - } - - resize(size: number) { - this._size = size * window.devicePixelRatio - this._app.renderer.resize(this._size, this._size) - this._zoomBlurFilter.center = [this._size / 2, this._size / 2] - } - - private _travel(delta: number) { - this._cameraZ += delta * 10 * travel.speed - if (Math.abs(travel.speed) > defaultSpeed) { - this._zoomBlurFilter.strength = Math.abs(travel.speed) / 45 - } else { - this._zoomBlurFilter.strength = defaultSpeed / 10 - } - this._renderStars() - } - - private _renderStars() { - const { renderer: { screen } } = this._app - - this._stars.forEach(star => { - const starSprite = star.sprite - if (this._cameraZ > star.z) { - this._randomizeStar(star, this._cameraZ) - } else if (star.z - this._cameraZ > 3000) { - this._randomizeStar(star, this._cameraZ, true) - } - - // map star 3d position to 2d with really simple projection - const z = star.z - this._cameraZ - starSprite.x = star.x * (this._fov / z) * screen.width + screen.width / 2 - starSprite.y = star.y * (this._fov / z) * screen.width + screen.height / 2 - - // calculate star scale & rotation. - const dxCenter = starSprite.x - screen.width / 2 - const dyCenter = starSprite.y - screen.height / 2 - const distanceScale = Math.max(0, (2000 - z) / 2000) - starSprite.scale.x = distanceScale * this._starBaseSize / 100 - - // star is looking towards center so that y axis is towards center. - // scale the star depending on how fast we are moving, what the stretchfactor is and depending on how far away it is from the center. - starSprite.scale.y = distanceScale * this._starBaseSize / 100 - starSprite.rotation = Math.atan2(dyCenter, dxCenter) + Math.PI / 2 - }) - } - - // calculate star positions with radial random coordinate so no star hits the camera. - private _randomizeStar(star: Star, cameraZ?: number, reverse = false) { - var deg = Math.random() * Math.PI * 2 - var distance = Math.random() * 50 + 1 - star.x = Math.cos(deg) * distance - star.y = Math.sin(deg) * distance - if (cameraZ === undefined) { - star.z = Math.random() * 2000 - } else { - if (reverse) { - star.z = cameraZ + Math.random() * 1000 - } else { - star.z = cameraZ + Math.random() * 1000 + 2000 - } - } - } -} - -interface Props { - size?: number - fov?: number - starBaseSize?: number - glitch?: boolean -} - -export default function Logo(props: Props) { - const { - size = 200, - fov = 50, - starBaseSize = 8, - glitch = false - } = props - const ref = useRef() - - useEffect(() => { - const canvas = new Canvas({ size, fov, starBaseSize, glitch }) - if (ref.current) { - canvas.mount(ref.current) - } - return () => { - if (ref.current) { - canvas.unmount(ref.current) - } - } - }, [size, fov, starBaseSize]) - - return ( -
travel.speed = 1.5} - onMouseLeave={() => travel.speed = defaultSpeed} - ref={el => { - if (el) { - ref.current = el - } - }} - /> - ) -} diff --git a/deno.json b/deno.json new file mode 100644 index 0000000..c947a1f --- /dev/null +++ b/deno.json @@ -0,0 +1,19 @@ +{ + "compilerOptions": { + "lib": [ + "dom", + "dom.iterable", + "dom.asynciterable", + "deno.ns" + ], + "types": [ + "https://deno.land/x/aleph@1.0.0-beta.16/types.d.ts" + ], + "jsx": "react-jsx", + "jsxImportSource": "https://esm.sh/react@18.2.0" + }, + "importMap": "import_map.json", + "tasks": { + "dev": "deno run -A -q server.ts --dev" + } +} diff --git a/import_map.json b/import_map.json index 96976fc..efec8b9 100644 --- a/import_map.json +++ b/import_map.json @@ -1,18 +1,18 @@ { "imports": { - "~/": "./", - "aleph/": "https://deno.land/x/aleph@v0.3.0-beta.18/", - "aleph/types": "https://deno.land/x/aleph@v0.3.0-beta.18/types.d.ts", - "aleph/web": "https://deno.land/x/aleph@v0.3.0-beta.18/framework/core/mod.ts", - "aleph/react": "https://deno.land/x/aleph@v0.3.0-beta.18/framework/react/mod.ts", - "react": "https://esm.sh/react@17.0.2", - "react-dom": "https://esm.sh/react-dom@17.0.2", - "marked": "https://esm.sh/marked@2.0.1", - "highlight": "https://esm.sh/highlight.js@10.7.1/lib/core", - "highlight-languages/": "https://esm.sh/highlight.js@10.7.1/lib/languages/", - "pixi": "https://esm.sh/pixi.js-legacy@6.1.2", - "@pixi/filter-glitch": "https://esm.sh/@pixi/filter-glitch@4.1.3", - "@pixi/filter-zoom-blur": "https://esm.sh/@pixi/filter-zoom-blur@4.1.3" + "components/": "./components/", + "std/": "https://deno.land/std@0.155.0/", + "aleph/": "https://deno.land/x/aleph@1.0.0-beta.16/", + "aleph/react": "https://deno.land/x/aleph@1.0.0-beta.16/runtime/react/mod.ts", + "aleph/react-client": "https://deno.land/x/aleph@1.0.0-beta.16/runtime/react/client.ts", + "aleph/react-server": "https://deno.land/x/aleph@1.0.0-beta.16/runtime/react/server.ts", + "aleph/react/mdx-loader": "https://deno.land/x/aleph@1.0.0-beta.16/runtime/react/mdx-loader.ts", + "react": "https://esm.sh/react@18.2.0", + "react-dom": "https://esm.sh/react-dom@18.2.0", + "react-dom/": "https://esm.sh/react-dom@18.2.0/", + "@unocss/preset-uno": "https://esm.sh/@unocss/preset-uno@0.45.14", + "remark-frontmatter": "https://esm.sh/remark-frontmatter@4.0.1", + "rehype-highlight": "https://esm.sh/rehype-highlight@5.0.2" }, "scopes": {} -} \ No newline at end of file +} diff --git a/index.html b/index.html new file mode 100644 index 0000000..710b324 --- /dev/null +++ b/index.html @@ -0,0 +1,16 @@ + + + + + + + + + + + +
+ + + + diff --git a/main.ts b/main.ts new file mode 100644 index 0000000..178b203 --- /dev/null +++ b/main.ts @@ -0,0 +1,3 @@ +import { bootstrap } from "aleph/react-client"; + +bootstrap(); diff --git a/pages/404.tsx b/pages/404.tsx deleted file mode 100644 index d7b835f..0000000 --- a/pages/404.tsx +++ /dev/null @@ -1,25 +0,0 @@ -import { dynamic, Fallback } from 'aleph/react' -import React from 'react' -import GreyTriangle from '~/components/GreyTriangle.tsx' - -const UniverseTriangle = dynamic(() => import('~/components/UniverseTriangle.tsx')) - -export default function E404() { - return ( - <> - - 404 - Page Not Found - - -
- }> - - -

404 - Page Not Found

-
- - ) -} diff --git a/pages/docs.tsx b/pages/docs.tsx deleted file mode 100644 index 84b3cdb..0000000 --- a/pages/docs.tsx +++ /dev/null @@ -1,361 +0,0 @@ -import React, { ComponentType, Fragment, useEffect, useMemo, useState } from 'react' -import { useRouter } from 'aleph/react' -import util from 'aleph/shared/util.ts' -import hljs from 'highlight' -import bash from 'highlight-languages/bash' -import javascript from 'highlight-languages/javascript' -import json from 'highlight-languages/json' -import typescript from 'highlight-languages/typescript' -import xml from 'highlight-languages/xml' -import Logo from '../components/Logo.tsx' - -hljs.registerLanguage('json', json) -hljs.registerLanguage('javascript', javascript) -hljs.registerLanguage('typescript', typescript) -hljs.registerLanguage('xml', xml) // depended by jsx -hljs.registerLanguage('bash', (hljs: any) => { - const l = bash(hljs) - l.keywords.built_in = 'cd deno aleph land' - return l -}) - -const description = 'The Documentation for Aleph.js' -const ogImage = 'https://alephjs.org/twitter_card.jpg' -const navMenu = [ - { - name: 'Documentation', - items: [ - { - title: 'About Aleph.js', - modifier: , - path: '/docs' - }, - { title: 'Get Started', path: '/docs/get-started' }, - { - title: 'Basic Features', - path: '/docs/basic-features', - submenu: [ - { title: 'Pages', path: '/pages' }, - { title: 'APIs', path: '/apis' }, - { title: 'Routing', path: '/routing' }, - { title: 'Built-in CSS Support', path: '/built-in-css-support' }, - { title: 'SSR & SSG', path: '/ssr-and-ssg' }, - { title: 'Static File Serving', path: '/static-file-serving' }, - { title: 'HMR with Fast Refresh', path: '/hmr-with-fast-refresh' }, - { title: 'Import From NPM', path: '/import-from-npm' }, - { title: 'Import Maps', path: '/import-maps' }, - ] - }, - { - title: 'Advanced Features', - path: '/docs/advanced-features', - submenu: [ - { title: '`useDeno` Hook', path: '/use-deno-hook' }, - { title: 'Dynamic Importing', path: '/dynamic-importing' }, - { title: 'Custom `App`', path: '/custom-app' }, - { title: 'Custom Error Page', path: '/custom-error-page' }, - { title: 'Custom Server', path: '/custom-server' }, - { title: 'JSX Magic', path: '/jsx-magic' }, - { title: 'Using Plugins', path: '/using-plugins' }, - ] - }, - { - title: 'Plugins', - path: '/docs/plugins', - submenu: [ - { title: 'Official Plugins', path: '/official-plugins' }, - { title: 'Community Plugins', path: '/community-plugins' }, - ] - }, - { title: 'Browser Support', path: '/docs/browser-support' }, - { title: 'Deployment', path: '/docs/deployment' }, - ] - }, - { - name: 'API Reference', - items: [ - { title: 'CLI', path: '/docs/api-reference/cli' }, - { title: 'Config', path: '/docs/api-reference/config' }, - { title: 'Framework API', path: '/docs/api-reference/framework-api' }, - { title: 'Plugin API', path: '/docs/api-reference/plugin-api' }, - ] - }, - { - name: 'Design', - items: [ - { title: 'Artworks', path: '/docs/design/artworks' }, - { title: 'Theme', path: '/docs/design/theme' }, - ] - }, -] - -interface Metadata { - title: string - authors: string[] - keywords?: string[] - editable?: boolean -} - -export default function Docs({ Page }: { Page?: ComponentType & { meta: Metadata } }) { - const { pathname: currentPath, routePath } = useRouter() - const [extended, setExtended] = useState(navMenu.map(m => m.items).flat().filter(item => item.submenu).reduce((m, item) => { - m[item.path] = routePath.startsWith(item.path) - return m - }, {} as Record)) - const [menuIsOpen, setMenuIsOpen] = useState(false) - const [searchWords, setSearchWords] = useState('') - const navLinks = useMemo<[[string, string] | null, [string, string] | null]>(() => { - const all: [string, string][] = [] - navMenu.forEach(g => g.items.forEach(item => { - if (item.submenu) { - item.submenu.forEach(({ title, path }) => { - all.push([title, item.path + (path === '/' ? '' : path)]) - }) - } else { - all.push([item.title, item.path]) - } - })) - const index = all.findIndex(([_, path]) => path === currentPath) - return [all[index - 1] || null, all[index + 1] || null] - }, [currentPath]) - const editUrl = useMemo(() => { - const md = routePath === '/docs' ? routePath + '/index.md' : routePath + '.md' - return 'https://github.com/alephjs/alephjs.org/edit/master/pages' + md - }, [routePath]) - const title = [Page?.meta.title, !Page?.meta.title.endsWith('Aleph.js') && 'Aleph.js'].filter(Boolean).join(' - ') - const filteredNavMenu = useMemo(() => { - if (searchWords === '') { - return navMenu - } - return navMenu.map(g => { - const includes = (item: any) => item.title.toLowerCase().includes(searchWords) - return { - ...g, items: g.items.filter(item => { - return includes(item) || item.submenu?.some(includes) - }).map(item => ({ ...item, submenu: item.submenu?.filter(subItem => includes(item) || includes(subItem)) })) - } - }).filter(g => g.items.length > 0) - }, [searchWords]) - - useEffect(() => { - setExtended(navMenu.map(m => m.items).flat().filter(item => item.submenu).reduce((m, item) => { - m[item.path] = routePath.startsWith(item.path) - return m - }, {} as Record)) - document.querySelectorAll('.docs .content pre > code').forEach(block => { - if (block.className.includes('language-')) { - hljs.highlightElement(block) - } - if (block.className.includes('language-bash')) { - for (let i = 0; i < block.childNodes.length; i++) { - const child = block.childNodes[i] - if (child.nodeName === '#text') { - const text = child.textContent! - if (text == '$ ') { - block.insertBefore(bashPromptSpan(), child) - block.removeChild(child) - } else { - const texts = text.split('\n$ ') - const n = texts.length - if (n > 1) { - for (let j = 0; j < n; j++) { - const t = texts[j] - if (t) { - const node = document.createTextNode(t + '\n') - block.insertBefore(node, child) - } else if (j == 0) { - const node = document.createTextNode('\n') - block.insertBefore(node, child) - } - if (j > 0) { - block.insertBefore(bashPromptSpan(), child) - } - } - block.removeChild(child) - } - } - } - } - } - }) - document.querySelectorAll('.docs .content video').forEach(block => { - const v = block as HTMLVideoElement - v.className = 'is-paused' - v.addEventListener('click', () => { - if (v.paused) { - v.play() - } else { - v.requestFullscreen() - } - }) - v.addEventListener('playing', () => v.className = 'is-playing') - v.addEventListener('pause', () => v.className = 'is-paused') - }) - }, [routePath]) - - return ( -
- - {title} - - {Page?.meta.keywords && ( - - )} - - - - - - - - - - - -
- {Page && } - {Page && ( - - )} - {(Page && Page.meta.editable !== false) && ( -

- Authors:  - {Array.isArray(Page.meta.authors) && Page.meta.authors.map(name => ( - - ))} - | - Edit this page on Github -

- )} - {!(Page && Page.meta.editable !== false) && ( -
- )} -
-
- ) -} - -function bashPromptSpan(prompt: string = '$') { - const span = document.createElement('span') - span.className = 'bash_prompt' - span.innerText = prompt + ' ' - return span -} diff --git a/pages/docs/advanced-features/custom-app.md b/pages/docs/advanced-features/custom-app.md deleted file mode 100644 index feb9b20..0000000 --- a/pages/docs/advanced-features/custom-app.md +++ /dev/null @@ -1,33 +0,0 @@ ---- -title: Custom `App` -authors: - - ije - - razermoon ---- - -# Custom `App` - -Aleph.js uses the `App` component to initialize pages. You can override it to control the page initialization, which allows you to: - -- Add global head element(meta, link...) -- Add global styles -- Add global layout elements -- Add custom `ErrorBoundary` -- Inject **props** into pages - -To override the default `App`, create an `app.tsx` file in the root directory: - -```tsx -import React from 'https://esm.sh/react' - -export default function App({ Page, pageProps }) { - return ( - <> - - - - - - ) -} -``` diff --git a/pages/docs/advanced-features/custom-error-page.md b/pages/docs/advanced-features/custom-error-page.md deleted file mode 100644 index f7b8594..0000000 --- a/pages/docs/advanced-features/custom-error-page.md +++ /dev/null @@ -1,31 +0,0 @@ ---- -title: Custom Error Page -authors: - - ije ---- - -# Custom Error Page - -By default, Aleph.js will render a default error page for bad requests, you can override it by creating `404.tsx` and `500.tsx` files in the `pages` directory: - -## 404 - -```tsx -// pages/404.tsx - -export default function E404() { - return

404 - Page Not Found

; -} -``` - -> check out our [404 page](/404). - -## 500 - -```tsx -// pages/500.tsx - -export default function E500({ error }: {error: Error}) { - return

500 - {error.message}

; -} -``` diff --git a/pages/docs/advanced-features/custom-server.md b/pages/docs/advanced-features/custom-server.md deleted file mode 100644 index 33ac722..0000000 --- a/pages/docs/advanced-features/custom-server.md +++ /dev/null @@ -1,44 +0,0 @@ ---- -title: Custom Server -authors: - - ije ---- - -# Custom Server - -By default, Aleph.js includes its own server with `aleph start`. A custom Aleph.js server allows you to start a server 100% programmatically in order to use custom server patterns. Most of the time, you will not need this – but it's available for complete customization. - -Custom server with Deno native http server: - -```ts -import { Aleph, Server } from 'https://deno.land/x/aleph/server/mod.ts' - -const aleph = new Aleph() -const server = new Server(aleph) -const listener = Deno.listen({ port: 8080 }) - -for await (const conn of listener) { - // In order to not be blocking, we need to handle each connection individually - // in its own async function. - (async () => { - const httpConn = Deno.serveHttp(conn) - for await (const e of httpConn) { - server.handle(e) - } - })() -} -``` - -Custom server with [oak](https://deno.land/x/oak): - -```ts -import { Aleph, oakify } from 'https://deno.land/x/aleph/server/mod.ts' -import { Application } from 'https://deno.land/x/oak/mod.ts' - -const app = new Application() -const aleph = new Aleph() - -app.use(oakify(aleph)) - -await app.listen({ port: 8080 }) -``` \ No newline at end of file diff --git a/pages/docs/advanced-features/dynamic-importing.md b/pages/docs/advanced-features/dynamic-importing.md deleted file mode 100644 index 3ffe5dc..0000000 --- a/pages/docs/advanced-features/dynamic-importing.md +++ /dev/null @@ -1,43 +0,0 @@ ---- -title: Dynamic Importing -authors: - - ije ---- - -# Dynamic Importing - -Aleph.js provides a [`dynamic`](/docs/api-reference/aleph/react/mod.ts#dynamic) HOC that allows you to import components asynchronously, in case you might want to import a component **asynchronously (lazy)** if it is too large or you don't want it to be rendered during **SSR**. - -```tsx -import React from 'https://esm.sh/react' -import { dynamic } from 'https://deno.land/x/aleph/framework/react/mod.ts' - -const Logo = dynamic(() => import('../components/logo.tsx')) - -export default function About() { - return ( -
- -

About Me

-
- ) -} -``` - -## Fallback - -Aleph.js also provides a `Fallback` component to render a fallback UI during the asynchronous component module is loading. - -```tsx -import React from 'https://esm.sh/react' -import { dynamic, Fallback } from 'https://deno.land/x/aleph/framework/react/mod.ts' - -const Logo = dynamic(() => import('../components/logo.tsx')) - -export default function About() { - return ( - loading...

}> - -
- ) -} diff --git a/pages/docs/advanced-features/jsx-magic.md b/pages/docs/advanced-features/jsx-magic.md deleted file mode 100644 index 75a38b8..0000000 --- a/pages/docs/advanced-features/jsx-magic.md +++ /dev/null @@ -1,123 +0,0 @@ ---- -title: JSX Magic -authors: - - ije ---- - -# JSX Magic - -Aleph.js uses [swc](https://swc.rs) to transpile your code to browser, with that we have the power to check your JSX code at compilation phase to implement some features with the native JSX elements. - -## Custom Head Meta - -You can add page meta tags under the native `head` element, and these tags will be wrote to the page html with SSR enabled. This is useful the [Search Engine Optimization](https://en.wikipedia.org/wiki/Search_engine_optimization) (**SEO**). - -```tsx -import React from 'https://esm.sh/react' - -export default function Index({ Page, pageProps }) { - return ( - <> - - Aleph.js - - - - -

Aleph.js

- - ); -} -``` - -## Importing Style from Files - -Import CSS files via `link` tag, and clean up them automatically when page(component) unactivated, or get better **CSS Modules** experience. - -```tsx -import React from 'https://esm.sh/react' - -export default function App() { - return ( - <> - - -

Hi :)

- - ) -} -``` - -## Inline Style - -Aleph.js provides a built-in `CSS-in-JS` solution **out-of-the-box** using the native JSX `style` element. - -```tsx -import React from 'https://esm.sh/react' - -export default function App() { - const color = 'black' - return ( - <> - -

Hi :)

- - ) -} -``` - -## Linking Between Pages - -Linking between pages in Aleph.js is very easy, just use the `a` tag with page path. - -You can set the `rel` to get some extra feature: - -- **nav**: play as `NavLink` -- **preload**: preload page's component code and SSR data - -```jsx -import React from "https://esm.sh/react"; - -export default function Nav() { - return ( - <> - Home - About - Contact - - ); -} -``` - -## Custom Scripts - -Aleph.js allows you to add custom scripts to your app, and these _scripts_ will be appended to the **html body** at build time. - -```tsx -import React from 'https://esm.sh/react' - -export default function App({ Page, pageProps }) { - return ( - <> - - - - - ) -} -``` diff --git a/pages/docs/advanced-features/use-deno-hook.md b/pages/docs/advanced-features/use-deno-hook.md deleted file mode 100644 index 91af510..0000000 --- a/pages/docs/advanced-features/use-deno-hook.md +++ /dev/null @@ -1,105 +0,0 @@ ---- -title: useDeno hook -authors: - - ije - - magus - - znk - - razermoon ---- - -# `useDeno` hook - -In Next.js, two functions called `getStaticProps` and `getServerSideProps` are used by the pages to fetch data at **build time (SSG)** or on **each request (SSR)** respectively. This solution isolates the `data` and `view` like different roles of the `back-end` and `front-end`. - -In Aleph.js, we prefer to use hooks. A **react hook** we provide called `useDeno` allows you to access **Deno runtime** in a component, that's more closed to React's credo. - -```jsx -import React from 'https://esm.sh/react' -import { useDeno } from 'https://deno.land/x/aleph/framework/react/mod.ts' - -export default function Page() { - const version = useDeno(() => { - return Deno.version - }) - - return ( -

Powered by Deno v{version.deno}

- ) -} -``` - -Even **asynchronous** callback is accepted: - -```jsx -import React from 'https://esm.sh/react' -import { useDeno, useRouter } from 'https://deno.land/x/aleph/framework/react/mod.ts' - -export default function Post() { - const { params } = useRouter() - const post = useDeno(async () => { - return await (await fetch(`https://.../post/${params.id}`)).json() - }) - - return ( -

{post.title}

- ) -} -``` - -## How It Works - -The `useDeno` hook receives a **callback** as first parameter that will be invoked during the server side rendering(SSR), then cache the returned result. In the browser, the callback will be ignored(tree-shaked), and the cached result will be used instead. - -## Access to Request - -Like the `ssr.props` option, you can access the [`Request`](https://developer.mozilla.org/en-US/docs/Web/API/Request/Request) in `useDeno` callback. - -```tsx -export default function Page() { - const isLogined = useDeno(req => { - return req.headers.get('Auth') === 'XXX' - }, { revalidate: true }) - - return ( -

isLogined: {isLogined}

- ) -} -``` - -## Revalidate - -An optional amount in seconds after which a page re-generation can occur (defaults is no revalidating). More on Incremental Static Regeneration (ISR). - -```jsx -import React from 'https://esm.sh/react' -import { useDeno } from 'https://deno.land/x/aleph/framework/react/mod.ts' - -export default function Page() { - const now = useDeno(() => { - return Date.now() - }, { revalidate: 1 }) - - return ( -

Server Time: {now}

- ) -} -``` - -If the `revalidate` equals `0` or `true`, then revalidates props each request. - -```jsx -export default function Page() { - const isLogined = useDeno(req => { - return req.headers.get('Auth') === 'XXX' - }, { revalidate: true }) - - return ( -

isLogined: {isLogined}

- ) -} -``` - -## Caveats - -- `useDeno` only works in **SSR** mode. -- To fetch data asynchronously at **build time (SSG)**, the `renderToString` method may be invoked repeatedly until all the async data is ready. diff --git a/pages/docs/advanced-features/using-plugins.md b/pages/docs/advanced-features/using-plugins.md deleted file mode 100644 index 6660548..0000000 --- a/pages/docs/advanced-features/using-plugins.md +++ /dev/null @@ -1,29 +0,0 @@ ---- -title: Using Plugins -authors: - - ije ---- - -# Using Plugins - -Aleph.js can be extended by the built-in Plugin System. An Aleph Plugin is an object with a `setup` method. You can import them as ES Modules in `.js` or `.ts`. - -To use a plugin, add it to the `plugins` array in the `aleph.config.ts`. **For example**, load markdown as pages: - -```ts -// aleph.config.ts - -import markdown from 'https://deno.land/x/aleph/plugins/markdown.ts' -import type { Config } from 'https://deno.land/x/aleph/types.d.ts' - -export default { - plugins: [ - markdown() - ] -} -``` - -## Finding Plugins - -- [Official Plugins](/docs/plugins/official-plugins) -- [Community Plugins](/docs/plugins/community-plugins) diff --git a/pages/docs/api-reference/cli.tsx b/pages/docs/api-reference/cli.tsx deleted file mode 100644 index e416ae2..0000000 --- a/pages/docs/api-reference/cli.tsx +++ /dev/null @@ -1,41 +0,0 @@ -import { join } from 'https://deno.land/std@0.106.0/path/mod.ts' -import { useDeno } from 'aleph/react' -import React from 'react' - -async function run(...cmd: string[]) { - const p = Deno.run({ - cmd, - stdout: 'piped', - stderr: 'inherit' - }) - const output = await p.output() - p.close() - return new TextDecoder().decode(output) -} - -export default function CLI() { - const { helpMessage } = useDeno(async () => { - return { - helpMessage: await run(Deno.execPath(), 'run', '-A', join(Deno.mainModule, '../../cli.ts'), '-h') - } - }) - - return ( -
-

CLI

-

Installation

-
$ deno run -A https://deno.land/x/aleph/install.ts
-

or use land without installation:

-
$ land aleph
-

Usage

-
{'$ aleph -h\n' + helpMessage.trim()}
-
- ) -} - -CLI.meta = { - title: 'CLI', - authors: ['ije'], - date: '2020-10-20', - editable: false -} diff --git a/pages/docs/api-reference/config.md b/pages/docs/api-reference/config.md deleted file mode 100644 index 0614c7b..0000000 --- a/pages/docs/api-reference/config.md +++ /dev/null @@ -1,172 +0,0 @@ ---- -title: Config -authors: - - ije ---- - -# Config - -For custom advanced behavior of Aleph.js, add an `aleph.config.ts` file in the root of your project directory. - -#### Framework - -Aleph.js is a fullstack framework in Deno. Currently we only support **React** as the frontend renderer. - -```ts -import type { Config } from 'https://deno.land/x/aleph/types.d.ts' - -export default { - framework: 'react' -} -``` - -#### Base Path - -`basePath` allows you to set a path prefix for the application. - -```ts -import type { Config } from 'https://deno.land/x/aleph/types.d.ts' - -export default { - basePath: '/docs' -} -``` - -#### Build - -`build` specifies the options for **Build** command. - -- **target**: `string` specifies the build target in production mode (default is **'es2015'**, available targets: **'es2015'** - **'es2021'**, and **'esnext'**). -- **browsers**: `Record` adds the target browsers for esbuild (supported browsers: **'chrome'** | **'edge'** | **'firefox'** | **'ios'** | **'safari'**). -- **outputDir**: `string` specifies the output directory for _build_ command (default is **'/dist'**). - -```ts -import type { Config } from 'https://deno.land/x/aleph/types.d.ts' - -export default { - build: { - target: 'es2015', - browsers: { chrome: 90, safari: 12 }, - outputDir: '/dist', - } -} -``` - -#### Configuring CSS - -`css` specifies the css processing options. - -- **cache**: `boolean` caches remote css to local if it equals `true`. -- **postcss**: `{ plugins: PostCSSPlugin[] }` specifies the postcss plugins. The `PostCSSPlugin` can be a name string that is imported from [esm.sh](https://esm.sh). -- **modules**: `CSSModulesOptions` specifies CSS modules behavior, the options are passed on to [postcss-modules](https://github.com/madyankin/postcss-modules). - -```ts -import type { Config } from 'https://deno.land/x/aleph/types.d.ts' - -export default { - css: { - cache: true, - postcss: { plugins: [ 'autoprefixer' ] }, - modules: { - scopeBehaviour: 'global', // can be 'global' or 'local' - } - } -} -``` - -#### SSR Options - -`ssr` enables **SSR** feature for your project, default is enable for all pages. - - -```ts -import type { Config } from 'https://deno.land/x/aleph/types.d.ts' - -export default { - ssr: true -} -``` - -Mix **SSR** and **SPA**: - -```ts -export default { - ssr: { - include: [/.html$/], - exclude: [/^\/admin\//], - } -} -``` - -Pure **SPA** mode: - -```ts -export default { - ssr: false -} -``` - -#### I18N - -`i18n` enables multiple locales for the routing. - -```ts -import type { Config } from 'https://deno.land/x/aleph/types.d.ts' - -export default { - i18n: { - defaultLocale: 'en', - locales: ['en', 'zh-CN'], - } -} -``` - -> Check [I18n routing](/docs/basic-features/routing#i18n-routing) documentation for **I18N** support. - -#### Server Options - -`server` specifies the options for **Aleph Server**. -- **middlewares:** `APIMiddleware[]` a list of _Middleware_ for API requests. -- **headers**: `Record` appends custom headers for server requests. -- **rewrites**: `Record` specifies the server rewrite map. -- **compress**: `boolean` enables compression(gzip/brotli) for static files and SSR content (default is **true** for production mode). - -```ts -import type { Config } from 'https://deno.land/x/aleph/types.d.ts' - -export default { - server: { - middlewares: [ - ({ response, data }, next) => { - response.headers.set('server', 'Aleph.js') - data.set('foo', 'bar') - next() - } - ], - headers: { - 'Server': 'Custom' - }, - rewrites: { - '/p/[id]': '/blog/[id]' - }, - compress: true - } -} -``` - -#### Plugins - -`plugins` provides some plugins to extend Aleph.js runtime. - -```ts -import markdown from 'https://deno.land/x/aleph/plugins/markdown.ts' -import type { Config } from 'https://deno.land/x/aleph/types.d.ts' - -export default { - plugins: [ - markdown() - ] -} -``` - -> To find Aleph plugins, check our [Official Plugins](/docs/plugins/official-plugins) and [Community Plugins](/docs/plugins/community-plugins). diff --git a/pages/docs/api-reference/framework-api.md b/pages/docs/api-reference/framework-api.md deleted file mode 100644 index 6d55775..0000000 --- a/pages/docs/api-reference/framework-api.md +++ /dev/null @@ -1,147 +0,0 @@ ---- -title: Framework API -authors: - - ije ---- - -# Framework API - -### `dynamic` hoc -`dynamic` allows you import a component asynchronously with `import(url)` syntax, and this component will be ignored at the SSR time. - -```tsx -import React from 'https://esm.sh/react' -import { dynamic } from 'https://deno.land/x/aleph/framework/react/mod.ts' - -const Logo = dynamic(() => import('../components/logo.tsx')) - -export default function About() { - return ( -
- -

About Me

-
- ) -} -``` - -- **@param** `load: () => Promise<{ default: ComponentType }>` -- **@return** `ComponentType` - -### `Fallback` component - -`Fallback`is a component to provide a fallback UI during the asynchronous component is loading. The fallback UI will be rendered at the SSR time. - -```tsx -import React from 'https://esm.sh/react' -import { dynamic, Fallback } from 'https://deno.land/x/aleph/framework/react/mod.ts' - -const Logo = dynamic(() => import('../components/logo.tsx')) - -export default function About() { - return ( -
- loading...

}> - -
-

About Me

-
- ) -} -``` - -- **@props** `{ to: ReactNode, children: ReactNode }` - -### `useDeno` hook - -`useDeno` receives a callback as first parameter that will be invoked at build time (SSR), then cache the callback result. In the browser, the callback will be ignored, and the cached result will be used instead. - -```tsx -import React from 'https://esm.sh/react' -import { useDeno } from 'https://deno.land/x/aleph/framework/react/mod.ts' - -export default function Page() { - const version = useDeno(() => { - return Deno.version - }) - - return ( -

Powered by Deno v{version.deno}

- ) -} -``` - -- **@param** `callback: () => T` -- **@param** `options?: { revalidate: number }` -- **@return** `T` - -### `useRouter` hook - -`useRouter` is a hook to use the `RouterURL` object. - -```jsx -import React from 'https://esm.sh/react' -import { useRouter } from 'https://deno.land/x/aleph/framework/react/mod.ts' - -export default function Component({ href, children }) { - const { - basePath, // string - locale, // string - defaultLocale, // string - locales, // string[] - pathname, // string - routePath, // string - params, // object - query, // URLSearchParams - } = useRouter() - - return

current pathname: {pathname}

-} -``` - -- **@return** `RouterURL` - -### `withRouter` hoc - -`withRouter` is a hoc to inject the `RouterURL` object to the component props. - -```jsx -import React, { Component } from 'https://esm.sh/react' -import { withRouter } from 'https://deno.land/x/aleph/framework/react/mod.ts' - -class MyComponent extends Component { - render() { - return

current pathname: {this.props.router.pathname}

- } -} - -export default withRouter(MyComponent) -``` - -- **@param** `component: ComponentType` -- **@return** `ComponentType` - -### `redirect` function - -`redirect` is a _low-level_ api to move between pages without refresh whole page, similarly to a SPA (single-page application). The second parameter `replace` is optional that makes `redirect` has same behavior with `history.replace`. - -```jsx -import React, { FC, useCallback } from 'https://esm.sh/react' -import { redirect } from 'https://deno.land/x/aleph/framework/core/mod.ts' - -export const Link: FC<{to: string, replace?: boolean}> = ({ to, replace, children }) => { - const onClick = useCallback(e => { - e.preventDefault() - redirect(to, replace) - }, [to, replace]) - - return ( - - {children} - - ) -} -``` - -- **@param** `path: string` -- **@param** `replace?: boolean` diff --git a/pages/docs/api-reference/plugin-api.md b/pages/docs/api-reference/plugin-api.md deleted file mode 100644 index d8e4348..0000000 --- a/pages/docs/api-reference/plugin-api.md +++ /dev/null @@ -1,330 +0,0 @@ ---- -title: Plugin API -authors: - - ije ---- - -# Plugin API - -In Aleph.js, a **Plugin** is an object with a `name` and a `setup` method. The -`setup` method will be invoked once before the Aleph server runtime is -initialized. - -```ts -type Plugin = { - name: string; - setup(aleph: Aleph): Promise | void; -} -``` - -## Writing First Aleph Plugin - -Here's a simple plugin example that allows you to add a virtual dist file to the -server: - -```ts -// aleph.config.ts - -import type { Config, Plugin } from 'https://deno.land/x/aleph/types.d.ts' - -const helloPlugin: Plugin = { - name: 'hello-plugin', - setup: aleph => { - aleph.addDist( - 'hello.js', - (new TextEncoder()).encode('console.log("Hello World!")'), - ) - }, -} - -export default { - plugins: [helloPlugin], -} -``` - -then you can download the `hello.js` file from -http://localhost:8080/_aleph/hello.js - -## Using `Aleph` Object - -The `Aleph` object is the server runtime reference of Aleph.js, that allows you -to hack into the server runtime lifecycle. - -#### Properties - -- **`mode`** specifies the build mode that should be **'development'** or - **'production'**. - ```ts - { - name: 'plugin-name', - setup: aleph => { - if (aleph.mode === 'development') { - console.log('development mode') - } - } - } - ``` -- **`workingDir`** shows the application absolute path that is a **read-only** - property. - ```ts - { - name: 'plugin-name', - setup: async aleph => { - const fp = path.join(aleph.workingDir, 'data.json') - const data = JSON.parse(await Deno.readFile(fp)) - } - } - ``` -- **`config`** is an object parsed from **'aleph.config.ts'**, you can update it to - add more options, check [Config](/docs/api-reference/config) to get more - usage. - ```ts - { - name: 'plugin-name', - setup: async aleph => { - aleph.config.env['foo'] = await getEnv('foo') - aleph.config.server.headers['X-Foo'] = 'bar' - } - } - ``` - -#### Methods - -- **`fetchModule`** fetches and caches the module source content. - ```ts - { - name: 'plugin-name', - setup: async aleph => { - const { content } = aleph.fetchModule(specifier) - } - } - ``` -- **`resolveImport`** resolves module import URL. - ```ts - { - name: 'plugin-name', - setup: async aleph => { - const bundleMode = true - const forceRefresh = true - const mod = aleph.addModule('https://deno.land/x/aleph/hello.ts', 'export default { ... }') - aleph.resolveImport(mod, '/app.tsx') // './-/deno.land/x/aleph/hello.js#XXX' - aleph.resolveImport(mod, '/app.tsx', !bundleMode, forceRefresh) // './-/deno.land/x/aleph/hello.bundling.js#XXX-TIME' - aleph.resolveImport(mod, '/app.tsx', bundleMode) // './-/deno.land/x/aleph/hello.bundling.js' - } - } - ``` -- **`addDist`** adds a virtual dist file to the server, then access it from `/_aleph/$NAME`. - ```ts - { - name: 'plugin-name', - setup: async aleph => { - aleph.addDist('hello.js', (new TextEncoder).encode('console.log("Hello World!")')) - } - } - ``` -- **`addModule`** adds a virtual module to the server, that can be a page, API, or CSS. - ```ts - { - name: 'plugin-name', - setup: async aleph => { - // adds a virtual module - aleph.addModule('https://deno.land/x/aleph/hello.ts', 'export default { ... }') - // adds a virtual module as API - aleph.addModule('api/hello.ts', 'export const handler = (req) => { ... }') - // adds a virtual module as Page - aleph.addModule('pages/hello.tsx', 'export default function Hello() { ... }') - // adds a virtual style module - aleph.addModule('style/app.css', 'body { font-family: sans-serif; }') - } - } - ``` - > The available module type: `js`, `jsx`, `ts`, `tsx` and `css`. - -#### Lifecycle Hooks - -- **`onResolve`** customizes how Aleph does path resolution. - ```ts - { - name: 'plugin-name', - setup: async aleph => { - aleph.onResolve(/.(md|markdown)$/, specifier => { - return { - // rewrite the import specifier to other - specifier: specifier, - // allows modules as page when it is in the `pages/` dir - asPage: true, - // allows modules to be updated at runtime during development - acceptHMR: true, - // don't download/compile remote modules, let browser handles it - external: false, - // defines any data that will be passed to the next `onResolve` hook - data: {} as any - } - }) - } - } - ``` -- **`onLoad`** allows you to load any content as a JS module, for example load - _markdown_ as pages. - ```ts - { - name: 'plugin-name', - setup: async aleph => { - // the `data` is passed from previous `onResolve` hook - aleph.onLoad(/.(md|markdown)$/, async ({ specifier, data }) => { - // loads and caches content as `Uint8Array` by the specifier - const { content } = await aleph.loadModule(specifier) - return { - // specifies the output code type (Available type: `css` | `js` | `jsx` | `ts` | `tsx`) - type: 'js', - // defines transformed code in above type - code: mdjs(content), - // provides source map if available - map: undefined, - } - }) - } - } - ``` -- **`onTransform`** injects code to compiled modules, you need to return an object with modified `code` or `undefined` to keep raw code. - ```ts - { - name: 'plugin-name', - setup: async aleph => { - // inject code to the `main.js` - aleph.onTransform('main', ({ module, code, map }) => { - return { - code: code + '\nconsole.log(":)")', - map: undefined, // provides source map if available - } - }) - // inject code to modules when the HMR is available - aleph.onTransform('hmr', ({ module, code, map }) => { - return { - code: code + '\nimport.meta.hot.accept(__REACT_REFRESH__)', - map: undefined, // provides source map if available - } - }) - // inject code to page modules - aleph.onTransform(/pages\//, ({ module, code, map, bundleMode }) => { - return { - code: code + `\nconsole.log("current module is ${module.specifier}")`, - map: undefined, // provides source map if available - } - }) - } - } - ``` -- **`onRender`** modifies the **SSR** output HTML and data. - ```ts - { - name: 'plugin-name', - setup: async aleph => { - aleph.onRender(({ path, html, data }) => { - html.head.push('') - }) - } - } - ``` - -## Examples - -The example plugins below are meant to give you an idea of the different types of things you can do with the plugin API. - -#### WASM loader - -This example plugin is a loader allows you to import `.wasm` files into JS module. - -```ts -import type { Plugin } from 'https://deno.land/x/aleph/types.d.ts' - -export default { - name: 'wasm-loader', - setup: aleph => { - aleph.onLoad(/\.wasm$/i, async ({ specifier }) => { - const { content } = await aleph.fetchModule(specifier) - return { - code: [ - `const wasmBytes = new Uint8Array([${content.join(',')}])`, - 'const wasmModule = new WebAssembly.Module(wasmBytes)', - 'const { exports } = new WebAssembly.Instance(wasmModule)', - 'export default exports', - ].join('\n'), - } - }) - }, -} -``` - -Now you can import `.wasm` files as ES Module: - -```ts -import wasm from '../lib/42.wasm' - -const answer = wasm.main() // 42 -``` - -#### Tailwind JIT for JSX - -Aleph's compiler will record the static class names in JSX files, with that you can create css on demand for tailwind very easily. - -```ts -import { basename } from 'https://deno.land/std/path/mod.ts' -import type { Plugin } from 'https://deno.land/x/aleph/types.d.ts' - -export default { - name: 'tailwind-loader', - setup: aleph => { - aleph.onTransform(/\.(j|t)sx$/i, async ({ module, code, bundleMode }) => { - const { specifier, deps, sourceHash, jsxStaticClassNames } = module - if (jsxStaticClassNames?.length) { - const url = specifier.replace(/\.(j|t)sx$/i, '') + '.tailwind.css' - const css = tailwindJITCompile(jsxStaticClassNames) - const cssModule = await aleph.addModule(url, css, true) - - return { - // import tailwind css - code: `import "${aleph.resolveImport(cssModule, specifier, bundleMode, true)}";\n${code}`, - // support SSR - extraDeps: [{ specifier: url, virtual: true }], - } - } - }) - } -} -``` - -#### Google Analytics - -This example plugin shows how to insert custom scripts to SSR output HTML. - -```ts -import { basename } from 'https://deno.land/std/path/mod.ts' -import type { Plugin } from 'https://deno.land/x/aleph/types.d.ts' - -export default { - name: 'google-analytics-plugin', - setup: aleph => { - const id = Deno.env.get('GTAID') - if (id && aleph.mode === 'production') { - aleph.onRender(({ html }) => { - html.scripts.push( - { - src: `https://www.googletagmanager.com/gtag/js?id=${encodeURIComponent(id)}`, - async: true - }, - ` - window.dataLayer = window.dataLayer || []; - function gtag() { - dataLayer.push(arguments); - } - gtag('js', new Date()); - gtag('config', ${JSON.stringify(id)}); - ` - ) - }) - } - } -} -``` diff --git a/pages/docs/basic-features/apis.md b/pages/docs/basic-features/apis.md deleted file mode 100644 index 59dc9eb..0000000 --- a/pages/docs/basic-features/apis.md +++ /dev/null @@ -1,109 +0,0 @@ ---- -title: APIs -authors: - - ije - - razermoon ---- - -# APIs - -Any file ends with `.ts`, `.js`, and `.mjs` inside the `api/` directory is mapped to `/api/*` and will be treated as an API endpoint. For an API to work, you need to export a function named to `handler`, that will accept the `APIContext` object as the first parameter. - -**For example**, the following API route `api/user.ts` sends a json response when you visit `/api/user`. - -```typescript -import type { APIHandler } from 'https://deno.land/x/aleph/types.d.ts' - -export const handler: APIHandler = ({ response }) => { - response.json({ name: 'Aleph' }) -} -``` - -## Dynamic API Routes - -Aleph.js supports **dynamic API routes** as well. For example, the route `api/user/[name].ts` has the following code: - -```typescript -import type { APIHandler } from 'https://deno.land/x/aleph/types.d.ts' - -export const handler: APIHandler = ({ response, router }) => { - response.json({ name: router.params.name }) -} -``` - -This route will handle all `/api/user/:name` requests and reply with a json response that has the `name` param. - -## `APIContext` Object - -The `APIContext` object aligns to the `Deno.RequestEvent` as the first argument of the API **handler** or **middleware**. - -```ts -interface APIContext extends Deno.RequestEvent { - data: Map // The data handled by middlewares. - response: APIResponse // An interface that aligns to the parts of the `Response` with helper methods - router: RouterURL // The router for the api routing. -} - -type APIHandler = { - (context: APIContext): Promise | void -} - -type APIMiddleware = { - (context: APIContext, next: () => void): Promise | void -} -``` - -## Middlewares - -In Aleph.js, a **middleware** of APIs is like `APIHandler` but with a `next` callback that calls next middleware or the final handler. You can have multiple middlewares that will be applied to all the API routes. - -To use middlewares add a `_middlewares.ts` in the `api/` directory and ensure export a middlewares array as default. - -```ts -// api/_middlewares.ts - -import type { APIMiddleware } from 'https://deno.land/x/aleph/types.d.ts' - -const data:APIMiddleware = ({ response, data }, next) => { - response.headers.set('server', 'Aleph.js') - data.set('foo', 'bar') - next() -} - -const auth:APIMiddleware = ({ response, data, request }, next) => { - const token = request.header.get('Authorization') - if (!token) { - response.status = 401 - response.body = 'Unauthorized' - return - } - - const currentUser = auth(token) - data.set('currentUser', currentUser) - next() - - // log message at last - console.log(response.headers.get('server')) - console.log(data.get('foo')) -} - -export default [data, auth] -``` - -As optional, you can also add global middlewares in `aleph.config.ts`, those will be put in front of `api/_middlewares.ts` defines: - -```ts -import type { Config } from 'https://deno.land/x/aleph/types.d.ts' - -export default { - server: { - middlewares: [ - ({ response, data }, next) => { - response.headers.set('server', 'Aleph.js') - data.set('foo', 'bar') - next() - } - ] - } -} -``` diff --git a/pages/docs/basic-features/built-in-css-support.md b/pages/docs/basic-features/built-in-css-support.md deleted file mode 100644 index cbfe52f..0000000 --- a/pages/docs/basic-features/built-in-css-support.md +++ /dev/null @@ -1,153 +0,0 @@ ---- -title: Built-in CSS Support -authors: - - ije ---- - -# Built-in CSS Support - -Aleph.js allows you to import **CSS** files with ESM syntax: - -```javascript -import '../style.css' -``` - -or external styles: - -```javascript -import 'https://esm.sh/tailwindcss/dist/tailwind.min.css' -``` - -## How It Works - -Aleph's built-in CSS Loader transpiles `.css` file as JS module that will be ignored in Deno runtime and be applied in browser. - -```javascript -import '../style.css'; -``` - -will become: - -```javascript -import '../style.css.js' -``` - -the `style.css.js` file looks like: - -```javascript -import { applyCSS } from 'https://deno.land/x/aleph/framework/core/style.ts' -applyCSS('/style/app.css', `${CSS_CODE}`) - -// Support HMR in development mode. -import.meta.hot.accept() -``` - -## Using `link` Tag - -**If you import CSS files using ESM syntax above, these CSS files will not be removed when page(component) unmounted.** To improve this, Aleph's compiler checks all the `link` JSX elements with `rel="stylesheet"` then transpiles them as ES modules, and these CSS files will be **cleaned up** automatically when current page(component) unmounted (we call it [JSX Magic](/docs/advanced-features/jsx-magic)). - -```tsx -import React from 'https://esm.sh/react' - -export default function App() { - return ( - <> - -

Hi :)

- - ) -} -``` - -## Inline CSS - -Aleph.js supports _Inline CSS_, that means you can write CSS in `.tsx` files directly, this concept is called **CSS-in-JS**: - -```tsx -import React from 'https://esm.sh/react' - -export default function App() { - const color = 'black' - return ( - <> - -

Hi :)

- - ) -} -``` - -## CSS Modules - -Any CSS file ending with `.module.css` is considered a [CSS modules](https://github.com/css-modules/css-modules) file: - -```tsx -import React from 'https://esm.sh/react' -import styles from '../style/app.module.css' - -export default function App() { - return ( - <> -

Hi :)

- - ) -} -``` - -With Aleph's [JSX Magic](/docs/advanced-features/jsx-magic) you can use the scoped class names via `$CLASSNAME` magic trick that gives you a better developer experience, when the CSS Modules is loaded by the `link` tag. - -```tsx -import React from 'https://esm.sh/react' - -export default function App() { - return ( - <> - -

Hi :)

- - ) -} -``` - -CSS modules behavior can be configured via the `css.modules` options, the options are passed on to [postcss-modules](https://github.com/madyankin/postcss-modules). - -```ts -// aleph.config.ts - -export default { - css: { - modules: { - scopeBehaviour: 'global', // can be 'global' or 'local' - } - } -} -``` - -## PostCSS - -If the `aleph.config.ts` contains valid `css.postcss` config, it will be automatically applied to all imported CSS. - -```ts -// aleph.config.ts - -export default { - css: { - postcss: { - plugins: ['postcss-nested', 'autoprefixer'] - } - } -} -``` - -## Global Stylesheet - -To add a global stylesheet to your application, import the CSS files in `app.tsx`. - -## CSS Imports (@import) - -Aleph.js use **esbuild** to bundle your css code that means you can use `@import` syntax safety. Or you can put the imported CSS files into the `public` directory then import them with _absolute_ URLs. diff --git a/pages/docs/basic-features/pages.md b/pages/docs/basic-features/pages.md deleted file mode 100644 index 461cb43..0000000 --- a/pages/docs/basic-features/pages.md +++ /dev/null @@ -1,112 +0,0 @@ ---- -title: Pages -authors: - - ije - - razermoon ---- - -# Pages - -In Aleph.js, a **page** is a [React Component](https://reactjs.org/docs/components-and-props.html) exported as **default** from a `.js`, `.jsx`, `.ts`, `.tsx`, or `.mjs` file in the `pages` directory. - -Each page is associated with a route based on its file name. - -**Example**: If you create `pages/about.tsx` and it exports a React component like below, it will be accessible at `/about`. - -```jsx -import React from 'https://esm.sh/react' - -export default function About() { - return

About Me

-} -``` - -> To learn more about routing, check out the [Routing documentation](/docs/basic-features/routing). - -## Pre-rendering - -By default, Aleph.js **pre-renders** every page. This means that Aleph.js generates HTML for each page in advance, instead of rendering it with client-side JavaScript. Pre-rendering can result in better performance and SEO. - -Each generated HTML page only needs a small amount of JavaScript. When a page is loaded by the browser, its JavaScript code runs and makes the page fully interactive. (This process is called _hydration_.) - -> To learn more about rendering, check out the [SSR & SSG](/docs/basic-features/ssr-and-ssg). - -## Linking Between Pages - -Aleph.js will check `anchor` tags ([JSX Magic](/docs/advanced-features/jsx-magic)) in JSX code to move between pages without refresh whole page, similarly to a SPA (single-page application). - -```tsx -import React from 'https://esm.sh/react' - -export default function Nav() { - return ( - <> - Home - About - Hello World - - ) -} -``` - -In the example above we have three links, each one maps a path (`href`) to the specified page: - -- `/` → `pages/index.tsx` -- `/about` → `pages/about.tsx` -- `/blog/hello-world` → `pages/blog/[slug].tsx` - -### NavLink - -You also can add `rel="nav"` property in the `anchor` tag to make it as `NavLink` that will add `active` className when the specific page is activated. You can change the active className via `data-active-className` or add active style using `data-active-style`. - -```tsx -import React from 'https://esm.sh/react' - -export default function Nav() { - return ( - <> - Home - About - Contact - - ) -} -``` - -### Using the `redirect` function - -You can also redirect pages with the `redirect` function: - -```jsx -import React, { FC, useCallback } from 'https://esm.sh/react' -import { redirect } from 'https://deno.land/x/aleph/framework/core/mod.ts' - -export const Link: FC<{to: string, replace?: boolean}> = ({ to, replace, children }) => { - const onClick = useCallback(e => { - e.preventDefault() - redirect(to, replace) - }, [to, replace]) - - return ( - - {children} - - ) -} -``` - -## Custom Page Loader - -By default Aleph.js only renders pages from `.js`, `.jsx`, `.ts`, `.tsx`, and `.mjs` files in the `pages` directory. You can add loader plugins to support more page formats, for example, load **markdown** as pages: - -```ts -import markdown from 'https://deno.land/x/aleph/plugins/markdown.ts' - -export default { - plugins: [ - markdown() - ] -} -``` - -> To learn more about plugins, check the [Using Plugins](/docs/advanced-features/using-plugins). diff --git a/pages/docs/basic-features/routing.md b/pages/docs/basic-features/routing.md deleted file mode 100644 index cadc203..0000000 --- a/pages/docs/basic-features/routing.md +++ /dev/null @@ -1,112 +0,0 @@ ---- -title: Routing -authors: - - ije - - razermoon ---- - -# Routing - -Aleph.js has a file-system based router built on the [concept of pages](/docs/basic-features/pages). -
-When a file (`.js`, `.jsx`, `.ts`, `.tsx`, and `.mjs`) is added to the `pages` directory, it is automatically available as a route. - -### Index Routes - -The router will automatically route files named `index` to the root of the directory. - -- `pages/index.tsx` → `/` -- `pages/blog/index.tsx` → `/blog` - -### Dynamic Routes - -To match a dynamic segment, you can use bracket syntax or start the segment with **$**: - -- `pages/blog/[slug].tsx` → `/blog/:slug` (`/blog/hello-world`) -- `pages/[username]/settings.tsx` → `/:username/settings` (`/foo/settings`) -- `pages/post/[...all].tsx` → `/post/*` (`/post/2020/id/title`) - -### Nested Routes - -Aleph.js supports nested route structures like: - -- `pages/blog.tsx` -- `pages/blog/[slug].tsx` - -In the example, routes in `/blog/:slug` will be rendered under the `pages/blog.tsx`, that is useful to create a **layout** for pages: - -```jsx -import React from 'https://esm.sh/react' -import BlogHeader from '../components/blog-header.tsx' - -export default function Blog({ Page, pageProps }) { - return ( - <> - - - - ) -} -``` - -## `RouterURL` Object - -To access the `RouterURL` object, you can use the `useRouter` hook in a component: - -```jsx -import { useRouter } from 'https://deno.land/x/aleph/framework/react/mod.ts' - -// hypothetically current location patname is '/post/hello-world?theme=dark' -export default function Component({ href, children }) { - const { - basePath, // string, should be '/' - locale, // string, should be 'en' - defaultLocale, // string, should be 'en' - locales, // string[], should be ['en'] - pathname, // string, should be '/post/hello-world' - routePath, // string, should be '/post/[slug]' - params, // object, should be {slug: 'hello-world'} - query, // URLSearchParams, `query.get('theme')` should be 'dark' - } = useRouter() - - return

current pathname: {pathname}

-} -``` - -## I18N routing - -Aleph.js don't provide **I18N** function directly, but the routing supports the **locale prefix**. You need to config the locales in `aleph.config.ts`: - -```ts -import type { Config } from 'https://deno.land/x/aleph/types.d.ts' - -export default { - i18n: { - defaultLocale: 'en', - locales: ['en', 'zh-CN'], - } -} -``` - -In the above example, all the routes will match paths with the **'zh-CN'** prefix, even if _zh-CN_ don't exist in the `pages` dir: - -- `pages/index.tsx` → `/` and `/zh-CN` (pathname is `/`) -- `pages/blog.tsx` → `/blog` and `/zh-CN/blog` (pathname is `/blog`) - -Now you can access **locale** in the [`Router`](/docs/api-reference/types.ts/#RouterURL) object using the [`useRouter`](/docs/api-reference/mod.ts#useRouter) hook: - -```jsx -import React from 'https://esm.sh/react' -import { useRouter } from 'https://deno.land/x/aleph/framework/react/mod.ts' - -export default function Page() { - const { locale } = useRouter() - - if (locale === 'zh-CN') { - return

你好 :)

- } - return

Hello :)

-} -``` - -And the `build` command (**SSG**) will generate all the locale pages from the locale list. diff --git a/pages/docs/basic-features/ssr-and-ssg.md b/pages/docs/basic-features/ssr-and-ssg.md deleted file mode 100644 index 545ec94..0000000 --- a/pages/docs/basic-features/ssr-and-ssg.md +++ /dev/null @@ -1,131 +0,0 @@ ---- -title: SSR & SSG -authors: - - ije - - Serdar Sever - - razermoon ---- - -# SSR & SSG - -By default, Aleph.js **pre-renders** every page. This means that Aleph.js generates HTML for each page in advance, instead of rendering it with client-side JavaScript. Pre-rendering can result in better performance and SEO. - -Each generated HTML page only needs a small amount of JavaScript. When a page is loaded by the browser, its JavaScript code runs and makes the page fully interactive. (This process is called _hydration_.) - -You can disable **SSR** functionality in `aleph.config.ts`: - -```ts -export default { - ssr: false, // SPA mode - ... -} -``` - -or specify exclude paths: - -```ts -export default { - ssr: { - exclude: [ - /^\/admin\//, - /^\/dashboard\// - ] - }, - ... -} -``` - -## SSR Options - -If you export an object called `ssr` with a `props` function from a page, Aleph.js will pre-render this page using the returned props by the `props` function at build time. The [`Request`](https://developer.mozilla.org/en-US/docs/Web/API/Request/Request) object is passed as the first parameter of the function. (This is equal to `getStaticProps` of Next.js) - -The `paths` in the `ssr` options returns a static paths if the page is a dynamic route. (This is equal to `getStaticPaths` of Next.js) - -```tsx -import React from 'https://esm.sh/react' -import type { SSROptions } from 'https://deno.land/x/aleph/types.d.ts' - -export const ssr: SSROptions = { - props: async req => { - return { - $revalidate: 1, // revalidate props after 1 second - username: await auth(req.headers.get('Auth-Token')), - serverTime: Date.now() - } - }, - paths: async () => { - return [] - } -} - -export default function Page(props) { - return ( -

Welcome back {props.username}, the server time is {props.serverTime}

- ) -} -``` - -We don't provide the `getServerSideProps` of Next.js, instead we allow the `ssr.props` access to `Request`, with `$revalidate` in the returned props equals `true` or `0`, you can complete same thing. - -```ts -export const ssr: SSROptions = { - props: async req => { - return { - $revalidate: 0, // revalidate props each request - username: await auth(req.headers.get('Auth-Token')) - } - } -} -``` - -## `useDeno` hook - -Aleph.js also provides an `useDeno` hook to mix the **Deno** runtime in your component: - -```tsx -import React from 'https://esm.sh/react' -import { useDeno } from 'https://deno.land/x/aleph/framework/react/mod.ts' - -export default function Page() { - const version = useDeno(() => { - return Deno.version - }) - - return ( -

Powered by Deno v{version.deno}

- ) -} -``` - -> To learn more `useDeno`, check the [`useDeno` Hook](/docs/advanced-features/use-deno-hook) documentation. - -## Static Site Generation (SSG) - -Aleph.js can export your app as a **static site** with rendered htmls, which can be served on any server or CDN. - -```bash -$ aleph build -``` - -For **dynamic routes** with SSR, your can define the **static paths** in the `ssr` options: - -```tsx -// pages/post/[id].tsx - -import type { SSROptions } from 'https://deno.land/x/aleph/types.d.ts' - -export const ssr: SSROptions = { - paths: async () => { - const posts = await (await fetch('https://.../api/posts')).json() - return posts.map(({ id }) => `/post/${id}`) - } -} - -export default function Post() { - return ( -
...
- ) -} -``` - -> See the [hello-world](https://alephjs-hello-world.vercel.app/) example on [Vercel](https://vercel.com). diff --git a/pages/docs/browser-support.md b/pages/docs/browser-support.md deleted file mode 100644 index f5541b7..0000000 --- a/pages/docs/browser-support.md +++ /dev/null @@ -1,32 +0,0 @@ ---- -title: Browser Support -authors: - - ije - - razermoon ---- - -# Browser Support - -Aleph.js requires a modern browser to support [native ES module imports](https://caniuse.com/#feat=es6-module) and **dynamic imports** during **development**: - -- Chrome >= 61 -- Edge >= 16 -- Firefox >= 60 -- Safari >= 11 -- Opera >= 48 - -## Build Target, Browserslist, and Polyfills - -Aleph.js uses **esbuild** to bundle modules at build time for production. You can set the `build.target` and `build.browsers` in `aleph.config.ts`, a **polyfills** file will be created automatically that is based on the build target. - -```ts -export default { - build: { - target: 'es2015', // available targets: es2015-es2021, esnext - browsers: { - chrome: 70, - safari: 12 - } - } -} -``` \ No newline at end of file diff --git a/pages/docs/deployment.md b/pages/docs/deployment.md deleted file mode 100644 index cf07669..0000000 --- a/pages/docs/deployment.md +++ /dev/null @@ -1,46 +0,0 @@ ---- -title: Deployment -authors: - - ije - - razermoon ---- - -# Deployment - -Aleph.js allows you to export your application to a **static site**, which can run standalone on any server: - -```bash -$ aleph build -``` - -or run it in **production** mode with Aleph.js server and get API routes/ISR support in Deno: - -```bash -$ deno run -A https://deno.land/x/aleph@${VERSION}/cli.ts start -``` - -## Deploy on Vercel (Recommended) - -To deploy your app to [Vercel](https://vercel.com), you need to configure your _vercel project_ manually: - -- **Build Command**: `deno run -A https://deno.land/x/aleph@${VERSION}/cli.ts build` -- **Output Directory**: `dist` (you can override it in `aleph.config.ts`) -- **Install Command**: `curl -fsSL https://deno.land/x/install/install.sh | DENO_INSTALL=/usr/local sh` -- **Functions Runtime**: [vercel-aleph@0.7.0](https://github.com/alephjs/vercel-aleph) - -![vercel-settings](/vercel-settings.png) - -> See the [hello-world](https://alephjs-hello-world.vercel.app/) example on [Vercel](https://vercel.com). - -## Deploy on Deno Deploy ™️ - -_In the plan, currently not supported._ - -## Deploy on Fleek - -To deploy your app to [Fleek](https://fleek.co), you need to configure your _fleek project_ manually: - -- **Build Command**: `deno run -A https://deno.land/x/aleph@${VERSION}/cli.ts build` -- **Docker Image Name**: `hayd/deno:latest` -- **Output Directory**: `dist` (**outputDir**, you can override it in `aleph.config.js`) -- **API Routes (Functions)**: _currently not supported_ diff --git a/pages/docs/design/artworks.tsx b/pages/docs/design/artworks.tsx deleted file mode 100644 index 95ff1d1..0000000 --- a/pages/docs/design/artworks.tsx +++ /dev/null @@ -1,20 +0,0 @@ -import React from 'react' - -export default function Artworks() { - return ( -
-

-

Logo (main) - Download

-
-

-

Logo (filled) - Download

-
- ) -} - -Artworks.meta = { - title: 'Artworks', - author: 'The Aleph.js Authors', - date: '2020-10-20', - editable: false -} diff --git a/pages/docs/design/theme.tsx b/pages/docs/design/theme.tsx deleted file mode 100644 index 9aba9d8..0000000 --- a/pages/docs/design/theme.tsx +++ /dev/null @@ -1,27 +0,0 @@ -import React from 'react' - -export default function Theme() { - return ( -
-

-
-

-
-

#D63369

-

Theme Color

-
-

#008181

-

Theme Color (second)

-
-

#111111

-

Theme Color (black)

-
- ) -} - -Theme.meta = { - title: 'Theme', - author: 'The Aleph.js Authors', - date: '2020-10-20', - editable: false -} diff --git a/pages/docs/get-started.md b/pages/docs/get-started.md deleted file mode 100644 index c6a52cc..0000000 --- a/pages/docs/get-started.md +++ /dev/null @@ -1,65 +0,0 @@ ---- -title: Get Started -authors: - - ije - - razermoon ---- - -# Get Started - -Welcome to use **Aleph.js**! - -If you are new to Aleph.js you should check out the [About](/docs/) page. - -## Installation - -you will need [Deno](https://deno.land/#installation) **1.13+** to run Aleph.js server. -```bash -$ deno run -A https://deno.land/x/aleph/install.ts -``` - -or use [land](https://deno.land/x/land) without installation: - -```bash -$ land aleph -``` - -## Usage - -**Create a new app**: - -```bash -$ aleph init -``` - -**Start the app in `development` mode**: - -```bash -$ aleph dev -``` - -**Start the app in `production` mode**: - -```bash -$ aleph start -``` - -The application will start at http://localhost:8080 by default. -
-The default port can be changed with `-p` (or `--port`) flag. - -**Build the app to a static site (SSG)**: - -```bash -$ aleph build -``` - -This will export a static site to the `output` directory, which can be run standalone on any server. - -> See the [hello-world](https://alephjs-hello-world.vercel.app/) example on [Vercel](https://vercel.com). - -**More usages**: - -```bash -$ aleph -h -``` diff --git a/pages/docs/index.md b/pages/docs/index.md deleted file mode 100644 index cf713f2..0000000 --- a/pages/docs/index.md +++ /dev/null @@ -1,62 +0,0 @@ ---- -title: About Aleph.js -authors: - - ije - - razermoon ---- - -# Aleph.js - -**Aleph.js** (or **Aleph** or **א** or **阿莱夫**, ˈɑːlɛf) is a fullstack framework in [Deno], inspired by [Next.js]. - -> The name is taken from the book [_The Aleph_] by **Jorge Luis Borges**. - -Different with Next.js, Aleph.js doesn't need **webpack** or other bundler since it uses the [ES Module] syntax during development. Every module only needs to be compiled once, and then cached on the disk. -When a module changes, Aleph.js just needs to re-compile that single module. There is no time wasted _re-bundling_ everytime a change is made. This, along with Hot Module Replacement (**HMR**) and **Fast Refresh**, leads to instant updates in the browser. - -Aleph.js uses modern tools to build your app. It transpiles code using [swc] in WASM with high performance, and bundles modules with [esbuild] at build time extremely fast. - -Aleph.js works on top of **Deno**, a _simple_, _modern_ and _secure_ runtime for JavaScript and TypeScript. All dependencies are imported using URLs, and managed by Deno cache system. No `package.json` and `node_modules` directory needed. - -```jsx -import React from 'https://esm.sh/react' -import Logo from '../components/logo.tsx' - -export default function Home() { - return ( -
- -

Hello World!

-
- ) -} -``` - -## Features - -- Zero Config -- Typescript in Deno -- ES Module Ready -- Import Maps -- HMR with Fast Refresh -- File-system Routing -- SSR/SSG -- JSX Magic -- Plugins System -- Powered by Modern Tools - -## Status - -Currently in **beta**, not ready for production. - -## License - -Under the [MIT] License. - -[_The Aleph_]: http://phinnweb.org/links/literature/borges/aleph.html -[ES Moudule]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Guide/Modules -[deno]: https://deno.land -[next.js]: https://nextjs.org -[swc]: https://swc.rs -[esbuild]: https://github.com/evanw/esbuild -[MIT]: https://opensource.org/licenses/MIT diff --git a/pages/docs/plugins/community-plugins.md b/pages/docs/plugins/community-plugins.md deleted file mode 100644 index 9acb4c7..0000000 --- a/pages/docs/plugins/community-plugins.md +++ /dev/null @@ -1,11 +0,0 @@ ---- -title: Community Plugins -authors: - - ije ---- - -# Community Plugins - -- [WindiCSS](https://deno.land/x/aleph_plugin_windicss) - The [WindiCSS](https://windicss.org/) Plugin for Aleph.js - -To contribute an Aleph.js Plugin, please check out the [Plugin API](/docs/api-reference/plugin-api) Reference. \ No newline at end of file diff --git a/pages/docs/plugins/official-plugins.md b/pages/docs/plugins/official-plugins.md deleted file mode 100644 index 6a71012..0000000 --- a/pages/docs/plugins/official-plugins.md +++ /dev/null @@ -1,34 +0,0 @@ ---- -title: Official Plugins -authors: - - ije ---- - -# Official Plugins - -- [CSS Loader](https://deno.land/x/aleph/plugins/css.ts) - builtin CSS loader (**don't use**) - ```ts - import 'https://esm.sh/tailwindcss/dist/tailwind.min.css' - ``` -- [JSON Loader](https://deno.land/x/aleph/plugins/json.ts) - load JSON as module - ```ts - import json from 'https://deno.land/x/aleph/plugins/json.ts' - - export default { - plugins: [ - json() - ] - } - ``` - **limit**: don't use it in APIs since Deno doesn't support JSON module yet. -- [Markdown Loader](https://deno.land/x/aleph/plugins/markdown.ts) - load markdown as page - ```ts - import markdown from 'https://deno.land/x/aleph/plugins/markdown.ts' - - export default { - plugins: [ - markdown() - ] - } - ``` - now, you can add `{dir}/{name}.md` in `pages/` dir as pages. You can link between pages with `[title](path)`. For code **highlight**, check [this example](https://github.com/alephjs/aleph.js/blob/master/examples/markdown-pages/aleph.config.ts#L7). diff --git a/pages/index.tsx b/pages/index.tsx deleted file mode 100644 index 7bc307f..0000000 --- a/pages/index.tsx +++ /dev/null @@ -1,94 +0,0 @@ -import { dynamic, Fallback, useDeno } from 'aleph/react' -import React from 'react' -import Button from '~/components/Button.tsx' -import GreyTriangle from '~/components/GreyTriangle.tsx' - -const thisYear = (new Date).getFullYear() -const title = 'Aleph.js' -const about = 'The Fullstack Framework in Deno.' -const keywords = [ - 'aleph', - 'alephjs', - 'aleph.js', - 'react', - 'full-stack', - 'framework', - 'ssr', - 'ssg', - 'deno', - 'typescript', - 'out-of-the-box', - 'esm', - 'hmr', - 'fast-refresh', - 'tooling' -] -const ogImage = 'https://alephjs.org/twitter_card.jpg' -const features = [ - { href: '/docs', title: 'Zero Config' }, - { href: '/docs', title: 'Typescript in Deno' }, - { href: '/docs/basic-features/import-from-npm', title: 'ES Module Ready' }, - { href: '/docs/basic-features/import-maps', title: 'Import Maps' }, - { href: '/docs/basic-features/hmr-with-fast-refresh', title: 'HMR with Fast Refresh' }, - { href: '/docs/basic-features/routing', title: 'File-system Routing' }, - { href: '/docs/basic-features/ssr-and-ssg', title: 'SSR & SSG' }, - { href: '/docs/basic-features/built-in-css-support', title: 'Built-in CSS Support' }, - { href: '/docs/advanced-features/jsx-magic', title: 'JSX Magic' }, - { href: '/docs/advanced-features/using-plugins', title: 'Plugin System' }, -] - -const UniverseTriangle = dynamic(() => import('~/components/UniverseTriangle.tsx')) - -export default function Home() { - const { version } = useDeno(() => ({ - version: Deno.env.get('ALEPH_VERSION') - })) - - return ( -
- - {title} - - - - - - - - - - - - -
- }> - - -

The Fullstack Framework in Deno.

-

Aleph.js gives you the best developer experience for building modern web applications:
TypeScript in Deno, ES module imports, file-system routing, SSR & SSG,
HMR with Fast Refresh, and more. No config needed.

-

Aleph.js gives you the best developer experience for building modern web applications in Deno.

- -
-
-

Features

-
    - {features.map(({ href, title }) => ( -
  • {title}
  • - ))} -
-
-
-

Copyright © {thisYear} postUI, Lab. All rights reserved.

-

Built by Aleph.js - v{version}

-

(MIT License)

-
-
- ) -} diff --git a/public/star.png b/public/star.png deleted file mode 100644 index a2b74e8..0000000 Binary files a/public/star.png and /dev/null differ diff --git a/public/vercel-settings.png b/public/vercel-settings.png deleted file mode 100644 index 53c8272..0000000 Binary files a/public/vercel-settings.png and /dev/null differ diff --git a/routes/_export.ts b/routes/_export.ts new file mode 100644 index 0000000..01db58d --- /dev/null +++ b/routes/_export.ts @@ -0,0 +1,306 @@ +// Imports route modules for serverless env that doesn't support the dynamic import. +// This module will be updated automaticlly in develoment mode, do NOT edit it manually. +// deno-fmt-ignore-file +// deno-lint-ignore-file +// @ts-nocheck +var Se=Object.defineProperty;var h=(n,e)=>{for(var re in e)Se(n,re,{get:e[re],enumerable:!0})};import*as Kn from"./index.tsx";import*as Qn from"./docs.tsx";var $={};h($,{default:()=>Re});import{Fragment as Ce,jsx as d,jsxs as _}from"https://esm.sh/react@18.2.0/jsx-runtime";function te(n){let e=Object.assign({h1:"h1",p:"p",a:"a",strong:"strong",ul:"ul",li:"li",h2:"h2",code:"code",pre:"pre",span:"span"},n.components);return _(Ce,{children:[d(e.h1,{children:"Browser Support"}),` +`,_(e.p,{children:[`Aleph.js requires a modern browser to support +`,d(e.a,{href:"https://caniuse.com/#feat=es6-module",children:"native ES module imports"})," and ",d(e.strong,{children:`dynamic +imports`}),":"]}),` +`,_(e.ul,{children:[` +`,d(e.li,{children:"Chrome >= 61"}),` +`,d(e.li,{children:"Edge >= 16"}),` +`,d(e.li,{children:"Firefox >= 60"}),` +`,d(e.li,{children:"Safari >= 11"}),` +`,d(e.li,{children:"Opera >= 48"}),` +`]}),` +`,d(e.h2,{children:"Build Target"}),` +`,_(e.p,{children:["Aleph.js uses ",d(e.strong,{children:"esbuild"}),` to bundle modules at optimization time for production. +You can set the `,d(e.code,{children:"optimization.buildTarget"})," in the server config."]}),` +`,d(e.pre,{children:_(e.code,{className:"hljs language-js",children:[d(e.span,{className:"hljs-keyword",children:"import"})," { serve } ",d(e.span,{className:"hljs-keyword",children:"from"})," ",d(e.span,{className:"hljs-string",children:'"aleph/server"'}),`; + +`,d(e.span,{className:"hljs-title function_",children:"serve"}),`({ + `,d(e.span,{className:"hljs-attr",children:"optimization"}),`: { + `,d(e.span,{className:"hljs-attr",children:"buildTarget"}),": ",d(e.span,{className:"hljs-string",children:'"es2020"'}),`, + }, +}); +`]})})]})}function Fe(n={}){let{wrapper:e}=n.components||{};return e?d(e,Object.assign({},n,{children:d(te,n)})):te(n)}var Re=Fe;var T={};h(T,{default:()=>Ae});import{Fragment as Oe,jsx as g,jsxs as y}from"https://esm.sh/react@18.2.0/jsx-runtime";function ce(n){let e=Object.assign({h1:"h1",p:"p",code:"code",pre:"pre",h2:"h2",a:"a",blockquote:"blockquote"},n.components);return y(Oe,{children:[g(e.h1,{children:"Deployment on your own host with Deno CLI"}),` +`,y(e.p,{children:["You can run your app in production mode by ",g(e.code,{children:"deno"})," CLI."]}),` +`,g(e.pre,{children:g(e.code,{className:"hljs language-bash",children:`deno run --allow-network --allow-env --allow-read --allow-write server.ts +`})}),` +`,g(e.h2,{children:"Deploy on Deno Deploy"}),` +`,y(e.p,{children:["To deploy your app to ",g(e.a,{href:"https://deno.com/deploy",children:"Deno Deploy"}),", please push your app to ",g(e.a,{href:"https://github.com",children:"Github"})," and create a new project on ",g(e.a,{href:"https://dash.deno.com/new",children:"Deno Deploy"}),"."]}),` +`,y(e.p,{children:["Then link to the Repo and set the entrypoint to ",g(e.code,{children:"server.ts"})]}),` +`,y(e.blockquote,{children:[` +`,y(e.p,{children:["See the ",g(e.a,{href:"https://aleph-hello.deno.dev/",children:"hello-world"})," example on ",g(e.a,{href:"https://deno.com/deploy",children:"Deno Deploy"}),"."]}),` +`]})]})}function Xe(n={}){let{wrapper:e}=n.components||{};return e?g(e,Object.assign({},n,{children:g(ce,n)})):ce(n)}var Ae=Xe;var P={};h(P,{default:()=>$e});import{Fragment as Le,jsx as a,jsxs as m}from"https://esm.sh/react@18.2.0/jsx-runtime";function ie(n){let e=Object.assign({h1:"h1",p:"p",strong:"strong",a:"a",blockquote:"blockquote",em:"em",code:"code",h2:"h2",ul:"ul",li:"li"},n.components);return m(Le,{children:[a(e.h1,{children:"Aleph.js"}),` +`,m(e.p,{children:[a(e.strong,{children:"Aleph.js"})," (or ",a(e.strong,{children:"Aleph"})," or ",a(e.strong,{children:"\u05D0"})," or ",a(e.strong,{children:"\u963F\u83B1\u592B"}),", ",a("samp",{children:"\u02C8\u0251\u02D0l\u025Bf"}),`) is a +fullstack framework in `,a(e.a,{href:"https://deno.land",children:"Deno"}),". Inspired by ",a(e.a,{href:"https://nextjs.org",children:"Next.js"}),", ",a(e.a,{href:"https://remix.run",children:"Remix"})," and ",a(e.a,{href:"https://vitejs.dev",children:"Vite"}),"."]}),` +`,m(e.blockquote,{children:[` +`,m(e.p,{children:["The name is taken from the book ",a(e.a,{href:"http://phinnweb.org/links/literature/borges/aleph.html",children:a(e.em,{children:"The Aleph"})})," by ",a(e.strong,{children:"Jorge Luis Borges"}),"."]}),` +`]}),` +`,m(e.p,{children:["Aleph.js is modern framework that doesn't need ",a(e.strong,{children:"webpack"}),` or other bundler +since it uses the `,a(e.a,{href:"https://developer.mozilla.org/en-US/docs/Web/JavaScript/Guide/Modules",children:"ES Module"}),` syntax during development. Every module only needs +to be compiled once, when a module changes, Aleph.js just needs to re-compile +that single module. There is no time wasted `,a(e.em,{children:"re-bundling"}),` everytime a change is +made. This, along with Hot Module Replacement (`,a(e.strong,{children:"HMR"}),") and ",a(e.strong,{children:"Fast Refresh"}),`, +leads to instant updates in the browser.`]}),` +`,m(e.p,{children:["Aleph.js uses modern tools to build your app. It transpiles code using ",a(e.a,{href:"https://swc.rs",children:"swc"}),` in +WASM with high performance, and bundles modules with `,a(e.a,{href:"https://github.com/evanw/esbuild",children:"esbuild"}),` at optimization +time extremely fast.`]}),` +`,m(e.p,{children:["Aleph.js works on top of ",a(e.strong,{children:"Deno"}),", a ",a(e.em,{children:"simple"}),", ",a(e.em,{children:"modern"})," and ",a(e.em,{children:"secure"}),` runtime for +JavaScript and TypeScript. All dependencies are imported using URLs, and managed +by Deno cache system. No `,a(e.code,{children:"package.json"})," and ",a(e.code,{children:"node_modules"})," directory needed."]}),` +`,a(e.h2,{children:"Features"}),` +`,m(e.ul,{children:[` +`,a(e.li,{children:"Zero Config"}),` +`,a(e.li,{children:"No build step"}),` +`,a(e.li,{children:"File-system Routing"}),` +`,a(e.li,{children:"Just-in-time Server-side Rendering(SSR)"}),` +`,a(e.li,{children:"Streaming SSR"}),` +`,a(e.li,{children:"Support Typescript/JSX in Deno out of the box"}),` +`,m(e.li,{children:["Built-in ",a(e.a,{href:"https://github.com/unocss/unocss",children:"Unocss"})," (automatic CSS)"]}),` +`,a(e.li,{children:"Import Maps"}),` +`,a(e.li,{children:"Hot Module Replacement (or HMR)"}),` +`,a(e.li,{children:"Support Middware"}),` +`,a(e.li,{children:"Custom Module Loader like MDX"}),` +`]}),` +`,a(e.h2,{children:"Supported frameworks"}),` +`,m(e.ul,{children:[` +`,a(e.li,{children:a(e.a,{href:"https://github.com/alephjs/aleph.js/tree/main/examples/react-app",children:"React"})}),` +`,a(e.li,{children:a(e.a,{href:"https://github.com/alephjs/aleph.js/tree/main/examples/react-mdx-app",children:"React with MDX"})}),` +`,a(e.li,{children:a(e.a,{href:"https://github.com/alephjs/aleph.js/tree/main/examples/vue-app",children:"Vue"})}),` +`,m(e.li,{children:[a(e.a,{href:"https://github.com/alephjs/aleph.js/tree/main/examples/solid-app",children:"Solid"}),` +`,a(e.em,{children:"Experimental"})]}),` +`,m(e.li,{children:[a(e.a,{href:"https://github.com/alephjs/aleph.js/tree/main/examples/yew-app",children:"Yew"})," ",a(e.em,{children:"Rust"})]}),` +`]}),` +`,m(e.p,{children:["Plan to support: ",a(e.a,{href:"https://svelte.dev/",children:"Svelte"}),", ",a(e.a,{href:"https://lit.dev/",children:"Lit"}),", etc.."]}),` +`,a(e.h2,{children:"Examples"}),` +`,m(e.p,{children:["Some demo apps deployed to ",a(e.a,{href:"https://deno.com/deploy",children:"Deno Deploy"}),":"]}),` +`,m(e.ul,{children:[` +`,a(e.li,{children:"React App: https://aleph-hello.deno.dev/"}),` +`,a(e.li,{children:"Vue App: https://aleph-vue.deno.dev/"}),` +`,a(e.li,{children:"REST API: https://aleph-api.deno.dev/"}),` +`,a(e.li,{children:"React 18 Suspense SSR: https://aleph-suspense-ssr.deno.dev/"}),` +`,a(e.li,{children:"UnoCSS(tailwind): https://aleph-unocss.deno.dev/"}),` +`,a(e.li,{children:"Monaco Editor: https://aleph-monaco-editor.deno.dev/"}),` +`,a(e.li,{children:"Yew SSR: https://aleph-yew.deno.dev/"}),` +`,a(e.li,{children:"Github OAuth Middleware: https://aleph-github-oauth.deno.dev/"}),` +`]}),` +`,a(e.h2,{children:"Real-world Apps"}),` +`,m(e.ul,{children:[` +`,a(e.li,{children:"Deno Deploy: https://dash.deno.com"}),` +`,m(e.li,{children:[`Meet Me: https://meet-me.deno.dev +(`,a(e.a,{href:"https://github.com/denoland/meet-me",children:"source"}),")"]}),` +`]}),` +`,a(e.h2,{children:"Status"}),` +`,m(e.p,{children:["Currently in ",a(e.strong,{children:"beta"}),", not ready for production."]}),` +`,a(e.h2,{children:"License"}),` +`,m(e.p,{children:["Under the ",a(e.a,{href:"https://opensource.org/licenses/MIT",children:"MIT"})," License."]})]})}function Ie(n={}){let{wrapper:e}=n.components||{};return e?a(e,Object.assign({},n,{children:a(ie,n)})):ie(n)}var $e=Ie;var W={};h(W,{default:()=>We});import{Fragment as Te,jsx as u,jsxs as M}from"https://esm.sh/react@18.2.0/jsx-runtime";function oe(n){let e=Object.assign({h1:"h1",p:"p",strong:"strong",a:"a",h2:"h2",pre:"pre",code:"code"},n.components);return M(Te,{children:[u(e.h1,{children:"Get Started!"}),` +`,M(e.p,{children:["Welcome to use ",u(e.strong,{children:"Aleph.js"}),"!"]}),` +`,M(e.p,{children:["If you are new to Aleph.js you should check out the ",u(e.a,{href:"/docs/",children:"About"})," page."]}),` +`,u(e.h2,{children:"Usage"}),` +`,u(e.p,{children:"Create a new app:"}),` +`,u(e.pre,{children:u(e.code,{className:"hljs language-bash",children:`deno run -A -r https://alephjs.org/init.ts +`})}),` +`,M(e.p,{children:["Start the app in ",u(e.code,{children:"development"})," mode:"]}),` +`,u(e.pre,{children:u(e.code,{className:"hljs language-bash",children:`deno task dev +`})}),` +`,M(e.p,{children:["Start the app in ",u(e.code,{children:"production"})," mode:"]}),` +`,u(e.pre,{children:u(e.code,{className:"hljs language-bash",children:`deno task start +`})}),` +`,u(e.p,{children:"Optimize the application (bundling, ssg, etc.):"}),` +`,u(e.pre,{children:u(e.code,{className:"hljs language-bash",children:`deno task opt +`})})]})}function Pe(n={}){let{wrapper:e}=n.components||{};return e?u(e,Object.assign({},n,{children:u(oe,n)})):oe(n)}var We=Pe;var H={};h(H,{default:()=>Ue});import{Fragment as He,jsx as v,jsxs as qe}from"https://esm.sh/react@18.2.0/jsx-runtime";function he(n){let e=Object.assign({h1:"h1",p:"p",em:"em"},n.components);return qe(He,{children:[v(e.h1,{children:"Vue"}),` +`,v(e.p,{children:v(e.em,{children:"WIP"})})]})}function Ee(n={}){let{wrapper:e}=n.components||{};return e?v(e,Object.assign({},n,{children:v(he,n)})):he(n)}var Ue=Ee;var q={};h(q,{default:()=>Ge});import{Fragment as Je,jsx as k,jsxs as Ye}from"https://esm.sh/react@18.2.0/jsx-runtime";function de(n){let e=Object.assign({h1:"h1",p:"p",em:"em"},n.components);return Ye(Je,{children:[k(e.h1,{children:"React with MDX"}),` +`,k(e.p,{children:k(e.em,{children:"WIP"})})]})}function Be(n={}){let{wrapper:e}=n.components||{};return e?k(e,Object.assign({},n,{children:k(de,n)})):de(n)}var Ge=Be;var E={};h(E,{default:()=>Ke});import{Fragment as ze,jsx as D,jsxs as Ve}from"https://esm.sh/react@18.2.0/jsx-runtime";function pe(n){let e=Object.assign({h1:"h1",p:"p",em:"em"},n.components);return Ve(ze,{children:[D(e.h1,{children:"SolidJS"}),` +`,D(e.p,{children:D(e.em,{children:"WIP"})})]})}function Ze(n={}){let{wrapper:e}=n.components||{};return e?D(e,Object.assign({},n,{children:D(pe,n)})):pe(n)}var Ke=Ze;var U={};h(U,{default:()=>sn});import{Fragment as Qe,jsx as S,jsxs as en}from"https://esm.sh/react@18.2.0/jsx-runtime";function me(n){let e=Object.assign({h1:"h1",p:"p",em:"em"},n.components);return en(Qe,{children:[S(e.h1,{children:"React"}),` +`,S(e.p,{children:S(e.em,{children:"WIP"})})]})}function nn(n={}){let{wrapper:e}=n.components||{};return e?S(e,Object.assign({},n,{children:S(me,n)})):me(n)}var sn=nn;var J={};h(J,{default:()=>tn});import{Fragment as an,jsx as C,jsxs as ln}from"https://esm.sh/react@18.2.0/jsx-runtime";function ue(n){let e=Object.assign({h1:"h1",p:"p",em:"em"},n.components);return ln(an,{children:[C(e.h1,{children:"Yew"}),` +`,C(e.p,{children:C(e.em,{children:"WIP"})})]})}function rn(n={}){let{wrapper:e}=n.components||{};return e?C(e,Object.assign({},n,{children:C(ue,n)})):ue(n)}var tn=rn;var Y={};h(Y,{default:()=>hn});import{Fragment as cn,jsx as l,jsxs as b}from"https://esm.sh/react@18.2.0/jsx-runtime";function je(n){let e=Object.assign({h1:"h1",p:"p",strong:"strong",code:"code",pre:"pre",span:"span",h2:"h2",em:"em",h4:"h4"},n.components);return b(cn,{children:[l(e.h1,{children:"Middleware API"}),` +`,b(e.p,{children:["In Aleph.js, a ",l(e.strong,{children:"Middleware"})," is an object with a ",l(e.code,{children:"name"})," and a ",l(e.code,{children:"fetch"}),` method. +The `,l(e.code,{children:"fetch"}),` method will be invoked when a request is received by the server. And +the `,l(e.code,{children:"fetch"})," method will end the request if returns a ",l(e.code,{children:"Response"})," object."]}),` +`,l(e.pre,{children:b(e.code,{className:"hljs language-ts",children:[l(e.span,{className:"hljs-keyword",children:"type"})," ",l(e.span,{className:"hljs-title class_",children:"Middleware"}),` = { + `,l(e.span,{className:"hljs-attr",children:"name"}),": ",l(e.span,{className:"hljs-built_in",children:"string"}),`; + `,l(e.span,{className:"hljs-title function_",children:"fetch"}),"(",l(e.span,{className:"hljs-attr",children:"req"}),": ",l(e.span,{className:"hljs-title class_",children:"Request"}),", ",l(e.span,{className:"hljs-attr",children:"context"}),": ",l(e.span,{className:"hljs-title class_",children:"Context"}),"): ",l(e.span,{className:"hljs-title class_",children:"Promise"}),"<",l(e.span,{className:"hljs-title class_",children:"Response"}),"> | ",l(e.span,{className:"hljs-title class_",children:"Response"})," | ",l(e.span,{className:"hljs-built_in",children:"void"}),`; +}; +`]})}),` +`,l(e.h2,{children:"Use Middlewares"}),` +`,l(e.p,{children:`Here's a simple plugin example that allows you to add a virtual dist file to the +server:`}),` +`,l(e.pre,{children:b(e.code,{className:"hljs language-ts",children:[l(e.span,{className:"hljs-comment",children:"// server.ts"}),` + +`,l(e.span,{className:"hljs-keyword",children:"import"})," foo ",l(e.span,{className:"hljs-keyword",children:"from"})," ",l(e.span,{className:"hljs-string",children:'"./middleware/foo.ts"'}),`; + +`,l(e.span,{className:"hljs-title function_",children:"serve"}),`({ + `,l(e.span,{className:"hljs-attr",children:"middlewares"}),`: [ + foo, + { + `,l(e.span,{className:"hljs-attr",children:"name"}),": ",l(e.span,{className:"hljs-string",children:'"my-middleware"'}),`, + `,l(e.span,{className:"hljs-title function_",children:"fetch"}),"(",l(e.span,{className:"hljs-params",children:"req"}),`) { + `,l(e.span,{className:"hljs-keyword",children:"if"})," (req.",l(e.span,{className:"hljs-property",children:"url"})," === ",l(e.span,{className:"hljs-string",children:'"/my-middleware"'}),`) { + `,l(e.span,{className:"hljs-keyword",children:"return"})," ",l(e.span,{className:"hljs-keyword",children:"new"})," ",l(e.span,{className:"hljs-title class_",children:"Response"}),"(",l(e.span,{className:"hljs-string",children:'"Hello, Middleware!"'}),`); + } + }, + }, + ], +}); +`]})}),` +`,b(e.h2,{children:["Use ",l(e.code,{children:"Context"})," Object"]}),` +`,l(e.p,{children:l(e.em,{children:"WIP"})}),` +`,l(e.h2,{children:"Examples"}),` +`,l(e.p,{children:`The example plugins below are meant to give you an idea of the different types +of things you can do with the plugin API.`}),` +`,l(e.h4,{children:"Google Analytics"}),` +`,l(e.p,{children:"This example plugin shows how to insert custom scripts to SSR output HTML using middleware."}),` +`,l(e.pre,{children:b(e.code,{className:"hljs language-ts",children:[l(e.span,{className:"hljs-comment",children:"// wIP"}),` +`]})})]})}function on(n={}){let{wrapper:e}=n.components||{};return e?l(e,Object.assign({},n,{children:l(je,n)})):je(n)}var hn=on;var B={};h(B,{default:()=>un});import{Fragment as dn,jsx as F,jsxs as pn}from"https://esm.sh/react@18.2.0/jsx-runtime";function ge(n){let e=Object.assign({h1:"h1",p:"p",em:"em"},n.components);return pn(dn,{children:[F(e.h1,{children:"Server Config"}),` +`,F(e.p,{children:F(e.em,{children:"WIP"})})]})}function mn(n={}){let{wrapper:e}=n.components||{};return e?F(e,Object.assign({},n,{children:F(ge,n)})):ge(n)}var un=mn;var G={};h(G,{default:()=>fn});import{Fragment as jn,jsx as r,jsxs as R}from"https://esm.sh/react@18.2.0/jsx-runtime";function fe(n){let e=Object.assign({h1:"h1",p:"p",a:"a",code:"code",pre:"pre",span:"span"},n.components);return R(jn,{children:[r(e.h1,{children:"Import Maps"}),` +`,R(e.p,{children:["Aleph.js supports ",r(e.a,{href:"https://github.com/WICG/import-maps",children:"import maps"}),". To use import maps, create a ",r(e.code,{children:"import_map.json"})," file in the root directory:"]}),` +`,r(e.pre,{children:R(e.code,{className:"hljs language-json",children:[r(e.span,{className:"hljs-punctuation",children:"{"}),` + `,r(e.span,{className:"hljs-attr",children:'"imports"'}),r(e.span,{className:"hljs-punctuation",children:":"})," ",r(e.span,{className:"hljs-punctuation",children:"{"}),` + `,r(e.span,{className:"hljs-attr",children:'"@/"'}),r(e.span,{className:"hljs-punctuation",children:":"})," ",r(e.span,{className:"hljs-string",children:'"./"'}),r(e.span,{className:"hljs-punctuation",children:","}),` + `,r(e.span,{className:"hljs-attr",children:'"react"'}),r(e.span,{className:"hljs-punctuation",children:":"})," ",r(e.span,{className:"hljs-string",children:'"https://esm.sh/react@18.2.0"'}),r(e.span,{className:"hljs-punctuation",children:","}),` + ... + `,r(e.span,{className:"hljs-punctuation",children:"}"}),` +`,r(e.span,{className:"hljs-punctuation",children:"}"}),` +`]})}),` +`,r(e.p,{children:"then in your code:"}),` +`,r(e.pre,{children:R(e.code,{className:"hljs language-tsx",children:[r(e.span,{className:"hljs-keyword",children:"import"})," ",r(e.span,{className:"hljs-title class_",children:"React"})," ",r(e.span,{className:"hljs-keyword",children:"from"})," ",r(e.span,{className:"hljs-string",children:'"react"'}),`; +`,r(e.span,{className:"hljs-keyword",children:"import"})," ",r(e.span,{className:"hljs-title class_",children:"Logo"})," ",r(e.span,{className:"hljs-keyword",children:"from"})," ",r(e.span,{className:"hljs-string",children:'"@/components/logo.tsx"'}),`; + +`,r(e.span,{className:"hljs-keyword",children:"export"})," ",r(e.span,{className:"hljs-keyword",children:"default"})," ",r(e.span,{className:"hljs-keyword",children:"function"})," ",r(e.span,{className:"hljs-title function_",children:"App"}),"(",r(e.span,{className:"hljs-params"}),`) { + `,r(e.span,{className:"hljs-keyword",children:"return"})," ",r(e.span,{className:"xml",children:R(e.span,{className:"hljs-tag",children:["<",r(e.span,{className:"hljs-name",children:"Logo"})," />"]})}),`; +} +`]})})]})}function gn(n={}){let{wrapper:e}=n.components||{};return e?r(e,Object.assign({},n,{children:r(fe,n)})):fe(n)}var fn=gn;var z={};h(z,{default:()=>yn});import{Fragment as Nn,jsx as i,jsxs as w}from"https://esm.sh/react@18.2.0/jsx-runtime";function Ne(n){let e=Object.assign({h1:"h1",p:"p",code:"code",pre:"pre",span:"span",ul:"ul",li:"li"},n.components);return w(Nn,{children:[i(e.h1,{children:"Static File Serving"}),` +`,w(e.p,{children:["Aleph.js will serve any static files in the project directory. Files inside the project directory can then be referenced by your code by using the base URL (",i(e.code,{children:"/"}),")."]}),` +`,w(e.p,{children:["For example, if you add an image ",i(e.code,{children:"assets/logo.png"}),", the following code will access the image:"]}),` +`,i(e.pre,{children:w(e.code,{className:"hljs language-jsx",children:[i(e.span,{className:"hljs-keyword",children:"export"})," ",i(e.span,{className:"hljs-keyword",children:"default"})," ",i(e.span,{className:"hljs-keyword",children:"function"})," ",i(e.span,{className:"hljs-title function_",children:"Logo"}),"(",i(e.span,{className:"hljs-params"}),`) { + `,i(e.span,{className:"hljs-keyword",children:"return"}),` ( + `,i(e.span,{className:"xml",children:w(e.span,{className:"hljs-tag",children:["<",i(e.span,{className:"hljs-name",children:"img"})," ",i(e.span,{className:"hljs-attr",children:"src"}),"=",i(e.span,{className:"hljs-string",children:'"/assets/logo.png"'})," ",i(e.span,{className:"hljs-attr",children:"alt"}),"=",i(e.span,{className:"hljs-string",children:'"Logo"'})," />"]})}),` + ) +} +`]})}),` +`,i(e.p,{children:"You can also serve other static assets such as:"}),` +`,w(e.ul,{children:[` +`,i(e.li,{children:i(e.code,{children:"favicon.ico"})}),` +`,i(e.li,{children:i(e.code,{children:"manifest.json"})}),` +`,i(e.li,{children:i(e.code,{children:"robots.txt"})}),` +`,i(e.li,{children:"Google Site Verification, etc"}),` +`]})]})}function xn(n={}){let{wrapper:e}=n.components||{};return e?i(e,Object.assign({},n,{children:i(Ne,n)})):Ne(n)}var yn=xn;var V={};h(V,{default:()=>_n});import{Fragment as bn,jsx as s,jsxs as p}from"https://esm.sh/react@18.2.0/jsx-runtime";function xe(n){let e=Object.assign({h1:"h1",p:"p",code:"code",pre:"pre",span:"span",h3:"h3",ul:"ul",li:"li",strong:"strong"},n.components);return p(bn,{children:[s(e.h1,{children:"Routing"}),` +`,p(e.p,{children:["Aleph.js has a file-system based router. When a file (",s(e.code,{children:".js"}),", ",s(e.code,{children:".jsx"}),", ",s(e.code,{children:".ts"}),`, +`,s(e.code,{children:".tsx"}),", and ",s(e.code,{children:".mjs"}),") is added to the ",s(e.code,{children:"routes"}),` directory, it is automatically +available as a route.`]}),` +`,p(e.p,{children:["You can configruate the ",s(e.code,{children:"router"})," in ",s(e.code,{children:"server.ts"}),"."]}),` +`,s(e.pre,{children:p(e.code,{className:"hljs language-js",children:[s(e.span,{className:"hljs-comment",children:"// server.ts"}),` + +`,s(e.span,{className:"hljs-keyword",children:"import"})," { server } ",s(e.span,{className:"hljs-keyword",children:"from"})," ",s(e.span,{className:"hljs-string",children:'"aleph/server"'}),`; + +`,s(e.span,{className:"hljs-title function_",children:"serve"}),`({ + `,s(e.span,{className:"hljs-attr",children:"router"}),`: { + `,s(e.span,{className:"hljs-comment",children:"// use glob"}),` + `,s(e.span,{className:"hljs-attr",children:"glob"}),": ",s(e.span,{className:"hljs-string",children:'"./routes/**/*.{tsx,jsx,mdx}"'}),`, + + `,s(e.span,{className:"hljs-comment",children:"// use dir prefix and exts"}),` + `,s(e.span,{className:"hljs-attr",children:"dir"}),": ",s(e.span,{className:"hljs-string",children:'"./routes"'}),`, + `,s(e.span,{className:"hljs-attr",children:"exts"}),": [",s(e.span,{className:"hljs-string",children:'".tsx"'}),", ",s(e.span,{className:"hljs-string",children:'".jsx"'}),", ",s(e.span,{className:"hljs-string",children:'".mdx"'}),`], + }, +}); +`]})}),` +`,s(e.h3,{children:"Index Routes"}),` +`,p(e.p,{children:["The router will automatically route files named ",s(e.code,{children:"index"}),` to the root of the +directory.`]}),` +`,p(e.ul,{children:[` +`,p(e.li,{children:[s(e.code,{children:"pages/index.tsx"})," \u2192 ",s(e.code,{children:"/"})]}),` +`,p(e.li,{children:[s(e.code,{children:"pages/blog/index.tsx"})," \u2192 ",s(e.code,{children:"/blog"})]}),` +`]}),` +`,s(e.h3,{children:"Dynamic Routes"}),` +`,p(e.p,{children:[`To match a dynamic segment, you can use bracket syntax or start the segment with +`,s(e.strong,{children:"$"}),":"]}),` +`,p(e.ul,{children:[` +`,p(e.li,{children:[s(e.code,{children:"pages/blog/$slug.tsx"})," \u2192 ",s(e.code,{children:"/blog/:slug"})," (",s(e.code,{children:"/blog/hello-world"}),")"]}),` +`,p(e.li,{children:[s(e.code,{children:"pages/$username/settings.tsx"})," \u2192 ",s(e.code,{children:"/:username/settings"})," (",s(e.code,{children:"/foo/settings"}),")"]}),` +`,p(e.li,{children:[s(e.code,{children:"pages/post/$all+.tsx"})," \u2192 ",s(e.code,{children:"/post/*"})," (",s(e.code,{children:"/post/2020/id/title"}),")"]}),` +`]}),` +`,s(e.h3,{children:"Nested Routes"}),` +`,s(e.p,{children:"Aleph.js supports nested route structures like:"}),` +`,p(e.ul,{children:[` +`,s(e.li,{children:s(e.code,{children:"pages/blog.tsx"})}),` +`,s(e.li,{children:s(e.code,{children:"pages/blog/$slug.tsx"})}),` +`]}),` +`,p(e.p,{children:["In the example, routes in ",s(e.code,{children:"/blog/:slug"}),` will be rendered under the +`,s(e.code,{children:"pages/blog.tsx"}),", that is useful to create a ",s(e.strong,{children:"layout"})," for pages:"]}),` +`,s(e.pre,{children:p(e.code,{className:"hljs language-jsx",children:[s(e.span,{className:"hljs-keyword",children:"import"})," ",s(e.span,{className:"hljs-title class_",children:"BlogHeader"})," ",s(e.span,{className:"hljs-keyword",children:"from"})," ",s(e.span,{className:"hljs-string",children:'"../components/blog-header.tsx"'}),`; + +`,s(e.span,{className:"hljs-keyword",children:"export"})," ",s(e.span,{className:"hljs-keyword",children:"default"})," ",s(e.span,{className:"hljs-keyword",children:"function"})," ",s(e.span,{className:"hljs-title function_",children:"Blog"}),"(",s(e.span,{className:"hljs-params",children:"{ children }"}),`) { + `,s(e.span,{className:"hljs-keyword",children:"return"}),` ( + `,p(e.span,{className:"xml",children:[s(e.span,{className:"hljs-tag",children:"<>"}),` + `,p(e.span,{className:"hljs-tag",children:["<",s(e.span,{className:"hljs-name",children:"BlogHeader"})," />"]}),` + `,p(e.span,{className:"hljs-tag",children:["<",s(e.span,{className:"hljs-name",children:"div"})," ",s(e.span,{className:"hljs-attr",children:"className"}),"=",s(e.span,{className:"hljs-string",children:'"blog-body"'}),">"]}),` + {children} + `,p(e.span,{className:"hljs-tag",children:[""]}),` + `,s(e.span,{className:"hljs-tag",children:""})]}),` + ); +} +`]})})]})}function wn(n={}){let{wrapper:e}=n.components||{};return e?s(e,Object.assign({},n,{children:s(xe,n)})):xe(n)}var _n=wn;var Z={};h(Z,{default:()=>kn});import{Fragment as Mn,jsx as t,jsxs as f}from"https://esm.sh/react@18.2.0/jsx-runtime";function ye(n){let e=Object.assign({h1:"h1",p:"p",strong:"strong",a:"a",pre:"pre",code:"code",span:"span",h2:"h2"},n.components);return f(Mn,{children:[t(e.h1,{children:"Import From NPM"}),` +`,f(e.p,{children:["Aleph.js uses ",t(e.strong,{children:"ESM"})," imports syntax in Deno. To import modules from ",t(e.strong,{children:"NPM"}),", you can use ",t(e.a,{href:"https://esm.sh",children:"esm.sh"})," CDN that is maintained by Aleph.js team."]}),` +`,t(e.pre,{children:f(e.code,{className:"hljs language-jsx",children:[t(e.span,{className:"hljs-keyword",children:"import"})," useSWR ",t(e.span,{className:"hljs-keyword",children:"from"})," ",t(e.span,{className:"hljs-string",children:"'https://esm.sh/swr'"}),` + +`,t(e.span,{className:"hljs-keyword",children:"export"})," ",t(e.span,{className:"hljs-keyword",children:"default"})," ",t(e.span,{className:"hljs-keyword",children:"function"})," ",t(e.span,{className:"hljs-title function_",children:"About"}),"(",t(e.span,{className:"hljs-params"}),`) { + `,t(e.span,{className:"hljs-keyword",children:"const"})," { data, error } = ",t(e.span,{className:"hljs-title function_",children:"useSWR"}),"(",t(e.span,{className:"hljs-string",children:"'/api/user'"}),`, fetcher) + + `,t(e.span,{className:"hljs-keyword",children:"if"}),` (error) { + `,t(e.span,{className:"hljs-keyword",children:"return"})," ",f(e.span,{className:"xml",children:[f(e.span,{className:"hljs-tag",children:["<",t(e.span,{className:"hljs-name",children:"div"}),">"]}),"failed to load",f(e.span,{className:"hljs-tag",children:[""]})]}),` + } + `,t(e.span,{className:"hljs-keyword",children:"if"}),` (!data) { + `,t(e.span,{className:"hljs-keyword",children:"return"})," ",f(e.span,{className:"xml",children:[f(e.span,{className:"hljs-tag",children:["<",t(e.span,{className:"hljs-name",children:"div"}),">"]}),"loading...",f(e.span,{className:"hljs-tag",children:[""]})]}),` + } + `,t(e.span,{className:"hljs-keyword",children:"return"})," ",f(e.span,{className:"xml",children:[f(e.span,{className:"hljs-tag",children:["<",t(e.span,{className:"hljs-name",children:"div"}),">"]}),"hello {data.name}!",f(e.span,{className:"hljs-tag",children:[""]})]}),` +} +`]})}),` +`,t(e.h2,{children:"Next"}),` +`,t(e.p,{children:"Deno is adding built-in npm support: https://deno.com/blog/changes#compatibility-with-node-and-npm"})]})}function vn(n={}){let{wrapper:e}=n.components||{};return e?t(e,Object.assign({},n,{children:t(ye,n)})):ye(n)}var kn=vn;var K={};h(K,{default:()=>Cn});import{Fragment as Dn,jsx as o,jsxs as j}from"https://esm.sh/react@18.2.0/jsx-runtime";function be(n){let e=Object.assign({h1:"h1",p:"p",code:"code",strong:"strong",pre:"pre",span:"span",h2:"h2",a:"a"},n.components);return j(Dn,{children:[o(e.h1,{children:"CSS Support"}),` +`,j(e.p,{children:["Aleph.js use the ",o(e.code,{children:"index.html"})," as page entry, that means you can add any ",o(e.strong,{children:"CSS"})," files with ",o(e.code,{children:"link"})]}),` +`,o(e.pre,{children:j(e.code,{className:"hljs language-html",children:[j(e.span,{className:"hljs-tag",children:["<",o(e.span,{className:"hljs-name",children:"html"}),">"]}),` + `,j(e.span,{className:"hljs-tag",children:["<",o(e.span,{className:"hljs-name",children:"head"}),">"]}),` + `,j(e.span,{className:"hljs-tag",children:["<",o(e.span,{className:"hljs-name",children:"title"}),">"]}),"Hello, World!",j(e.span,{className:"hljs-tag",children:[""]}),` + `,j(e.span,{className:"hljs-tag",children:["<",o(e.span,{className:"hljs-name",children:"link"})," ",o(e.span,{className:"hljs-attr",children:"rel"}),"=",o(e.span,{className:"hljs-string",children:'"stylesheet"'})," ",o(e.span,{className:"hljs-attr",children:"href"}),"=",o(e.span,{className:"hljs-string",children:'"./style/app.css"'}),">"]}),` + `,j(e.span,{className:"hljs-tag",children:[""]}),` + `,j(e.span,{className:"hljs-tag",children:["<",o(e.span,{className:"hljs-name",children:"body"}),">"]}),` + `,j(e.span,{className:"hljs-tag",children:["<",o(e.span,{className:"hljs-name",children:"h1"}),">"]}),"Hello, World!",j(e.span,{className:"hljs-tag",children:[""]}),` + `,j(e.span,{className:"hljs-tag",children:[""]}),` +`,j(e.span,{className:"hljs-tag",children:[""]}),` +`]})}),` +`,j(e.h2,{children:["CSS Imports (",o(e.code,{children:"@import"}),")"]}),` +`,j(e.p,{children:["Aleph.js uses ",o(e.a,{href:"https://lightningcss.dev/",children:"lightningcss"})," to bundle your CSS code that means you can use ",o(e.code,{children:"@import"})," syntax safety."]})]})}function Sn(n={}){let{wrapper:e}=n.components||{};return e?o(e,Object.assign({},n,{children:o(be,n)})):be(n)}var Cn=Sn;var Q={};h(Q,{default:()=>On});import{Fragment as Fn,jsx as c,jsxs as x}from"https://esm.sh/react@18.2.0/jsx-runtime";function we(n){let e=Object.assign({h1:"h1",p:"p",strong:"strong",code:"code",pre:"pre",span:"span",h2:"h2",blockquote:"blockquote",a:"a",h3:"h3",ul:"ul",li:"li"},n.components);return x(Fn,{children:[c(e.h1,{children:"Hot Module Replacement"}),` +`,x(e.p,{children:["Hot Module Replacement (or HMR) allows modules to be updated at runtime without the need for a full refresh during ",c(e.strong,{children:"development"}),". To support this, Aleph.js's built-in ",c(e.strong,{children:"HMR"})," module creates a ",c(e.strong,{children:"WebSocket"})," connection to the dev server on the client, and the server listens for file changes. Once the HMR client gets an update signal from the server, Aleph.js will re-import the updated module."]}),` +`,c(e.p,{children:"For a CSS module, the old style will be removed after the new one is applied. For a React Component, Fast Refresh will re-render the component view without losing component state."}),` +`,x(e.p,{children:["You can use the ",c(e.code,{children:"hot"})," api to handle the module update event."]}),` +`,c(e.pre,{children:x(e.code,{className:"hljs language-js",children:[c(e.span,{className:"hljs-keyword",children:"import"}),".",c(e.span,{className:"hljs-property",children:"meta"}),".",c(e.span,{className:"hljs-property",children:"hot"}),"?.",c(e.span,{className:"hljs-title function_",children:"accept"}),"(",x(e.span,{className:"hljs-function",children:["(",c(e.span,{className:"hljs-params",children:"mod"}),")=>"]}),`{ + `,c(e.span,{className:"hljs-comment",children:"// update UI"}),` +}) + +`,c(e.span,{className:"hljs-comment",children:"// just reload the page when the module is updated"}),` +`,c(e.span,{className:"hljs-keyword",children:"import"}),".",c(e.span,{className:"hljs-property",children:"meta"}),".",c(e.span,{className:"hljs-property",children:"hot"}),"?.",c(e.span,{className:"hljs-title function_",children:"decline"}),`() +`]})}),` +`,c(e.h2,{children:"React Fast Refresh"}),` +`,x(e.blockquote,{children:[` +`,x(e.p,{children:[`It's a reimplementation of "hot reloading" with full support from React. It's originally `,c(e.a,{href:"https://twitter.com/dan_abramov/status/1169687758849400832",children:"shipped for React Native"}),", but most of the implementation is platform-independent. The plan is to use it across the board, as a replacement for purely userland solutions (like react-hot-loader). ",c(e.a,{href:"https://github.com/facebook/react/issues/16604#issuecomment-528663101",children:"\xB9"})]}),` +`]}),` +`,c("video",{src:"/fast-refresh.mp4",loop:!0,autoplay:!0,muted:!0}),` +`,c(e.h3,{children:"Limits"}),` +`,x(e.ul,{children:[` +`,c(e.li,{children:"Fast Refresh only supports functional components with hooks."}),` +`,x(e.li,{children:["Functional components using default exports must be named: ",c(e.code,{children:"export default function ComponentName() { ... }"}),"."]}),` +`]})]})}function Rn(n={}){let{wrapper:e}=n.components||{};return e?c(e,Object.assign({},n,{children:c(we,n)})):we(n)}var On=Rn;var ee={};h(ee,{default:()=>In});import{Fragment as Xn,jsx as O,jsxs as An}from"https://esm.sh/react@18.2.0/jsx-runtime";function _e(n){let e=Object.assign({h1:"h1",p:"p",em:"em"},n.components);return An(Xn,{children:[O(e.h1,{children:"Server"}),` +`,O(e.p,{children:O(e.em,{children:"WIP"})})]})}function Ln(n={}){let{wrapper:e}=n.components||{};return e?O(e,Object.assign({},n,{children:O(_e,n)})):_e(n)}var In=Ln;var ne={};h(ne,{default:()=>Pn});import{Fragment as $n,jsx as N,jsxs as X}from"https://esm.sh/react@18.2.0/jsx-runtime";function Me(n){let e=Object.assign({h1:"h1",p:"p",strong:"strong",em:"em",code:"code",pre:"pre",span:"span"},n.components);return X($n,{children:[N(e.h1,{children:"Server-side Rendering"}),` +`,X(e.p,{children:["By default, Aleph.js ",N(e.strong,{children:"pre-renders"})," every page. This means that Aleph.js generates HTML for each page in advance, instead of rendering it with client-side JavaScript. Pre-rendering can result in better performance and SEO."]}),` +`,X(e.p,{children:["Each generated HTML page only needs a small amount of JavaScript. When a page is loaded by the browser, its JavaScript code runs and makes the page fully interactive. (This process is called ",N(e.em,{children:"hydration"}),".)"]}),` +`,X(e.p,{children:["You can disable ",N(e.strong,{children:"SSR"})," functionality in ",N(e.code,{children:"server.ts"}),":"]}),` +`,N(e.pre,{children:X(e.code,{className:"hljs language-ts",children:[N(e.span,{className:"hljs-title function_",children:"server"}),`({ + `,N(e.span,{className:"hljs-attr",children:"ssr"}),": ",N(e.span,{className:"hljs-literal",children:"true"}),` +}) +`]})})]})}function Tn(n={}){let{wrapper:e}=n.components||{};return e?N(e,Object.assign({},n,{children:N(Me,n)})):Me(n)}var Pn=Tn;var se={};h(se,{default:()=>En});import{Fragment as Wn,jsx as A,jsxs as Hn}from"https://esm.sh/react@18.2.0/jsx-runtime";function ve(n){let e=Object.assign({h1:"h1",p:"p",em:"em"},n.components);return Hn(Wn,{children:[A(e.h1,{children:"Unocss"}),` +`,A(e.p,{children:A(e.em,{children:"WIP"})})]})}function qn(n={}){let{wrapper:e}=n.components||{};return e?A(e,Object.assign({},n,{children:A(ve,n)})):ve(n)}var En=qn;var ae={};h(ae,{default:()=>Bn});import{Fragment as Un,jsx as L,jsxs as Jn}from"https://esm.sh/react@18.2.0/jsx-runtime";function ke(n){let e=Object.assign({h1:"h1",p:"p",em:"em"},n.components);return Jn(Un,{children:[L(e.h1,{children:"Vue"}),` +`,L(e.p,{children:L(e.em,{children:"WIP"})})]})}function Yn(n={}){let{wrapper:e}=n.components||{};return e?L(e,Object.assign({},n,{children:L(ke,n)})):ke(n)}var Bn=Yn;var le={};h(le,{default:()=>Zn});import{Fragment as Gn,jsx as I,jsxs as zn}from"https://esm.sh/react@18.2.0/jsx-runtime";function De(n){let e=Object.assign({h1:"h1",p:"p",em:"em"},n.components);return zn(Gn,{children:[I(e.h1,{children:"React"}),` +`,I(e.p,{children:I(e.em,{children:"WIP"})})]})}function Vn(n={}){let{wrapper:e}=n.components||{};return e?I(e,Object.assign({},n,{children:I(De,n)})):De(n)}var Zn=Vn;var _s={"/":Kn,"/docs":Qn,"/docs/browser-support":$,"/docs/deployment":T,"/docs/index":P,"/docs/get-started":W,"/docs/framework/vue":H,"/docs/framework/react-mdx":q,"/docs/framework/solid":E,"/docs/framework/react":U,"/docs/framework/yew":J,"/docs/api-reference/middleware":Y,"/docs/api-reference/server-config":B,"/docs/basic-concepts/import-maps":G,"/docs/basic-concepts/static-file-serving":z,"/docs/basic-concepts/routing":V,"/docs/basic-concepts/import-from-npm":Z,"/docs/basic-concepts/css-support":K,"/docs/basic-concepts/hmr":Q,"/docs/basic-concepts/server":ee,"/docs/basic-concepts/ssr":ne,"/docs/basic-concepts/unocss":se,"/docs/api-reference/framework/vue":ae,"/docs/api-reference/framework/react":le,depGraph:{"modules":[{"specifier":"./routes/docs/browser-support.md"},{"specifier":"./routes/docs/deployment.md"},{"specifier":"./routes/docs/index.md"},{"specifier":"./routes/docs/get-started.md"},{"specifier":"./routes/docs/framework/vue.md"},{"specifier":"./routes/docs/framework/react-mdx.md"},{"specifier":"./routes/docs/framework/solid.md"},{"specifier":"./routes/docs/framework/react.md"},{"specifier":"./routes/docs/framework/yew.md"},{"specifier":"./routes/docs/api-reference/middleware.md"},{"specifier":"./routes/docs/api-reference/server-config.md"},{"specifier":"./routes/docs/basic-concepts/import-maps.md"},{"specifier":"./routes/docs/basic-concepts/static-file-serving.md"},{"specifier":"./routes/docs/basic-concepts/routing.md"},{"specifier":"./routes/docs/basic-concepts/import-from-npm.md"},{"specifier":"./routes/docs/basic-concepts/css-support.md"},{"specifier":"./routes/docs/basic-concepts/hmr.md"},{"specifier":"./routes/docs/basic-concepts/server.md"},{"specifier":"./routes/docs/basic-concepts/ssr.md"},{"specifier":"./routes/docs/basic-concepts/unocss.md"},{"specifier":"./routes/docs/api-reference/framework/vue.md"},{"specifier":"./routes/docs/api-reference/framework/react.md"}]}};export{_s as default}; diff --git a/routes/docs.tsx b/routes/docs.tsx new file mode 100644 index 0000000..fbd0c3f --- /dev/null +++ b/routes/docs.tsx @@ -0,0 +1,278 @@ +import { Fragment, useEffect, useMemo, useState } from "react"; +import { Head, NavLink, useRouter } from "aleph/react"; +import Header from "components/Header.tsx"; + +type Menu = { + name: string; + items: MenuItem[]; +}; + +type MenuItem = { + title: string; + path: string; + category?: string; + submenu?: MenuItem[]; +}; + +const description = "The Documentation for Aleph.js"; +const ogImage = "https://alephjs.org/twitter_card.jpg"; +const navMenu: Menu[] = [ + { + name: "Documentation", + items: [ + { + title: "About", + path: "/docs", + }, + { title: "Get Started", path: "/docs/get-started" }, + { + title: "Basic Concepts", + path: "/docs/basic-concepts", + submenu: [ + { title: "Server", path: "/server" }, + { title: "Routing", path: "/routing" }, + { title: "Server-side Rendering", path: "/ssr" }, + { title: "CSS Support", path: "/css-support" }, + { title: "Unocss", path: "/unocss" }, + { title: "Static File Serving", path: "/static-file-serving" }, + { title: "Hot Module Replacement", path: "/hmr" }, + { title: "Import From NPM", path: "/import-from-npm" }, + { title: "Import Maps", path: "/import-maps" }, + ], + }, + { + title: "Framework", + path: "/docs/framework", + submenu: [ + { title: "React", path: "/react" }, + { title: "React with MDX", path: "/react-mdx" }, + { title: "Vue", path: "/vue" }, + { title: "SolidJS", path: "/solid" }, + { title: "Yew", path: "/yew" }, + ], + }, + { title: "Browser Support", path: "/docs/browser-support" }, + { title: "Deployment", path: "/docs/deployment" }, + ], + }, + { + name: "API Reference", + items: [ + { title: "Server Config", path: "/docs/api-reference/server-config" }, + { + title: "Framework API", + path: "/docs/api-reference/framework", + submenu: [ + { title: "React", path: "/react" }, + { title: "Vue", path: "/vue" }, + ], + }, + { title: "Middleware API", path: "/docs/api-reference/middleware" }, + ], + }, +]; + +export default function Docs({ children }: React.PropsWithChildren) { + const { url } = useRouter(); + const [extended, setExtended] = useState( + navMenu.map((m) => m.items).flat().filter((item) => item.submenu).reduce( + (m, item) => { + m[item.path] = url.pathname.startsWith(item.path); + return m; + }, + {} as Record, + ), + ); + const [menuIsOpen, setMenuIsOpen] = useState(false); + const [searchWords, setSearchWords] = useState(""); + const navLinks = useMemo<[[string, string] | null, [string, string] | null]>( + () => { + const all: [string, string][] = []; + navMenu.forEach((g) => + g.items.forEach((item) => { + if (item.submenu) { + item.submenu.forEach(({ title, path }) => { + all.push([title, item.path + (path === "/" ? "" : path)]); + }); + } else { + all.push([item.title, item.path]); + } + }) + ); + const index = all.findIndex(([_, path]) => path === url.pathname); + return [all[index - 1] || null, all[index + 1] || null]; + }, + [url.pathname], + ); + const editUrl = useMemo(() => { + const md = url.pathname === "/docs" + ? url.pathname + "/index.md" + : url.pathname + ".md"; + return `https://github.com/alephjs/alephjs.org/edit/master/routes${md}`; + }, [url.pathname]); + const filteredNavMenu = useMemo(() => { + if (searchWords === "") { + return navMenu; + } + return navMenu.map((g) => { + const includes = (item: MenuItem) => + item.title.toLowerCase().includes(searchWords); + return { + ...g, + items: g.items.filter((item) => { + return includes(item) || item.submenu?.some(includes); + }).map((item) => ({ + ...item, + submenu: item.submenu?.filter((subItem) => + includes(item) || includes(subItem) + ), + })), + }; + }).filter((g) => g.items.length > 0); + }, [searchWords]); + + useEffect(() => { + setExtended( + navMenu.map((m) => m.items).flat().filter((item) => item.submenu).reduce( + (m, item) => { + m[item.path] = url.pathname.startsWith(item.path); + return m; + }, + {} as Record, + ), + ); + document.querySelectorAll(".makedown-body video").forEach((block) => { + const v = block as HTMLVideoElement; + v.className = "is-paused"; + v.addEventListener("click", () => { + if (v.paused) { + v.play(); + } else { + v.requestFullscreen(); + } + }); + v.addEventListener("playing", () => v.className = "is-playing"); + v.addEventListener("pause", () => v.className = "is-paused"); + }); + }, [url.pathname]); + + return ( + <> + + + + + + + + + +
+
+ +
+ {children} +
+
+ + ); +} diff --git a/routes/docs/api-reference/framework/react.md b/routes/docs/api-reference/framework/react.md new file mode 100644 index 0000000..4986b6e --- /dev/null +++ b/routes/docs/api-reference/framework/react.md @@ -0,0 +1,10 @@ +--- +title: React +authors: + - ije + - razermoon +--- + +# React + +_WIP_ \ No newline at end of file diff --git a/routes/docs/api-reference/framework/vue.md b/routes/docs/api-reference/framework/vue.md new file mode 100644 index 0000000..b994706 --- /dev/null +++ b/routes/docs/api-reference/framework/vue.md @@ -0,0 +1,10 @@ +--- +title: Vue +authors: + - ije + - razermoon +--- + +# Vue + +_WIP_ \ No newline at end of file diff --git a/routes/docs/api-reference/middleware.md b/routes/docs/api-reference/middleware.md new file mode 100644 index 0000000..40ce1f7 --- /dev/null +++ b/routes/docs/api-reference/middleware.md @@ -0,0 +1,60 @@ +--- +title: Plugin API +authors: + - ije +--- + +# Middleware API + +In Aleph.js, a **Middleware** is an object with a `name` and a `fetch` method. +The `fetch` method will be invoked when a request is received by the server. And +the `fetch` method will end the request if returns a `Response` object. + +```ts +type Middleware = { + name: string; + fetch(req: Request, context: Context): Promise | Response | void; +}; +``` + +## Use Middlewares + +Here's a simple plugin example that allows you to add a virtual dist file to the +server: + +```ts +// server.ts + +import foo from "./middleware/foo.ts"; + +serve({ + middlewares: [ + foo, + { + name: "my-middleware", + fetch(req) { + if (req.url === "/my-middleware") { + return new Response("Hello, Middleware!"); + } + }, + }, + ], +}); +``` + +## Use `Context` Object + +_WIP_ + +## Examples + +The example plugins below are meant to give you an idea of the different types +of things you can do with the plugin API. + +#### Google Analytics + +This example plugin shows how to insert custom scripts to SSR output HTML using middleware. + +```ts +// wIP +``` diff --git a/routes/docs/api-reference/server-config.md b/routes/docs/api-reference/server-config.md new file mode 100644 index 0000000..ec6ebd3 --- /dev/null +++ b/routes/docs/api-reference/server-config.md @@ -0,0 +1,9 @@ +--- +title: Server Config +authors: + - ije +--- + +# Server Config + +_WIP_ diff --git a/routes/docs/basic-concepts/css-support.md b/routes/docs/basic-concepts/css-support.md new file mode 100644 index 0000000..d852df0 --- /dev/null +++ b/routes/docs/basic-concepts/css-support.md @@ -0,0 +1,25 @@ +--- +title: CSS Support +authors: + - ije +--- + +# CSS Support + +Aleph.js use the `index.html` as page entry, that means you can add any **CSS** files with `link` + +```html + + + Hello, World! + + + +

Hello, World!

+ + +``` + +## CSS Imports (`@import`) + +Aleph.js uses [lightningcss](https://lightningcss.dev/) to bundle your CSS code that means you can use `@import` syntax safety. \ No newline at end of file diff --git a/pages/docs/basic-features/hmr-with-fast-refresh.md b/routes/docs/basic-concepts/hmr.md similarity index 82% rename from pages/docs/basic-features/hmr-with-fast-refresh.md rename to routes/docs/basic-concepts/hmr.md index fcd8dc4..b233f5a 100644 --- a/pages/docs/basic-features/hmr-with-fast-refresh.md +++ b/routes/docs/basic-concepts/hmr.md @@ -1,22 +1,28 @@ --- -title: HMR with Fast Refresh +title: Hot Module Replacement authors: - ije - razermoon --- -# HMR with Fast Refresh +# Hot Module Replacement Hot Module Replacement (or HMR) allows modules to be updated at runtime without the need for a full refresh during **development**. To support this, Aleph.js's built-in **HMR** module creates a **WebSocket** connection to the dev server on the client, and the server listens for file changes. Once the HMR client gets an update signal from the server, Aleph.js will re-import the updated module. For a CSS module, the old style will be removed after the new one is applied. For a React Component, Fast Refresh will re-render the component view without losing component state. -Aleph.js supports full HMR out-of-the-box for the following served files: +You can use the `hot` api to handle the module update event. -- **JSX**/**TSX** in `pages` and `components` directory -- Files loaded by loader plugin with `acceptHMR` enabled +```js +import.meta.hot?.accept((mod)=>{ + // update UI +}) -## What Is Fast Refresh +// just reload the page when the module is updated +import.meta.hot?.decline() +``` + +## React Fast Refresh > It's a reimplementation of "hot reloading" with full support from React. It's originally [shipped for React Native](https://twitter.com/dan_abramov/status/1169687758849400832), but most of the implementation is platform-independent. The plan is to use it across the board, as a replacement for purely userland solutions (like react-hot-loader). [¹] diff --git a/pages/docs/basic-features/import-from-npm.md b/routes/docs/basic-concepts/import-from-npm.md similarity index 83% rename from pages/docs/basic-features/import-from-npm.md rename to routes/docs/basic-concepts/import-from-npm.md index 8c276ff..87727d0 100644 --- a/pages/docs/basic-features/import-from-npm.md +++ b/routes/docs/basic-concepts/import-from-npm.md @@ -24,3 +24,7 @@ export default function About() { return
hello {data.name}!
} ``` + +## Next + +Deno is adding built-in npm support: https://deno.com/blog/changes#compatibility-with-node-and-npm \ No newline at end of file diff --git a/pages/docs/basic-features/import-maps.md b/routes/docs/basic-concepts/import-maps.md similarity index 60% rename from pages/docs/basic-features/import-maps.md rename to routes/docs/basic-concepts/import-maps.md index 364924e..182702b 100644 --- a/pages/docs/basic-features/import-maps.md +++ b/routes/docs/basic-concepts/import-maps.md @@ -13,7 +13,7 @@ Aleph.js supports [import maps](https://github.com/WICG/import-maps). To use imp { "imports": { "@/": "./", - "react": "https://esm.sh/react@17.0.2", + "react": "https://esm.sh/react@18.2.0", ... } } @@ -25,17 +25,8 @@ then in your code: import React from "react"; import Logo from "@/components/logo.tsx"; -export default function Hi() { +export default function App() { return ; } ``` -If you are using **VS Code**, please add below settings to `.vscode/settings.json`: - -```json -{ - "deno.enable": true, - "deno.unstable": true, - "deno.importMap": "./import_map.json" -} -``` diff --git a/routes/docs/basic-concepts/routing.md b/routes/docs/basic-concepts/routing.md new file mode 100644 index 0000000..366d82d --- /dev/null +++ b/routes/docs/basic-concepts/routing.md @@ -0,0 +1,73 @@ +--- +title: Routing +authors: + - ije + - razermoon +--- + +# Routing + +Aleph.js has a file-system based router. When a file (`.js`, `.jsx`, `.ts`, +`.tsx`, and `.mjs`) is added to the `routes` directory, it is automatically +available as a route. + +You can configruate the `router` in `server.ts`. + +```js +// server.ts + +import { server } from "aleph/server"; + +serve({ + router: { + // use glob + glob: "./routes/**/*.{tsx,jsx,mdx}", + + // use dir prefix and exts + dir: "./routes", + exts: [".tsx", ".jsx", ".mdx"], + }, +}); +``` + +### Index Routes + +The router will automatically route files named `index` to the root of the +directory. + +- `pages/index.tsx` → `/` +- `pages/blog/index.tsx` → `/blog` + +### Dynamic Routes + +To match a dynamic segment, you can use bracket syntax or start the segment with +**$**: + +- `pages/blog/$slug.tsx` → `/blog/:slug` (`/blog/hello-world`) +- `pages/$username/settings.tsx` → `/:username/settings` (`/foo/settings`) +- `pages/post/$all+.tsx` → `/post/*` (`/post/2020/id/title`) + +### Nested Routes + +Aleph.js supports nested route structures like: + +- `pages/blog.tsx` +- `pages/blog/$slug.tsx` + +In the example, routes in `/blog/:slug` will be rendered under the +`pages/blog.tsx`, that is useful to create a **layout** for pages: + +```jsx +import BlogHeader from "../components/blog-header.tsx"; + +export default function Blog({ children }) { + return ( + <> + +
+ {children} +
+ + ); +} +``` diff --git a/routes/docs/basic-concepts/server.md b/routes/docs/basic-concepts/server.md new file mode 100644 index 0000000..c66cfd4 --- /dev/null +++ b/routes/docs/basic-concepts/server.md @@ -0,0 +1,9 @@ +--- +title: Server +authors: + - ije +--- + +# Server + +_WIP_ \ No newline at end of file diff --git a/routes/docs/basic-concepts/ssr.md b/routes/docs/basic-concepts/ssr.md new file mode 100644 index 0000000..4ca086f --- /dev/null +++ b/routes/docs/basic-concepts/ssr.md @@ -0,0 +1,21 @@ +--- +title: Server-side Rendering +authors: + - ije + - Serdar Sever + - razermoon +--- + +# Server-side Rendering + +By default, Aleph.js **pre-renders** every page. This means that Aleph.js generates HTML for each page in advance, instead of rendering it with client-side JavaScript. Pre-rendering can result in better performance and SEO. + +Each generated HTML page only needs a small amount of JavaScript. When a page is loaded by the browser, its JavaScript code runs and makes the page fully interactive. (This process is called _hydration_.) + +You can disable **SSR** functionality in `server.ts`: + +```ts +server({ + ssr: true +}) +``` diff --git a/pages/docs/basic-features/static-file-serving.md b/routes/docs/basic-concepts/static-file-serving.md similarity index 50% rename from pages/docs/basic-features/static-file-serving.md rename to routes/docs/basic-concepts/static-file-serving.md index 194820c..a81afc3 100644 --- a/pages/docs/basic-features/static-file-serving.md +++ b/routes/docs/basic-concepts/static-file-serving.md @@ -7,16 +7,14 @@ authors: # Static File Serving -Aleph.js will serve any static files in the `public` directory. Files inside the `public` directory can then be referenced by your code by using the base URL (`/`). +Aleph.js will serve any static files in the project directory. Files inside the project directory can then be referenced by your code by using the base URL (`/`). -For example, if you add an image `public/logo.png`, the following code will access the image: +For example, if you add an image `assets/logo.png`, the following code will access the image: ```jsx -import React from "https://esm.sh/react" - export default function Logo() { return ( - Logo + Logo ) } ``` diff --git a/routes/docs/basic-concepts/unocss.md b/routes/docs/basic-concepts/unocss.md new file mode 100644 index 0000000..cfe8a96 --- /dev/null +++ b/routes/docs/basic-concepts/unocss.md @@ -0,0 +1,9 @@ +--- +title: Unocss +authors: + - ije +--- + +# Unocss + +_WIP_ \ No newline at end of file diff --git a/routes/docs/browser-support.md b/routes/docs/browser-support.md new file mode 100644 index 0000000..763365e --- /dev/null +++ b/routes/docs/browser-support.md @@ -0,0 +1,33 @@ +--- +title: Browser Support +authors: + - ije + - razermoon +--- + +# Browser Support + +Aleph.js requires a modern browser to support +[native ES module imports](https://caniuse.com/#feat=es6-module) and **dynamic +imports**: + +- Chrome >= 61 +- Edge >= 16 +- Firefox >= 60 +- Safari >= 11 +- Opera >= 48 + +## Build Target + +Aleph.js uses **esbuild** to bundle modules at optimization time for production. +You can set the `optimization.buildTarget` in the server config. + +```js +import { serve } from "aleph/server"; + +serve({ + optimization: { + buildTarget: "es2020", + }, +}); +``` diff --git a/routes/docs/deployment.md b/routes/docs/deployment.md new file mode 100644 index 0000000..55d7af3 --- /dev/null +++ b/routes/docs/deployment.md @@ -0,0 +1,22 @@ +--- +title: Deployment +authors: + - ije + - razermoon +--- + +# Deployment on your own host with Deno CLI + +You can run your app in production mode by `deno` CLI. + +```bash +deno run --allow-network --allow-env --allow-read --allow-write server.ts +``` + +## Deploy on Deno Deploy + +To deploy your app to [Deno Deploy](https://deno.com/deploy), please push your app to [Github](https://github.com) and create a new project on [Deno Deploy](https://dash.deno.com/new). + +Then link to the Repo and set the entrypoint to `server.ts` + +> See the [hello-world](https://aleph-hello.deno.dev/) example on [Deno Deploy](https://deno.com/deploy). diff --git a/routes/docs/framework/react-mdx.md b/routes/docs/framework/react-mdx.md new file mode 100644 index 0000000..1b65e7c --- /dev/null +++ b/routes/docs/framework/react-mdx.md @@ -0,0 +1,9 @@ +--- +title: Framework/React with MDX +authors: + - ije +--- + +# React with MDX + +_WIP_ \ No newline at end of file diff --git a/routes/docs/framework/react.md b/routes/docs/framework/react.md new file mode 100644 index 0000000..ad4e3f4 --- /dev/null +++ b/routes/docs/framework/react.md @@ -0,0 +1,9 @@ +--- +title: Framework/React +authors: + - ije +--- + +# React + +_WIP_ \ No newline at end of file diff --git a/routes/docs/framework/solid.md b/routes/docs/framework/solid.md new file mode 100644 index 0000000..f20f09c --- /dev/null +++ b/routes/docs/framework/solid.md @@ -0,0 +1,9 @@ +--- +title: Framework/SolidJS +authors: + - ije +--- + +# SolidJS + +_WIP_ \ No newline at end of file diff --git a/routes/docs/framework/vue.md b/routes/docs/framework/vue.md new file mode 100644 index 0000000..d5a9888 --- /dev/null +++ b/routes/docs/framework/vue.md @@ -0,0 +1,9 @@ +--- +title: Framework/Vue +authors: + - ije +--- + +# Vue + +_WIP_ \ No newline at end of file diff --git a/routes/docs/framework/yew.md b/routes/docs/framework/yew.md new file mode 100644 index 0000000..73abb78 --- /dev/null +++ b/routes/docs/framework/yew.md @@ -0,0 +1,9 @@ +--- +title: Framework/Yew +authors: + - ije +--- + +# Yew + +_WIP_ \ No newline at end of file diff --git a/routes/docs/get-started.md b/routes/docs/get-started.md new file mode 100644 index 0000000..91a72ec --- /dev/null +++ b/routes/docs/get-started.md @@ -0,0 +1,38 @@ +--- +title: Get Started +authors: + - ije + - razermoon +--- + +# Get Started! + +Welcome to use **Aleph.js**! + +If you are new to Aleph.js you should check out the [About](/docs/) page. + +## Usage + +Create a new app: + +```bash +deno run -A -r https://alephjs.org/init.ts +``` + +Start the app in `development` mode: + +```bash +deno task dev +``` + +Start the app in `production` mode: + +```bash +deno task start +``` + +Optimize the application (bundling, ssg, etc.): + +```bash +deno task opt +``` diff --git a/routes/docs/index.md b/routes/docs/index.md new file mode 100644 index 0000000..63d43ff --- /dev/null +++ b/routes/docs/index.md @@ -0,0 +1,91 @@ +--- +title: About Aleph.js +authors: + - ije + - razermoon +--- + +# Aleph.js + +**Aleph.js** (or **Aleph** or **א** or **阿莱夫**, ˈɑːlɛf) is a +fullstack framework in [Deno]. Inspired by [Next.js], [Remix] and [Vite]. + +> The name is taken from the book [_The Aleph_] by **Jorge Luis Borges**. + +Aleph.js is modern framework that doesn't need **webpack** or other bundler +since it uses the [ES Module] syntax during development. Every module only needs +to be compiled once, when a module changes, Aleph.js just needs to re-compile +that single module. There is no time wasted _re-bundling_ everytime a change is +made. This, along with Hot Module Replacement (**HMR**) and **Fast Refresh**, +leads to instant updates in the browser. + +Aleph.js uses modern tools to build your app. It transpiles code using [swc] in +WASM with high performance, and bundles modules with [esbuild] at optimization +time extremely fast. + +Aleph.js works on top of **Deno**, a _simple_, _modern_ and _secure_ runtime for +JavaScript and TypeScript. All dependencies are imported using URLs, and managed +by Deno cache system. No `package.json` and `node_modules` directory needed. + +## Features + +- Zero Config +- No build step +- File-system Routing +- Just-in-time Server-side Rendering(SSR) +- Streaming SSR +- Support Typescript/JSX in Deno out of the box +- Built-in [Unocss] (automatic CSS) +- Import Maps +- Hot Module Replacement (or HMR) +- Support Middware +- Custom Module Loader like MDX + +## Supported frameworks + +- [React](https://github.com/alephjs/aleph.js/tree/main/examples/react-app) +- [React with MDX](https://github.com/alephjs/aleph.js/tree/main/examples/react-mdx-app) +- [Vue](https://github.com/alephjs/aleph.js/tree/main/examples/vue-app) +- [Solid](https://github.com/alephjs/aleph.js/tree/main/examples/solid-app) + _Experimental_ +- [Yew](https://github.com/alephjs/aleph.js/tree/main/examples/yew-app) _Rust_ + +Plan to support: [Svelte](https://svelte.dev/), [Lit](https://lit.dev/), etc.. + +## Examples + +Some demo apps deployed to [Deno Deploy](https://deno.com/deploy): + +- React App: https://aleph-hello.deno.dev/ +- Vue App: https://aleph-vue.deno.dev/ +- REST API: https://aleph-api.deno.dev/ +- React 18 Suspense SSR: https://aleph-suspense-ssr.deno.dev/ +- UnoCSS(tailwind): https://aleph-unocss.deno.dev/ +- Monaco Editor: https://aleph-monaco-editor.deno.dev/ +- Yew SSR: https://aleph-yew.deno.dev/ +- Github OAuth Middleware: https://aleph-github-oauth.deno.dev/ + +## Real-world Apps + +- Deno Deploy: https://dash.deno.com +- Meet Me: https://meet-me.deno.dev + ([source](https://github.com/denoland/meet-me)) + +## Status + +Currently in **beta**, not ready for production. + +## License + +Under the [MIT] License. + +[_The Aleph_]: http://phinnweb.org/links/literature/borges/aleph.html +[ES Module]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Guide/Modules +[deno]: https://deno.land +[Unocss]: https://github.com/unocss/unocss +[next.js]: https://nextjs.org +[Remix]: https://remix.run +[Vite]: https://vitejs.dev +[swc]: https://swc.rs +[esbuild]: https://github.com/evanw/esbuild +[MIT]: https://opensource.org/licenses/MIT diff --git a/routes/index.tsx b/routes/index.tsx new file mode 100644 index 0000000..7d3e0ca --- /dev/null +++ b/routes/index.tsx @@ -0,0 +1,83 @@ +import { Head, Link, useData } from "aleph/react"; +import Button from "components/Button.tsx"; +import Header from "components/Header.tsx"; + +const title = "Aleph.js"; +const about = "The Fullstack Framework in Deno."; +const keywords = [ + "aleph", + "alephjs", + "aleph.js", + "react", + "vue", + "full-stack", + "framework", + "ssr", + "deno", + "typescript", + "out-of-the-box", + "esm", + "hmr", + "fast-refresh", + "tooling", +]; +const ogImage = "https://alephjs.org/twitter_card.jpg"; + +type DataProps = { + version: string; +}; + +export const data: Data = { + cacheTtl: 60 * 60, // cache for 1 hour + get: async (req, ctx) => { + const versions = await fetch( + `https://cdn.deno.land/aleph/meta/versions.json`, + ).then((res) => res.json()); + return { version: versions.latest }; + }, +}; + +export default function Home() { + const { data: { version } } = useData(); + + return ( + <> + + {title} + + + + + + + + + + + +
+
+

+ The Fullstack Framework in Deno. +

+

+ Aleph.js{" "} + gives you the best developer experience for building web applications. +

+
+ + + +
+
+ + ); +} diff --git a/server.ts b/server.ts new file mode 100644 index 0000000..8012d2e --- /dev/null +++ b/server.ts @@ -0,0 +1,35 @@ +import presetUno from "@unocss/preset-uno"; +import { serve } from "aleph/react-server"; +import MDXLoader from "aleph/react/mdx-loader"; +import routes from "./routes/_export.ts"; +import remarkFrontmatter from "remark-frontmatter"; +import rehypeHighlight from "rehype-highlight"; + +serve({ + router: { + glob: "./routes/**/*.{ts,tsx,mdx,md}", + routes, + }, + unocss: { + presets: [presetUno()], + }, + loaders: [ + new MDXLoader({ + remarkPlugins: [remarkFrontmatter], + rehypePlugins: [rehypeHighlight], + }), + ], + middlewares: [ + { + name: "proxy-module", + fetch(req: Request) { + if (req.url.endsWith(".ts")) { + return fetch( + "https://deno.land/x/aleph" + (new URL(req.url)).pathname, + ); + } + }, + }, + ], + ssr: true, +}); diff --git a/style/app.css b/style/app.css deleted file mode 100644 index 5dd1273..0000000 --- a/style/app.css +++ /dev/null @@ -1,132 +0,0 @@ -:root { - --header-height: 80px; - --theme-color: #d63369; -} - -* { - margin: 0; - padding: 0; - border: none; - outline: none; - font: inherit; - font-size: 100%; - vertical-align: baseline; - background: transparent; -} - -html { - font-size: 15px; -} - -body { - font-family: -apple-system, BlinkMacSystemFont, system-ui, 'Segoe UI', 'Helvetica Neue', Helvetica, Roboto, Ubuntu, Tahoma, Arial, 'Apple Color Emoji', 'Segoe UI Emoji', 'Segoe UI Symbol', 'PingFang SC', 'Hiragino Sans GB', 'WenQuanYi Micro Hei', 'Microsoft YaHei', 'Heiti SC', sans-serif; - color: #333; - text-rendering: optimizeLegibility; -} - -ul, -ol { - list-style: none; -} - -a { - color: #000; - text-decoration: none; -} - -strong, b { - font-weight: 600; -} - -em, i { - font-style: italic; -} - -.fullscreen-page { - display: flex; - align-items: center; - justify-content: center; - flex-direction: column; - width: 100%; - height: calc(100vh - var(--header-height)); - - &:after { - display: block; - height: var(--header-height); - content: ''; - } - - .logo { - cursor: none; - } - - h1 { - line-height: 1.27; - font-size: 36px; - font-family: Gotham, 'Helvetica Neue', Helvetica, Arial, Verdana, sans-serif; - font-weight: 700; - letter-spacing: -1.5px; - text-align: center; - color: #111; - - @media screen and (max-width: 690px) and (min-width: 480px) { - font-size: 30px; - } - @media screen and (max-width: 480px) { - font-size: 25px; - } - } - - .intro { - width: 90%; - max-width: 600px; - margin-top: 6px; - line-height: 1.27; - font-size: 15px; - text-align: center; - color: #666; - - &.short { - width: 80%; - display: none; - } - - strong { - font-weight: 600; - } - - @media screen and (max-width: 690px) { - display: none; - - &.short { - display: block; - } - } - } - - .buttons { - width: 90%; - margin-top: 18px; - text-align: center; - - a { - display: inline-block; - margin: 0 6px; - } - - @media screen and (max-width: 690px) { - a { - display: block; - margin: 0; - margin-top: 9px; - - button { - box-sizing: border-box; - width: 100%; - height: 36px!important; - border-radius: 18px!important; - } - } - } - } -} diff --git a/style/docs.css b/style/docs.css deleted file mode 100644 index 28fbfc6..0000000 --- a/style/docs.css +++ /dev/null @@ -1,708 +0,0 @@ -/* https://esm.sh/highlightjs@9.16.2/styles/github.css */ -.hljs{display:block;overflow-x:auto;padding:.5em;color:#333;background:#f8f8f8}.hljs-comment,.hljs-quote{color:#998;font-style:italic}.hljs-keyword,.hljs-selector-tag,.hljs-subst{color:#111;font-weight:500}.hljs-literal,.hljs-number,.hljs-template-variable,.hljs-variable{color:teal}.hljs-doctag,.hljs-string{color:var(--theme-color)}.hljs-section,.hljs-selector-id,.hljs-title{color:var(--theme-color);font-weight:600}.hljs-subst{font-weight:400}.hljs-class .hljs-title,.hljs-type{color:#458;font-weight:600}.hljs-attribute,.hljs-name,.hljs-tag{color:#999;font-weight:400}.hljs-tag .hljs-name {color:teal;font-weight:500}.hljs-tag .hljs-attr {color:#454545;font-weight:500}.hljs-link,.hljs-regexp{color:#009926}.hljs-bullet,.hljs-symbol{color:var(--theme-color)}.hljs-built_in,.hljs-builtin-name{color:teal}.hljs-meta{color:#999;font-weight:600}.hljs-deletion{background:#fdd}.hljs-addition{background:#dfd}.hljs-emphasis{font-style:italic}.hljs-strong{font-weight:600} - -.docs { - width: 90%; - max-width: 1200px; - margin: 0 auto; - - @media screen and (max-width: 900px) { - width: 100%; - } - - aside { - position: sticky; - top: 80px; - z-index: 98; - float: left; - width: 270px; - - .search { - width: 100%; - padding-top: 6px; - - input { - box-shadow: none; - box-sizing: border-box; - display: inline-block; - font-size: 14px; - line-height: 36px; - width: 100%; - height: 36px; - padding: 0 12px; - border: 1px solid #eee; - border-radius: 6px; - outline: 0px; - color: #000; - background-color: transparent; - caret-color: #000; - transition: border-color .3s linear, color .3s linear; - text-overflow: ellipsis; - -webkit-appearance: none; - - &:hover { - color: #000; - border-color: #999; - } - &:focus { - color: #111; - border-color: #333; - } - } - } - - nav { - width: 100%; - height: calc(100vh - 80px - 6px - 36px - 2.1rem); - overflow: hidden; - overflow-y: auto; - margin-top: 2.1rem; - - &:after { - display: block; - height: 1.8rem; - content: ""; - } - - h2 { - line-height: 1.27; - font-size: 0.9rem; - font-weight: 400; - text-transform: uppercase; - color: #999; - } - - ul + h2 { - margin-top: 2.1rem; - } - - ul { - li { - margin-top: 15px; - - &.indent { - border-left: 1px solid rgb(234, 234, 234); - padding-left: 19px; - margin-left: 3px; - - & + li.indent { - margin-top: 0; - padding-top: 18px; - } - } - - label { - line-height: 1.27; - font-size: 1rem; - color: #333; - cursor: pointer; - user-select: none; - - svg { - display: inline-block; - margin-right: 14px; - transition: transform .15s linear; - } - &.open svg { - margin-left: 1px; - margin-right: 13px; - transform: rotate(90deg); - } - } - - a { - position: relative; - display: flex; - align-items: center; - line-height: 1.27; - font-size: 1rem; - color: #333; - user-select: none; - - .aleph-logo { - position: absolute; - top: -1px; - right: 1px; - font-size: 1.44rem; - color: #ccc; - opacity: 0.9; - transition: color .2s linear; - } - - &:before { - flex-basis: 4px; - flex-shrink: 0; - display: block; - width: 4px; - height: 4px; - margin-right: 16px; - border-radius: 50%; - background-color: #999; - transition: background-color .15s linear; - content: ""; - } - - &:hover { - color: #000; - } - - &.active { - color: #000; - font-weight: 600; - } - - &.active:before, - &:hover:before { - background-color: #111; - } - - &.active .aleph-logo, - &:hover .aleph-logo { - color: #D63369; - } - } - } - } - } - - .menu-button { - display: none; - width: 90%; - height: 18px; - margin: 0 auto; - padding: 18px 0; - line-height: 18px; - font-size: 1rem; - font-weight: 500; - color: #333; - user-select: none; - cursor: pointer; - - svg { - display: inline-block; - margin-right: 14px; - transition: transform .15s linear; - } - - &.open svg { - margin-left: 1px; - margin-right: 13px; - transform: rotate(90deg); - } - } - - @media screen and (max-width: 900px) { - top: -36px; - float: none; - width: 100%; - border-bottom: 1px solid #eee; - background-color: rgba(255, 255, 255, 0.95); - backdrop-filter: blur(6px); - - .search { - width: 90%; - margin: 0 auto; - padding-top: 0; - } - - .menu-button { - display: block; - } - - nav { - display: none; - margin-top: 0; - height: calc(100vh - 18px - 18px - 18px); - - &.open { - display: block; - } - - h2, ul { - width: 90%; - margin: 0 auto; - } - } - } - } - - .content { - padding-left: 360px; - - @media screen and (max-width: 900px) { - width: 90%; - margin: 5vw auto 0; - padding-left: 0; - } - - .doc-page { - h1, h2, h3, h4, h5 { - line-height: 1.27; - font-weight: 500; - color: #000; - } - - h1 { - margin-bottom: 1.2rem; - font-size: 2.4rem; - font-weight: 700; - } - h1:first-child { - margin-top: 0; - } - - h2 { - margin-top: 3rem; - font-size: 1.8rem; - } - - h3 { - margin-top: 2.7rem; - font-size: 1.5rem; - } - - h4 { - margin-top: 2.4rem; - font-size: 1.27rem; - } - - h5 { - margin-top: 2.1rem; - font-size: 1rem; - } - - p { - margin-top: 1.5rem; - line-height: 1.5; - } - - p + p { - margin-top: 1.2rem; - } - - h4 + p { - margin-top: 0.9rem; - } - - a { - color: var(--theme-color); - box-shadow: 0 1px 0 0 currentColor; - - &:hover { - box-shadow: none; - } - } - - img { - max-width: 100%; - border-radius: 6px; - } - - video { - max-width: 100%; - border-radius: 6px; - - &.is-paused { - cursor: url('/play_circle.svg'), pointer; - } - } - - code { - font-family: 'Dank Mono', 'Source Code Pro', monospace; - } - - samp { - font-family: monospace; - font-weight: 700; - - &::before, - &::after { - font-weight: 500; - color: #aaa; - content: '/' - } - } - - pre { - box-sizing: border-box; - overflow-x: auto; - width: 100%; - margin-top: 1.5rem; - border-radius: 6px; - line-height: 1.5; - color: #333; - background-color: #f8f8f8; - white-space: pre; - -webkit-overflow-scrolling: touch; - - & > code { - display: block; - padding: 1.5rem; - - .bash_prompt { - color: #bbb; - user-select: none; - } - } - } - - li > pre { - margin-top: 1rem; - margin-bottom: 1rem; - } - - :not(pre) > code { - display: inline; - white-space: pre-wrap; - color: var(--theme-color); - - &::before, - &::after { - color: currentColor; - content: '`' - } - } - - details { - padding: 0.5rem 1rem; - margin: 1.5rem 0; - border: 1px solid #eaeaea; - border-radius: 6px; - background-color: #fafafa; - - &[open] { - overflow: hidden; - } - - & > * { - margin: 0; - } - & > summary { - font-weight: 500; - outline: none; - cursor: pointer; - } - } - - blockquote { - color: #666; - padding: 0.3rem 1.5rem; - margin: 1.5rem 0; - border-left: 2px solid #ccc; - - p { - margin: 0; - } - a { - box-shadow: none; - } - } - - hr { - border: 0; - border-top: 1px solid #eaeaea; - margin-top: 1.5rem; - } - - ul, ol { - padding-left: 1.5rem; - margin-top: 1.5rem; - } - - p + ul, - p + ol { - margin-top: 0.75rem; - } - - ol { - list-style-type: decimal; - } - - li + li { - margin-top: 0.6rem; - } - - ul li:before { - position: absolute; - margin-left: -1rem; - color: #aaa; - content: '-'; - } - } - - .edit-link { - margin-top: 30px; - padding: 15px 0; - border-top: 1px solid #f3f3f3; - color: #333; - - span { - margin-left: 8px; - color: #ccc; - } - - a { - position: relative; - display: inline-block; - margin-left: 8px; - line-height: 1.8rem; - font-size: 0.95rem; - color: #333; - transition: 0.2s color; - vertical-align: middle; - - &.author { - display: inline-block; - width: 24px; - height: 24px; - margin-left: 2px; - border-radius: 50%; - overflow: hidden; - - img { - width: 24px; - height: 24px; - } - } - - &:hover { - color: #000; - } - &:hover:after { - content: ' 👍🏿' - } - } - } - - .doc-page-nav { - display: flex; - justify-content: space-between; - align-items: center; - margin-top: 45px; - - .page-nav-link { - display: inline-flex; - align-items: center; - - & > span { - display: inline-flex; - align-items: center; - font-size: 16px; - height: 24px; - color: #999; - transition: color 0.2s; - } - - & > a { - display: inline-block; - height: 24px; - line-height: 24px; - font-size: 16px; - color: #333; - transition: color 0.2s; - } - - &:hover > span { - color: #333; - } - - &:hover > a { - color: #000; - } - - span + a, - a + span { - padding-left: 6px; - } - } - } - - .bottom-space { - height: 60px; - } - } - - .theme, - .artworks { - padding: 150px 0; - text-align: center; - background-image: url('/grid.svg'); - background-size: 24px 24px; - - h3 { - margin-top: 12px; - line-height: 1.5; - font-family: 'Source Code Pro', 'Courier Prime Code', Courier, monospace; - font-size: 1rem; - font-weight: 600; - color: #333; - - a { - color: #999; - font-size: 0.9rem; - - &:hover { - color: #333; - } - } - } - - img { - display: inline-block; - width: 200px; - margin: 0 30px; - - &.md { - width: 150px; - } - - &.sm { - width: 120px; - } - } - - .color { - display: inline-flex; - align-items: center; - justify-content: center; - width: 150px; - height: 150px; - border-radius: 50%; - font-size: 15px; - font-weight: 500; - font-family: Din; - color: #fff; - } - - .space { - display: block; - height: 36px; - } - .space.sm { - height: 12px; - } - .space.lg { - height: 120px; - } - } - - .api-doc-page { - header { - display: flex; - align-items: center; - top: 14px; - height: 42px; - width: 75%; - background-color: tranparent; - backdrop-filter: none; - - @media screen and (max-width: 900px) { - display: none; - } - - a { - color: var(--theme-color); - box-shadow: 0 1px 0 0 currentColor; - - &:hover { - box-shadow: none; - } - } - } - - section { - margin: 30px 0; - padding: 30px; - border: 1px solid #eee; - border-radius: 6px; - transition: border-color 0.15s ease-in; - opacity: 0.9; - - &:hover { - border-color: #aaa; - opacity: 1; - } - - .keyword { - color: var(--theme-color) - } - - h2 { - font-size: 1.5rem; - line-height: 1.5; - &.single-line { - font-size: 1.2rem; - } - } - - h3 { - margin-top: 30px; - font-size: 1.2rem; - color: #999; - } - - .jsdoc { - code { - background: #f6f3f3; - padding: 0 9px; - border-radius: 3px; - } - p { - padding-top: 3px; - line-height: 1.27; - color: #515151; - a { - color: #515151; - box-shadow: 0 1px 0 0 #aaa; - - &:hover { - box-shadow: none; - } - } - } - ul, ol { - padding-left: 1.5rem; - - li { - margin-top: 0.6rem; - } - } - ol { - list-style-type: decimal; - } - ul li:before { - position: absolute; - margin-left: -1rem; - color: #aaa; - content: '-'; - } - } - - h2 code, - pre code { - font-family: 'Dank Mono', 'Source Code Pro', 'Courier Prime Code', Courier, monospace; - } - - .def { - margin-top: 18px; - - pre { - font-size: 1.2rem; - font-weight: 600; - color: #000; - white-space: pre-wrap; - } - - .separator { - font-weight: 400; - color: #999; - } - } - } - - details > summary { - cursor: pointer; - color: #999; - } - } -} diff --git a/style/header.css b/style/header.css deleted file mode 100644 index 0dbc7bf..0000000 --- a/style/header.css +++ /dev/null @@ -1,70 +0,0 @@ -header { - position: sticky; - top: 0; - left: 0; - z-index: 99; - width: 100%; - height: var(--header-height); - background-color: rgba(255, 255, 255, 0.95); - backdrop-filter: blur(6px); - - &.scrollable { - @media screen and (max-width: 900px) { - position: relative; - } - } - - .small { - height: 60px; - } - - .wrapper { - display: flex; - align-items: center; - justify-content: space-between; - width: 90%; - max-width: 1200px; - height: 100%; - margin: 0 auto; - } - - h1 a { - display: inline-block; - font-size: 1rem; - color: #333; - } - - nav ul { - li + li { - margin-left: 24px; - } - - li { - float: left; - height: 24px; - - a { - font-size: 13px; - line-height: 24px; - color: #333; - - &:hover { - color: #000; - } - } - a.icon { - display: inline-flex; - align-items: center; - width: 20px; - height: 20px; - font-size: 20px; - color: #454545; - transition: color 0.2s ease-in-out; - - &:hover { - color: #000; - } - } - } - } -} diff --git a/style/hljs.css b/style/hljs.css new file mode 100644 index 0000000..a7bb80f --- /dev/null +++ b/style/hljs.css @@ -0,0 +1,104 @@ +/* copied from https://esm.sh/highlightjs@9.16.2/styles/github.css */ + +.hljs { + display: block; + overflow-x: auto; + padding: .5em; + color: #333; + background: #f8f8f8 +} + +.hljs-comment, +.hljs-quote { + color: #998; + font-style: italic +} + +.hljs-keyword, +.hljs-selector-tag, +.hljs-subst { + color: #111; + font-weight: 500 +} + +.hljs-literal, +.hljs-number, +.hljs-template-variable, +.hljs-variable { + color: teal +} + +.hljs-doctag, +.hljs-string { + color: var(--theme-color) +} + +.hljs-section, +.hljs-selector-id, +.hljs-title { + color: var(--theme-color); + font-weight: 500 +} + +.hljs-subst { + font-weight: 400 +} + +.hljs-class .hljs-title, +.hljs-type { + color: #458; + font-weight: 500 +} + +.hljs-attribute, +.hljs-name, +.hljs-tag { + color: #999; + font-weight: 400 +} + +.hljs-tag .hljs-name { + color: teal; + font-weight: 500 +} + +.hljs-tag .hljs-attr { + color: #454545; + font-weight: 500 +} + +.hljs-link, +.hljs-regexp { + color: #926711 +} + +.hljs-bullet, +.hljs-symbol { + color: var(--theme-color) +} + +.hljs-built_in, +.hljs-builtin-name { + color: teal +} + +.hljs-meta { + color: #999; + font-weight: 500 +} + +.hljs-deletion { + background: #fdd +} + +.hljs-addition { + background: #dfd +} + +.hljs-emphasis { + font-style: italic +} + +.hljs-strong { + font-weight: 500 +} diff --git a/style/index.css b/style/index.css deleted file mode 100644 index 3b46bde..0000000 --- a/style/index.css +++ /dev/null @@ -1,72 +0,0 @@ -.index-page { - .logo { - position: relative; - top: 18px; - } - - h1 { - position: relative; - white-space: pre; - } - - section { - display: flex; - align-items: center; - justify-content: left; - flex-direction: column; - - h2 { - display: inline-block; - margin-bottom: 60px; - line-height: 1.5; - font-family: Gotham, 'Helvetica Neue', Helvetica, Arial, sans-serif; - letter-spacing: -1px; - font-size: 21px; - font-weight: 600; - text-align: center; - color: #111; - border-bottom: 3px solid #333; - } - - ul { - width: 90%; - max-width: 540px; - - li:first-child { - border-top: 1px solid #eee; - } - - li { - border-bottom: 1px solid #eee; - } - - a { - display: block; - padding: 15px 6px; - line-height: 1.27; - font-weight: 500; - color: inherit; - transition: all 160ms ease-out; - - &:hover { - color: #000; - background-color: #f6f6f6; - } - } - } - } - - footer { - display: flex; - align-items: center; - justify-content: left; - flex-direction: column; - padding: 120px 0 45px; - - p { - font-size: 14px; - line-height: 1.5; - color: #999; - } - } -} diff --git a/style/mdx.css b/style/mdx.css new file mode 100644 index 0000000..88ff077 --- /dev/null +++ b/style/mdx.css @@ -0,0 +1,141 @@ +@import url("./hljs.css"); + +:root { + --theme-color: #d63369; +} + +.markdown-body { + overflow: hidden; + color: #333; + + & a { + color: var(--theme-color); + + &:hover { + text-decoration: underline; + } + } + + & strong { + font-weight: 600; + color: #111; + } + + & em { + font-style: italic; + } + + & h1, + & h2, + & h3, + & h4 { + line-height: 1.2; + font-weight: 500; + color: #111; + } + + & h1 { + margin-top: 3.2rem; + font-size: 2.4rem; + font-weight: 700; + } + + & h1:first-child { + margin-top: 0; + } + + & h2 { + margin-top: 2.8rem; + font-size: 1.8rem; + } + + & h3 { + margin-top: 2.4rem; + font-size: 1.5rem; + } + + & h4 { + margin-top: 2.0rem; + font-size: 1.27rem; + } + + & > p { + margin-top: 1.2rem; + line-height: 1.5; + } + + & ul { + padding-left: 1.2rem; + margin-top: 0.9rem; + list-style: none; + } + + & ul li:before { + position: absolute; + margin-left: -1rem; + color: #aaa; + content: '-'; + } + + & ol { + margin-top: 0.9rem; + list-style: decimal inside; + } + + & ul, & ol { + & li + li { + margin-top: 0.5rem; + } + & li p { + display: inline; + } + } + + & pre { + box-sizing: border-box; + overflow-x: auto; + width: 100%; + margin-top: 1.5rem; + border-radius: 6px; + line-height: 1.5; + color: #333; + background-color: #f6f6f6; + white-space: pre; + overflow-x: auto; + -webkit-overflow-scrolling: touch; + + &>code { + display: block; + padding: 1rem 1.2rem; + font-size: 0.9rem; + } + } + + & :not(pre)>code { + display: inline; + white-space: pre-wrap; + font-size: 90%; + color: var(--theme-color); + + &::before, + &::after { + color: currentColor; + content: '`' + } + } + + & blockquote { + color: #666; + padding: 0.3rem 1.2rem; + margin: 1.2rem 0; + border-left: 2px solid #ccc; + + & p { + margin: 0; + } + + & a { + box-shadow: none; + } + } +} \ No newline at end of file diff --git a/tsconfig.json b/tsconfig.json deleted file mode 100644 index e47bac3..0000000 --- a/tsconfig.json +++ /dev/null @@ -1,5 +0,0 @@ -{ - "compilerOptions": { - "lib": ["deno.ns", "dom"] - } -} \ No newline at end of file diff --git a/vercel.json b/vercel.json deleted file mode 100644 index d6695ec..0000000 --- a/vercel.json +++ /dev/null @@ -1,7 +0,0 @@ -{ - "functions": { - "api/**/*.{j,t}s": { - "runtime": "vercel-aleph@0.7.0" - } - } -} \ No newline at end of file