feat: multiple changes
Signed-off-by: Avior <git@avior.me>
This commit is contained in:
parent
d8f203f434
commit
3e91597dca
2
.github/workflows/lint.yml
vendored
2
.github/workflows/lint.yml
vendored
@ -19,7 +19,7 @@ jobs:
|
|||||||
run: npm ci
|
run: npm ci
|
||||||
|
|
||||||
- name: Run BiomeJS
|
- name: Run BiomeJS
|
||||||
run: npm run lint
|
run: npx biome ci .
|
||||||
# - uses: mongolyy/reviewdog-action-biome@v1
|
# - uses: mongolyy/reviewdog-action-biome@v1
|
||||||
# with:
|
# with:
|
||||||
# github_token: ${{ secrets.GITHUB_TOKEN }}
|
# github_token: ${{ secrets.GITHUB_TOKEN }}
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
|
import node from '@astrojs/node'
|
||||||
|
import tailwind from '@astrojs/tailwind'
|
||||||
import { defineConfig } from 'astro/config'
|
import { defineConfig } from 'astro/config'
|
||||||
import tailwind from "@astrojs/tailwind"
|
|
||||||
import node from "@astrojs/node"
|
|
||||||
import routing from './hooks/routing'
|
import routing from './hooks/routing'
|
||||||
|
|
||||||
// const faviconHook = {
|
// const faviconHook = {
|
||||||
@ -18,7 +18,7 @@ import routing from './hooks/routing'
|
|||||||
export default defineConfig({
|
export default defineConfig({
|
||||||
// Use the NodeJS adapter
|
// Use the NodeJS adapter
|
||||||
adapter: node({
|
adapter: node({
|
||||||
mode: "standalone"
|
mode: 'standalone'
|
||||||
}),
|
}),
|
||||||
|
|
||||||
// some settings to the build output
|
// some settings to the build output
|
||||||
@ -66,7 +66,5 @@ export default defineConfig({
|
|||||||
usePolling: !!(process.env.USE_POLLING ?? process.env.WSL_DISTRO_NAME)
|
usePolling: !!(process.env.USE_POLLING ?? process.env.WSL_DISTRO_NAME)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
}
|
||||||
|
})
|
||||||
|
|
||||||
})
|
|
10
biome.json
10
biome.json
@ -24,7 +24,8 @@
|
|||||||
"noStaticOnlyClass": "warn",
|
"noStaticOnlyClass": "warn",
|
||||||
"noUselessTypeConstraint": "warn",
|
"noUselessTypeConstraint": "warn",
|
||||||
"noVoid": "error",
|
"noVoid": "error",
|
||||||
"useSimplifiedLogicExpression": "warn"
|
"useSimplifiedLogicExpression": "warn",
|
||||||
|
"noForEach": "off"
|
||||||
},
|
},
|
||||||
"performance": {
|
"performance": {
|
||||||
"noBarrelFile": "error",
|
"noBarrelFile": "error",
|
||||||
@ -46,7 +47,7 @@
|
|||||||
"options": {
|
"options": {
|
||||||
"strictCase": true,
|
"strictCase": true,
|
||||||
"requireAscii": true,
|
"requireAscii": true,
|
||||||
"filenameCases": ["camelCase", "export"]
|
"filenameCases": ["camelCase", "PascalCase", "export"]
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"useForOf": "error",
|
"useForOf": "error",
|
||||||
@ -61,6 +62,9 @@
|
|||||||
"useNodeAssertStrict": "warn",
|
"useNodeAssertStrict": "warn",
|
||||||
"useNumberNamespace": "warn",
|
"useNumberNamespace": "warn",
|
||||||
"useSingleCaseStatement": "warn"
|
"useSingleCaseStatement": "warn"
|
||||||
|
},
|
||||||
|
"suspicious": {
|
||||||
|
"noEmptyInterface": "off"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
@ -74,7 +78,7 @@
|
|||||||
"semicolons": "asNeeded",
|
"semicolons": "asNeeded",
|
||||||
"quoteStyle": "single",
|
"quoteStyle": "single",
|
||||||
"trailingComma": "none",
|
"trailingComma": "none",
|
||||||
"lineWidth": 200,
|
"lineWidth": 120,
|
||||||
"bracketSameLine": true
|
"bracketSameLine": true
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,8 +1,8 @@
|
|||||||
import { expect, test } from '@playwright/test'
|
import { expect, test } from '@playwright/test'
|
||||||
|
|
||||||
test('has title', async ({ page }) => {
|
test('has title', async ({ page }) => {
|
||||||
await page.goto('/');
|
await page.goto('/')
|
||||||
|
|
||||||
// Expect a title "to contain" a substring.
|
// Expect a title "to contain" a substring.
|
||||||
await expect(page).toHaveTitle(/Dzeio/);
|
await expect(page).toHaveTitle(/Dzeio/)
|
||||||
})
|
})
|
||||||
|
@ -53,14 +53,14 @@ async function updateRoutes(output: string, routes: Array<string>) {
|
|||||||
let file = baseFile
|
let file = baseFile
|
||||||
file += `\n\nexport type Routes = ${routes.map((it) => `'${it}'`).join(' | ')}`
|
file += `\n\nexport type Routes = ${routes.map((it) => `'${it}'`).join(' | ')}`
|
||||||
|
|
||||||
file += '\n\nexport default function route(route: Routes, query?: Record<string, string | number>) {'
|
file +=
|
||||||
|
'\n\nexport default function route(route: Routes, query?: Record<string, string | number>) {'
|
||||||
file += '\n\treturn formatRoute(route, query)'
|
file += '\n\treturn formatRoute(route, query)'
|
||||||
file += '\n}\n'
|
file += '\n}\n'
|
||||||
|
|
||||||
await fs.writeFile(output, file)
|
await fs.writeFile(output, file)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* format the path back to an url usable by the app
|
* format the path back to an url usable by the app
|
||||||
*
|
*
|
||||||
@ -141,7 +141,6 @@ const integration: () => AstroIntegration = () => ({
|
|||||||
await updateRoutes(outputFile, files)
|
await updateRoutes(outputFile, files)
|
||||||
},
|
},
|
||||||
'astro:server:setup': async ({ server }) => {
|
'astro:server:setup': async ({ server }) => {
|
||||||
|
|
||||||
// get the files list
|
// get the files list
|
||||||
const files = (await Promise.all([
|
const files = (await Promise.all([
|
||||||
await getFiles(pagesFolder).then((ev) => ev.map((it) => formatPath(pagesFolder, it))),
|
await getFiles(pagesFolder).then((ev) => ev.map((it) => formatPath(pagesFolder, it))),
|
||||||
@ -158,7 +157,7 @@ const integration: () => AstroIntegration = () => ({
|
|||||||
|
|
||||||
let removeExtension = true
|
let removeExtension = true
|
||||||
let folder = pagesFolder
|
let folder = pagesFolder
|
||||||
if(path.startsWith(publicFolder)) {
|
if (path.startsWith(publicFolder)) {
|
||||||
removeExtension = false
|
removeExtension = false
|
||||||
folder = publicFolder
|
folder = publicFolder
|
||||||
} else if (!path.startsWith(folder)) {
|
} else if (!path.startsWith(folder)) {
|
||||||
@ -178,7 +177,7 @@ const integration: () => AstroIntegration = () => ({
|
|||||||
path = path.replace(/\\/g, '/')
|
path = path.replace(/\\/g, '/')
|
||||||
let removeExtension = true
|
let removeExtension = true
|
||||||
let folder = pagesFolder
|
let folder = pagesFolder
|
||||||
if(path.startsWith(publicFolder)) {
|
if (path.startsWith(publicFolder)) {
|
||||||
removeExtension = false
|
removeExtension = false
|
||||||
folder = publicFolder
|
folder = publicFolder
|
||||||
}
|
}
|
||||||
|
@ -12,32 +12,39 @@ export default defineConfig({
|
|||||||
fullyParallel: true,
|
fullyParallel: true,
|
||||||
forbidOnly: !!process.env.CI,
|
forbidOnly: !!process.env.CI,
|
||||||
retries: process.env.CI ? 2 : 0,
|
retries: process.env.CI ? 2 : 0,
|
||||||
workers: process.env.CI ? 1 : undefined as any,
|
workers: process.env.CI ? 1 : (undefined as any),
|
||||||
reporter: process.env.CI ? 'list' : [['html', {
|
reporter: process.env.CI
|
||||||
|
? 'list'
|
||||||
|
: [
|
||||||
|
[
|
||||||
|
'html',
|
||||||
|
{
|
||||||
outputFolder: './playwright/report',
|
outputFolder: './playwright/report',
|
||||||
open: 'never'
|
open: 'never'
|
||||||
}]],
|
}
|
||||||
|
]
|
||||||
|
],
|
||||||
use: {
|
use: {
|
||||||
baseURL: 'http://localhost:3000',
|
baseURL: 'http://localhost:3000',
|
||||||
trace: 'on-first-retry',
|
trace: 'on-first-retry'
|
||||||
},
|
},
|
||||||
|
|
||||||
/* Configure projects for major browsers */
|
/* Configure projects for major browsers */
|
||||||
projects: [
|
projects: [
|
||||||
{
|
{
|
||||||
name: 'chromium',
|
name: 'chromium',
|
||||||
use: { ...devices['Desktop Chrome'] },
|
use: { ...devices['Desktop Chrome'] }
|
||||||
},
|
},
|
||||||
|
|
||||||
{
|
{
|
||||||
name: 'firefox',
|
name: 'firefox',
|
||||||
use: { ...devices['Desktop Firefox'] },
|
use: { ...devices['Desktop Firefox'] }
|
||||||
},
|
},
|
||||||
|
|
||||||
{
|
{
|
||||||
name: 'webkit',
|
name: 'webkit',
|
||||||
use: { ...devices['Desktop Safari'] },
|
use: { ...devices['Desktop Safari'] }
|
||||||
},
|
}
|
||||||
|
|
||||||
/* Test against mobile viewports. */
|
/* Test against mobile viewports. */
|
||||||
// {
|
// {
|
||||||
@ -58,5 +65,5 @@ export default defineConfig({
|
|||||||
// name: 'Google Chrome',
|
// name: 'Google Chrome',
|
||||||
// use: { ...devices['Desktop Chrome'], channel: 'chrome' },
|
// use: { ...devices['Desktop Chrome'], channel: 'chrome' },
|
||||||
// },
|
// },
|
||||||
],
|
]
|
||||||
})
|
})
|
||||||
|
@ -7,12 +7,11 @@ export interface Props extends astroHTML.JSX.AnchorHTMLAttributes {
|
|||||||
}
|
}
|
||||||
|
|
||||||
const classes = [
|
const classes = [
|
||||||
"button",
|
'button',
|
||||||
{outline: Astro.props.outline},
|
{ outline: Astro.props.outline },
|
||||||
{ghost: Astro.props.ghost},
|
{ ghost: Astro.props.ghost },
|
||||||
Astro.props.class
|
Astro.props.class
|
||||||
]
|
]
|
||||||
|
|
||||||
---
|
---
|
||||||
{'href' in Astro.props && (
|
{'href' in Astro.props && (
|
||||||
<a class:list={classes} {...objectOmit(Astro.props, 'type') as any}>
|
<a class:list={classes} {...objectOmit(Astro.props, 'type') as any}>
|
||||||
|
@ -1,12 +1,9 @@
|
|||||||
---
|
---
|
||||||
import { getImage } from 'astro:assets'
|
import { getImage } from 'astro:assets'
|
||||||
import AstroUtils from '../../libs/AstroUtils'
|
|
||||||
import { objectOmit } from '@dzeio/object-util'
|
import { objectOmit } from '@dzeio/object-util'
|
||||||
|
import AstroUtils from '../../libs/AstroUtils'
|
||||||
|
|
||||||
const formats = [
|
const formats = ['avif', 'webp']
|
||||||
'avif',
|
|
||||||
'webp'
|
|
||||||
]
|
|
||||||
|
|
||||||
export interface Props extends Omit<astroHTML.JSX.ImgHTMLAttributes, 'src'> {
|
export interface Props extends Omit<astroHTML.JSX.ImgHTMLAttributes, 'src'> {
|
||||||
src: ImageMetadata | string
|
src: ImageMetadata | string
|
||||||
@ -17,7 +14,7 @@ export interface Props extends Omit<astroHTML.JSX.ImgHTMLAttributes, 'src'> {
|
|||||||
|
|
||||||
type PictureResult = {
|
type PictureResult = {
|
||||||
format: 'new'
|
format: 'new'
|
||||||
formats: Array<{format: string, img: Awaited<ReturnType<typeof getImage>>}>
|
formats: Array<{ format: string, img: Awaited<ReturnType<typeof getImage>> }>
|
||||||
src: Awaited<ReturnType<typeof getImage>>
|
src: Awaited<ReturnType<typeof getImage>>
|
||||||
} | {
|
} | {
|
||||||
format: 'raw'
|
format: 'raw'
|
||||||
@ -38,14 +35,25 @@ async function resolvePicture(image: ImageMetadata | string): Promise<PictureRes
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const imageFormats: Array<{format: string, img: Awaited<ReturnType<typeof getImage>>}> = await Promise.all(
|
const imageFormats: Array<{ format: string, img: Awaited<ReturnType<typeof getImage>> }> =
|
||||||
|
await Promise.all(
|
||||||
formats.map(async (it) => ({
|
formats.map(async (it) => ({
|
||||||
img: await getImage({src: Astro.props.src, format: it, width: Astro.props.width, height: Astro.props.height}),
|
img: await getImage({
|
||||||
|
src: Astro.props.src,
|
||||||
|
format: it,
|
||||||
|
width: Astro.props.width,
|
||||||
|
height: Astro.props.height
|
||||||
|
}),
|
||||||
format: it
|
format: it
|
||||||
}))
|
}))
|
||||||
)
|
)
|
||||||
|
|
||||||
const orig = await getImage({src: Astro.props.src, format: ext, width: Astro.props.width, height: Astro.props.height})
|
const orig = await getImage({
|
||||||
|
src: Astro.props.src,
|
||||||
|
format: ext,
|
||||||
|
width: Astro.props.width,
|
||||||
|
height: Astro.props.height
|
||||||
|
})
|
||||||
|
|
||||||
return {
|
return {
|
||||||
format: 'new',
|
format: 'new',
|
||||||
@ -62,7 +70,6 @@ const res = await AstroUtils.wrap<Result>(async () => {
|
|||||||
})
|
})
|
||||||
|
|
||||||
const props = objectOmit(Astro.props, 'src', 'srcDark', 'class')
|
const props = objectOmit(Astro.props, 'src', 'srcDark', 'class')
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
{res.light.format === 'new' && (
|
{res.light.format === 'new' && (
|
||||||
|
@ -6,7 +6,6 @@ interface Props extends Omit<astroHTML.JSX.InputHTMLAttributes, 'type'> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
const baseProps = objectOmit(Astro.props, 'label', 'block')
|
const baseProps = objectOmit(Astro.props, 'label', 'block')
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
<div class:list={[{parent: Astro.props.block}]}>
|
<div class:list={[{parent: Astro.props.block}]}>
|
||||||
|
@ -7,7 +7,7 @@ export interface Props extends Omit<astroHTML.JSX.InputHTMLAttributes, 'type'> {
|
|||||||
block?: boolean
|
block?: boolean
|
||||||
suffix?: string
|
suffix?: string
|
||||||
prefix?: string
|
prefix?: string
|
||||||
options: Array<string | number | {title: string | number, description?: string | number | null}>
|
options: Array<string | number | { title: string | number, description?: string | number | null }>
|
||||||
}
|
}
|
||||||
|
|
||||||
const baseProps = objectOmit(Astro.props, 'label', 'block', 'suffix', 'prefix', 'options')
|
const baseProps = objectOmit(Astro.props, 'label', 'block', 'suffix', 'prefix', 'options')
|
||||||
|
@ -87,8 +87,8 @@ export function setOnTableClick(table: HTMLTableElement, fn: (row: number, cell:
|
|||||||
table.querySelectorAll<HTMLTableCellElement>('td').forEach((it) => {
|
table.querySelectorAll<HTMLTableCellElement>('td').forEach((it) => {
|
||||||
it.addEventListener('click', () => {
|
it.addEventListener('click', () => {
|
||||||
const row = it.parentElement as HTMLTableRowElement
|
const row = it.parentElement as HTMLTableRowElement
|
||||||
const rowIdx = parseInt(row.dataset.row as string)
|
const rowIdx = Number.parseInt(row.dataset.row as string)
|
||||||
const cellIdx = parseInt(it.dataset.cell as string)
|
const cellIdx = Number.parseInt(it.dataset.cell as string)
|
||||||
fn(rowIdx, cellIdx)
|
fn(rowIdx, cellIdx)
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
@ -9,10 +9,9 @@ export interface Props {
|
|||||||
|
|
||||||
if (Astro.props.icoPath !== '/favicon.ico') {
|
if (Astro.props.icoPath !== '/favicon.ico') {
|
||||||
console.warn('It is recommanded that the ICO file should be located at /favicon.ico')
|
console.warn('It is recommanded that the ICO file should be located at /favicon.ico')
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
const appleTouch = await getImage({src: Astro.props.png, width: 180, height: 180})
|
const appleTouch = await getImage({ src: Astro.props.png, width: 180, height: 180 })
|
||||||
---
|
---
|
||||||
|
|
||||||
<>
|
<>
|
||||||
|
@ -1,36 +0,0 @@
|
|||||||
import { getImage } from 'astro:assets'
|
|
||||||
|
|
||||||
export default class Manifest {
|
|
||||||
static async create(baseImage: ImageMetadata, options: {
|
|
||||||
name: string
|
|
||||||
color?: string
|
|
||||||
images?: Array<number>
|
|
||||||
}) {
|
|
||||||
const [
|
|
||||||
i192,
|
|
||||||
i512
|
|
||||||
] = await Promise.all([
|
|
||||||
getImage({src: baseImage, format: 'png', width: 192, height: 192}),
|
|
||||||
getImage({src: baseImage, format: 'png', width: 512, height: 512})
|
|
||||||
])
|
|
||||||
return JSON.stringify({
|
|
||||||
name: options.name,
|
|
||||||
short_name: options.name,
|
|
||||||
icons: [
|
|
||||||
{
|
|
||||||
src: i192.src,
|
|
||||||
sizes: "192x192",
|
|
||||||
type: "image/png"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
src: i512.src,
|
|
||||||
sizes: "512x512",
|
|
||||||
type: "image/png"
|
|
||||||
}
|
|
||||||
],
|
|
||||||
theme_color: options.color ?? "#fff",
|
|
||||||
background_color: options.color ?? "#fff",
|
|
||||||
display: "standalone"
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
|
@ -2,8 +2,8 @@
|
|||||||
const year = new Date().getFullYear()
|
const year = new Date().getFullYear()
|
||||||
|
|
||||||
export interface Props {
|
export interface Props {
|
||||||
links?: Array<{href: string, target?: string, display: string}>
|
links?: Array<{ href: string, target?: string, display: string }>
|
||||||
socials?: Array<{href: string, target?: string, icon: any}>
|
socials?: Array<{ href: string, target?: string, icon: any }>
|
||||||
}
|
}
|
||||||
---
|
---
|
||||||
|
|
||||||
|
@ -1,8 +1,8 @@
|
|||||||
---
|
---
|
||||||
import Logo from 'assets/components/layouts/Header/logo.svg'
|
|
||||||
import Picture from 'components/global/Picture.astro'
|
|
||||||
import Button from 'components/global/Button.astro'
|
|
||||||
import { objectMap } from '@dzeio/object-util'
|
import { objectMap } from '@dzeio/object-util'
|
||||||
|
import Logo from 'assets/components/layouts/Header/logo.svg'
|
||||||
|
import Button from 'components/global/Button.astro'
|
||||||
|
import Picture from 'components/global/Picture.astro'
|
||||||
|
|
||||||
export interface Props {
|
export interface Props {
|
||||||
right?: Record<string, string>
|
right?: Record<string, string>
|
||||||
|
@ -4,16 +4,19 @@ import { defineCollection, z } from 'astro:content'
|
|||||||
// 2. Define your collection(s)
|
// 2. Define your collection(s)
|
||||||
const projectsCollection = defineCollection({
|
const projectsCollection = defineCollection({
|
||||||
type: 'content',
|
type: 'content',
|
||||||
schema: ({ image }) => z.object({
|
schema: ({ image }) =>
|
||||||
|
z.object({
|
||||||
title: z.string(),
|
title: z.string(),
|
||||||
description: z.string().optional(),
|
description: z.string().optional(),
|
||||||
image: image().optional(),
|
image: image().optional(),
|
||||||
link: z.object({
|
link: z
|
||||||
|
.object({
|
||||||
href: z.string(),
|
href: z.string(),
|
||||||
rel: z.string().optional(),
|
rel: z.string().optional(),
|
||||||
text: z.string().optional(),
|
text: z.string().optional(),
|
||||||
target: z.string().optional()
|
target: z.string().optional()
|
||||||
}).optional(),
|
})
|
||||||
|
.optional(),
|
||||||
disabled: z.string().optional(),
|
disabled: z.string().optional(),
|
||||||
created: z.date().optional(),
|
created: z.date().optional(),
|
||||||
updated: z.date().optional(),
|
updated: z.date().optional(),
|
||||||
@ -21,7 +24,6 @@ const projectsCollection = defineCollection({
|
|||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
|
|
||||||
// 3. Export a single `collections` object to register your collection(s)
|
// 3. Export a single `collections` object to register your collection(s)
|
||||||
// This key should match your collection directory name in "src/content"
|
// This key should match your collection directory name in "src/content"
|
||||||
export const collections = {
|
export const collections = {
|
||||||
|
6
src/env.d.ts
vendored
6
src/env.d.ts
vendored
@ -4,14 +4,12 @@
|
|||||||
/**
|
/**
|
||||||
* Environment variables declaration
|
* Environment variables declaration
|
||||||
*/
|
*/
|
||||||
interface ImportMetaEnv {
|
interface ImportMetaEnv {}
|
||||||
}
|
|
||||||
|
|
||||||
interface ImportMeta {
|
interface ImportMeta {
|
||||||
readonly env: ImportMetaEnv;
|
readonly env: ImportMetaEnv
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
declare namespace App {
|
declare namespace App {
|
||||||
/**
|
/**
|
||||||
* Middlewares variables
|
* Middlewares variables
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
---
|
---
|
||||||
import Favicon from 'components/layouts/Favicon/Favicon.astro'
|
import type IconPNG from 'assets/layouts/Head/favicon.png'
|
||||||
import IconSVG from 'assets/layouts/Head/favicon.svg'
|
import IconSVG from 'assets/layouts/Head/favicon.svg'
|
||||||
import IconPNG from 'assets/layouts/Head/favicon.png'
|
import Favicon from 'components/layouts/Favicon/Favicon.astro'
|
||||||
|
|
||||||
export interface Props {
|
export interface Props {
|
||||||
/**
|
/**
|
||||||
@ -29,7 +29,7 @@ export interface Props {
|
|||||||
*/
|
*/
|
||||||
twitter?: {
|
twitter?: {
|
||||||
title?: string | undefined
|
title?: string | undefined
|
||||||
card?: "summary" | "summary_large_image" | "app" | "player" | undefined
|
card?: 'summary' | 'summary_large_image' | 'app' | 'player' | undefined
|
||||||
site?: string | undefined
|
site?: string | undefined
|
||||||
creator?: string | undefined
|
creator?: string | undefined
|
||||||
} | undefined
|
} | undefined
|
||||||
@ -46,9 +46,14 @@ export interface Props {
|
|||||||
|
|
||||||
const props = Astro.props
|
const props = Astro.props
|
||||||
|
|
||||||
const image = props.image ? Array.isArray(props.image) ? props.image : [props.image] : undefined
|
const image = props.image ? (Array.isArray(props.image) ? props.image : [props.image]) : undefined
|
||||||
|
|
||||||
const canonical = typeof Astro.props.canonical === 'string' ? Astro.props.canonical : Astro.props.canonical === false ? undefined : Astro.url.href
|
const canonical =
|
||||||
|
typeof Astro.props.canonical === 'string'
|
||||||
|
? Astro.props.canonical
|
||||||
|
: Astro.props.canonical === false
|
||||||
|
? undefined
|
||||||
|
: Astro.url.href
|
||||||
---
|
---
|
||||||
|
|
||||||
<!-- Charset -->
|
<!-- Charset -->
|
||||||
|
@ -1,9 +1,9 @@
|
|||||||
---
|
---
|
||||||
import Footer from 'components/layouts/Footer.astro'
|
import Footer from 'components/layouts/Footer.astro'
|
||||||
import Base, { type Props as BaseProps } from './Base.astro'
|
|
||||||
import Header from 'components/layouts/Header.astro'
|
import Header from 'components/layouts/Header.astro'
|
||||||
import { Mail, Phone } from 'lucide-astro'
|
import { Mail, Phone } from 'lucide-astro'
|
||||||
import { Github, Linkedin } from 'simple-icons-astro'
|
import { Github, Linkedin } from 'simple-icons-astro'
|
||||||
|
import Base, { type Props as BaseProps } from './Base.astro'
|
||||||
|
|
||||||
export interface Props extends BaseProps {
|
export interface Props extends BaseProps {
|
||||||
/**
|
/**
|
||||||
|
@ -4,9 +4,7 @@ type Fn<T extends HTMLElement> = (el: Component<T>) => void | Promise<void>
|
|||||||
* Component client side initialisation class
|
* Component client side initialisation class
|
||||||
*/
|
*/
|
||||||
export default class Component<T extends HTMLElement> {
|
export default class Component<T extends HTMLElement> {
|
||||||
private constructor(
|
private constructor(public element: T) {}
|
||||||
public element: T
|
|
||||||
) {}
|
|
||||||
|
|
||||||
public handled(value: boolean): this
|
public handled(value: boolean): this
|
||||||
public handled(): boolean
|
public handled(): boolean
|
||||||
|
@ -4,7 +4,6 @@
|
|||||||
* Following https://developer.mozilla.org/en-US/docs/Web/HTTP/Status an extension of the RFC9110
|
* Following https://developer.mozilla.org/en-US/docs/Web/HTTP/Status an extension of the RFC9110
|
||||||
*/
|
*/
|
||||||
enum StatusCode {
|
enum StatusCode {
|
||||||
|
|
||||||
/****************
|
/****************
|
||||||
* 1xx Requests *
|
* 1xx Requests *
|
||||||
****************/
|
****************/
|
||||||
@ -283,7 +282,7 @@ enum StatusCode {
|
|||||||
/**
|
/**
|
||||||
* Indicates that the client needs to authenticate to gain network access.
|
* Indicates that the client needs to authenticate to gain network access.
|
||||||
*/
|
*/
|
||||||
NETWORK_AUTHENTIFICATION_REQUIRED,
|
NETWORK_AUTHENTIFICATION_REQUIRED
|
||||||
}
|
}
|
||||||
|
|
||||||
export default StatusCode
|
export default StatusCode
|
||||||
|
@ -58,9 +58,13 @@ export default interface RFC7807 {
|
|||||||
* @param error the error (base items are type, status, title details and instance)
|
* @param error the error (base items are type, status, title details and instance)
|
||||||
* @returns
|
* @returns
|
||||||
*/
|
*/
|
||||||
export function buildRFC7807(error: RFC7807 & Record<string, any>, response: ResponseBuilder = new ResponseBuilder()): Response {
|
export function buildRFC7807(
|
||||||
response.addHeader('Content-Type', 'application/problem+json')
|
error: RFC7807 & Record<string, any>,
|
||||||
|
response: ResponseBuilder = new ResponseBuilder()
|
||||||
|
): Response {
|
||||||
|
return response
|
||||||
|
.addHeader('Content-Type', 'application/problem+json')
|
||||||
.body(JSON.stringify(error))
|
.body(JSON.stringify(error))
|
||||||
.status(error.status ?? 500)
|
.status(error.status ?? 500)
|
||||||
return response.build()
|
.build()
|
||||||
}
|
}
|
||||||
|
@ -5,7 +5,6 @@ import StatusCode from './HTTP/StatusCode'
|
|||||||
* Simple builde to create a new Response object
|
* Simple builde to create a new Response object
|
||||||
*/
|
*/
|
||||||
export default class ResponseBuilder {
|
export default class ResponseBuilder {
|
||||||
|
|
||||||
public static redirect(location: string, statusCode: number = StatusCode.FOUND) {
|
public static redirect(location: string, statusCode: number = StatusCode.FOUND) {
|
||||||
const resp = new ResponseBuilder()
|
const resp = new ResponseBuilder()
|
||||||
resp.addHeader('Location', location)
|
resp.addHeader('Location', location)
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
import { sequence } from "astro/middleware"
|
import { sequence } from 'astro/middleware'
|
||||||
|
|
||||||
import logger from './logger'
|
import logger from './logger'
|
||||||
|
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
import { defineMiddleware } from "astro/middleware"
|
import { defineMiddleware } from 'astro/middleware'
|
||||||
import ResponseBuilder from 'libs/ResponseBuilder'
|
import ResponseBuilder from 'libs/ResponseBuilder'
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -7,7 +7,10 @@ import ResponseBuilder from 'libs/ResponseBuilder'
|
|||||||
export default defineMiddleware(async ({ request, url }, next) => {
|
export default defineMiddleware(async ({ request, url }, next) => {
|
||||||
const now = new Date()
|
const now = new Date()
|
||||||
// Date of request User-Agent 32 first chars request Method
|
// Date of request User-Agent 32 first chars request Method
|
||||||
let prefix = `\x1b[2m${now.toISOString()}\x1b[22m ${request.headers.get('user-agent')?.slice(0, 32).padEnd(32)} ${request.method.padEnd(7)}`
|
let prefix = `\x1b[2m${now.toISOString()}\x1b[22m ${request.headers
|
||||||
|
.get('user-agent')
|
||||||
|
?.slice(0, 32)
|
||||||
|
.padEnd(32)} ${request.method.padEnd(7)}`
|
||||||
|
|
||||||
const fullURL = url.toString()
|
const fullURL = url.toString()
|
||||||
const path = fullURL.slice(fullURL.indexOf(url.pathname, fullURL.indexOf(url.host)))
|
const path = fullURL.slice(fullURL.indexOf(url.pathname, fullURL.indexOf(url.host)))
|
||||||
@ -26,14 +29,22 @@ export default defineMiddleware(async ({ request, url }, next) => {
|
|||||||
|
|
||||||
if (import.meta.env.PROD) {
|
if (import.meta.env.PROD) {
|
||||||
// HTTP Status time to execute path of request
|
// HTTP Status time to execute path of request
|
||||||
console.log(`${prefix} \x1b[34m[${res.status}]\x1b[0m \x1b[2m${(new Date().getTime() - now.getTime()).toFixed(0).padStart(5, ' ')}ms\x1b[22m ${path}`)
|
console.log(
|
||||||
|
`${prefix} \x1b[34m[${res.status}]\x1b[0m \x1b[2m${(new Date().getTime() - now.getTime())
|
||||||
|
.toFixed(0)
|
||||||
|
.padStart(5, ' ')}ms\x1b[22m ${path}`
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
return res
|
return res
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
if (import.meta.env.PROD) {
|
if (import.meta.env.PROD) {
|
||||||
// time to execute path of request
|
// time to execute path of request
|
||||||
console.log(`${prefix} \x1b[34m[500]\x1b[0m \x1b[2m${(new Date().getTime() - now.getTime()).toFixed(0).padStart(5, ' ')}ms\x1b[22m ${path}`)
|
console.log(
|
||||||
|
`${prefix} \x1b[34m[500]\x1b[0m \x1b[2m${(new Date().getTime() - now.getTime())
|
||||||
|
.toFixed(0)
|
||||||
|
.padStart(5, ' ')}ms\x1b[22m ${path}`
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
// add a full line dash to not miss it
|
// add a full line dash to not miss it
|
||||||
|
@ -4,7 +4,6 @@
|
|||||||
* you MUST call it through the `DaoFactory` file
|
* you MUST call it through the `DaoFactory` file
|
||||||
*/
|
*/
|
||||||
export default abstract class Dao<Object extends { id: any } = { id: any }> {
|
export default abstract class Dao<Object extends { id: any } = { id: any }> {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* insert a new object into the source
|
* insert a new object into the source
|
||||||
*
|
*
|
||||||
@ -19,7 +18,8 @@ export default abstract class Dao<Object extends { id: any } = { id: any }> {
|
|||||||
* @param obj the object to create
|
* @param obj the object to create
|
||||||
* @returns the object with it's id filled if create or null otherwise
|
* @returns the object with it's id filled if create or null otherwise
|
||||||
*/
|
*/
|
||||||
public insert: Dao<Object>['create'] = (obj: Parameters<Dao<Object>['create']>[0]) => this.create(obj)
|
public insert: Dao<Object>['create'] = (obj: Parameters<Dao<Object>['create']>[0]) =>
|
||||||
|
this.create(obj)
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* find the list of objects having elements from the query
|
* find the list of objects having elements from the query
|
||||||
@ -35,7 +35,8 @@ export default abstract class Dao<Object extends { id: any } = { id: any }> {
|
|||||||
* @param query a partial object which filter depending on the elements, if not set it will fetch everything
|
* @param query a partial object which filter depending on the elements, if not set it will fetch everything
|
||||||
* @returns an array containing the list of elements that match with the query
|
* @returns an array containing the list of elements that match with the query
|
||||||
*/
|
*/
|
||||||
public find: Dao<Object>['findAll'] = (query: Parameters<Dao<Object>['findAll']>[0]) => this.findAll(query)
|
public find: Dao<Object>['findAll'] = (query: Parameters<Dao<Object>['findAll']>[0]) =>
|
||||||
|
this.findAll(query)
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* find an object by it's id
|
* find an object by it's id
|
||||||
@ -46,7 +47,7 @@ export default abstract class Dao<Object extends { id: any } = { id: any }> {
|
|||||||
* @returns
|
* @returns
|
||||||
*/
|
*/
|
||||||
public findById(id: Object['id']): Promise<Object | null> {
|
public findById(id: Object['id']): Promise<Object | null> {
|
||||||
return this.findOne({id: id} as Partial<Object>)
|
return this.findOne({ id: id } as Partial<Object>)
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -91,14 +92,16 @@ export default abstract class Dao<Object extends { id: any } = { id: any }> {
|
|||||||
if (!query) {
|
if (!query) {
|
||||||
return null
|
return null
|
||||||
}
|
}
|
||||||
return await this.update({...query, ...changes})
|
return await this.update({ ...query, ...changes })
|
||||||
}
|
}
|
||||||
/**
|
/**
|
||||||
* update the remote reference of the object or create it if not found
|
* update the remote reference of the object or create it if not found
|
||||||
* @param obj the object to update/insert
|
* @param obj the object to update/insert
|
||||||
* @returns the object is updated/inserted or null otherwise
|
* @returns the object is updated/inserted or null otherwise
|
||||||
*/
|
*/
|
||||||
public async upsert(object: Object | Omit<Object, 'id' | 'created' | 'updated'>): Promise<Object | null> {
|
public async upsert(
|
||||||
|
object: Object | Omit<Object, 'id' | 'created' | 'updated'>
|
||||||
|
): Promise<Object | null> {
|
||||||
if ('id' in object) {
|
if ('id' in object) {
|
||||||
return this.update(object)
|
return this.update(object)
|
||||||
}
|
}
|
||||||
|
@ -9,8 +9,7 @@
|
|||||||
*
|
*
|
||||||
* Touch this interface to define which key is linked to which Dao
|
* Touch this interface to define which key is linked to which Dao
|
||||||
*/
|
*/
|
||||||
interface DaoItem {
|
interface DaoItem {}
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Class to get any DAO
|
* Class to get any DAO
|
||||||
@ -48,7 +47,8 @@ export default class DaoFactory {
|
|||||||
*/
|
*/
|
||||||
private static initDao(item: keyof DaoItem): any | undefined {
|
private static initDao(item: keyof DaoItem): any | undefined {
|
||||||
switch (item) {
|
switch (item) {
|
||||||
default: return undefined
|
default:
|
||||||
|
return undefined
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,9 +1,9 @@
|
|||||||
---
|
---
|
||||||
import MainLayout from 'layouts/MainLayout.astro'
|
|
||||||
import I404 from 'assets/pages/404/404.svg'
|
|
||||||
import I404Light from 'assets/pages/404/404.light.svg'
|
import I404Light from 'assets/pages/404/404.light.svg'
|
||||||
|
import I404 from 'assets/pages/404/404.svg'
|
||||||
import Button from 'components/global/Button.astro'
|
import Button from 'components/global/Button.astro'
|
||||||
import Picture from 'components/global/Picture.astro'
|
import Picture from 'components/global/Picture.astro'
|
||||||
|
import MainLayout from 'layouts/MainLayout.astro'
|
||||||
---
|
---
|
||||||
|
|
||||||
<MainLayout>
|
<MainLayout>
|
||||||
|
@ -7,7 +7,7 @@ module.exports = {
|
|||||||
theme: {
|
theme: {
|
||||||
fontFamily: {
|
fontFamily: {
|
||||||
// add your default font below
|
// add your default font below
|
||||||
'sans': ['Font Name', ...defaultTheme.fontFamily.sans]
|
sans: ['Font Name', ...defaultTheme.fontFamily.sans]
|
||||||
},
|
},
|
||||||
extend: {
|
extend: {
|
||||||
colors: {
|
colors: {
|
||||||
@ -21,7 +21,7 @@ module.exports = {
|
|||||||
// add a default padding to the container
|
// add a default padding to the container
|
||||||
DEFAULT: '1rem'
|
DEFAULT: '1rem'
|
||||||
}
|
}
|
||||||
},
|
}
|
||||||
},
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -3,7 +3,7 @@ import { expect, test } from 'vitest'
|
|||||||
// Edit an assertion and save to see HMR in action
|
// Edit an assertion and save to see HMR in action
|
||||||
|
|
||||||
test('Math.sqrt()', () => {
|
test('Math.sqrt()', () => {
|
||||||
expect(Math.sqrt(4)).toBe(2);
|
expect(Math.sqrt(4)).toBe(2)
|
||||||
expect(Math.sqrt(144)).toBe(12);
|
expect(Math.sqrt(144)).toBe(12)
|
||||||
expect(Math.sqrt(2)).toBe(Math.SQRT2);
|
expect(Math.sqrt(2)).toBe(Math.SQRT2)
|
||||||
});
|
})
|
||||||
|
@ -4,10 +4,8 @@ import { getViteConfig } from 'astro/config'
|
|||||||
|
|
||||||
export default getViteConfig({
|
export default getViteConfig({
|
||||||
test: {
|
test: {
|
||||||
include: [
|
include: ['./tests/**.ts']
|
||||||
'./tests/**.ts'
|
|
||||||
]
|
|
||||||
/* for example, use global to avoid globals imports (describe, test, expect): */
|
/* for example, use global to avoid globals imports (describe, test, expect): */
|
||||||
// globals: true,
|
// globals: true,
|
||||||
},
|
}
|
||||||
});
|
})
|
||||||
|
Loading…
x
Reference in New Issue
Block a user