Signed-off-by: Avior <github@avior.me>
This commit is contained in:
parent
f112ac6b50
commit
b4e122b617
60
.gitignore
vendored
60
.gitignore
vendored
@ -1,29 +1,31 @@
|
||||
# build output
|
||||
dist/
|
||||
|
||||
# generated types
|
||||
.astro/
|
||||
|
||||
# dependencies
|
||||
node_modules/
|
||||
|
||||
# logs
|
||||
npm-debug.log*
|
||||
yarn-debug.log*
|
||||
yarn-error.log*
|
||||
pnpm-debug.log*
|
||||
|
||||
# environment variables
|
||||
.env
|
||||
.env.production
|
||||
|
||||
# macOS-specific files
|
||||
.DS_Store
|
||||
|
||||
slicers/*
|
||||
|
||||
# Coverage
|
||||
coverage/
|
||||
|
||||
# Playwright
|
||||
/playwright/
|
||||
# build output
|
||||
dist/
|
||||
|
||||
# generated types
|
||||
.astro/
|
||||
|
||||
# dependencies
|
||||
node_modules/
|
||||
|
||||
# logs
|
||||
npm-debug.log*
|
||||
yarn-debug.log*
|
||||
yarn-error.log*
|
||||
pnpm-debug.log*
|
||||
|
||||
# environment variables
|
||||
.env
|
||||
.env.production
|
||||
|
||||
# macOS-specific files
|
||||
.DS_Store
|
||||
|
||||
slicers/*
|
||||
|
||||
# Coverage
|
||||
coverage/
|
||||
|
||||
# Playwright
|
||||
/playwright/
|
||||
|
||||
/src/route.ts
|
||||
|
@ -1,33 +1,34 @@
|
||||
import { defineConfig } from 'astro/config'
|
||||
import tailwind from "@astrojs/tailwind"
|
||||
import node from "@astrojs/node"
|
||||
|
||||
// const faviconHook = {
|
||||
// name: 'Favicon',
|
||||
// hooks: {
|
||||
// "astro:build:setup": async () => {
|
||||
// await Manifest.create('./src/assets/favicon.png', {
|
||||
// name: 'Template'
|
||||
// })
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
|
||||
// https://astro.build/config
|
||||
export default defineConfig({
|
||||
import { defineConfig } from 'astro/config'
|
||||
import tailwind from "@astrojs/tailwind"
|
||||
import node from "@astrojs/node"
|
||||
import routing from './hooks/routing'
|
||||
|
||||
// const faviconHook = {
|
||||
// name: 'Favicon',
|
||||
// hooks: {
|
||||
// "astro:build:setup": async () => {
|
||||
// await Manifest.create('./src/assets/favicon.png', {
|
||||
// name: 'Template'
|
||||
// })
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
|
||||
// https://astro.build/config
|
||||
export default defineConfig({
|
||||
// Use the NodeJS adapter
|
||||
adapter: node({
|
||||
mode: "standalone"
|
||||
}),
|
||||
|
||||
// some settings to the build output
|
||||
build: {
|
||||
build: {
|
||||
// the asset path
|
||||
assets: 'assets',
|
||||
assets: 'assets',
|
||||
|
||||
// inline the stylesheet if it is small enough
|
||||
inlineStylesheets: 'auto'
|
||||
},
|
||||
inlineStylesheets: 'auto'
|
||||
},
|
||||
|
||||
// Compress the HTML output
|
||||
compressHTML: true,
|
||||
@ -36,7 +37,7 @@ export default defineConfig({
|
||||
output: 'server',
|
||||
|
||||
// Add TailwindCSS
|
||||
integrations: [tailwind()],
|
||||
integrations: [tailwind(), routing()],
|
||||
|
||||
// prefetch links
|
||||
prefetch: {
|
||||
@ -47,25 +48,25 @@ export default defineConfig({
|
||||
site: 'https://example.com',
|
||||
|
||||
// the Output server
|
||||
server: {
|
||||
host: true,
|
||||
port: 3000
|
||||
},
|
||||
server: {
|
||||
host: true,
|
||||
port: 3000
|
||||
},
|
||||
|
||||
// Remove the trailing slash by default
|
||||
trailingSlash: 'never',
|
||||
trailingSlash: 'never',
|
||||
|
||||
// Dev Server
|
||||
vite: {
|
||||
server: {
|
||||
watch: {
|
||||
vite: {
|
||||
server: {
|
||||
watch: {
|
||||
// Ignore some paths
|
||||
ignored: [],
|
||||
// support WSL strange things
|
||||
usePolling: !!process.env.WSL_DISTRO_NAME
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
// support WSL strange things
|
||||
usePolling: !!process.env.WSL_DISTRO_NAME
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
})
|
||||
|
||||
})
|
||||
|
199
hooks/routing.ts
Normal file
199
hooks/routing.ts
Normal file
@ -0,0 +1,199 @@
|
||||
import type { AstroIntegration } from 'astro'
|
||||
import fs from 'node:fs/promises'
|
||||
|
||||
const baseFile = `
|
||||
import { objectLoop } from '@dzeio/object-util'
|
||||
|
||||
/**
|
||||
* Format a Route with \`[param]\` elements with data in them
|
||||
*
|
||||
* limits: currently does not support \`[...param]\`
|
||||
*
|
||||
* @param url {string} the url to format
|
||||
* @param params {Record<string, string | number>} parameters to add to the URL (in [] first else in the query)
|
||||
*
|
||||
* @returns the URL formatted with the params
|
||||
*/
|
||||
export function formatRoute<T extends string>(url: T, params?: Record<string, string | number>): string {
|
||||
let result: string = url
|
||||
|
||||
// early return if there are no params
|
||||
if (!params) {
|
||||
return result
|
||||
}
|
||||
|
||||
// external queries for the URL
|
||||
let externalQueries = ''
|
||||
|
||||
// loop through the parameters
|
||||
objectLoop(params, (value, key) => {
|
||||
const search = \`[\${key}]\`
|
||||
value = encodeURI(value.toString())
|
||||
if (!result.includes(search)) {
|
||||
externalQueries += \`\${encodeURI(key)}=\${value}&\`
|
||||
} else {
|
||||
result = result.replace(search, value)
|
||||
}
|
||||
})
|
||||
|
||||
// add items to the external queries if they are set
|
||||
if (externalQueries) {
|
||||
externalQueries = '?' + externalQueries.slice(0, externalQueries.length - 1)
|
||||
}
|
||||
|
||||
return result + externalQueries
|
||||
}`.trim()
|
||||
|
||||
/**
|
||||
* Generate the file that contains every routes
|
||||
*
|
||||
* @param output the output file location
|
||||
*/
|
||||
async function updateRoutes(output: string, routes: Array<string>) {
|
||||
let file = baseFile
|
||||
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\treturn formatRoute(route, query)'
|
||||
file += '\n}\n'
|
||||
|
||||
await fs.writeFile(output, file)
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* format the path back to an url usable by the app
|
||||
*
|
||||
* @param path the path to format
|
||||
* @returns the path formatted as a URL
|
||||
*/
|
||||
function formatPath(basePath: string, path: string, removeExtension = true): string {
|
||||
// remove the base path
|
||||
path = path.replace(basePath, '')
|
||||
|
||||
// remove the extension if asked
|
||||
if (removeExtension) {
|
||||
const lastDot = path.lastIndexOf('.')
|
||||
path = path.slice(0, lastDot)
|
||||
}
|
||||
|
||||
// remove the index from the element
|
||||
if (path.endsWith('/index')) {
|
||||
path = path.replace('/index', '')
|
||||
}
|
||||
|
||||
// handle the `/` endpoint
|
||||
if (path === '') {
|
||||
path = '/'
|
||||
}
|
||||
|
||||
return path
|
||||
}
|
||||
|
||||
/**
|
||||
* get every files recursivelly in a specific directory
|
||||
*
|
||||
* @param path the path to search
|
||||
* @returns the list of files recursivelly in the specific directory
|
||||
*/
|
||||
async function getFiles(path: string): Promise<Array<string>> {
|
||||
const dir = await fs.readdir(path)
|
||||
let files: Array<string> = []
|
||||
for (const file of dir) {
|
||||
if (file.startsWith('_')) continue
|
||||
const filePath = path + '/' + file
|
||||
if ((await fs.stat(filePath)).isDirectory()) {
|
||||
files = files.concat(await getFiles(filePath))
|
||||
} else {
|
||||
files.push(filePath)
|
||||
}
|
||||
}
|
||||
return files
|
||||
}
|
||||
|
||||
let publicFolder!: string
|
||||
let srcFolder!: string
|
||||
let pagesFolder!: string
|
||||
let outputFile!: string
|
||||
|
||||
/**
|
||||
* launch the integration
|
||||
* @returns the routng integration
|
||||
*/
|
||||
const integration: () => AstroIntegration = () => ({
|
||||
name: 'Routing',
|
||||
hooks: {
|
||||
'astro:config:setup': async ({ config }) => {
|
||||
publicFolder = config.publicDir.toString().replace('file:///', '')
|
||||
srcFolder = config.srcDir.toString().replace('file:///', '')
|
||||
if (process.platform !== 'win32') {
|
||||
srcFolder = '/' + srcFolder
|
||||
publicFolder = '/' + publicFolder
|
||||
}
|
||||
pagesFolder = srcFolder + 'pages'
|
||||
outputFile = srcFolder + 'route.ts'
|
||||
|
||||
// get the files list
|
||||
const files = (await Promise.all([
|
||||
await getFiles(pagesFolder).then((ev) => ev.map((it) => formatPath(pagesFolder, it))),
|
||||
await getFiles(publicFolder).then((ev) => ev.map((it) => formatPath(publicFolder, it, false)))
|
||||
])).flat()
|
||||
await updateRoutes(outputFile, files)
|
||||
},
|
||||
'astro:server:setup': async ({ server }) => {
|
||||
|
||||
// get the files list
|
||||
const files = (await Promise.all([
|
||||
await getFiles(pagesFolder).then((ev) => ev.map((it) => formatPath(pagesFolder, it))),
|
||||
await getFiles(publicFolder).then((ev) => ev.map((it) => formatPath(publicFolder, it, false)))
|
||||
])).flat()
|
||||
|
||||
// watch FS changes for new files to add them to the route list
|
||||
server.watcher.on('add', (path) => {
|
||||
path = path.replace(/\\/g, '/')
|
||||
|
||||
// ignore files starting with '_'
|
||||
const filename = path.slice(path.lastIndexOf('/') + 1)
|
||||
if (filename.startsWith('_')) return
|
||||
|
||||
let removeExtension = true
|
||||
let folder = pagesFolder
|
||||
if(path.startsWith(publicFolder)) {
|
||||
removeExtension = false
|
||||
folder = publicFolder
|
||||
} else if (!path.startsWith(folder)) {
|
||||
return
|
||||
}
|
||||
|
||||
// format the path
|
||||
path = formatPath(folder, path, removeExtension)
|
||||
|
||||
// update the router
|
||||
files.push(path)
|
||||
updateRoutes(outputFile, files)
|
||||
})
|
||||
|
||||
// watch FS changes for removed files to remove them from the list
|
||||
server.watcher.on('unlink', (path) => {
|
||||
path = path.replace(/\\/g, '/')
|
||||
let removeExtension = true
|
||||
let folder = pagesFolder
|
||||
if(path.startsWith(publicFolder)) {
|
||||
removeExtension = false
|
||||
folder = publicFolder
|
||||
}
|
||||
path = formatPath(folder, path, removeExtension)
|
||||
|
||||
const index = files.indexOf(path)
|
||||
|
||||
files.splice(index, 1)
|
||||
updateRoutes(outputFile, files)
|
||||
})
|
||||
|
||||
// run the script once
|
||||
await updateRoutes(outputFile, files)
|
||||
}
|
||||
}
|
||||
})
|
||||
|
||||
export default integration
|
Loading…
x
Reference in New Issue
Block a user