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(build): allow modifying slugify options #2898

Merged
merged 2 commits into from
Dec 9, 2024
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
15 changes: 15 additions & 0 deletions docs/content/docs/1.getting-started/3.configuration.md
Original file line number Diff line number Diff line change
Expand Up @@ -234,6 +234,21 @@ export default defineNuxtConfig({

Read more about adding languages in the [Shiki documentation](https://github.com/shikijs/shiki/blob/main/docs/languages.md#adding-grammar).

## `pathMeta`

Content module uses files path to generate the slug, default title and content order, you can customize this behavior with `pathMeta` option.

### `pathMeta.forceLeadingSlash`

If set to `true`, the path will be prefixed with a leading slash. Default value is `true`.


### `pathMeta.slugifyOptions`

Content module uses [slugify](https://github.com/simov/slugify) to generate the slug, you can customize the behavior of slugify with this option.

Checkout [slugify options](https://github.com/simov/slugify#options) for more information.

## `database`

By default Nuxt Content uses a local SQLite database to store and query content. If you like to use another database or you plan to deploy on Cloudflare Workers, you can modify this option.
Expand Down
1 change: 1 addition & 0 deletions src/module.ts
Original file line number Diff line number Diff line change
Expand Up @@ -66,6 +66,7 @@ export default defineNuxtModule<ModuleOptions>({
},
},
build: {
pathMeta: {},
markdown: {},
yaml: {},
csv: {
Expand Down
2 changes: 2 additions & 0 deletions src/types/module.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ import type { BuiltinLanguage as ShikiLang, BuiltinTheme as ShikiTheme, Language
import type { ListenOptions } from 'listhen'
import type { GitInfo } from '../utils/git'
import type { MarkdownPlugin } from './content'
import type { PathMetaOptions } from './path-meta'

export interface D1DatabaseConfig {
type: 'd1'
Expand Down Expand Up @@ -152,6 +153,7 @@ export interface ModuleOptions {
themes?: (ShikiTheme | ThemeRegistrationAny)[]
}
}
pathMeta: PathMetaOptions
/**
* Options for yaml parser.
*
Expand Down
33 changes: 33 additions & 0 deletions src/types/path-meta.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
export interface SlugifyOptions {
/**
* Characters to remove from the slug
*
* @default undefined
*/
remove?: RegExp
replacement?: string
/**
* Convert the slug to lowercase
*
* @default true
*/
lower?: boolean
strict?: boolean
locale?: string
trim?: boolean
}

export interface PathMetaOptions {
/**
* If set to `true`, the path will be prefixed with a leading slash.
*
* @default true
*/
forceLeadingSlash?: boolean
/**
* Slugify options
*
* @see https://github.com/simov/slugify#options
*/
slugifyOptions?: SlugifyOptions
}
13 changes: 7 additions & 6 deletions src/utils/content/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -99,26 +99,27 @@ async function _getHighlightPlugin(options: HighlighterOptions) {

export async function parseContent(key: string, content: string, collection: ResolvedCollection, nuxt?: Nuxt) {
const mdcOptions = (nuxt?.options as unknown as { mdc: MDCModuleOptions })?.mdc || {}
const contentOptions = (nuxt?.options as unknown as { content: ModuleOptions })?.content?.build?.markdown || {}
const { pathMeta = {}, markdown = {} } = (nuxt?.options as unknown as { content: ModuleOptions })?.content?.build || {}

const rehypeHighlightPlugin = contentOptions.highlight !== false
? await getHighlightPluginInstance(defu(contentOptions.highlight as HighlighterOptions, mdcOptions.highlight, { compress: true }))
const rehypeHighlightPlugin = markdown.highlight !== false
? await getHighlightPluginInstance(defu(markdown.highlight as HighlighterOptions, mdcOptions.highlight, { compress: true }))
: undefined

const parsedContent = await transformContent(key, content, {
pathMeta: pathMeta,
markdown: {
compress: true,
...mdcOptions,
...contentOptions,
...markdown,
rehypePlugins: {
highlight: rehypeHighlightPlugin,
...mdcOptions?.rehypePlugins,
...contentOptions?.rehypePlugins,
...markdown?.rehypePlugins,
},
remarkPlugins: {
'remark-emoji': {},
...mdcOptions?.remarkPlugins,
...contentOptions?.remarkPlugins,
...markdown?.remarkPlugins,
},
highlight: undefined,
},
Expand Down
16 changes: 10 additions & 6 deletions src/utils/content/transformers/path-meta.ts
Original file line number Diff line number Diff line change
@@ -1,22 +1,26 @@
import { pascalCase } from 'scule'
import slugify from 'slugify'
import { withoutTrailingSlash, withLeadingSlash } from 'ufo'
import defu from 'defu'
import type { PathMetaOptions } from '../../../types/path-meta'
import { defineTransformer } from './utils'

const SEMVER_REGEX = /^\d+(?:\.\d+)*(?:\.x)?$/

interface PathMetaOptions {
respectPathCase?: boolean
const defaultOptions: PathMetaOptions = {
slugifyOptions: {
lower: true,
},
}

export default defineTransformer({
name: 'path-meta',
extensions: ['.*'],
transform(content, options: PathMetaOptions = {}) {
const { respectPathCase = false } = options
const opts = defu(options, defaultOptions)
const { basename, extension, stem } = describeId(content.id)
// Check first part for locale name
const filePath = generatePath(stem, { respectPathCase })
const filePath = generatePath(stem, opts)

return {
path: filePath,
Expand All @@ -34,8 +38,8 @@ export default defineTransformer({
* @param path file full path
* @returns generated slug
*/
export const generatePath = (path: string, { forceLeadingSlash = true, respectPathCase = false } = {}): string => {
path = path.split('/').map(part => slugify(refineUrlPart(part), { lower: !respectPathCase })).join('/')
export const generatePath = (path: string, { forceLeadingSlash = true, slugifyOptions = {} } = {}): string => {
path = path.split('/').map(part => slugify(refineUrlPart(part), slugifyOptions)).join('/')
return forceLeadingSlash ? withLeadingSlash(withoutTrailingSlash(path)) : path
}

Expand Down
7 changes: 7 additions & 0 deletions test/unit/parseContent.path-meta.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -71,6 +71,13 @@ const testCases = {
extension: 'md',
stem: 'indexer.draft',
},
'content/فرهنگ/فارسی/فرهنگ.md': {
__description: 'Handle special chars in file name',
title: 'فرهنگ',
path: '/frhng/farsy/frhng',
extension: 'md',
stem: 'فرهنگ/فارسی/فرهنگ',
},
}

describe('Transformer (path-meta)', () => {
Expand Down
Loading