Skip to content

Commit

Permalink
feat: slugify filter from Jekyll, #443
Browse files Browse the repository at this point in the history
  • Loading branch information
harttle committed May 13, 2024
1 parent 50253a9 commit 47ddc11
Show file tree
Hide file tree
Showing 8 changed files with 215 additions and 4 deletions.
1 change: 1 addition & 0 deletions docs/source/_data/sidebar.yml
Original file line number Diff line number Diff line change
Expand Up @@ -82,6 +82,7 @@ filters:
shift: shift.html
size: size.html
slice: slice.html
slugify: slugify.html
sort: sort.html
sort_natural: sort_natural.html
split: split.html
Expand Down
2 changes: 1 addition & 1 deletion docs/source/filters/overview.md
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ Categories | Filters
--- | ---
Math | plus, minus, modulo, times, floor, ceil, round, divided_by, abs, at_least, at_most
String | append, prepend, capitalize, upcase, downcase, strip, lstrip, rstrip, strip_newlines, split, replace, replace_first, replace_last,remove, remove_first, remove_last, truncate, truncatewords, normalize_whitespace, number_of_words, array_to_sentence_string
HTML/URI | escape, escape_once, url_encode, url_decode, strip_html, newline_to_br, xml_escape, cgi_escape, uri_escape
HTML/URI | escape, escape_once, url_encode, url_decode, strip_html, newline_to_br, xml_escape, cgi_escape, uri_escape, slugify
Array | slice, map, sort, sort_natural, uniq, where, where_exp, group_by, group_by_exp, find, find_exp, first, last, join, reverse, concat, compact, size, push, pop, shift, unshift
Date | date, date_to_xmlschema, date_to_rfc822, date_to_string, date_to_long_string
Misc | default, json, jsonify, inspect, raw, to_integer
Expand Down
61 changes: 61 additions & 0 deletions docs/source/filters/slugify.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
---
title: slugify
---

{% since %}v10.13.0{% endsince %}

Convert a string into a lowercase URL "slug". The slugify filter accepts 2 options:

1. `mode: string`. The default is `"default"`. They are as follows (with what they filter):
- `"none"`: no characters
- `"raw"`: spaces
- `"default"`: spaces and non-alphanumeric characters
- `"pretty"`: spaces and non-alphanumeric characters except for `._~!$&'()+,;=@`
- `"ascii"`: spaces, non-alphanumeric, and non-ASCII characters
- `"latin"`: like default, except Latin characters are first transliterated (e.g. àèïòü to aeiou).
2. `case: boolean`. The default is `false`. The original case of slug will be retained if set to `true`.

Input
```liquid
{{ "The _config.yml file" | slugify }}
```
Output
```
the-config-yml-file
```

Input
```liquid
{{ "The _config.yml file" | slugify: "pretty" }}
```
Output
```
the-_config.yml-file
```

Input
```liquid
{{ "The _cönfig.yml file" | slugify: "ascii" }}
```
Output
```
the-c-nfig-yml-file
```

Input
```liquid
{{ "The cönfig.yml file" | slugify: "latin" }}
```
Output
```
the-config-yml-file
```

Input
```liquid
{{ "The cönfig.yml file" | slugify: "latin", true }}
```
Output
```
The-config-yml-file
```
2 changes: 1 addition & 1 deletion docs/source/zh-cn/filters/overview.md
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ LiquidJS 共支持 40+ 个过滤器,可以分为如下几类:
--- | ---
数学 | plus, minus, modulo, times, floor, ceil, round, divided_by, abs, at_least, at_most
字符串 | append, prepend, capitalize, upcase, downcase, strip, lstrip, rstrip, strip_newlines, split, replace, replace_first, replace_last, remove, remove_first, remove_last, truncate, truncatewords, normalize_whitespace, number_of_words, array_to_sentence_string
HTML/URI | escape, escape_once, url_encode, url_decode, strip_html, newline_to_br, xml_escape, cgi_escape, uri_escape
HTML/URI | escape, escape_once, url_encode, url_decode, strip_html, newline_to_br, xml_escape, cgi_escape, uri_escape, slugify
数组 | slice, map, sort, sort_natural, uniq, where, where_exp, group_by, group_by_exp, find, find_exp, first, last, join, reverse, concat, compact, size, push, pop, shift, unshift
日期 | date, date_to_xmlschema, date_to_rfc822, date_to_string, date_to_long_string
其他 | default, json, jsonify, inspect, raw, to_integer
Expand Down
59 changes: 59 additions & 0 deletions docs/source/zh-cn/filters/slugify.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
---
title: slugify
---

将字符串转换为小写的 URL “slug”。`slugify` 过滤器接受两个选项:

1. `mode: string`。默认为`"default"`,它可选的值如下:
- `"none"`:没有字符
- `"raw"`:空格
- `"default"`:空格和非字母数字字符
- `"pretty"`:空格和非字母数字字符,但排除 `._~!$&'()+,;=@`
- `"ascii"`:空格、非字母数字和非 ASCII 字符
- `"latin"`:与默认相同,但拉丁字符首先进行音译(例如,àèïòü 转换为 aeiou)。
2. `case: boolean`。默认为 `false`。如果为 `true`,则保留 `slug` 原本的大小写。

