Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Feat/kit routes improvements #412

Merged
merged 4 commits into from
Nov 5, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions .changeset/fifty-ants-argue.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
'vite-plugin-kit-routes': patch
---

fix group routes management
5 changes: 5 additions & 0 deletions .changeset/popular-bears-tan.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
'vite-plugin-kit-routes': patch
---

manage optional params
5 changes: 5 additions & 0 deletions .changeset/wet-insects-retire.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
'vite-plugin-kit-routes': patch
---

manage matchers
38 changes: 25 additions & 13 deletions packages/vite-plugin-kit-routes/src/lib/ROUTES.ts
Original file line number Diff line number Diff line change
@@ -1,52 +1,64 @@
export const PAGES = {
_ROOT: (sp?: Record<string, string>) => {
_ROOT: (sp?: Record<string, string | number>) => {
return `/${appendSp(sp)}`
},
contract: (sp?: Record<string, string>) => {
contract: (sp?: Record<string, string | number>) => {
return `/contract${appendSp(sp)}`
},
contract_id: (params: { id: string }, sp?: Record<string, string>) => {
contract_id: (params: { id: string | number }, sp?: Record<string, string | number>) => {
return `/contract/${params.id}${appendSp(sp)}`
},
site: (sp?: Record<string, string>) => {
gp_logged_one: (sp?: Record<string, string | number>) => {
return `/gp/one${appendSp(sp)}`
},
gp_public_two: (sp?: Record<string, string | number>) => {
return `/gp/two${appendSp(sp)}`
},
lang_lang: (params?: { lang?: string | number }, sp?: Record<string, string | number>) => {
return `/lang/${params?.lang ?? ''}${appendSp(sp)}`
},
match_id_int: (params: { id: string | number }, sp?: Record<string, string | number>) => {
return `/match/${params.id}${appendSp(sp)}`
},
site: (sp?: Record<string, string | number>) => {
return `/site${appendSp(sp)}`
},
site_id: (params: { id: string }, sp?: Record<string, string>) => {
site_id: (params: { id: string | number }, sp?: Record<string, string | number>) => {
return `/site/${params.id}${appendSp(sp)}`
},
site_contract_siteId_contractId: (
params: { siteId: string; contractId: string },
sp?: Record<string, string>,
params: { siteId: string | number; contractId: string | number },
sp?: Record<string, string | number>,
) => {
return `/site_contract/${params.siteId}-${params.contractId}${appendSp(sp)}`
},
}

export const SERVERS = {
contract: (method: 'GET' | 'POST', sp?: Record<string, string>) => {
contract: (method: 'GET' | 'POST', sp?: Record<string, string | number>) => {
return `/contract${appendSp(sp)}`
},
site: (method: 'GET', sp?: Record<string, string>) => {
site: (method: 'GET', sp?: Record<string, string | number>) => {
return `/site${appendSp(sp)}`
},
}

export const ACTIONS = {
contract_id: (params: { id: string }) => {
contract_id: (params: { id: string | number }) => {
return `/contract/${params.id}`
},
site: (action: 'action1' | 'action2') => {
return `/site?/${action}`
},
site_contract_siteId_contractId: (
action: 'sendSomething',
params: { siteId: string; contractId: string },
params: { siteId: string | number; contractId: string | number },
) => {
return `/site_contract/${params.siteId}-${params.contractId}?/${action}`
},
}

const appendSp = (sp?: Record<string, string>) => {
const appendSp = (sp?: Record<string, string | number>) => {
if (sp === undefined) return ''
return `?${new URLSearchParams(sp || {}).toString()}`
return `?${new URLSearchParams((sp as Record<string, string>) || {}).toString()}`
}
119 changes: 71 additions & 48 deletions packages/vite-plugin-kit-routes/src/lib/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,7 @@ export function formatKey(key: string, options?: Options) {
return key
}

const toReplace = ['/', '[', ']', '(', ')', '-']
const toReplace = ['/', '[', ']', '(', ')', '-', '=']
let toRet = key
.split('')
.map(c => (toReplace.includes(c) ? '_' : c))
Expand Down Expand Up @@ -74,6 +74,7 @@ const log = new Log('Kit Routes')
const getFileKeys = (
lookFor: '+page.svelte' | '+page.server.ts' | '+server.ts',
options?: Options,
withAppendSp?: boolean,
) => {
const files = readdirSync(routes_path(), { recursive: true }) as string[]
return (
Expand All @@ -83,29 +84,56 @@ const getFileKeys = (
// Keep the sorting at this level, it will make more sense
.sort()
.map(file => {
return { toUse: formatKey(file, options), original: file }
const paramsFromPath = extractParamsFromPath(file)
const href = file.replace(/\([^)]*\)/g, '').replace(/\/+/g, '/')
let toRet = href
paramsFromPath.params.forEach(c => {
const sMatcher = `${c.matcher ? `=${c.matcher}` : ''}`
if (c.optional) {
toRet = toRet.replaceAll(`[[${c.name + sMatcher}]]`, `\${params?.${c.name} ?? ''}`)
} else {
toRet = toRet.replaceAll(`[${c.name + sMatcher}]`, `\${params.${c.name}}`)
}
})
return {
toUse: formatKey(file, options),
original: file,
paramsFromPath,
toReturn: `${toRet}${withAppendSp ? `\${appendSp(sp)}` : ``}`,
}
})
)
}

function formatExtractParamsFromPath(file_path: string) {
return extractParamsFromPath(file_path).map(c => `${c}: string`)
}

export function extractParamsFromPath(path: string): string[] {
// Use a regular expression to match parameter placeholders like '[param]'
const paramPattern = /\[([^\]]+)]/g
export function extractParamsFromPath(path: string): {
params: { name: string; optional: boolean; matcher?: string }[]
formatArgs: string[]
isAllOptional: boolean
} {
const paramPattern = /\[+([^\]]+)]+/g
const params = []

let match
while ((match = paramPattern.exec(path)) !== null) {
// The matched parameter name is in the first capturing group
params.push(match[1])
// Check if it's surrounded by double brackets indicating an optional parameter
const isOptional = match[0].startsWith('[[')
const matcher = match[1].split('=')
if (matcher.length === 2) {
params.push({
// name: `${matcher[0]}_${matcher[1]}`,
name: matcher[0],
optional: isOptional,
matcher: matcher[1],
})
} else {
params.push({ name: match[1], optional: isOptional })
}
}

return params
const format = params.map(c => `${c.name}${c.optional ? '?' : ''}: string | number`)
const isAllOptional = params.filter(c => !c.optional).length === 0
return { params, formatArgs: format, isAllOptional }
}

const getMethodsOfServerFiles = (path: string) => {
const code = read(`${routes_path()}/${path}/${'+server.ts'}`)

Expand Down Expand Up @@ -164,55 +192,51 @@ const getActionsOfServerPages = (path: string) => {
}

const run = (options?: Options) => {
const pages = getFileKeys('+page.svelte', options)
const server = getFileKeys('+page.server.ts', options)
const files_server = getFileKeys('+server.ts', options)
const pages = getFileKeys('+page.svelte', options, true)
const servers = getFileKeys('+server.ts', options, true)
const pages_server = getFileKeys('+page.server.ts', options)

const result = write(generated_file_path(options), [
`export const PAGES = {
${pages
.map(key => {
const params = []
const pFormPath = formatExtractParamsFromPath(key.original)
if (pFormPath.length > 0) {
params.push(`params: {${pFormPath}}`)
if (key.paramsFromPath.params.length > 0) {
params.push(
`params${key.paramsFromPath.isAllOptional ? '?' : ''}: {${
key.paramsFromPath.formatArgs
}}`,
)
}
params.push(`sp?: Record<string, string>`)
return (
`"${key.toUse}": (${params.join(', ')}) => ` +
`{ return \`${key.original
.replaceAll('[', '${params.')
.replaceAll(']', '}')}\${appendSp(sp)}\` }`
)
params.push(`sp?: Record<string, string | number>`)
return `"${key.toUse}": (${params.join(', ')}) => { return \`${key.toReturn}\` }`
})
.join(',\n ')}
}

export const SERVERS = {
${files_server
${servers
.map(key => {
const params = []
const methods = getMethodsOfServerFiles(key.original)
if (methods.length > 0) {
params.push(`method: ${methods.map(c => `'${c}'`).join(' | ')}`)
}
const pFormPath = formatExtractParamsFromPath(key.original)
if (pFormPath.length > 0) {
params.push(`params: {${pFormPath}}`)
if (key.paramsFromPath.params.length > 0) {
params.push(
`params${key.paramsFromPath.isAllOptional ? '?' : ''}: {${
key.paramsFromPath.formatArgs
}}`,
)
}
params.push(`sp?: Record<string, string>`)
return (
`"${key.toUse}": (${params.join(', ')}) => ` +
`{ return \`${key.original
.replaceAll('[', '${params.')
.replaceAll(']', '}')}\${appendSp(sp)}\` }`
)
params.push(`sp?: Record<string, string | number>`)
return `"${key.toUse}": (${params.join(', ')}) => { return \`${key.toReturn}\` }`
})
.join(',\n ')}
}

export const ACTIONS = {
${server
${pages_server
.map(key => {
const params = []
const actions = getActionsOfServerPages(key.original)
Expand All @@ -222,26 +246,25 @@ export const ACTIONS = {
} else {
params.push(`action: ${actions.map(c => `'${c}'`).join(' | ')}`)
actionsFormat = `?/\${action}`
// actionsFormat = `coucou`
}
const pFormPath = formatExtractParamsFromPath(key.original)
if (pFormPath.length > 0) {
params.push(`params: {${pFormPath}}`)
if (key.paramsFromPath.params.length > 0) {
params.push(
`params${key.paramsFromPath.isAllOptional ? '?' : ''}: {${
key.paramsFromPath.formatArgs
}}`,
)
}
return (
`"${key.toUse}": (${params.join(', ')}) => ` +
`{ return \`` +
`${key.original.replaceAll('[', '${params.').replaceAll(']', '}')}` +
`${actionsFormat}` +
`\`}`
` { return \`${key.toReturn}${actionsFormat}\` }`
)
})
.join(',\n ')}
}

const appendSp = (sp?: Record<string, string>) => {
const appendSp = (sp?: Record<string, string | number>) => {
if (sp === undefined) return ''
return \`?\${new URLSearchParams(sp || {}).toString()}\`
return \`?\${new URLSearchParams(sp as Record<string, string> || {}).toString()}\`
}
`,
])
Expand Down
80 changes: 68 additions & 12 deletions packages/vite-plugin-kit-routes/src/lib/plugins.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,28 +4,84 @@ import { extractParamsFromPath, formatKey } from './index.js'
describe('vite-plugin-kit-routes', () => {
it('get id', async () => {
expect(extractParamsFromPath('/site/[id]')).toMatchInlineSnapshot(`
[
"id",
]
{
"formatArgs": [
"id: string | number",
],
"isAllOptional": false,
"params": [
{
"name": "id",
"optional": false,
},
],
}
`)
})

it('get params & id', async () => {
expect(extractParamsFromPath('/site/[param]/[id]')).toMatchInlineSnapshot(`
[
"param",
"id",
]
{
"formatArgs": [
"param: string | number",
"id: string | number",
],
"isAllOptional": false,
"params": [
{
"name": "param",
"optional": false,
},
{
"name": "id",
"optional": false,
},
],
}
`)
})

it('get params & id', async () => {
expect(extractParamsFromPath('/[param]site/[yop](group)/[id]')).toMatchInlineSnapshot(`
[
"param",
"yop",
"id",
]
{
"formatArgs": [
"param: string | number",
"yop: string | number",
"id: string | number",
],
"isAllOptional": false,
"params": [
{
"name": "param",
"optional": false,
},
{
"name": "yop",
"optional": false,
},
{
"name": "id",
"optional": false,
},
],
}
`)
})

it('get optional param', async () => {
expect(extractParamsFromPath('/lang/[[lang]]')).toMatchInlineSnapshot(`
{
"formatArgs": [
"lang?: string | number",
],
"isAllOptional": true,
"params": [
{
"name": "lang",
"optional": true,
},
],
}
`)
})

Expand Down
5 changes: 5 additions & 0 deletions packages/vite-plugin-kit-routes/src/params/int.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
import type { ParamMatcher } from '@sveltejs/kit'

export const match: ParamMatcher = param => {
return /^\d+$/.test(param)
}
Loading