generated from avior/template-web-astro
chore: up to date to latest standards
This commit is contained in:
66
hooks/eslint-plugin/index.js
Normal file
66
hooks/eslint-plugin/index.js
Normal file
@ -0,0 +1,66 @@
|
||||
export default {
|
||||
rules: {
|
||||
'use-logger': {
|
||||
meta: {
|
||||
type: 'problem',
|
||||
docs: {
|
||||
description: 'Disallow use of console.log',
|
||||
category: 'Best Practices',
|
||||
recommended: true
|
||||
},
|
||||
messages: {
|
||||
noConsoleLog: "Using 'console.log' is not recommended for usage, use instead `Logger.info()` from `libs/Logger`."
|
||||
},
|
||||
schema: [] // No options for this rule
|
||||
},
|
||||
create(context) {
|
||||
return {
|
||||
MemberExpression(node) {
|
||||
if (
|
||||
node.object.name === 'console' &&
|
||||
node.property.name === 'log'
|
||||
) {
|
||||
context.report({
|
||||
node,
|
||||
messageId: 'noConsoleLog'
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
'prefer-text-content': {
|
||||
meta: {
|
||||
type: 'suggestion',
|
||||
docs: {
|
||||
description: 'Prefer textContent over innerText',
|
||||
category: 'Best Practices',
|
||||
recommended: false,
|
||||
},
|
||||
fixable: 'code',
|
||||
schema: [],
|
||||
messages: {
|
||||
useTextContent: "Use 'textContent' instead of 'innerText'.",
|
||||
},
|
||||
},
|
||||
create(context) {
|
||||
return {
|
||||
MemberExpression(node) {
|
||||
if (
|
||||
node.property &&
|
||||
node.property.name === 'innerText'
|
||||
) {
|
||||
context.report({
|
||||
node,
|
||||
messageId: 'useTextContent',
|
||||
fix(fixer) {
|
||||
return fixer.replaceText(node.property, 'textContent')
|
||||
}
|
||||
})
|
||||
}
|
||||
},
|
||||
}
|
||||
},
|
||||
}
|
||||
}
|
||||
}
|
25
hooks/test.ts
Normal file
25
hooks/test.ts
Normal file
@ -0,0 +1,25 @@
|
||||
import type { AstroIntegration, AstroIntegrationLogger } from 'astro'
|
||||
|
||||
const setup = (step: keyof AstroIntegration['hooks']) => (ctx: { logger: AstroIntegrationLogger }) => {
|
||||
ctx.logger.info(step)
|
||||
}
|
||||
|
||||
const integration: AstroIntegration = {
|
||||
name: 'hooks',
|
||||
hooks: {
|
||||
'astro:config:setup': setup('astro:config:setup'),
|
||||
'astro:config:done': setup('astro:config:done'),
|
||||
'astro:server:setup': setup('astro:server:setup'),
|
||||
'astro:server:start': setup('astro:server:start'),
|
||||
'astro:server:done': setup('astro:server:done'),
|
||||
'astro:build:start': setup('astro:build:start'),
|
||||
'astro:build:setup': setup('astro:build:setup'),
|
||||
'astro:build:generated': setup('astro:build:generated'),
|
||||
'astro:build:ssr': setup('astro:build:ssr'),
|
||||
'astro:build:done': setup('astro:build:done'),
|
||||
'astro:route:setup': setup('astro:route:setup'),
|
||||
'astro:route:resolved': setup('astro:route:resolved')
|
||||
}
|
||||
}
|
||||
|
||||
export default integration
|
117
hooks/typesafe-api.ts
Normal file
117
hooks/typesafe-api.ts
Normal file
@ -0,0 +1,117 @@
|
||||
import { objectMap, objectRemap } from '@dzeio/object-util'
|
||||
import type { AstroIntegration } from 'astro'
|
||||
import fs from 'fs/promises'
|
||||
|
||||
interface Config {
|
||||
output?: string
|
||||
}
|
||||
|
||||
/**
|
||||
* 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>> {
|
||||
path = decodeURI(path)
|
||||
try {
|
||||
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 if (file.endsWith('.ts')) {
|
||||
files.push(filePath)
|
||||
}
|
||||
}
|
||||
return files
|
||||
} catch {
|
||||
return []
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 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): string {
|
||||
// remove the base path
|
||||
path = path.replace(decodeURI(basePath), '')
|
||||
|
||||
// handle the `/` endpoint
|
||||
if (path === '') {
|
||||
path = '/'
|
||||
}
|
||||
|
||||
return path
|
||||
}
|
||||
|
||||
async function run(config?: Config) {
|
||||
|
||||
// get the files list
|
||||
const files = await getFiles('./src/pages/api').then((ev) => ev.map((it) => formatPath('./src/pages/api', it)))
|
||||
|
||||
const methods = ['GET', 'POST', 'DELETE']
|
||||
|
||||
let content = ''
|
||||
const items: Record<string, Record<string, string>> = {}
|
||||
for (const entry of files) {
|
||||
const file = await fs.readFile('./src/pages/api' + entry, 'utf-8')
|
||||
const availableMethods: Array<string> = []
|
||||
for (const method of methods) {
|
||||
if (file.includes(method)) {
|
||||
availableMethods.push(method)
|
||||
}
|
||||
}
|
||||
if (availableMethods.length === 0) {
|
||||
continue
|
||||
}
|
||||
const prefix = entry.replace(/[/.[\]-]/g, '')
|
||||
content += `import type { ${availableMethods.map((it) => `${it} as ${prefix}${it}`).join((', '))} } from './pages/api${entry}'\n`
|
||||
|
||||
|
||||
let path = entry
|
||||
// remove the extension if asked
|
||||
const lastDot = path.lastIndexOf('.')
|
||||
path = path.slice(0, lastDot)
|
||||
|
||||
// remove the index from the element
|
||||
if (path.endsWith('/index')) {
|
||||
path = path.replace('/index', '')
|
||||
}
|
||||
|
||||
items[path] = {
|
||||
...objectRemap(availableMethods, (value) => ({ key: value as string, value: `${prefix}${value as string}` }))
|
||||
}
|
||||
}
|
||||
|
||||
content += `\ninterface APIRoutes {
|
||||
${objectMap(items, (record, key) => `\t'${key}': {
|
||||
${objectMap(record, (value, method) => ` ${method}: typeof ${value}`).join('\n')}
|
||||
}`).join('\n')}
|
||||
}`
|
||||
|
||||
content += '\n\nexport default APIRoutes\n'
|
||||
|
||||
await fs.writeFile(config?.output ?? './src/api-routes.d.ts', content)
|
||||
}
|
||||
|
||||
/**
|
||||
* launch the integration
|
||||
* @returns the routng integration
|
||||
*/
|
||||
const integration = (config?: Config): AstroIntegration => ({
|
||||
name: 'routing',
|
||||
hooks: {
|
||||
'astro:build:setup': () => run(config),
|
||||
'astro:server:setup': () => run(config)
|
||||
}
|
||||
})
|
||||
|
||||
export default integration
|
45
hooks/version.ts
Normal file
45
hooks/version.ts
Normal file
@ -0,0 +1,45 @@
|
||||
import type { AstroIntegration } from 'astro'
|
||||
import { exec as execSync } from 'node:child_process'
|
||||
import { promisify } from 'node:util'
|
||||
|
||||
const exec = promisify(execSync)
|
||||
|
||||
/**
|
||||
* launch the integration
|
||||
* @returns the routng integration
|
||||
*/
|
||||
const integration: () => AstroIntegration = () => ({
|
||||
name: 'version',
|
||||
hooks: {
|
||||
'astro:config:setup': async (setup) => {
|
||||
try {
|
||||
const commit = (await exec('git rev-parse HEAD')).stdout
|
||||
const branch = (await exec('git rev-parse --abbrev-ref HEAD')).stdout
|
||||
const tag = (await exec('git tag -l --points-at HEAD')).stdout
|
||||
const envs: Record<string, string> = {}
|
||||
if (commit) {
|
||||
envs['import.meta.env.GIT_COMMIT'] = JSON.stringify(commit.slice(0, 8).trim())
|
||||
}
|
||||
if (branch) {
|
||||
envs['import.meta.env.GIT_BRANCH'] = JSON.stringify(branch.trim())
|
||||
}
|
||||
if (tag) {
|
||||
envs['import.meta.env.GIT_TAG'] = JSON.stringify(tag.trim())
|
||||
}
|
||||
|
||||
setup.updateConfig({
|
||||
vite: {
|
||||
define: {
|
||||
...envs
|
||||
}
|
||||
}
|
||||
})
|
||||
} catch (error: any) {
|
||||
setup.logger.warn(error.toString())
|
||||
setup.logger.warn('could not setup GIT envs')
|
||||
}
|
||||
}
|
||||
}
|
||||
})
|
||||
|
||||
export default integration
|
Reference in New Issue
Block a user