Skip to content
39 changes: 39 additions & 0 deletions services/dash-badge-content-helpers.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
/**
* Helpers for parsing badge content from a dash seperated single parameter.
*
* @module
*/

/**
* Split string on dashses, but escape dash if double.
* Escape slash if double.
*
* @param {string} s - String to split.
* @returns {string[]} Parts of the string, split on unescaped dashes.
*/
function splitDashSeparatedOptionalParams(s) {
const parts = []
let cur = ''
for (let i = 0; i < s.length; ) {
const ch = s[i]
const next = s[i + 1]
if (ch === '-' && next === '-') {
cur += '-'
i += 2
} else if (ch === '/' && next === '/') {
cur += '/'
i += 2
} else if (ch === '-') {
parts.push(cur)
cur = ''
i += 1
} else {
cur += ch
i += 1
}
}
parts.push(cur)
return parts
}

export { splitDashSeparatedOptionalParams }
13 changes: 13 additions & 0 deletions services/dash-badge-content-helpers.spec.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
import { test, given } from 'sazerac'
import { splitDashSeparatedOptionalParams } from './dash-badge-content-helpers.js'

describe('Dash badge content helpers', function () {
test(splitDashSeparatedOptionalParams, () => {
given('foo-bar-baz').expect(['foo', 'bar', 'baz'])
given('foo--bar-baz').expect(['foo-bar', 'baz'])
given('foo-bar--baz').expect(['foo', 'bar-baz'])
given('foo--bar--baz').expect(['foo-bar-baz'])
given('foo//bar-baz').expect(['foo/bar', 'baz'])
given('foo//bar//baz').expect(['foo/bar/baz'])
})
})
12 changes: 12 additions & 0 deletions services/static-badge/static-badge-colon-redirect.service.js

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

this is undocumented, so i think its better as a redirect with its own path rather then adding logic to usage of badgeContent of the service

Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
import { redirector } from '../index.js'

export default redirector({
category: 'static',
name: 'StaticBadgeColonRedirect',
route: {
base: '',
pattern: '\\::badgeContent',
},
transformPath: ({ badgeContent }) => `/badge/${badgeContent}`,
dateAdded: new Date('2026-05-21'),
})
22 changes: 22 additions & 0 deletions services/static-badge/static-badge-colon-redirect.tester.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
import { createServiceTester } from '../tester.js'
export const t = await createServiceTester()

t.create('static badge colon redirect: basic redirect')
.get('/:label-message-blue')
.expectRedirect('/badge/label-message-blue.svg')

t.create('static badge colon redirect: with spaces and dash encoding')
.get('/:all%20one%20color-red')
.expectRedirect('/badge/all%20one%20color-red.svg')

t.create('static badge colon redirect: double dash and underscore encoding')
.get('/:best--license-Apache--2.0-blue')
.expectRedirect('/badge/best--license-Apache--2.0-blue.svg')

t.create('static badge colon redirect: missing label')
.get('/:-message-blue')
.expectRedirect('/badge/-message-blue.svg')

t.create('static badge colon redirect: missing message')
.get('/:label--blue')
.expectRedirect('/badge/label--blue.svg')
28 changes: 24 additions & 4 deletions services/static-badge/static-badge.service.js
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
import { escapeFormat } from '../../core/badge-urls/path-helpers.js'
import { InvalidParameter } from '../../core/base-service/errors.js'
import { BaseStaticService } from '../index.js'
import { splitDashSeparatedOptionalParams } from '../dash-badge-content-helpers.js'

const description = `
The static badge accepts a single required path parameter which encodes either:
Expand Down Expand Up @@ -61,8 +63,7 @@ export default class StaticBadge extends BaseStaticService {
static category = 'static'
static route = {
base: '',
format: '(?::|badge/)((?:[^-]|--)*?)-?((?:[^-]|--)*)-((?:[^-.]|--)+)',
capture: ['label', 'message', 'color'],
pattern: 'badge/:badgeContent',
}

static openApi = {
Expand All @@ -85,7 +86,26 @@ export default class StaticBadge extends BaseStaticService {
},
}

handle({ label, message, color }) {
return { label: escapeFormat(label), message: escapeFormat(message), color }
handle({ badgeContent }) {
const parts = splitDashSeparatedOptionalParams(badgeContent)
switch (parts.length) {
case 2: {
const [message, color] = parts
return { label: '', message: escapeFormat(message), color }
}
case 3: {
const [label, message, color] = parts
return {
label: escapeFormat(label),
message: escapeFormat(message),
color,
}
}
default:
throw new InvalidParameter({
prettyMessage:
'badgeContent must have either 2 or 3 dash-separated parts',
})
}
}
}
2 changes: 1 addition & 1 deletion services/static-badge/static-badge.tester.js
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@ t.create('Not a valid color')
})

t.create('Missing message')
.get('/badge/label--blue.json')
.get('/badge/label-%20-blue.json')

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is hardly in use, users get the exact same result using a space - - instead of --.
This was not consistent with our docs and logic of escaping -- => -. So i find the that confusing and think we should drop support. for label--color syntax in favor of using label- -color

.expectBadge({ label: 'label', message: '', color: 'blue' })

t.create('Missing label')
Expand Down
26 changes: 1 addition & 25 deletions services/website/website-redirect.service.js
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import { escapeFormat } from '../../core/badge-urls/path-helpers.js'
import { redirector } from '../index.js'
import { splitDashSeparatedOptionalParams } from '../dash-badge-content-helpers.js'

function escapeFormatSlashes(t) {
return (
Expand All @@ -9,31 +10,6 @@ function escapeFormatSlashes(t) {
)
}

function splitDashSeparatedOptionalParams(s) {

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is now a helper function shared with Website

const parts = []
let cur = ''
for (let i = 0; i < s.length; ) {
const ch = s[i]
const next = s[i + 1]
if (ch === '-' && next === '-') {
cur += '-'
i += 2
} else if (ch === '/' && next === '/') {
cur += '/'
i += 2
} else if (ch === '-') {
parts.push(cur)
cur = ''
i += 1
} else {
cur += ch
i += 1
}
}
parts.push(cur)
return parts
}

/*
Old documentation, for reference:

Expand Down
Loading