Skip to content

Commit 5a397ed

Browse files
committed
fix, lint, refactor
1 parent 9d5f746 commit 5a397ed

File tree

6 files changed

+61
-68
lines changed

6 files changed

+61
-68
lines changed

package.json

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,8 @@
66
"exports": "./lib/index.js",
77
"types": "lib",
88
"files": [
9-
"lib"
9+
"lib",
10+
"public"
1011
],
1112
"scripts": {
1213
"css": "tailwindcss -i ./views/input.css -o ./public/output.css",
@@ -15,8 +16,9 @@
1516
"dev": "concurrently npm:watch-*",
1617
"build": "rm -rf lib && tsc && npm run css",
1718
"test": "node --import tsx/esm --test src/**/*.test.ts",
19+
"lint": "eslint src --ext .ts --ignore-path .gitignore",
1820
"prepare": "husky install",
19-
"prepack": "npm run build"
21+
"prepublishOnly": "npm run build"
2022
},
2123
"keywords": [],
2224
"author": "",

src/app.test.ts

Lines changed: 9 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,11 @@
1-
import test from 'node:test'
21
import assert from 'node:assert/strict'
32
import { writeFileSync } from 'node:fs'
43
import { join } from 'node:path'
5-
import { promisify } from 'node:util'
4+
import test from 'node:test'
65

6+
import getPort from 'get-port'
77
import { Low, Memory } from 'lowdb'
88
import { temporaryDirectory } from 'tempy'
9-
import getPort from 'get-port'
109

1110
import { createApp } from './app.js'
1211
import { Data } from './service.js'
@@ -27,7 +26,6 @@ type HTTPMethods =
2726
| 'PUT'
2827
| 'OPTIONS'
2928

30-
3129
const port = await getPort()
3230

3331
// Create custom static dir with an html file
@@ -52,9 +50,7 @@ await new Promise<void>((resolve, reject) => {
5250
}
5351
})
5452