输入
```liquid
{{ "The _config.yml file" | slugify }}
```
输出
```
the-config-yml-file
```

输入
```liquid
{{ "The _config.yml file" | slugify: "pretty" }}
```
输出
```
the-_config.yml-file
```

输入
```liquid
{{ "The _cönfig.yml file" | slugify: "ascii" }}
```
输出
```
the-c-nfig-yml-file
```

输入
```liquid
{{ "The cönfig.yml file" | slugify: "latin" }}
```
输出
```
the-config-yml-file
```

输入
```liquid
{{ "The cönfig.yml file" | slugify: "latin", true }}
```
输出
```
The-config-yml-file
```
4 changes: 2 additions & 2 deletions docs/themes/navy/source/css/_variables.styl
Original file line number Diff line number Diff line change
Expand Up @@ -36,8 +36,8 @@ vendor-prefixes = webkit moz ms official
font-sans = "Helvetica Neue", Helvetica, Arial, sans-serif
font-serif = Garamond, Georgia, "Times New Roman", serif
font-mono = "Source Code Pro", Monaco, Menlo, Consolas, monospace
font-size = 15px
line-height = 1.6em
font-size = 16px
line-height = 1.8em

// Layout
max-width = 1800px
Expand Down
41 changes: 41 additions & 0 deletions src/filters/url.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,3 +8,44 @@ export const cgi_escape = (x: string) => encodeURIComponent(stringify(x))
export const uri_escape = (x: string) => encodeURI(stringify(x))
.replace(/%5B/g, '[')
.replace(/%5D/g, ']')

const rSlugifyDefault = /[^\p{M}\p{L}\p{Nd}]+/ug
const rSlugifyReplacers = {
'raw': /\s+/g,
'default': rSlugifyDefault,
'pretty': /[^\p{M}\p{L}\p{Nd}._~!$&'()+,;=@]+/ug,
'ascii': /[^A-Za-z0-9]+/g,
'latin': rSlugifyDefault,
'none': null
}

export function slugify (str: string, mode: keyof typeof rSlugifyReplacers = 'default', cased = false): string {
str = stringify(str)

const replacer = rSlugifyReplacers[mode]
if (replacer) {
if (mode === 'latin') str = removeAccents(str)
str = str.replace(replacer, '-').replace(/^-|-$/g, '')
}

return cased ? str : str.toLowerCase()
}

function removeAccents (str: string): string {
return str.replace(/[àáâãäå]/g, 'a')
.replace(/[æ]/g, 'ae')
.replace(/[ç]/g, 'c')
.replace(/[èéêë]/g, 'e')
.replace(/[ìíîï]/g, 'i')
.replace(/[ð]/g, 'd')
.replace(/[ñ]/g, 'n')
.replace(/[òóôõöø]/g, 'o')
.replace(/[ùúûü]/g, 'u')
.replace(/[ýÿ]/g, 'y')
.replace(/[ß]/g, 'ss')
.replace(/[œ]/g, 'oe')
.replace(/[þ]/g, 'th')
.replace(/[ẞ]/g, 'SS')
.replace(/[Œ]/g, 'OE')
.replace(/[Þ]/g, 'TH')
}
49 changes: 49 additions & 0 deletions test/integration/filters/url.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -42,4 +42,53 @@ describe('filters/url', () => {
expect(html).toEqual(reserved)
})
})

describe('slugify', () => {
describe('slugify', () => {
it('should slugify with default mode', () => {
const html = liquid.parseAndRenderSync('{{ "The _config.yml file" | slugify }}')
expect(html).toEqual('the-config-yml-file')
})

it('should slugify with pretty mode', () => {
const html = liquid.parseAndRenderSync('{{ "The _config.yml file" | slugify: "pretty" }}')
expect(html).toEqual('the-_config.yml-file')
})

it('should slugify with ascii mode', () => {
const html = liquid.parseAndRenderSync('{{ "The _cönfig.yml file" | slugify: "ascii" }}')
expect(html).toEqual('the-c-nfig-yml-file')
})

it('should slugify with latin mode', () => {
const html = liquid.parseAndRenderSync('{{ "The cönfig.yml file" | slugify: "latin" }}')
expect(html).toEqual('the-config-yml-file')
})

it('should slugify with none mode', () => {
const html = liquid.parseAndRenderSync('{{ "The _config.yml file" | slugify: "none" }}')
expect(html).toEqual('the _config.yml file')
})

it('should slugify with invalid mode', () => {
const html = liquid.parseAndRenderSync('{{ "The _config.yml file" | slugify: "invalid_mode" }}')
expect(html).toEqual('the _config.yml file')
})

it('should slugify with empty string', () => {
const html = liquid.parseAndRenderSync('{{ "" | slugify }}')
expect(html).toEqual('')
})

it('should slugify with cased=false', () => {
const html = liquid.parseAndRenderSync('{{ "Test String" | slugify: "pretty", false }}')
expect(html).toEqual('test-string')
})

it('should slugify with cased=true', () => {
const html = liquid.parseAndRenderSync('{{ "Test String" | slugify: "pretty", true }}')
expect(html).toEqual('Test-String')
})
})
})
})

0 comments on commit 47ddc11

Please sign in to comment.