docs: huge migration to runtime asset cache
This commit is contained in:
@@ -6,9 +6,6 @@ COPY / ./
|
|||||||
RUN ls -R
|
RUN ls -R
|
||||||
|
|
||||||
RUN npm install -g pnpm
|
RUN npm install -g pnpm
|
||||||
RUN pnpm install --unsafe-perm --frozen-lockfile
|
|
||||||
RUN pnpm -C packages/docs build
|
|
||||||
RUN pnpm install --prod --unsafe-perm --frozen-lockfile
|
RUN pnpm install --prod --unsafe-perm --frozen-lockfile
|
||||||
RUN pnpm store prune
|
|
||||||
|
|
||||||
CMD [ "pnpm", "-C", "packages/docs", "start" ]
|
CMD [ "pnpm", "-C", "packages/docs", "start" ]
|
||||||
|
|||||||
2
packages/docs/.gitignore
vendored
Normal file
2
packages/docs/.gitignore
vendored
Normal file
@@ -0,0 +1,2 @@
|
|||||||
|
.asset-cache
|
||||||
|
node_modules
|
||||||
@@ -1,12 +0,0 @@
|
|||||||
import type { BuildOptions } from "esbuild"
|
|
||||||
import packageJson from "./package.json"
|
|
||||||
|
|
||||||
export const esbuildConfig: BuildOptions = {
|
|
||||||
entryPoints: [packageJson.source],
|
|
||||||
bundle: true,
|
|
||||||
outfile: packageJson.main,
|
|
||||||
format: "esm",
|
|
||||||
target: "node16",
|
|
||||||
platform: "node",
|
|
||||||
external: Object.keys(packageJson.dependencies),
|
|
||||||
}
|
|
||||||
@@ -2,16 +2,14 @@
|
|||||||
"name": "reacord-docs-new",
|
"name": "reacord-docs-new",
|
||||||
"type": "module",
|
"type": "module",
|
||||||
"private": true,
|
"private": true,
|
||||||
"source": "./src/main.tsx",
|
|
||||||
"main": "./dist/main.js",
|
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"build": "esmo --no-warnings scripts/build.ts",
|
"serve": "esmo --experimental-import-meta-resolve --experimental-json-modules --no-warnings --enable-source-maps src/main.tsx | pino-colada",
|
||||||
"dev": "esmo --no-warnings scripts/dev.ts | pino-colada",
|
"dev": "nodemon --exec \"pnpm serve\" --watch src --ext ts,tsx,md,css",
|
||||||
"start": "NODE_ENV=production pnpm serve | pino-colada",
|
"start": "NODE_ENV=production pnpm serve",
|
||||||
"serve": "node --experimental-import-meta-resolve --experimental-json-modules --no-warnings --enable-source-maps dist/main.js",
|
|
||||||
"typecheck": "tsc --noEmit"
|
"typecheck": "tsc --noEmit"
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
|
"@heroicons/react": "^1.0.5",
|
||||||
"@tailwindcss/typography": "^0.5.0",
|
"@tailwindcss/typography": "^0.5.0",
|
||||||
"clsx": "^1.1.1",
|
"clsx": "^1.1.1",
|
||||||
"compression": "^1.7.4",
|
"compression": "^1.7.4",
|
||||||
@@ -34,7 +32,6 @@
|
|||||||
"tailwindcss": "^3.0.8"
|
"tailwindcss": "^3.0.8"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@heroicons/react": "^1.0.5",
|
|
||||||
"@types/browser-sync": "^2.26.3",
|
"@types/browser-sync": "^2.26.3",
|
||||||
"@types/compression": "^1.7.2",
|
"@types/compression": "^1.7.2",
|
||||||
"@types/express": "^4.17.13",
|
"@types/express": "^4.17.13",
|
||||||
@@ -46,8 +43,8 @@
|
|||||||
"@types/wait-on": "^5.3.1",
|
"@types/wait-on": "^5.3.1",
|
||||||
"autoprefixer": "^10.4.1",
|
"autoprefixer": "^10.4.1",
|
||||||
"browser-sync": "^2.27.7",
|
"browser-sync": "^2.27.7",
|
||||||
"chokidar": "^3.5.2",
|
|
||||||
"execa": "^6.0.0",
|
"execa": "^6.0.0",
|
||||||
|
"nodemon": "^2.0.15",
|
||||||
"rxjs": "^7.5.1",
|
"rxjs": "^7.5.1",
|
||||||
"tsup": "^5.11.10",
|
"tsup": "^5.11.10",
|
||||||
"type-fest": "^2.8.0",
|
"type-fest": "^2.8.0",
|
||||||
|
|||||||
@@ -1,3 +0,0 @@
|
|||||||
import { build } from "esbuild"
|
|
||||||
import { esbuildConfig } from "../esbuild.config"
|
|
||||||
await build(esbuildConfig)
|
|
||||||
@@ -1,138 +0,0 @@
|
|||||||
import browserSync from "browser-sync"
|
|
||||||
import chokidar from "chokidar"
|
|
||||||
import type { ExecaChildProcess } from "execa"
|
|
||||||
import { execa } from "execa"
|
|
||||||
import pino from "pino"
|
|
||||||
import { concatMap, debounceTime, Observable, tap } from "rxjs"
|
|
||||||
import waitOn from "wait-on"
|
|
||||||
import packageJson from "../package.json"
|
|
||||||
|
|
||||||
const console = pino()
|
|
||||||
|
|
||||||
function awaitChildStopped(child: ExecaChildProcess) {
|
|
||||||
if (child.killed) return
|
|
||||||
return new Promise((resolve) => child.once("close", resolve))
|
|
||||||
}
|
|
||||||
|
|
||||||
class App {
|
|
||||||
app: ExecaChildProcess | undefined
|
|
||||||
|
|
||||||
async start() {
|
|
||||||
console.info(this.app ? "Restarting app..." : "Starting app...")
|
|
||||||
|
|
||||||
await this.stop()
|
|
||||||
|
|
||||||
const [command, ...flags] = packageJson.scripts.serve.split(/\s+/)
|
|
||||||
this.app = execa(command!, flags, {
|
|
||||||
stdio: "inherit",
|
|
||||||
detached: true,
|
|
||||||
})
|
|
||||||
|
|
||||||
this.app.catch((error) => {
|
|
||||||
if (error.signal !== "SIGINT") {
|
|
||||||
console.error(error)
|
|
||||||
}
|
|
||||||
})
|
|
||||||
|
|
||||||
void this.app.on("close", () => {
|
|
||||||
this.app = undefined
|
|
||||||
})
|
|
||||||
|
|
||||||
await waitOn({ resources: ["http-get://localhost:3000"] })
|
|
||||||
|
|
||||||
console.info("App running")
|
|
||||||
}
|
|
||||||
|
|
||||||
async stop() {
|
|
||||||
if (this.app) {
|
|
||||||
if (this.app.pid != undefined) {
|
|
||||||
process.kill(-this.app.pid, "SIGINT")
|
|
||||||
} else {
|
|
||||||
this.app.kill("SIGINT")
|
|
||||||
}
|
|
||||||
await awaitChildStopped(this.app)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
class Builder {
|
|
||||||
child = execa("tsup", ["--watch"], {
|
|
||||||
stdio: "inherit",
|
|
||||||
})
|
|
||||||
|
|
||||||
async stop() {
|
|
||||||
this.child.kill()
|
|
||||||
await awaitChildStopped(this.child)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
class Browser {
|
|
||||||
browser = browserSync.create()
|
|
||||||
|
|
||||||
constructor() {
|
|
||||||
this.browser.emitter.on("init", () => {
|
|
||||||
console.info("Browsersync started")
|
|
||||||
})
|
|
||||||
this.browser.emitter.on("browser:reload", () => {
|
|
||||||
console.info("Browser reloaded")
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
init() {
|
|
||||||
this.browser.init({
|
|
||||||
proxy: "http://localhost:3000",
|
|
||||||
port: 3001,
|
|
||||||
ui: false,
|
|
||||||
logLevel: "silent",
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
reload() {
|
|
||||||
this.browser.reload()
|
|
||||||
}
|
|
||||||
|
|
||||||
stop() {
|
|
||||||
this.browser.exit()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
class Watcher {
|
|
||||||
subscription = new Observable<string>((subscriber) => {
|
|
||||||
chokidar
|
|
||||||
.watch(packageJson.main, { ignored: /node_modules/, ignoreInitial: true })
|
|
||||||
.on("all", (_, path) => subscriber.next(path))
|
|
||||||
})
|
|
||||||
.pipe(
|
|
||||||
tap((path) => console.info(`Changed:`, path)),
|
|
||||||
debounceTime(100),
|
|
||||||
concatMap(async () => {
|
|
||||||
await this.app.start()
|
|
||||||
this.browser.reload()
|
|
||||||
}),
|
|
||||||
)
|
|
||||||
.subscribe()
|
|
||||||
|
|
||||||
constructor(private app: App, private browser: Browser) {}
|
|
||||||
|
|
||||||
stop() {
|
|
||||||
this.subscription.unsubscribe()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
const app = new App()
|
|
||||||
const builder = new Builder()
|
|
||||||
const browser = new Browser()
|
|
||||||
const watcher = new Watcher(app, browser)
|
|
||||||
|
|
||||||
process.on("SIGINT", async () => {
|
|
||||||
console.info("Shutting down...")
|
|
||||||
try {
|
|
||||||
await Promise.all([app, browser, watcher, builder].map((it) => it.stop()))
|
|
||||||
} catch (error) {
|
|
||||||
console.error(error)
|
|
||||||
}
|
|
||||||
process.exit()
|
|
||||||
})
|
|
||||||
|
|
||||||
await app.start()
|
|
||||||
browser.init()
|
|
||||||
10
packages/docs/src/asset-builder/asset-builder-context.tsx
Normal file
10
packages/docs/src/asset-builder/asset-builder-context.tsx
Normal file
@@ -0,0 +1,10 @@
|
|||||||
|
import { createContext, useContext } from "react"
|
||||||
|
import { AssetBuilder } from "../asset-builder/asset-builder.js"
|
||||||
|
import { raise } from "../helpers/raise.js"
|
||||||
|
|
||||||
|
const Context = createContext<AssetBuilder>()
|
||||||
|
|
||||||
|
export const AssetBuilderProvider = Context.Provider
|
||||||
|
|
||||||
|
export const useAssetBuilder = () =>
|
||||||
|
useContext(Context) ?? raise("AssetBuilderProvider not found")
|
||||||
85
packages/docs/src/asset-builder/asset-builder.ts
Normal file
85
packages/docs/src/asset-builder/asset-builder.ts
Normal file
@@ -0,0 +1,85 @@
|
|||||||
|
import { RequestHandler } from "express"
|
||||||
|
import { createHash } from "node:crypto"
|
||||||
|
import { readFileSync } from "node:fs"
|
||||||
|
import { mkdir, stat, writeFile } from "node:fs/promises"
|
||||||
|
import { dirname, extname, join, parse } from "node:path"
|
||||||
|
|
||||||
|
export type Asset = {
|
||||||
|
file: string
|
||||||
|
url: string
|
||||||
|
content: Buffer
|
||||||
|
}
|
||||||
|
|
||||||
|
export type AssetTransformer = {
|
||||||
|
transform: (asset: Asset) => Promise<AssetTransformResult | undefined>
|
||||||
|
}
|
||||||
|
|
||||||
|
export type AssetTransformResult = {
|
||||||
|
content: string
|
||||||
|
type: string
|
||||||
|
}
|
||||||
|
|
||||||
|
export class AssetBuilder {
|
||||||
|
// map of asset urls to asset objects
|
||||||
|
private library = new Map<string, Asset>()
|
||||||
|
|
||||||
|
constructor(
|
||||||
|
private cacheFolder: string,
|
||||||
|
private transformers: AssetTransformer[],
|
||||||
|
) {}
|
||||||
|
|
||||||
|
// accepts a path to a file, then returns a url to where the built file will be served
|
||||||
|
// the url will include a hash of the file contents
|
||||||
|
file(file: string | URL): string {
|
||||||
|
if (file instanceof URL) {
|
||||||
|
file = file.pathname
|
||||||
|
}
|
||||||
|
|
||||||
|
const existing = this.library.get(file)
|
||||||
|
if (existing) {
|
||||||
|
return existing.url
|
||||||
|
}
|
||||||
|
|
||||||
|
const content = readFileSync(file)
|
||||||
|
const hash = createHash("sha256").update(content).digest("hex").slice(0, 8)
|
||||||
|
|
||||||
|
const { name, ext } = parse(file)
|
||||||
|
const url = `/${name}.${hash}${ext}`
|
||||||
|
this.library.set(url, { file, url, content })
|
||||||
|
return url
|
||||||
|
}
|
||||||
|
|
||||||
|
middleware(): RequestHandler {
|
||||||
|
return async (req, res, next) => {
|
||||||
|
try {
|
||||||
|
const asset = this.library.get(req.path)
|
||||||
|
if (!asset) return next()
|
||||||
|
|
||||||
|
const file = join(this.cacheFolder, asset.url)
|
||||||
|
const extension = extname(file)
|
||||||
|
|
||||||
|
const stats = await stat(file).catch(() => undefined)
|
||||||
|
if (stats?.isFile()) {
|
||||||
|
res
|
||||||
|
.status(200)
|
||||||
|
.type(extension.endsWith("tsx") ? "text/javascript" : extension)
|
||||||
|
.sendFile(file)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
for (const transformer of this.transformers) {
|
||||||
|
const result = await transformer.transform(asset)
|
||||||
|
if (result) {
|
||||||
|
await mkdir(dirname(file), { recursive: true })
|
||||||
|
await writeFile(file, result.content)
|
||||||
|
return res.type(extension).send(result.content)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
next()
|
||||||
|
} catch (error) {
|
||||||
|
next(error)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
28
packages/docs/src/asset-builder/transform-esbuild.ts
Normal file
28
packages/docs/src/asset-builder/transform-esbuild.ts
Normal file
@@ -0,0 +1,28 @@
|
|||||||
|
import { build } from "esbuild"
|
||||||
|
import { readFile } from "node:fs/promises"
|
||||||
|
import { dirname } from "node:path"
|
||||||
|
import { AssetTransformer } from "./asset-builder.js"
|
||||||
|
|
||||||
|
export const transformEsbuild: AssetTransformer = {
|
||||||
|
async transform(asset) {
|
||||||
|
if (asset.file.match(/\.tsx?$/)) {
|
||||||
|
const scriptBuild = await build({
|
||||||
|
bundle: true,
|
||||||
|
stdin: {
|
||||||
|
contents: await readFile(asset.file, "utf-8"),
|
||||||
|
sourcefile: asset.file,
|
||||||
|
loader: "tsx",
|
||||||
|
resolveDir: dirname(asset.file),
|
||||||
|
},
|
||||||
|
target: ["chrome89", "firefox89"],
|
||||||
|
format: "esm",
|
||||||
|
write: false,
|
||||||
|
})
|
||||||
|
|
||||||
|
return {
|
||||||
|
content: scriptBuild.outputFiles[0]!.text,
|
||||||
|
type: "text/javascript",
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
}
|
||||||
17
packages/docs/src/asset-builder/transform-postcss.ts
Normal file
17
packages/docs/src/asset-builder/transform-postcss.ts
Normal file
@@ -0,0 +1,17 @@
|
|||||||
|
import postcss from "postcss"
|
||||||
|
import tailwindcss from "tailwindcss"
|
||||||
|
import { AssetTransformer } from "./asset-builder.js"
|
||||||
|
|
||||||
|
export const transformPostCss: AssetTransformer = {
|
||||||
|
async transform(asset) {
|
||||||
|
if (!asset.file.match(/\.css$/)) return
|
||||||
|
|
||||||
|
const result = await postcss(tailwindcss).process(asset.content, {
|
||||||
|
from: asset.file,
|
||||||
|
})
|
||||||
|
return {
|
||||||
|
content: result.css,
|
||||||
|
type: "text/css",
|
||||||
|
}
|
||||||
|
},
|
||||||
|
}
|
||||||
@@ -1,23 +0,0 @@
|
|||||||
import * as React from "react"
|
|
||||||
import { mainLinks } from "../data/main-links"
|
|
||||||
import type { AppLinkProps } from "./app-link"
|
|
||||||
import { AppLink } from "./app-link"
|
|
||||||
import { PopoverMenu } from "./popover-menu"
|
|
||||||
|
|
||||||
export type MainNavigationMobileMenuData = {
|
|
||||||
guideLinks: AppLinkProps[]
|
|
||||||
}
|
|
||||||
|
|
||||||
export function render(data: MainNavigationMobileMenuData) {
|
|
||||||
return (
|
|
||||||
<PopoverMenu>
|
|
||||||
{mainLinks.map((link) => (
|
|
||||||
<AppLink {...link} key={link.to} className={PopoverMenu.itemClass} />
|
|
||||||
))}
|
|
||||||
<hr className="border-0 h-[2px] bg-black/50" />
|
|
||||||
{data.guideLinks.map((link) => (
|
|
||||||
<AppLink {...link} key={link.to} className={PopoverMenu.itemClass} />
|
|
||||||
))}
|
|
||||||
</PopoverMenu>
|
|
||||||
)
|
|
||||||
}
|
|
||||||
@@ -1,16 +0,0 @@
|
|||||||
import type { ComponentPropsWithoutRef } from "react"
|
|
||||||
import React from "react"
|
|
||||||
import type { Merge } from "type-fest"
|
|
||||||
|
|
||||||
export function Script({
|
|
||||||
children,
|
|
||||||
...props
|
|
||||||
}: Merge<ComponentPropsWithoutRef<"script">, { children: string }>) {
|
|
||||||
return (
|
|
||||||
<script
|
|
||||||
type="module"
|
|
||||||
dangerouslySetInnerHTML={{ __html: children }}
|
|
||||||
{...props}
|
|
||||||
/>
|
|
||||||
)
|
|
||||||
}
|
|
||||||
@@ -1,26 +1,13 @@
|
|||||||
import clsx from "clsx"
|
import clsx from "clsx"
|
||||||
import React from "react"
|
import React from "react"
|
||||||
import { AppLink } from "../components/app-link"
|
import { AppLink } from "../navigation/app-link"
|
||||||
import { MainNavigation } from "../components/main-navigation"
|
import { guideLinks } from "../navigation/guide-links"
|
||||||
import { guideLinks } from "../data/guide-links"
|
import { MainNavigation } from "../navigation/main-navigation"
|
||||||
import { Html } from "../html"
|
import { docsProseClass, linkClass, maxWidthContainer } from "../ui/components"
|
||||||
import {
|
|
||||||
docsProseClass,
|
|
||||||
linkClass,
|
|
||||||
maxWidthContainer,
|
|
||||||
} from "../styles/components"
|
|
||||||
|
|
||||||
export default function DocsPage({
|
export default function GuidePage({ html }: { html: string }) {
|
||||||
title,
|
|
||||||
description,
|
|
||||||
html,
|
|
||||||
}: {
|
|
||||||
title: string
|
|
||||||
description: string
|
|
||||||
html: string
|
|
||||||
}) {
|
|
||||||
return (
|
return (
|
||||||
<Html title={`${title} | Reacord`} description={description}>
|
<>
|
||||||
<header className="bg-slate-700/30 shadow sticky top-0 backdrop-blur-sm transition z-10 flex">
|
<header className="bg-slate-700/30 shadow sticky top-0 backdrop-blur-sm transition z-10 flex">
|
||||||
<div className={maxWidthContainer}>
|
<div className={maxWidthContainer}>
|
||||||
<MainNavigation />
|
<MainNavigation />
|
||||||
@@ -42,6 +29,6 @@ export default function DocsPage({
|
|||||||
dangerouslySetInnerHTML={{ __html: html }}
|
dangerouslySetInnerHTML={{ __html: html }}
|
||||||
/>
|
/>
|
||||||
</main>
|
</main>
|
||||||
</Html>
|
</>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
@@ -1,51 +0,0 @@
|
|||||||
import { build } from "esbuild"
|
|
||||||
import { readFile } from "node:fs/promises"
|
|
||||||
import { dirname } from "node:path"
|
|
||||||
import React from "react"
|
|
||||||
import { Script } from "../components/script"
|
|
||||||
|
|
||||||
let nextId = 0
|
|
||||||
|
|
||||||
export async function createHydrater<Data>(scriptFilePath: string) {
|
|
||||||
const id = `hydrate-root-${nextId}`
|
|
||||||
nextId += 1
|
|
||||||
|
|
||||||
const scriptSource = await readFile(scriptFilePath, "utf-8")
|
|
||||||
|
|
||||||
const scriptBuild = await build({
|
|
||||||
bundle: true,
|
|
||||||
stdin: {
|
|
||||||
contents: [scriptSource, clientBootstrap(id)].join(";\n"),
|
|
||||||
sourcefile: scriptFilePath,
|
|
||||||
loader: "tsx",
|
|
||||||
resolveDir: dirname(scriptFilePath),
|
|
||||||
},
|
|
||||||
target: ["chrome89", "firefox89"],
|
|
||||||
format: "esm",
|
|
||||||
write: false,
|
|
||||||
})
|
|
||||||
|
|
||||||
const serverModule = await import(scriptFilePath)
|
|
||||||
|
|
||||||
return function Hydrater({ data }: { data: Data }) {
|
|
||||||
return (
|
|
||||||
<>
|
|
||||||
<div id={id} data-server-data={JSON.stringify(data)}>
|
|
||||||
{serverModule.render(data)}
|
|
||||||
</div>
|
|
||||||
<Script>{scriptBuild.outputFiles[0]?.text!}</Script>
|
|
||||||
</>
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
function clientBootstrap(id: string) {
|
|
||||||
return /* ts */ `
|
|
||||||
import { createRoot } from "react-dom"
|
|
||||||
|
|
||||||
const rootElement = document.querySelector("#${id}")
|
|
||||||
const data = JSON.parse(rootElement.dataset.serverData)
|
|
||||||
|
|
||||||
createRoot(rootElement).render(render(data))
|
|
||||||
`
|
|
||||||
}
|
|
||||||
3
packages/docs/src/helpers/raise.ts
Normal file
3
packages/docs/src/helpers/raise.ts
Normal file
@@ -0,0 +1,3 @@
|
|||||||
|
export function raise(error: unknown): never {
|
||||||
|
throw error instanceof Error ? error : new Error(String(error))
|
||||||
|
}
|
||||||
@@ -1,7 +0,0 @@
|
|||||||
import type { Response } from "express"
|
|
||||||
import { renderToStaticMarkup } from "react-dom/server.js"
|
|
||||||
|
|
||||||
export function sendJsx(res: Response, jsx: React.ReactElement) {
|
|
||||||
res.set("Content-Type", "text/html")
|
|
||||||
res.send(`<!DOCTYPE html>\n${renderToStaticMarkup(jsx)}`)
|
|
||||||
}
|
|
||||||
@@ -1,26 +0,0 @@
|
|||||||
import { build } from "esbuild"
|
|
||||||
import type { RequestHandler } from "express"
|
|
||||||
import { readFile } from "node:fs/promises"
|
|
||||||
import { dirname } from "node:path"
|
|
||||||
|
|
||||||
export async function serveCompiledScript(
|
|
||||||
scriptFilePath: string,
|
|
||||||
): Promise<RequestHandler> {
|
|
||||||
const scriptBuild = await build({
|
|
||||||
bundle: true,
|
|
||||||
stdin: {
|
|
||||||
contents: await readFile(scriptFilePath, "utf-8"),
|
|
||||||
sourcefile: scriptFilePath,
|
|
||||||
loader: "tsx",
|
|
||||||
resolveDir: dirname(scriptFilePath),
|
|
||||||
},
|
|
||||||
target: ["chrome89", "firefox89"],
|
|
||||||
format: "esm",
|
|
||||||
write: false,
|
|
||||||
})
|
|
||||||
|
|
||||||
return (req, res) => {
|
|
||||||
res.setHeader("Content-Type", "application/javascript")
|
|
||||||
res.end(scriptBuild.outputFiles[0]!.contents)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,5 +0,0 @@
|
|||||||
import type { RequestHandler } from "express"
|
|
||||||
|
|
||||||
export function serveFile(path: string): RequestHandler {
|
|
||||||
return (req, res) => res.sendFile(path)
|
|
||||||
}
|
|
||||||
@@ -1,25 +0,0 @@
|
|||||||
import type { RequestHandler } from "express"
|
|
||||||
import { readFile } from "node:fs/promises"
|
|
||||||
import { fileURLToPath } from "node:url"
|
|
||||||
import type { Result } from "postcss"
|
|
||||||
import postcss from "postcss"
|
|
||||||
import tailwindcss from "tailwindcss"
|
|
||||||
|
|
||||||
const tailwindTemplatePath = fileURLToPath(
|
|
||||||
await import.meta.resolve!("tailwindcss/tailwind.css"),
|
|
||||||
)
|
|
||||||
|
|
||||||
const tailwindTemplate = await readFile(tailwindTemplatePath, "utf-8")
|
|
||||||
|
|
||||||
let result: Result | undefined
|
|
||||||
|
|
||||||
export function serveTailwindCss(): RequestHandler {
|
|
||||||
return async (req, res) => {
|
|
||||||
if (!result || process.env.NODE_ENV !== "production") {
|
|
||||||
result = await postcss(tailwindcss).process(tailwindTemplate, {
|
|
||||||
from: tailwindTemplatePath,
|
|
||||||
})
|
|
||||||
}
|
|
||||||
res.set("Content-Type", "text/css").send(result.css)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,10 +0,0 @@
|
|||||||
import { useState } from "react"
|
|
||||||
import { useWindowEvent } from "./use-window-event"
|
|
||||||
|
|
||||||
export function useScrolled() {
|
|
||||||
const [scrolled, setScrolled] = useState(
|
|
||||||
typeof window !== "undefined" ? window.scrollY > 0 : false,
|
|
||||||
)
|
|
||||||
useWindowEvent("scroll", () => setScrolled(window.scrollY > 0))
|
|
||||||
return scrolled
|
|
||||||
}
|
|
||||||
@@ -1,11 +0,0 @@
|
|||||||
import { useEffect } from "react"
|
|
||||||
|
|
||||||
export function useWindowEvent<EventType extends keyof WindowEventMap>(
|
|
||||||
type: EventType,
|
|
||||||
handler: (event: WindowEventMap[EventType]) => void,
|
|
||||||
) {
|
|
||||||
useEffect(() => {
|
|
||||||
window.addEventListener(type, handler)
|
|
||||||
return () => window.removeEventListener(type, handler)
|
|
||||||
})
|
|
||||||
}
|
|
||||||
@@ -1,6 +1,11 @@
|
|||||||
import packageJson from "reacord/package.json"
|
import packageJson from "reacord/package.json"
|
||||||
import type { ReactNode } from "react"
|
import type { ReactNode } from "react"
|
||||||
import React from "react"
|
import React from "react"
|
||||||
|
import { useAssetBuilder } from "./asset-builder/asset-builder-context.js"
|
||||||
|
|
||||||
|
const tailwindCssPath = new URL(
|
||||||
|
await import.meta.resolve!("tailwindcss/tailwind.css"),
|
||||||
|
).pathname
|
||||||
|
|
||||||
export function Html({
|
export function Html({
|
||||||
title = "Reacord",
|
title = "Reacord",
|
||||||
@@ -11,6 +16,7 @@ export function Html({
|
|||||||
description?: string
|
description?: string
|
||||||
children: ReactNode
|
children: ReactNode
|
||||||
}) {
|
}) {
|
||||||
|
const assets = useAssetBuilder()
|
||||||
return (
|
return (
|
||||||
<html lang="en" className="bg-slate-900 text-slate-100">
|
<html lang="en" className="bg-slate-900 text-slate-100">
|
||||||
<head>
|
<head>
|
||||||
@@ -28,10 +34,11 @@ export function Html({
|
|||||||
href="https://fonts.googleapis.com/css2?family=JetBrains+Mono:wght@500&family=Rubik:ital,wght@0,300;0,400;0,500;1,300;1,400;1,500&display=swap"
|
href="https://fonts.googleapis.com/css2?family=JetBrains+Mono:wght@500&family=Rubik:ital,wght@0,300;0,400;0,500;1,300;1,400;1,500&display=swap"
|
||||||
rel="stylesheet"
|
rel="stylesheet"
|
||||||
/>
|
/>
|
||||||
<link href="/tailwind.css" rel="stylesheet" />
|
<link href={assets.file(tailwindCssPath)} rel="stylesheet" />
|
||||||
<link href="/prism-theme.css" rel="stylesheet" />
|
<link
|
||||||
|
href={assets.file(new URL("ui/prism-theme.css", import.meta.url))}
|
||||||
<script type="module" src="/popover-menu.client.js" />
|
rel="stylesheet"
|
||||||
|
/>
|
||||||
|
|
||||||
<title>{title}</title>
|
<title>{title}</title>
|
||||||
</head>
|
</head>
|
||||||
|
|||||||
35
packages/docs/src/landing/landing.tsx
Normal file
35
packages/docs/src/landing/landing.tsx
Normal file
@@ -0,0 +1,35 @@
|
|||||||
|
import packageJson from "reacord/package.json"
|
||||||
|
import React from "react"
|
||||||
|
import { renderMarkdownFile } from "../helpers/markdown"
|
||||||
|
import { MainNavigation } from "../navigation/main-navigation"
|
||||||
|
import { maxWidthContainer } from "../ui/components"
|
||||||
|
|
||||||
|
const landingExample = await renderMarkdownFile(
|
||||||
|
new URL("landing-example.md", import.meta.url).pathname,
|
||||||
|
)
|
||||||
|
|
||||||
|
export function Landing() {
|
||||||
|
return (
|
||||||
|
<div className="flex flex-col min-w-0 min-h-screen text-center">
|
||||||
|
<header className={maxWidthContainer}>
|
||||||
|
<MainNavigation />
|
||||||
|
</header>
|
||||||
|
<div className="px-4 pb-8 flex flex-1">
|
||||||
|
<main className="px-4 py-6 rounded-lg shadow bg-slate-800 space-y-5 m-auto w-full max-w-xl">
|
||||||
|
<h1 className="text-6xl font-light">reacord</h1>
|
||||||
|
<section
|
||||||
|
className="mx-auto text-sm sm:text-base"
|
||||||
|
dangerouslySetInnerHTML={{ __html: landingExample.html }}
|
||||||
|
/>
|
||||||
|
<p className="text-2xl font-light">{packageJson.description}</p>
|
||||||
|
<a
|
||||||
|
href="/guides/getting-started"
|
||||||
|
className="inline-block px-4 py-3 text-xl transition rounded-lg bg-emerald-700 hover:translate-y-[-2px] hover:bg-emerald-800 hover:shadow"
|
||||||
|
>
|
||||||
|
Get Started
|
||||||
|
</a>
|
||||||
|
</main>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
}
|
||||||
@@ -1,23 +1,35 @@
|
|||||||
import compression from "compression"
|
import compression from "compression"
|
||||||
import type { ErrorRequestHandler, Request } from "express"
|
import type { ErrorRequestHandler, Request, Response } from "express"
|
||||||
import express from "express"
|
import express from "express"
|
||||||
import Router from "express-promise-router"
|
import Router from "express-promise-router"
|
||||||
import httpTerminator from "http-terminator"
|
import httpTerminator from "http-terminator"
|
||||||
import pino from "pino"
|
import pino from "pino"
|
||||||
import pinoHttp from "pino-http"
|
import pinoHttp from "pino-http"
|
||||||
import * as React from "react"
|
import * as React from "react"
|
||||||
|
import { renderToStaticMarkup } from "react-dom/server.js"
|
||||||
|
import { AssetBuilderProvider } from "./asset-builder/asset-builder-context.js"
|
||||||
|
import { AssetBuilder } from "./asset-builder/asset-builder.js"
|
||||||
|
import { transformEsbuild } from "./asset-builder/transform-esbuild.js"
|
||||||
|
import { transformPostCss } from "./asset-builder/transform-postcss.js"
|
||||||
import { fromProjectRoot } from "./constants"
|
import { fromProjectRoot } from "./constants"
|
||||||
|
import GuidePage from "./guides/guide-page"
|
||||||
import { renderMarkdownFile } from "./helpers/markdown"
|
import { renderMarkdownFile } from "./helpers/markdown"
|
||||||
import { sendJsx } from "./helpers/send-jsx"
|
import { Html } from "./html.js"
|
||||||
import { serveCompiledScript } from "./helpers/serve-compiled-script"
|
import { Landing } from "./landing/landing"
|
||||||
import { serveFile } from "./helpers/serve-file"
|
|
||||||
import { serveTailwindCss } from "./helpers/tailwind"
|
|
||||||
import DocsPage from "./pages/docs"
|
|
||||||
import { Landing } from "./pages/landing"
|
|
||||||
|
|
||||||
const logger = pino()
|
const logger = pino()
|
||||||
const port = process.env.PORT || 3000
|
const port = process.env.PORT || 3000
|
||||||
|
|
||||||
|
const assets = new AssetBuilder(fromProjectRoot(".asset-cache"), [
|
||||||
|
transformEsbuild,
|
||||||
|
transformPostCss,
|
||||||
|
])
|
||||||
|
|
||||||
|
export function sendJsx(res: Response, jsx: React.ReactElement) {
|
||||||
|
res.set("Content-Type", "text/html")
|
||||||
|
res.send(`<!DOCTYPE html>\n${renderToStaticMarkup(jsx)}`)
|
||||||
|
}
|
||||||
|
|
||||||
const errorHandler: ErrorRequestHandler = (error, request, response, next) => {
|
const errorHandler: ErrorRequestHandler = (error, request, response, next) => {
|
||||||
response.status(500).send(error.message)
|
response.status(500).send(error.message)
|
||||||
logger.error(error)
|
logger.error(error)
|
||||||
@@ -26,36 +38,31 @@ const errorHandler: ErrorRequestHandler = (error, request, response, next) => {
|
|||||||
const router = Router()
|
const router = Router()
|
||||||
.use(pinoHttp({ logger }))
|
.use(pinoHttp({ logger }))
|
||||||
.use(compression())
|
.use(compression())
|
||||||
.get("/tailwind.css", serveTailwindCss())
|
.use(assets.middleware())
|
||||||
|
|
||||||
.get(
|
.get("/guides/*", async (req: Request<{ 0: string }>, res) => {
|
||||||
"/prism-theme.css",
|
|
||||||
serveFile(fromProjectRoot("src/styles/prism-theme.css")),
|
|
||||||
)
|
|
||||||
|
|
||||||
.get(
|
|
||||||
"/popover-menu.client.js",
|
|
||||||
await serveCompiledScript(
|
|
||||||
fromProjectRoot("src/components/popover-menu.client.tsx"),
|
|
||||||
),
|
|
||||||
)
|
|
||||||
|
|
||||||
.get("/docs/*", async (req: Request<{ 0: string }>, res) => {
|
|
||||||
const { html, data } = await renderMarkdownFile(
|
const { html, data } = await renderMarkdownFile(
|
||||||
fromProjectRoot(`src/docs/${req.params[0]}.md`),
|
new URL(`guides/${req.params[0]}.md`, import.meta.url).pathname,
|
||||||
)
|
)
|
||||||
sendJsx(
|
sendJsx(
|
||||||
res,
|
res,
|
||||||
<DocsPage
|
<AssetBuilderProvider value={assets}>
|
||||||
title={data.title}
|
<Html title={`${data.title} | Reacord`} description={data.description}>
|
||||||
description={data.description}
|
<GuidePage html={html} />
|
||||||
html={html}
|
</Html>
|
||||||
/>,
|
</AssetBuilderProvider>,
|
||||||
)
|
)
|
||||||
})
|
})
|
||||||
|
|
||||||
.get("/", (req, res) => {
|
.get("/", (req, res) => {
|
||||||
sendJsx(res, <Landing />)
|
sendJsx(
|
||||||
|
res,
|
||||||
|
<AssetBuilderProvider value={assets}>
|
||||||
|
<Html>
|
||||||
|
<Landing />
|
||||||
|
</Html>
|
||||||
|
</AssetBuilderProvider>,
|
||||||
|
)
|
||||||
})
|
})
|
||||||
|
|
||||||
.use(errorHandler)
|
.use(errorHandler)
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
import React from "react"
|
import React from "react"
|
||||||
import { ExternalLink } from "./external-link"
|
import { ExternalLink } from "../dom/external-link"
|
||||||
|
|
||||||
export type AppLinkProps = {
|
export type AppLinkProps = {
|
||||||
type: "internal" | "external"
|
type: "internal" | "external"
|
||||||
@@ -2,15 +2,14 @@ import glob from "fast-glob"
|
|||||||
import grayMatter from "gray-matter"
|
import grayMatter from "gray-matter"
|
||||||
import { readFile } from "node:fs/promises"
|
import { readFile } from "node:fs/promises"
|
||||||
import { join } from "node:path"
|
import { join } from "node:path"
|
||||||
import type { AppLinkProps } from "../components/app-link"
|
import type { AppLinkProps } from "./app-link"
|
||||||
import { fromProjectRoot } from "../constants"
|
|
||||||
|
|
||||||
const docsFolderPath = fromProjectRoot("src/docs")
|
const docsFolder = new URL("../guides", import.meta.url).pathname
|
||||||
const guideFiles = await glob("**/*.md", { cwd: docsFolderPath })
|
const guideFiles = await glob("**/*.md", { cwd: docsFolder })
|
||||||
|
|
||||||
const entries = await Promise.all(
|
const entries = await Promise.all(
|
||||||
guideFiles.map(async (file) => {
|
guideFiles.map(async (file) => {
|
||||||
const content = await readFile(join(docsFolderPath, file), "utf-8")
|
const content = await readFile(join(docsFolder, file), "utf-8")
|
||||||
const { data } = grayMatter(content)
|
const { data } = grayMatter(content)
|
||||||
|
|
||||||
let order = Number(data.order)
|
let order = Number(data.order)
|
||||||
@@ -19,7 +18,7 @@ const entries = await Promise.all(
|
|||||||
}
|
}
|
||||||
|
|
||||||
return {
|
return {
|
||||||
route: `/docs/${file.replace(/\.mdx?$/, "")}`,
|
route: `/guides/${file.replace(/\.mdx?$/, "")}`,
|
||||||
title: String(data.title || ""),
|
title: String(data.title || ""),
|
||||||
order,
|
order,
|
||||||
}
|
}
|
||||||
@@ -4,13 +4,13 @@ import {
|
|||||||
ExternalLinkIcon,
|
ExternalLinkIcon,
|
||||||
} from "@heroicons/react/solid/esm"
|
} from "@heroicons/react/solid/esm"
|
||||||
import React from "react"
|
import React from "react"
|
||||||
import type { AppLinkProps } from "../components/app-link"
|
import { inlineIconClass } from "../ui/components"
|
||||||
import { inlineIconClass } from "../styles/components"
|
import type { AppLinkProps } from "./app-link"
|
||||||
|
|
||||||
export const mainLinks: AppLinkProps[] = [
|
export const mainLinks: AppLinkProps[] = [
|
||||||
{
|
{
|
||||||
type: "internal",
|
type: "internal",
|
||||||
to: "/docs/getting-started",
|
to: "/guides/getting-started",
|
||||||
label: (
|
label: (
|
||||||
<>
|
<>
|
||||||
<DocumentTextIcon className={inlineIconClass} /> Guides
|
<DocumentTextIcon className={inlineIconClass} /> Guides
|
||||||
@@ -19,7 +19,7 @@ export const mainLinks: AppLinkProps[] = [
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
type: "internal",
|
type: "internal",
|
||||||
to: "/docs/api",
|
to: "/guides/api",
|
||||||
label: (
|
label: (
|
||||||
<>
|
<>
|
||||||
<CodeIcon className={inlineIconClass} /> API Reference
|
<CodeIcon className={inlineIconClass} /> API Reference
|
||||||
@@ -1,9 +1,9 @@
|
|||||||
import React from "react"
|
import React from "react"
|
||||||
import { guideLinks } from "../data/guide-links"
|
import { linkClass } from "../ui/components"
|
||||||
import { mainLinks } from "../data/main-links"
|
import { PopoverMenu } from "../ui/popover-menu"
|
||||||
import { linkClass } from "../styles/components"
|
|
||||||
import { AppLink } from "./app-link"
|
import { AppLink } from "./app-link"
|
||||||
import { PopoverMenu } from "./popover-menu"
|
import { guideLinks } from "./guide-links"
|
||||||
|
import { mainLinks } from "./main-links"
|
||||||
|
|
||||||
export function MainNavigation() {
|
export function MainNavigation() {
|
||||||
return (
|
return (
|
||||||
@@ -1,39 +0,0 @@
|
|||||||
import packageJson from "reacord/package.json"
|
|
||||||
import React from "react"
|
|
||||||
import { MainNavigation } from "../components/main-navigation"
|
|
||||||
import { fromProjectRoot } from "../constants"
|
|
||||||
import { renderMarkdownFile } from "../helpers/markdown"
|
|
||||||
import { Html } from "../html"
|
|
||||||
import { maxWidthContainer } from "../styles/components"
|
|
||||||
|
|
||||||
const landingExample = await renderMarkdownFile(
|
|
||||||
fromProjectRoot("src/components/landing-example.md"),
|
|
||||||
)
|
|
||||||
|
|
||||||
export function Landing() {
|
|
||||||
return (
|
|
||||||
<Html>
|
|
||||||
<div className="flex flex-col min-w-0 min-h-screen text-center">
|
|
||||||
<header className={maxWidthContainer}>
|
|
||||||
<MainNavigation />
|
|
||||||
</header>
|
|
||||||
<div className="px-4 pb-8 flex flex-1">
|
|
||||||
<main className="px-4 py-6 rounded-lg shadow bg-slate-800 space-y-5 m-auto w-full max-w-xl">
|
|
||||||
<h1 className="text-6xl font-light">reacord</h1>
|
|
||||||
<section
|
|
||||||
className="mx-auto text-sm sm:text-base"
|
|
||||||
dangerouslySetInnerHTML={{ __html: landingExample.html }}
|
|
||||||
/>
|
|
||||||
<p className="text-2xl font-light">{packageJson.description}</p>
|
|
||||||
<a
|
|
||||||
href="/docs/getting-started"
|
|
||||||
className="inline-block px-4 py-3 text-xl transition rounded-lg bg-emerald-700 hover:translate-y-[-2px] hover:bg-emerald-800 hover:shadow"
|
|
||||||
>
|
|
||||||
Get Started
|
|
||||||
</a>
|
|
||||||
</main>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</Html>
|
|
||||||
)
|
|
||||||
}
|
|
||||||
@@ -1,9 +1,11 @@
|
|||||||
import { MenuAlt4Icon } from "@heroicons/react/outline/esm"
|
import { MenuAlt4Icon } from "@heroicons/react/outline/esm"
|
||||||
import clsx from "clsx"
|
import clsx from "clsx"
|
||||||
import React from "react"
|
import React from "react"
|
||||||
import { linkClass } from "../styles/components"
|
import { useAssetBuilder } from "../asset-builder/asset-builder-context.js"
|
||||||
|
import { linkClass } from "./components"
|
||||||
|
|
||||||
export function PopoverMenu({ children }: { children: React.ReactNode }) {
|
export function PopoverMenu({ children }: { children: React.ReactNode }) {
|
||||||
|
const assets = useAssetBuilder()
|
||||||
return (
|
return (
|
||||||
<div data-popover className="relative">
|
<div data-popover className="relative">
|
||||||
<button data-popover-button title="Menu" className={linkClass}>
|
<button data-popover-button title="Menu" className={linkClass}>
|
||||||
@@ -16,6 +18,10 @@ export function PopoverMenu({ children }: { children: React.ReactNode }) {
|
|||||||
>
|
>
|
||||||
{children}
|
{children}
|
||||||
</div>
|
</div>
|
||||||
|
<script
|
||||||
|
type="module"
|
||||||
|
src={assets.file(new URL("popover-menu.client.tsx", import.meta.url))}
|
||||||
|
/>
|
||||||
</div>
|
</div>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
16
pnpm-lock.yaml
generated
16
pnpm-lock.yaml
generated
@@ -51,7 +51,6 @@ importers:
|
|||||||
'@types/wait-on': ^5.3.1
|
'@types/wait-on': ^5.3.1
|
||||||
autoprefixer: ^10.4.1
|
autoprefixer: ^10.4.1
|
||||||
browser-sync: ^2.27.7
|
browser-sync: ^2.27.7
|
||||||
chokidar: ^3.5.2
|
|
||||||
clsx: ^1.1.1
|
clsx: ^1.1.1
|
||||||
compression: ^1.7.4
|
compression: ^1.7.4
|
||||||
esbuild: latest
|
esbuild: latest
|
||||||
@@ -64,6 +63,7 @@ importers:
|
|||||||
http-terminator: ^3.0.4
|
http-terminator: ^3.0.4
|
||||||
markdown-it: ^12.3.0
|
markdown-it: ^12.3.0
|
||||||
markdown-it-prism: ^2.2.1
|
markdown-it-prism: ^2.2.1
|
||||||
|
nodemon: ^2.0.15
|
||||||
pino: ^7.6.2
|
pino: ^7.6.2
|
||||||
pino-colada: ^2.2.2
|
pino-colada: ^2.2.2
|
||||||
pino-http: ^6.5.0
|
pino-http: ^6.5.0
|
||||||
@@ -78,6 +78,7 @@ importers:
|
|||||||
typescript: ^4.5.4
|
typescript: ^4.5.4
|
||||||
wait-on: ^6.0.0
|
wait-on: ^6.0.0
|
||||||
dependencies:
|
dependencies:
|
||||||
|
'@heroicons/react': 1.0.5_react@18.0.0-rc.0
|
||||||
'@tailwindcss/typography': 0.5.0_tailwindcss@3.0.8
|
'@tailwindcss/typography': 0.5.0_tailwindcss@3.0.8
|
||||||
clsx: 1.1.1
|
clsx: 1.1.1
|
||||||
compression: 1.7.4
|
compression: 1.7.4
|
||||||
@@ -99,7 +100,6 @@ importers:
|
|||||||
react-dom: 18.0.0-rc.0_react@18.0.0-rc.0
|
react-dom: 18.0.0-rc.0_react@18.0.0-rc.0
|
||||||
tailwindcss: 3.0.8_cefe482e8d38053bbf3d5815e0c551b3
|
tailwindcss: 3.0.8_cefe482e8d38053bbf3d5815e0c551b3
|
||||||
devDependencies:
|
devDependencies:
|
||||||
'@heroicons/react': 1.0.5_react@18.0.0-rc.0
|
|
||||||
'@types/browser-sync': 2.26.3
|
'@types/browser-sync': 2.26.3
|
||||||
'@types/compression': 1.7.2
|
'@types/compression': 1.7.2
|
||||||
'@types/express': 4.17.13
|
'@types/express': 4.17.13
|
||||||
@@ -111,8 +111,8 @@ importers:
|
|||||||
'@types/wait-on': 5.3.1
|
'@types/wait-on': 5.3.1
|
||||||
autoprefixer: 10.4.1_postcss@8.4.5
|
autoprefixer: 10.4.1_postcss@8.4.5
|
||||||
browser-sync: 2.27.7
|
browser-sync: 2.27.7
|
||||||
chokidar: 3.5.2
|
|
||||||
execa: 6.0.0
|
execa: 6.0.0
|
||||||
|
nodemon: 2.0.15
|
||||||
rxjs: 7.5.1
|
rxjs: 7.5.1
|
||||||
tsup: 5.11.10_typescript@4.5.4
|
tsup: 5.11.10_typescript@4.5.4
|
||||||
type-fest: 2.8.0
|
type-fest: 2.8.0
|
||||||
@@ -355,6 +355,12 @@ packages:
|
|||||||
hasBin: true
|
hasBin: true
|
||||||
dev: true
|
dev: true
|
||||||
|
|
||||||
|
/@babel/parser/7.16.7:
|
||||||
|
resolution: {integrity: sha512-sR4eaSrnM7BV7QPzGfEX5paG/6wrZM3I0HDzfIAK06ESvo9oy3xBuVBxE3MbQaKNhvg8g/ixjMWo2CGpzpHsDA==}
|
||||||
|
engines: {node: '>=6.0.0'}
|
||||||
|
hasBin: true
|
||||||
|
dev: true
|
||||||
|
|
||||||
/@babel/plugin-syntax-async-generators/7.8.4_@babel+core@7.16.5:
|
/@babel/plugin-syntax-async-generators/7.8.4_@babel+core@7.16.5:
|
||||||
resolution: {integrity: sha512-tycmZxkGfZaxhMRbXlPXuVFpdWlXpir2W4AMhSJgRKzk/eDlIXOhb2LHWoLpDF7TEHylV5zNhykX6KAgHJmTNw==}
|
resolution: {integrity: sha512-tycmZxkGfZaxhMRbXlPXuVFpdWlXpir2W4AMhSJgRKzk/eDlIXOhb2LHWoLpDF7TEHylV5zNhykX6KAgHJmTNw==}
|
||||||
peerDependencies:
|
peerDependencies:
|
||||||
@@ -523,7 +529,7 @@ packages:
|
|||||||
'@babel/helper-function-name': 7.16.0
|
'@babel/helper-function-name': 7.16.0
|
||||||
'@babel/helper-hoist-variables': 7.16.0
|
'@babel/helper-hoist-variables': 7.16.0
|
||||||
'@babel/helper-split-export-declaration': 7.16.0
|
'@babel/helper-split-export-declaration': 7.16.0
|
||||||
'@babel/parser': 7.16.6
|
'@babel/parser': 7.16.7
|
||||||
'@babel/types': 7.16.7
|
'@babel/types': 7.16.7
|
||||||
debug: 4.3.3
|
debug: 4.3.3
|
||||||
globals: 11.12.0
|
globals: 11.12.0
|
||||||
@@ -609,7 +615,7 @@ packages:
|
|||||||
react: '>= 16'
|
react: '>= 16'
|
||||||
dependencies:
|
dependencies:
|
||||||
react: 18.0.0-rc.0
|
react: 18.0.0-rc.0
|
||||||
dev: true
|
dev: false
|
||||||
|
|
||||||
/@humanwhocodes/config-array/0.9.2:
|
/@humanwhocodes/config-array/0.9.2:
|
||||||
resolution: {integrity: sha512-UXOuFCGcwciWckOpmfKDq/GyhlTf9pN/BzG//x8p8zTOFEcGuA68ANXheFS0AGvy3qgZqLBUkMs7hqzqCKOVwA==}
|
resolution: {integrity: sha512-UXOuFCGcwciWckOpmfKDq/GyhlTf9pN/BzG//x8p8zTOFEcGuA68ANXheFS0AGvy3qgZqLBUkMs7hqzqCKOVwA==}
|
||||||
|
|||||||
Reference in New Issue
Block a user