55-
import {createWriteStream} from 'fs'
56-
57-
test('createApp', async () => {
53+
await test('createApp', async () => {
5854
// URLs
5955
const POSTS = '/posts'
6056
const POST_1 = '/posts/1'
@@ -101,7 +97,11 @@ test('createApp', async () => {
10197
for (const tc of arr) {
10298
const response = await fetch(`http://localhost:${port}${tc.url}`, {
10399
method: tc.method,
104-
});
105-
assert.equal(response.status, tc.statusCode, `${response.status} !== ${tc.statusCode} ${tc.method} ${tc.url} failed`);
100+
})
101+
assert.equal(
102+
response.status,
103+
tc.statusCode,
104+
`${response.status} !== ${tc.statusCode} ${tc.method} ${tc.url} failed`,
105+
)
106106
}
107107
})

src/app.ts

Lines changed: 27 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -2,13 +2,12 @@ import { dirname, isAbsolute, join } from 'node:path'
22
import { fileURLToPath } from 'node:url'
33

44
import { App } from '@tinyhttp/app'
5+
import { Eta } from 'eta'
56
import { Low } from 'lowdb'
6-
import sirv from 'sirv'
77
import { json } from 'milliparsec'
8-
import { Eta } from 'eta'
9-
import { z } from 'zod'
8+
import sirv from 'sirv'
109

11-
import { Data, Service } from './service.js'
10+
import { Data, isItem, Service } from './service.js'
1211

1312
const __dirname = dirname(fileURLToPath(import.meta.url))
1413
const isProduction = process.env['NODE_ENV'] === 'production'
@@ -20,19 +19,9 @@ export type AppOptions = {
2019

2120
const eta = new Eta({
2221
views: join(__dirname, '../views'),
23-
cache: isProduction
22+
cache: isProduction,
2423
})
2524

26-
function dataHandler(req, res, next) {
27-
const { data } = res.locals
28-
if (data === undefined) {
29-
res.sendStatus(404)
30-
} else {
31-
if (req.method === 'POST') res.status(201)
32-
res.json(data)
33-
}
34-
}
35-
3625
export function createApp(db: Low<Data>, options: AppOptions = {}) {
3726
// Create service
3827
const service = new Service(db)
@@ -45,12 +34,13 @@ export function createApp(db: Low<Data>, options: AppOptions = {}) {
4534

4635
// Static files
4736
app.use(sirv(join(__dirname, '../public')))
48-
options
49-
.static
50-
?.map((path) => isAbsolute(path) ? path : join(process.cwd(), path))
37+
options.static
38+
?.map((path) => (isAbsolute(path) ? path : join(process.cwd(), path)))
5139
.forEach((dir) => app.use(sirv(dir, { dev: !isProduction })))
5240

53-
app.get('/', (_req, res) => res.send(eta.render('index.html', { data: db.data })))
41+
app.get('/', (_req, res) =>
42+
res.send(eta.render('index.html', { data: db.data })),
43+
)
5444

5545
app.get('/:name', (req, res, next) => {
5646
const { name = '' } = req.params
@@ -66,19 +56,25 @@ export function createApp(db: Low<Data>, options: AppOptions = {}) {
6656

6757
app.post('/:name', async (req, res, next) => {
6858
const { name = '' } = req.params
69-
res.locals['data'] = await service.create(name, req.body)
59+
if (isItem(req.body)) {
60+
res.locals['data'] = await service.create(name, req.body)
61+
}
7062
next()
7163
})
7264

7365
app.put('/:name/:id', async (req, res, next) => {
7466
const { name = '', id = '' } = req.params
75-
res.locals['data'] = await service.update(name, id, req.body)
67+
if (isItem(req.body)) {
68+
res.locals['data'] = await service.update(name, id, req.body)
69+
}
7670
next()
7771
})
7872

7973
app.patch('/:name/:id', async (req, res, next) => {
8074
const { name = '', id = '' } = req.params
81-
res.locals['data'] = await service.patch(name, id, req.body)
75+
if (isItem(req.body)) {
76+
res.locals['data'] = await service.patch(name, id, req.body)
77+
}
8278
next()
8379
})
8480

@@ -88,7 +84,15 @@ export function createApp(db: Low<Data>, options: AppOptions = {}) {
8884
next()
8985
})
9086

91-
app.use('/:name', dataHandler)
87+
app.use('/:name', (req, res) => {
88+
const { data } = res.locals
89+
if (data === undefined) {
90+
res.sendStatus(404)
91+
} else {
92+
if (req.method === 'POST') res.status(201)
93+
res.json(data)
94+
}
95+
})
9296

9397
return app
9498
}

src/bin.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -70,7 +70,7 @@ const db = new Low<Data>(observer, {})
7070
await db.read()
7171

7272
// Create app
73-
const app = await createApp(db, { logger: false, static: values.static })
73+
const app = createApp(db, { logger: false, static: values.static })
7474

7575
function routes(db: Low<Data>): string[] {
7676
return Object.keys(db.data).map((key) => `http://${host}:${port}/${key}`)

src/service.test.ts

Lines changed: 8 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
1-
import test from 'node:test'
21
import assert from 'node:assert/strict'
2+
import test from 'node:test'
33

44
import { Low, Memory } from 'lowdb'
55
import { ParsedUrlQuery } from 'querystring'
@@ -35,7 +35,7 @@ type Test = {
3535
error?: Error
3636
}
3737

38-
test('findById', () => {
38+
await test('findById', () => {
3939
reset()
4040
assert.deepEqual(service.findById(POSTS, '1', {}), db.data?.[POSTS]?.[0])
4141
assert.equal(service.findById(POSTS, UNKNOWN_ID, {}), undefined)
@@ -50,7 +50,7 @@ test('findById', () => {
5050
assert.equal(service.findById(UNKNOWN_RESOURCE, '1', {}), undefined)
5151
})
5252

53-
test('find', () => {
53+
await test('find', () => {
5454
const arr: Test[] = [
5555
{
5656
name: POSTS,
@@ -223,17 +223,17 @@ test('find', () => {
223223
}
224224
})
225225

226-
test('create', async () => {
226+
await test('create', async () => {
227227
reset()
228228
const post = { title: 'new post' }
229229
const res = await service.create(POSTS, post)
230230
assert.equal(res?.['title'], post.title)
231-
assert.equal(res?.['id'].length, 4, 'id should be 4 chars')
231+
assert.equal(typeof res?.['id'], 'string', 'id should be a string')
232232

233233
assert.equal(await service.create(UNKNOWN_RESOURCE, post), undefined)
234234
})
235235

236-
test('update', async () => {
236+
await test('update', async () => {
237237
reset()
238238
const post = { id: 'xxx', title: 'updated post' }
239239
const res = await service.update(POSTS, post1.id, post)
@@ -247,7 +247,7 @@ test('update', async () => {
247247
assert.equal(await service.update(POSTS, UNKNOWN_ID, post), undefined)
248248
})
249249

250-
test('patch', async () => {
250+
await test('patch', async () => {
251251
reset()
252252
const post = { id: 'xxx', title: 'updated post' }
253253
const res = await service.patch(POSTS, post1.id, post)
@@ -259,7 +259,7 @@ test('patch', async () => {
259259
assert.equal(await service.patch(POSTS, UNKNOWN_ID, post), undefined)
260260
})
261261

262-
test('destroy', async () => {
262+
await test('destroy', async () => {
263263
reset()
264264
let prevLength = db.data?.[POSTS]?.length || 0
265265
await service.destroy(POSTS, post1.id)

src/service.ts

Lines changed: 12 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -5,27 +5,14 @@ import inflection from 'inflection'
55
import { Low } from 'lowdb'
66
import sortOn from 'sort-on'
77

8-
export type Item = {
9-
id: string
10-
[key: string]: unknown
11-
}
128
export type Headers = Record<string, string>
139

14-
export type Data = { [key: string]: Item[] }
10+
export type Item = Record<string, unknown>
1511

16-
function isItem(obj: unknown): obj is Item {
17-
if (typeof obj !== 'object' || obj === null) {
18-
return false
19-
}
12+
export type Data = Record<string, Item[]>
2013

21-
const item = obj as Record<string, unknown>
22-
return (
23-
'id' in item &&
24-
typeof item['id'] === 'string' &&
25-
Object.keys(item).every(
26-
(key) => key === 'id' || typeof item[key] !== 'function',
27-
)
28-
)
14+
export function isItem(obj: unknown): obj is Item {
15+
return typeof obj === 'object' && obj !== null
2916
}
3017

3118
export function isData(obj: unknown): obj is Record<string, Item[]> {
@@ -79,7 +66,7 @@ function include(
7966
}
8067
const foreignKey = `${related}Id`
8168
const relatedItem = relatedData.find((relatedItem: Item) => {
82-
return relatedItem.id === item[foreignKey]
69+
return relatedItem['id'] === item[foreignKey]
8370
})
8471
return { ...item, [related]: relatedItem }
8572
}
@@ -91,7 +78,7 @@ function include(
9178

9279
const foreignKey = `${inflection.singularize(name)}Id`
9380
const relatedItems = relatedData.filter(
94-
(relatedItem: Item) => relatedItem[foreignKey] === item.id,
81+
(relatedItem: Item) => relatedItem[foreignKey] === item['id'],
9582
)
9683

9784
return { ...item, [related]: relatedItems }
@@ -149,7 +136,7 @@ export class Service {
149136
id: string,
150137
query: { _include?: string[] },
151138
): Item | undefined {
152-
let item = this.#get(name)?.find((item) => item.id === id)
139+
let item = this.#get(name)?.find((item) => item['id'] === id)
153140
query._include?.forEach((related) => {
154141
if (item !== undefined) item = include(this.#db, name, item, related)
155142
})
@@ -339,12 +326,12 @@ export class Service {
339326
const items = this.#get(name)
340327
if (items === undefined) return
341328

342-
const index = items.findIndex((item) => item.id === id)
329+
const index = items.findIndex((item) => item['id'] === id)
343330
if (index === -1) return
344331

345332
const item = items.at(index)
346333
if (item) {
347-
const nextItem = { ...body, id: item.id }
334+
const nextItem = { ...body, id: item['id'] }
348335
items.splice(index, 1, nextItem)
349336
await this.#db.write()
350337
return nextItem
@@ -360,12 +347,12 @@ export class Service {
360347
const items = this.#get(name)
361348
if (items === undefined) return
362349

363-
const index = items.findIndex((item) => item.id === id)
350+
const index = items.findIndex((item) => item['id'] === id)
364351
if (index === -1) return
365352

366353
const item = items.at(index)
367354
if (item) {
368-
const nextItem = { ...item, ...body, id: item.id }
355+
const nextItem = { ...item, ...body, id: item['id'] }
369356
items.splice(index, 1, nextItem)
370357
await this.#db.write()
371358
return nextItem
@@ -381,7 +368,7 @@ export class Service {
381368
const items = this.#get(name)
382369
if (items === undefined) return
383370

384-
const index = items.findIndex((item) => item.id === id)
371+
const index = items.findIndex((item) => item['id'] === id)
385372
if (index === -1) return
386373
const item = items.splice(index, 1)[0]
387374

0 commit comments

Comments
 (0)