Skip to content

Commit

Permalink
fix(nextra): allow to contain dots in page filenames (#546)
Browse files Browse the repository at this point in the history
* fix(nextra): allow to contain dots in page filenames

* add changeset
  • Loading branch information
dimaMachina committed Jul 22, 2022
1 parent 4a7cc10 commit efd95ec
Show file tree
Hide file tree
Showing 24 changed files with 216 additions and 87 deletions.
5 changes: 5 additions & 0 deletions .changeset/two-dots-play.md
@@ -0,0 +1,5 @@
---
'nextra': patch
---

fix(nextra): allow to contain dots in page filenames
3 changes: 1 addition & 2 deletions examples/swr-site/next.config.js
Expand Up @@ -9,8 +9,7 @@ const withNextra = require("nextra")({

module.exports = withNextra({
i18n: {
// locales: ["en-US", "zh-CN", "es-ES", "ja", "ko", "ru"],
locales: ["en-US", "zh-CN"],
locales: ["en-US", "es-ES", "ja", "ko", "ru", "zh-CN"],
defaultLocale: "en-US",
},
redirects: () => {
Expand Down
@@ -0,0 +1 @@
# You can add dots in your file names
@@ -0,0 +1 @@
# Puede agregar puntos en los nombres de sus archivos
@@ -0,0 +1 @@
# ファイル名にドットを追加できます
@@ -0,0 +1 @@
# 파일 이름에 점을 추가할 수 있습니다.
@@ -0,0 +1 @@
# Вы можете добавить точки в имя своих файлов
@@ -0,0 +1 @@
# 您可以在文件名中添加点
3 changes: 2 additions & 1 deletion examples/swr-site/pages/docs/advanced/meta.en-US.json
Expand Up @@ -11,5 +11,6 @@
"cache": "Cache",
"performance": "Performance",
"react-native": "React Native",
"more": "More: A Super Super Super Super Long Directory"
"more": "More: A Super Super Super Super Long Directory",
"file-name.with.DOTS": "Filenames with dots"
}
3 changes: 2 additions & 1 deletion examples/swr-site/pages/docs/advanced/meta.es-ES.json
@@ -1,5 +1,6 @@
{
"cache": "Cache",
"performance": "Rendimiento",
"react-native": "React Native"
"react-native": "React Native",
"file-name.with.DOTS": "Nombres de archivos con puntos"
}
3 changes: 2 additions & 1 deletion examples/swr-site/pages/docs/advanced/meta.ja.json
@@ -1,5 +1,6 @@
{
"cache": "キャッシュ",
"performance": "パフォーマンス",
"react-native": "React Native"
"react-native": "React Native",
"file-name.with.DOTS": "ドット付きのファイル名"
}
3 changes: 2 additions & 1 deletion examples/swr-site/pages/docs/advanced/meta.ko.json
@@ -1,5 +1,6 @@
{
"cache": "캐시",
"performance": "성능",
"react-native": "React Native"
"react-native": "React Native",
"file-name.with.DOTS": "점이 있는 파일 이름"
}
3 changes: 2 additions & 1 deletion examples/swr-site/pages/docs/advanced/meta.ru.json
@@ -1,5 +1,6 @@
{
"cache": "Кеш",
"performance": "Производительность",
"react-native": "React Native"
"react-native": "React Native",
"file-name.with.DOTS": "Имя файла может содержать точки"
}
3 changes: 2 additions & 1 deletion examples/swr-site/pages/docs/advanced/meta.zh-CN.json
@@ -1,5 +1,6 @@
{
"cache": "缓存",
"performance": "性能",
"react-native": "React Native"
"react-native": "React Native",
"file-name.with.DOTS": "带点的文件名"
}
Expand Up @@ -47,6 +47,11 @@ exports[`Page Process > pageMap en-US 1`] = `
"name": "cache",
"route": "/docs/advanced/cache",
},
{
"locale": "en-US",
"name": "file-name.with.DOTS",
"route": "/docs/advanced/file-name.with.DOTS",
},
{
"locale": "en-US",
"meta": {
Expand All @@ -60,6 +65,7 @@ exports[`Page Process > pageMap en-US 1`] = `
"type": "separator",
},
"cache": "Cache",
"file-name.with.DOTS": "Filenames with dots",
"more": "More: A Super Super Super Super Long Directory",
"performance": "Performance",
"react-native": "React Native",
Expand Down Expand Up @@ -392,10 +398,16 @@ exports[`Page Process > pageMap zh-CN 1`] = `
"name": "cache",
"route": "/docs/advanced/cache",
},
{
"locale": "zh-CN",
"name": "file-name.with.DOTS",
"route": "/docs/advanced/file-name.with.DOTS",
},
{
"locale": "zh-CN",
"meta": {
"cache": "缓存",
"file-name.with.DOTS": "带点的文件名",
"performance": "性能",
"react-native": "React Native",
},
Expand Down
File renamed without changes.
108 changes: 108 additions & 0 deletions packages/nextra/__test__/utils.test.ts
@@ -0,0 +1,108 @@
import { describe, it, expect } from 'vitest'
import { parseFileName } from '../src/utils'

describe('Utils', () => {
describe('parseFileName()', () => {
describe('extension', () => {
it('parse different extensions', () => {
expect(parseFileName('./foo.bar.baz-qux.zh-CN.js')).toEqual({
name: 'foo.bar.baz-qux',
locale: 'zh-CN',
ext: '.js'
})
expect(parseFileName('MY_FOLDER/foo.bar.qux.mtsx')).toEqual({
name: 'foo.bar.qux',
locale: '',
ext: '.mtsx'
})
expect(parseFileName('docs-folder/foo.bar.ba-qu.zh-CN.json5')).toEqual({
name: 'foo.bar.ba-qu',
locale: 'zh-CN',
ext: '.json5'
})
expect(parseFileName('hello/foo.bar.ba-qu.txt')).toEqual({
name: 'foo.bar.ba-qu',
locale: '',
ext: '.txt'
})
})

it('parse no extension', () => {
expect(parseFileName('bazru-RU')).toEqual({
name: 'bazru-RU',
locale: '',
ext: ''
})
})
})

describe('locale', () => {
it('with 2 letters', () => {
expect(parseFileName('foo.ru.mdx')).toEqual({
name: 'foo',
locale: 'ru',
ext: '.mdx'
})
})

it('with 4 letters', () => {
expect(parseFileName('foo.en-US.jsx')).toEqual({
name: 'foo',
locale: 'en-US',
ext: '.jsx'
})
})

it('not with incorrect case', () => {
const fileNames = [
'foo.Ru.mdx',
'foo.rU.mdx',
'foo.RU.mdx',

'foo.Ru-ru.mdx',
'foo.Ru-Ru.mdx',
'foo.Ru-rU.mdx',
'foo.Ru-RU.mdx',

'foo.rU-ru.mdx',
'foo.rU-Ru.mdx',
'foo.rU-rU.mdx',
'foo.rU-RU.mdx',

'foo.RU-ru.mdx',
'foo.RU-Ru.mdx',
'foo.RU-rU.mdx',
'foo.RU-RU.mdx',

'foo.ru-ru.mdx',
'foo.ru-Ru.mdx',
'foo.ru-rU.mdx'
]

for (const fileName of fileNames) {
expect(parseFileName(fileName).locale).toBe('')
}
})

it('not without leading dot', () => {
expect(parseFileName('fooen-US.mdx')).toEqual({
name: 'fooen-US',
locale: '',
ext: '.mdx'
})
})

it('not with underscore', () => {
expect(parseFileName('foo.en_US.mdx').locale).toBe('')
})

it('not for 5 letters', () => {
expect(parseFileName('foo.en-USZ.json')).toEqual({
name: 'foo.en-USZ',
locale: '',
ext: '.json'
})
})
})
})
})
5 changes: 5 additions & 0 deletions packages/nextra/src/constants.ts
@@ -0,0 +1,5 @@
export const MARKDOWN_EXTENSION_REGEX = /\.mdx?$/

export const IS_PRODUCTION = process.env.NODE_ENV === 'production'

export const LOCALE_REGEX = /\.([a-z]{2}(-[A-Z]{2})?)$/
4 changes: 2 additions & 2 deletions packages/nextra/src/index.js
@@ -1,8 +1,8 @@
import { NextraPlugin, pageMapCache } from './plugin'
import { MARKDOWN_EXTENSION_REGEX } from './constants'

const defaultExtensions = ['js', 'jsx', 'ts', 'tsx']
const markdownExtensions = ['md', 'mdx']
const markdownExtensionTest = /\.mdx?$/

module.exports =
(...args) =>
Expand Down Expand Up @@ -38,7 +38,7 @@ module.exports =
}

config.module.rules.push({
test: markdownExtensionTest,
test: MARKDOWN_EXTENSION_REGEX,
use: [
options.defaultLoaders.babel,
{
Expand Down
18 changes: 8 additions & 10 deletions packages/nextra/src/loader.ts
Expand Up @@ -7,13 +7,11 @@ import { LoaderContext } from 'webpack'
import { Repository } from '@napi-rs/simple-git'

import { addPage } from './content-dump'
import { getLocaleFromFilename } from './utils'
import { parseFileName } from './utils'
import { compileMdx } from './compile'
import { getPageMap, findPagesDir } from './page-map'
import { collectFiles } from './plugin'

const extension = /\.mdx?$/
const isProductionBuild = process.env.NODE_ENV === 'production'
import { MARKDOWN_EXTENSION_REGEX, IS_PRODUCTION } from './constants'

// TODO: create this as a webpack plugin.
const indexContentEmitted = new Set()
Expand Down Expand Up @@ -55,15 +53,15 @@ async function loader(
return ''
}

const filename = resourcePath.slice(resourcePath.lastIndexOf('/') + 1)
const fileLocale = getLocaleFromFilename(filename)
const filename = path.basename(resourcePath)
const fileLocale = parseFileName(filename).locale

// Check if there's a theme provided
if (!theme) {
throw new Error('No Nextra theme found!')
}

const { items: pageMapResult, fileMap } = isProductionBuild
const { items: pageMapResult, fileMap } = IS_PRODUCTION
? pageMapCache.get()!
: await collectFiles(pagesDir, '/')

Expand All @@ -74,7 +72,7 @@ async function loader(
defaultLocale
)

if (!isProductionBuild) {
if (!IS_PRODUCTION) {
// Add the entire directory `pages` as the dependency
// so we can generate the correct page map.
context.addContextDependency(pagesDir)
Expand Down Expand Up @@ -106,7 +104,7 @@ async function loader(
layoutConfig = slash(path.resolve(layoutConfig))
}

if (isProductionBuild && indexContentEmitted.has(filename)) {
if (IS_PRODUCTION && indexContentEmitted.has(filename)) {
unstable_flexsearch = false
}

Expand All @@ -124,7 +122,7 @@ async function loader(

if (unstable_flexsearch) {
// We only add .MD and .MDX contents
if (extension.test(filename) && data.searchable !== false) {
if (MARKDOWN_EXTENSION_REGEX.test(filename) && data.searchable !== false) {
addPage({
fileLocale: fileLocale || 'default',
route,
Expand Down
7 changes: 2 additions & 5 deletions packages/nextra/src/locales.ts
@@ -1,8 +1,6 @@
import { NextResponse } from 'next/server'
import type { NextRequest } from 'next/server'

const PUBLIC_FILE = /\.(.*)$/

type LegacyMiddlewareCookies = { [key: string]: string }
type StableMiddlewareCookies = Map<string, string>

Expand All @@ -20,9 +18,8 @@ export function locales(request: NextRequest) {
const { nextUrl } = request

const shouldHandleLocale =
!PUBLIC_FILE.test(nextUrl.pathname) &&
!nextUrl.pathname.includes('/api/') &&
!nextUrl.pathname.includes('/_next/') &&
!/^\/(api|_next)\//.test(nextUrl.pathname) &&
!/\.(jpe?g|svg|png|webmanifest)$/.test(nextUrl.pathname) &&
nextUrl.locale !== ''

if (!shouldHandleLocale) return
Expand Down
28 changes: 13 additions & 15 deletions packages/nextra/src/page-map.ts
@@ -1,11 +1,9 @@
import { PageMapItem } from './types'
import { getLocaleFromFilename, existsSync } from './utils'
import { parseFileName, existsSync } from './utils'
import path from 'path'
import filterRouteLocale from './filter-route-locale'
export const extension = /\.mdx?$/
export const metaExtension = /meta\.?([a-zA-Z-]+)?\.json/

export function findPagesDir(dir: string = process.cwd()): string {
export function findPagesDir(dir = process.cwd()): string {
// prioritize ./pages over ./src/pages
if (existsSync(path.join(dir, 'pages'))) return 'pages'
if (existsSync(path.join(dir, 'src/pages'))) return 'src/pages'
Expand All @@ -17,24 +15,24 @@ export function findPagesDir(dir: string = process.cwd()): string {

export function getPageMap(
currentResourcePath: string,
pageMaps: PageMapItem[],
pageMap: PageMapItem[],
fileMap: Record<string, PageMapItem>,
defaultLocale: string
) {
const activeRouteLocale = getLocaleFromFilename(currentResourcePath)
): [PageMapItem[], string, string] {
const activeRouteLocale = parseFileName(currentResourcePath).locale
const pageItem = fileMap[currentResourcePath]
const metaPath = path.dirname(currentResourcePath)
const metaExtension = activeRouteLocale ? `${activeRouteLocale}.json` : `json`
const pageMeta =
fileMap[`${metaPath}/meta.${metaExtension}`]?.meta?.[pageItem.name]
const title =
(typeof pageMeta === 'string' ? pageMeta : pageMeta?.title) || pageItem.name
if (activeRouteLocale) {
return [
filterRouteLocale(pageMaps, activeRouteLocale, defaultLocale),
fileMap[currentResourcePath].route,
title
]
}
return [pageMaps, fileMap[currentResourcePath].route, title]

return [
activeRouteLocale
? filterRouteLocale(pageMap, activeRouteLocale, defaultLocale)
: pageMap,
pageItem.route,
title
]
}

0 comments on commit efd95ec

Please sign in to comment.