🫧 foam
This commit is contained in:
commit
67f8b4b25c
34
.gitignore
vendored
Normal file
34
.gitignore
vendored
Normal file
|
|
@ -0,0 +1,34 @@
|
||||||
|
# dependencies (bun install)
|
||||||
|
node_modules
|
||||||
|
|
||||||
|
# output
|
||||||
|
out
|
||||||
|
dist
|
||||||
|
*.tgz
|
||||||
|
|
||||||
|
# code coverage
|
||||||
|
coverage
|
||||||
|
*.lcov
|
||||||
|
|
||||||
|
# logs
|
||||||
|
logs
|
||||||
|
_.log
|
||||||
|
report.[0-9]_.[0-9]_.[0-9]_.[0-9]_.json
|
||||||
|
|
||||||
|
# dotenv environment variable files
|
||||||
|
.env
|
||||||
|
.env.development.local
|
||||||
|
.env.test.local
|
||||||
|
.env.production.local
|
||||||
|
.env.local
|
||||||
|
|
||||||
|
# caches
|
||||||
|
.eslintcache
|
||||||
|
.cache
|
||||||
|
*.tsbuildinfo
|
||||||
|
|
||||||
|
# IntelliJ based IDEs
|
||||||
|
.idea
|
||||||
|
|
||||||
|
# Finder (MacOS) folder config
|
||||||
|
.DS_Store
|
||||||
8
README.md
Normal file
8
README.md
Normal file
|
|
@ -0,0 +1,8 @@
|
||||||
|
# 🫧 foam
|
||||||
|
|
||||||
|
## Quickstart
|
||||||
|
|
||||||
|
bun install
|
||||||
|
bun dev
|
||||||
|
bun cli:install
|
||||||
|
foam <your website's directory>
|
||||||
25
bin/foam
Executable file
25
bin/foam
Executable file
|
|
@ -0,0 +1,25 @@
|
||||||
|
#!/usr/bin/env bun
|
||||||
|
|
||||||
|
import { statSync } from 'fs'
|
||||||
|
import { join } from 'path'
|
||||||
|
import { fileURLToPath } from 'url'
|
||||||
|
|
||||||
|
const __dirname = fileURLToPath(new URL('.', import.meta.url))
|
||||||
|
const { startWeb } = await import(join(__dirname, '../src/server.ts'))
|
||||||
|
|
||||||
|
function main() {
|
||||||
|
const root = Bun.argv[2]
|
||||||
|
|
||||||
|
if (!isDir(root)) {
|
||||||
|
console.error('usage: foam <directory>')
|
||||||
|
process.exit(1)
|
||||||
|
}
|
||||||
|
|
||||||
|
startWeb(root)
|
||||||
|
}
|
||||||
|
|
||||||
|
function isDir(path: string): boolean {
|
||||||
|
try { return statSync(path).isDirectory() } catch { return false }
|
||||||
|
}
|
||||||
|
|
||||||
|
main()
|
||||||
75
bun.lock
Normal file
75
bun.lock
Normal file
|
|
@ -0,0 +1,75 @@
|
||||||
|
{
|
||||||
|
"lockfileVersion": 1,
|
||||||
|
"workspaces": {
|
||||||
|
"": {
|
||||||
|
"name": "foam",
|
||||||
|
"dependencies": {
|
||||||
|
"hono": "^4.10.4",
|
||||||
|
"shrimp": "git+https://git.nose.space/probablycorey/shrimp#risky-business",
|
||||||
|
},
|
||||||
|
"devDependencies": {
|
||||||
|
"@types/bun": "latest",
|
||||||
|
},
|
||||||
|
"peerDependencies": {
|
||||||
|
"typescript": "^5",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
"packages": {
|
||||||
|
"@codemirror/autocomplete": ["@codemirror/autocomplete@6.19.1", "", { "dependencies": { "@codemirror/language": "^6.0.0", "@codemirror/state": "^6.0.0", "@codemirror/view": "^6.17.0", "@lezer/common": "^1.0.0" } }, "sha512-q6NenYkEy2fn9+JyjIxMWcNjzTL/IhwqfzOut1/G3PrIFkrbl4AL7Wkse5tLrQUUyqGoAKU5+Pi5jnnXxH5HGw=="],
|
||||||
|
|
||||||
|
"@codemirror/commands": ["@codemirror/commands@6.10.0", "", { "dependencies": { "@codemirror/language": "^6.0.0", "@codemirror/state": "^6.4.0", "@codemirror/view": "^6.27.0", "@lezer/common": "^1.1.0" } }, "sha512-2xUIc5mHXQzT16JnyOFkh8PvfeXuIut3pslWGfsGOhxP/lpgRm9HOl/mpzLErgt5mXDovqA0d11P21gofRLb9w=="],
|
||||||
|
|
||||||
|
"@codemirror/language": ["@codemirror/language@6.11.3", "", { "dependencies": { "@codemirror/state": "^6.0.0", "@codemirror/view": "^6.23.0", "@lezer/common": "^1.1.0", "@lezer/highlight": "^1.0.0", "@lezer/lr": "^1.0.0", "style-mod": "^4.0.0" } }, "sha512-9HBM2XnwDj7fnu0551HkGdrUrrqmYq/WC5iv6nbY2WdicXdGbhR/gfbZOH73Aqj4351alY1+aoG9rCNfiwS1RA=="],
|
||||||
|
|
||||||
|
"@codemirror/lint": ["@codemirror/lint@6.9.2", "", { "dependencies": { "@codemirror/state": "^6.0.0", "@codemirror/view": "^6.35.0", "crelt": "^1.0.5" } }, "sha512-sv3DylBiIyi+xKwRCJAAsBZZZWo82shJ/RTMymLabAdtbkV5cSKwWDeCgtUq3v8flTaXS2y1kKkICuRYtUswyQ=="],
|
||||||
|
|
||||||
|
"@codemirror/search": ["@codemirror/search@6.5.11", "", { "dependencies": { "@codemirror/state": "^6.0.0", "@codemirror/view": "^6.0.0", "crelt": "^1.0.5" } }, "sha512-KmWepDE6jUdL6n8cAAqIpRmLPBZ5ZKnicE8oGU/s3QrAVID+0VhLFrzUucVKHG5035/BSykhExDL/Xm7dHthiA=="],
|
||||||
|
|
||||||
|
"@codemirror/state": ["@codemirror/state@6.5.2", "", { "dependencies": { "@marijn/find-cluster-break": "^1.0.0" } }, "sha512-FVqsPqtPWKVVL3dPSxy8wEF/ymIEuVzF1PK3VbUgrxXpJUSHQWWZz4JMToquRxnkw+36LTamCZG2iua2Ptq0fA=="],
|
||||||
|
|
||||||
|
"@codemirror/view": ["@codemirror/view@6.38.6", "", { "dependencies": { "@codemirror/state": "^6.5.0", "crelt": "^1.0.6", "style-mod": "^4.1.0", "w3c-keyname": "^2.2.4" } }, "sha512-qiS0z1bKs5WOvHIAC0Cybmv4AJSkAXgX5aD6Mqd2epSLlVJsQl8NG23jCVouIgkh4All/mrbdsf2UOLFnJw0tw=="],
|
||||||
|
|
||||||
|
"@lezer/common": ["@lezer/common@1.3.0", "", {}, "sha512-L9X8uHCYU310o99L3/MpJKYxPzXPOS7S0NmBaM7UO/x2Kb2WbmMLSkfvdr1KxRIFYOpbY0Jhn7CfLSUDzL8arQ=="],
|
||||||
|
|
||||||
|
"@lezer/generator": ["@lezer/generator@1.8.0", "", { "dependencies": { "@lezer/common": "^1.1.0", "@lezer/lr": "^1.3.0" }, "bin": { "lezer-generator": "src/lezer-generator.cjs" } }, "sha512-/SF4EDWowPqV1jOgoGSGTIFsE7Ezdr7ZYxyihl5eMKVO5tlnpIhFcDavgm1hHY5GEonoOAEnJ0CU0x+tvuAuUg=="],
|
||||||
|
|
||||||
|
"@lezer/highlight": ["@lezer/highlight@1.2.3", "", { "dependencies": { "@lezer/common": "^1.3.0" } }, "sha512-qXdH7UqTvGfdVBINrgKhDsVTJTxactNNxLk7+UMwZhU13lMHaOBlJe9Vqp907ya56Y3+ed2tlqzys7jDkTmW0g=="],
|
||||||
|
|
||||||
|
"@lezer/lr": ["@lezer/lr@1.4.3", "", { "dependencies": { "@lezer/common": "^1.0.0" } }, "sha512-yenN5SqAxAPv/qMnpWW0AT7l+SxVrgG+u0tNsRQWqbrz66HIl8DnEbBObvy21J5K7+I1v7gsAnlE2VQ5yYVSeA=="],
|
||||||
|
|
||||||
|
"@marijn/find-cluster-break": ["@marijn/find-cluster-break@1.0.2", "", {}, "sha512-l0h88YhZFyKdXIFNfSWpyjStDjGHwZ/U7iobcK1cQQD8sejsONdQtTVU+1wVN1PBw40PiiHB1vA5S7VTfQiP9g=="],
|
||||||
|
|
||||||
|
"@types/bun": ["@types/bun@1.3.1", "", { "dependencies": { "bun-types": "1.3.1" } }, "sha512-4jNMk2/K9YJtfqwoAa28c8wK+T7nvJFOjxI4h/7sORWcypRNxBpr+TPNaCfVWq70tLCJsqoFwcf0oI0JU/fvMQ=="],
|
||||||
|
|
||||||
|
"@types/node": ["@types/node@24.10.0", "", { "dependencies": { "undici-types": "~7.16.0" } }, "sha512-qzQZRBqkFsYyaSWXuEHc2WR9c0a0CXwiE5FWUvn7ZM+vdy1uZLfCunD38UzhuB7YN/J11ndbDBcTmOdxJo9Q7A=="],
|
||||||
|
|
||||||
|
"@types/react": ["@types/react@19.2.2", "", { "dependencies": { "csstype": "^3.0.2" } }, "sha512-6mDvHUFSjyT2B2yeNx2nUgMxh9LtOWvkhIU3uePn2I2oyNymUAX1NIsdgviM4CH+JSrp2D2hsMvJOkxY+0wNRA=="],
|
||||||
|
|
||||||
|
"bun-plugin-tailwind": ["bun-plugin-tailwind@0.0.15", "", { "peerDependencies": { "typescript": "^5.0.0" } }, "sha512-qtAXMNGG4R0UGGI8zWrqm2B7BdXqx48vunJXBPzfDOHPA5WkRUZdTSbE7TFwO4jLhYqSE23YMWsM9NhE6ovobw=="],
|
||||||
|
|
||||||
|
"bun-types": ["bun-types@1.3.1", "", { "dependencies": { "@types/node": "*" }, "peerDependencies": { "@types/react": "^19" } }, "sha512-NMrcy7smratanWJ2mMXdpatalovtxVggkj11bScuWuiOoXTiKIu2eVS1/7qbyI/4yHedtsn175n4Sm4JcdHLXw=="],
|
||||||
|
|
||||||
|
"codemirror": ["codemirror@6.0.2", "", { "dependencies": { "@codemirror/autocomplete": "^6.0.0", "@codemirror/commands": "^6.0.0", "@codemirror/language": "^6.0.0", "@codemirror/lint": "^6.0.0", "@codemirror/search": "^6.0.0", "@codemirror/state": "^6.0.0", "@codemirror/view": "^6.0.0" } }, "sha512-VhydHotNW5w1UGK0Qj96BwSk/Zqbp9WbnyK2W/eVMv4QyF41INRGpjUhFJY7/uDNuudSc33a/PKr4iDqRduvHw=="],
|
||||||
|
|
||||||
|
"crelt": ["crelt@1.0.6", "", {}, "sha512-VQ2MBenTq1fWZUH9DJNGti7kKv6EeAuYr3cLwxUWhIu1baTaXh4Ib5W2CqHVqib4/MqbYGJqiL3Zb8GJZr3l4g=="],
|
||||||
|
|
||||||
|
"csstype": ["csstype@3.1.3", "", {}, "sha512-M1uQkMl8rQK/szD0LNhtqxIPLpimGm8sOBwU7lLnCpSbTyY3yeU1Vc7l4KT5zT4s/yOxHH5O7tIuuLOCnLADRw=="],
|
||||||
|
|
||||||
|
"hono": ["hono@4.10.4", "", {}, "sha512-YG/fo7zlU3KwrBL5vDpWKisLYiM+nVstBQqfr7gCPbSYURnNEP9BDxEMz8KfsDR9JX0lJWDRNc6nXX31v7ZEyg=="],
|
||||||
|
|
||||||
|
"reefvm": ["reefvm@git+https://git.nose.space/defunkt/reefvm#0f39e9401eb7a0a7c906e150127f9829458a79b6", { "peerDependencies": { "typescript": "^5" } }, "0f39e9401eb7a0a7c906e150127f9829458a79b6"],
|
||||||
|
|
||||||
|
"shrimp": ["shrimp@git+https://git.nose.space/probablycorey/shrimp#4fc1a965ebedeb28c7c2a2ac981400ff0a7278d9", { "dependencies": { "@codemirror/view": "^6.38.3", "@lezer/generator": "^1.8.0", "bun-plugin-tailwind": "^0.0.15", "codemirror": "^6.0.2", "hono": "^4.9.8", "reefvm": "git+https://git.nose.space/defunkt/reefvm", "tailwindcss": "^4.1.11" } }, "4fc1a965ebedeb28c7c2a2ac981400ff0a7278d9"],
|
||||||
|
|
||||||
|
"style-mod": ["style-mod@4.1.3", "", {}, "sha512-i/n8VsZydrugj3Iuzll8+x/00GH2vnYsk1eomD8QiRrSAeW6ItbCQDtfXCeJHd0iwiNagqjQkvpvREEPtW3IoQ=="],
|
||||||
|
|
||||||
|
"tailwindcss": ["tailwindcss@4.1.16", "", {}, "sha512-pONL5awpaQX4LN5eiv7moSiSPd/DLDzKVRJz8Q9PgzmAdd1R4307GQS2ZpfiN7ZmekdQrfhZZiSE5jkLR4WNaA=="],
|
||||||
|
|
||||||
|
"typescript": ["typescript@5.9.3", "", { "bin": { "tsc": "bin/tsc", "tsserver": "bin/tsserver" } }, "sha512-jl1vZzPDinLr9eUt3J/t7V6FgNEw9QjvBPdysz9KfQDD41fQrC2Y4vKQdiaUpFT4bXlb1RHhLpp8wtm6M5TgSw=="],
|
||||||
|
|
||||||
|
"undici-types": ["undici-types@7.16.0", "", {}, "sha512-Zz+aZWSj8LE6zoxD+xrjh4VfkIG8Ya6LvYkZqtUQGJPZjYl53ypCaUwWqo7eI0x66KBGeRo+mlBEkMSeSZ38Nw=="],
|
||||||
|
|
||||||
|
"w3c-keyname": ["w3c-keyname@2.2.8", "", {}, "sha512-dpojBhNsCNN7T82Tm7k26A6G9ML3NkhDsnw9n/eoxSRlVBB4CEtIQ/KTCLI2Fwf3ataSXRhYFkQi3SlnFwPvPQ=="],
|
||||||
|
}
|
||||||
|
}
|
||||||
23
package.json
Normal file
23
package.json
Normal file
|
|
@ -0,0 +1,23 @@
|
||||||
|
{
|
||||||
|
"name": "foam",
|
||||||
|
"module": "src/server.tsx",
|
||||||
|
"type": "module",
|
||||||
|
"private": true,
|
||||||
|
"bin": "./bin/foam",
|
||||||
|
"scripts": {
|
||||||
|
"dev": "bun run --hot src/server.tsx",
|
||||||
|
"start": "bun run src/server.tsx",
|
||||||
|
"cli:install": "ln -s \"$(pwd)/bin/foam\" ~/.bun/bin/foam",
|
||||||
|
"cli:remove": "rm ~/.bun/bin/foam"
|
||||||
|
},
|
||||||
|
"devDependencies": {
|
||||||
|
"@types/bun": "latest"
|
||||||
|
},
|
||||||
|
"peerDependencies": {
|
||||||
|
"typescript": "^5"
|
||||||
|
},
|
||||||
|
"dependencies": {
|
||||||
|
"hono": "^4.10.4",
|
||||||
|
"shrimp": "git+https://git.nose.space/probablycorey/shrimp#risky-business"
|
||||||
|
}
|
||||||
|
}
|
||||||
10
site/index.sh
Normal file
10
site/index.sh
Normal file
|
|
@ -0,0 +1,10 @@
|
||||||
|
h1 🦐 Welcome!!!
|
||||||
|
p This is a (b shrimp) page!
|
||||||
|
p 'ID:' (params.id)
|
||||||
|
p (a href='/profile' Profile)
|
||||||
|
p (a href='/pets/cat' My Cat)
|
||||||
|
hr
|
||||||
|
|
||||||
|
div style='margin:0 auto;width:50%;text-align:center;':
|
||||||
|
img src='/Honk.png' width='50%'
|
||||||
|
end
|
||||||
1
site/pets/cat.sh
Normal file
1
site/pets/cat.sh
Normal file
|
|
@ -0,0 +1 @@
|
||||||
|
p Meow
|
||||||
3
site/profile.sh
Normal file
3
site/profile.sh
Normal file
|
|
@ -0,0 +1,3 @@
|
||||||
|
h1 🦐 This is the profile page.
|
||||||
|
p 'It\'s' still (b Shrimp) (nospace) .
|
||||||
|
p 'ID:' (params.id)
|
||||||
BIN
site/pub/Honk.png
Normal file
BIN
site/pub/Honk.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 1.3 MiB |
0
src/cli.ts
Normal file
0
src/cli.ts
Normal file
78
src/ribbit.ts
Normal file
78
src/ribbit.ts
Normal file
|
|
@ -0,0 +1,78 @@
|
||||||
|
import { runCode } from 'shrimp'
|
||||||
|
|
||||||
|
const buffer: string[] = []
|
||||||
|
const NOSPACE_TOKEN = '!!ribbit-nospace!!'
|
||||||
|
const TAG_TOKEN = '!!ribbit-tag!!'
|
||||||
|
const SELF_CLOSING = ["area", "base", "br", "col", "embed", "hr", "img", "input", "link", "meta", "param", "source", "track", "wbr"]
|
||||||
|
const HTML5_TAGS = [
|
||||||
|
"a", "abbr", "address", "area", "article", "aside", "audio", "b", "base",
|
||||||
|
"bdi", "bdo", "blockquote", "body", "br", "button", "canvas", "caption",
|
||||||
|
"cite", "code", "col", "colgroup", "data", "datalist", "dd", "del",
|
||||||
|
"details", "dfn", "dialog", "div", "dl", "dt", "em", "embed", "fieldset",
|
||||||
|
"figcaption", "figure", "footer", "form", "h1", "h2", "h3", "h4", "h5",
|
||||||
|
"h6", "head", "header", "hgroup", "hr", "html", "i", "iframe", "img",
|
||||||
|
"input", "ins", "kbd", "label", "legend", "li", "link", "main", "map",
|
||||||
|
"mark", "meta", "meter", "nav", "noscript", "object", "ol", "optgroup",
|
||||||
|
"option", "output", "p", "picture", "pre", "progress", "q", "rp", "rt",
|
||||||
|
"ruby", "s", "samp", "script", "section", "select", "slot", "small",
|
||||||
|
"source", "span", "strong", "style", "sub", "summary", "sup", "svg",
|
||||||
|
"table", "tbody", "td", "template", "textarea", "tfoot", "th", "thead",
|
||||||
|
"time", "title", "tr", "track", "u", "ul", "var", "video", "wbr"
|
||||||
|
]
|
||||||
|
|
||||||
|
export async function wrapAndRunCode(code: string, globals?: Record<string, any>): Promise<any> {
|
||||||
|
return await runCode("ribbit:\n " + code + "\nend", Object.assign({}, ribbitGlobals, globals))
|
||||||
|
}
|
||||||
|
|
||||||
|
export const ribbitGlobals = {
|
||||||
|
ribbit: async (cb: Function) => {
|
||||||
|
await cb()
|
||||||
|
const output = buffer.join("\n")
|
||||||
|
buffer.length = 0
|
||||||
|
return output
|
||||||
|
},
|
||||||
|
tag: async (tagFn: Function, atDefaults = {}) =>
|
||||||
|
(atNamed = {}, ...args: any[]) => tagFn(Object.assign({}, atDefaults, atNamed), ...args),
|
||||||
|
nospace: () => NOSPACE_TOKEN,
|
||||||
|
echo: (...args: any[]) => console.log(...args)
|
||||||
|
}
|
||||||
|
|
||||||
|
for (const name of HTML5_TAGS) {
|
||||||
|
(ribbitGlobals as any)[name] = (atNamed: {}, ...args: any[]) => tag(name, atNamed, ...args)
|
||||||
|
; (ribbitGlobals as any)[name].tagName = name
|
||||||
|
}
|
||||||
|
|
||||||
|
const tagBlock = async (tagName: string, props = {}, fn: Function) => {
|
||||||
|
const attrs = Object.entries(props).map(([key, value]) => `${key}="${value}"`)
|
||||||
|
const space = attrs.length ? ' ' : ''
|
||||||
|
|
||||||
|
buffer.push(`<${tagName}${space}${attrs.join(' ')}>`)
|
||||||
|
await fn()
|
||||||
|
buffer.push(`</${tagName}>`)
|
||||||
|
}
|
||||||
|
|
||||||
|
const tagCall = (tagName: string, atNamed = {}, ...args: any[]) => {
|
||||||
|
args = args.map(arg => typeof arg === 'function' ? arg.tagName : arg)
|
||||||
|
|
||||||
|
const attrs = Object.entries(atNamed).map(([key, value]) => `${key}="${value}"`)
|
||||||
|
const space = attrs.length ? ' ' : ''
|
||||||
|
const children = args
|
||||||
|
.reverse()
|
||||||
|
.map(a => a === TAG_TOKEN ? buffer.pop() : a)
|
||||||
|
.reverse().join(' ')
|
||||||
|
.replaceAll(` ${NOSPACE_TOKEN} `, '')
|
||||||
|
|
||||||
|
if (SELF_CLOSING.includes(tagName))
|
||||||
|
buffer.push(`<${tagName}${space}${attrs.join(' ')} />`)
|
||||||
|
else
|
||||||
|
buffer.push(`<${tagName}${space}${attrs.join(' ')}>${children}</${tagName}>`)
|
||||||
|
}
|
||||||
|
|
||||||
|
const tag = async (tagName: string, atNamed = {}, ...args: any[]) => {
|
||||||
|
if (typeof args[0] === 'function')
|
||||||
|
await tagBlock(tagName, atNamed, args[0])
|
||||||
|
else
|
||||||
|
tagCall(tagName, atNamed, ...args)
|
||||||
|
|
||||||
|
return TAG_TOKEN
|
||||||
|
}
|
||||||
38
src/server.ts
Normal file
38
src/server.ts
Normal file
|
|
@ -0,0 +1,38 @@
|
||||||
|
import { Hono } from 'hono'
|
||||||
|
import { serveStatic } from 'hono/bun'
|
||||||
|
import { join, resolve } from 'path'
|
||||||
|
import { wrapAndRunCode } from './ribbit'
|
||||||
|
|
||||||
|
export function startWeb(rootPath: string) {
|
||||||
|
const root = resolve(rootPath)
|
||||||
|
const app = new Hono()
|
||||||
|
|
||||||
|
// console logging
|
||||||
|
app.use("*", async (c, next) => {
|
||||||
|
const start = Date.now()
|
||||||
|
await next()
|
||||||
|
const end = Date.now()
|
||||||
|
console.log(`${c.res.status} ${c.req.method} ${c.req.url} (${end - start}ms)`)
|
||||||
|
})
|
||||||
|
|
||||||
|
// static files get served out of pub/
|
||||||
|
app.use('/*', serveStatic({ root: join(root, 'pub') }))
|
||||||
|
|
||||||
|
app.on(['GET', 'POST'], ['/', '/:page{.+}'], async c => {
|
||||||
|
const page = c.req.param('page') || 'index'
|
||||||
|
const params = c.req.query()
|
||||||
|
|
||||||
|
const file = Bun.file(join(root, `${page}.sh`))
|
||||||
|
if (await file.exists()) {
|
||||||
|
return c.html(await wrapAndRunCode(await file.text(), { params }))
|
||||||
|
} else {
|
||||||
|
return c.text('404 Not Found', 404)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
console.log('🫧 Server started at http://localhost:3000')
|
||||||
|
return Bun.serve({
|
||||||
|
fetch: app.fetch,
|
||||||
|
port: 3000,
|
||||||
|
})
|
||||||
|
}
|
||||||
29
tsconfig.json
Normal file
29
tsconfig.json
Normal file
|
|
@ -0,0 +1,29 @@
|
||||||
|
{
|
||||||
|
"compilerOptions": {
|
||||||
|
// Environment setup & latest features
|
||||||
|
"lib": ["ESNext"],
|
||||||
|
"target": "ESNext",
|
||||||
|
"module": "Preserve",
|
||||||
|
"moduleDetection": "force",
|
||||||
|
"jsx": "react-jsx",
|
||||||
|
"allowJs": true,
|
||||||
|
|
||||||
|
// Bundler mode
|
||||||
|
"moduleResolution": "bundler",
|
||||||
|
"allowImportingTsExtensions": true,
|
||||||
|
"verbatimModuleSyntax": true,
|
||||||
|
"noEmit": true,
|
||||||
|
|
||||||
|
// Best practices
|
||||||
|
"strict": true,
|
||||||
|
"skipLibCheck": true,
|
||||||
|
"noFallthroughCasesInSwitch": true,
|
||||||
|
"noUncheckedIndexedAccess": true,
|
||||||
|
"noImplicitOverride": true,
|
||||||
|
|
||||||
|
// Some stricter flags (disabled by default)
|
||||||
|
"noUnusedLocals": false,
|
||||||
|
"noUnusedParameters": false,
|
||||||
|
"noPropertyAccessFromIndexSignature": false
|
||||||
|
}
|
||||||
|
}
|
||||||
Loading…
Reference in New Issue
Block a user