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

Underscore Handling Fixes #47581

Merged
merged 6 commits into from Mar 28, 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.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
24 changes: 1 addition & 23 deletions packages/next/src/build/webpack/loaders/metadata/discover.ts
Expand Up @@ -5,34 +5,12 @@ import type {
} from './types'
import path from 'path'
import { stringify } from 'querystring'
import { STATIC_METADATA_IMAGES } from '../../../../lib/metadata/is-metadata-route'

const METADATA_TYPE = 'metadata'

export const METADATA_RESOURCE_QUERY = '?__next_metadata'

export const STATIC_METADATA_IMAGES = {
icon: {
filename: 'icon',
extensions: ['ico', 'jpg', 'jpeg', 'png', 'svg'],
},
apple: {
filename: 'apple-icon',
extensions: ['jpg', 'jpeg', 'png'],
},
favicon: {
filename: 'favicon',
extensions: ['ico'],
},
openGraph: {
filename: 'opengraph-image',
extensions: ['jpg', 'jpeg', 'png', 'gif'],
},
twitter: {
filename: 'twitter-image',
extensions: ['jpg', 'jpeg', 'png', 'gif'],
},
} as const

// Produce all compositions with filename (icon, apple-icon, etc.) with extensions (png, jpg, etc.)
async function enumMetadataFiles(
dir: string,
Expand Down
23 changes: 22 additions & 1 deletion packages/next/src/lib/metadata/is-metadata-route.ts
@@ -1,4 +1,25 @@
import { STATIC_METADATA_IMAGES } from '../../build/webpack/loaders/metadata/discover'
export const STATIC_METADATA_IMAGES = {
icon: {
filename: 'icon',
extensions: ['ico', 'jpg', 'jpeg', 'png', 'svg'],
},
apple: {
filename: 'apple-icon',
extensions: ['jpg', 'jpeg', 'png'],
},
favicon: {
filename: 'favicon',
extensions: ['ico'],
},
openGraph: {
filename: 'opengraph-image',
extensions: ['jpg', 'jpeg', 'png', 'gif'],
},
twitter: {
filename: 'twitter-image',
extensions: ['jpg', 'jpeg', 'png', 'gif'],
},
} as const

// Match routes that are metadata routes, e.g. /sitemap.xml, /favicon.<ext>, /<icon>.<ext>, etc.
// TODO-METADATA: support more metadata routes with more extensions
Expand Down
2 changes: 1 addition & 1 deletion packages/next/src/server/dev/next-dev-server.ts
Expand Up @@ -555,7 +555,7 @@ export default class DevServer extends Server {
continue
}
// Ignore files/directories starting with `_` in the app directory
if (normalizePathSep(fileName).includes('/_')) {
if (normalizePathSep(pageName).includes('/_')) {
Copy link
Member Author

Choose a reason for hiding this comment

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

We use the pageName instead of the fileName because the fileName includes path parts outside the Next.js directory.

continue
}

Expand Down
Expand Up @@ -18,8 +18,8 @@ export class AbsoluteFilenameNormalizer implements Normalizer {
private readonly pagesType: 'pages' | 'app' | 'root'
) {}

public normalize(pathname: string): string {
return absolutePathToPage(pathname, {
public normalize(filename: string): string {
return absolutePathToPage(filename, {
extensions: this.extensions,
keepIndex: false,
dir: this.dir,
Expand Down
@@ -0,0 +1,28 @@
import { Normalizers } from '../../normalizers'
import { Normalizer } from '../../normalizer'
import { PrefixingNormalizer } from '../../prefixing-normalizer'

export class AppBundlePathNormalizer extends PrefixingNormalizer {
constructor() {
super('app')
}

public normalize(page: string): string {
return super.normalize(page)
}
}

export class DevAppBundlePathNormalizer extends Normalizers {
constructor(pageNormalizer: Normalizer) {
super([
// This should normalize the filename to a page.
pageNormalizer,
// Normalize the app page to a pathname.
new AppBundlePathNormalizer(),
])
}

public normalize(filename: string): string {
return super.normalize(filename)
}
}
@@ -0,0 +1,12 @@
import { SERVER_DIRECTORY } from '../../../../../shared/lib/constants'
import { PrefixingNormalizer } from '../../prefixing-normalizer'

export class AppFilenameNormalizer extends PrefixingNormalizer {
constructor(distDir: string) {
super(distDir, SERVER_DIRECTORY)
}

public normalize(manifestFilename: string): string {
return super.normalize(manifestFilename)
}
}
@@ -0,0 +1,11 @@
import { AbsoluteFilenameNormalizer } from '../../absolute-filename-normalizer'

/**
* DevAppPageNormalizer is a normalizer that is used to normalize a pathname
* to a page in the `app` directory.
*/
export class DevAppPageNormalizer extends AbsoluteFilenameNormalizer {
constructor(appDir: string, extensions: ReadonlyArray<string>) {
super(appDir, extensions, 'app')
}
}
@@ -0,0 +1,36 @@
import { normalizeAppPath } from '../../../../../shared/lib/router/utils/app-paths'
import { Normalizers } from '../../normalizers'
import { wrapNormalizerFn } from '../../wrap-normalizer-fn'
import { UnderscoreNormalizer } from '../../underscore-normalizer'
import { Normalizer } from '../../normalizer'

export class AppPathnameNormalizer extends Normalizers {
constructor() {
super([
// The pathname to match should have the trailing `/page` and other route
// group information stripped from it.
wrapNormalizerFn(normalizeAppPath),
// The page should have the `%5F` characters replaced with `_` characters.
new UnderscoreNormalizer(),
])
}

public normalize(page: string): string {
return super.normalize(page)
}
}

export class DevAppPathnameNormalizer extends Normalizers {
constructor(pageNormalizer: Normalizer) {
super([
// This should normalize the filename to a page.
pageNormalizer,
// Normalize the app page to a pathname.
new AppPathnameNormalizer(),
])
}

public normalize(filename: string): string {
return super.normalize(filename)
}
}
34 changes: 34 additions & 0 deletions packages/next/src/server/future/normalizers/built/app/index.ts
@@ -0,0 +1,34 @@
import {
AppBundlePathNormalizer,
DevAppBundlePathNormalizer,
} from './app-bundle-path-normalizer'
import { AppFilenameNormalizer } from './app-filename-normalizer'
import { DevAppPageNormalizer } from './app-page-normalizer'
import {
AppPathnameNormalizer,
DevAppPathnameNormalizer,
} from './app-pathname-normalizer'

export class AppNormalizers {
public readonly filename: AppFilenameNormalizer
public readonly pathname: AppPathnameNormalizer
public readonly bundlePath: AppBundlePathNormalizer

constructor(distDir: string) {
this.filename = new AppFilenameNormalizer(distDir)
this.pathname = new AppPathnameNormalizer()
this.bundlePath = new AppBundlePathNormalizer()
}
}

export class DevAppNormalizers {
public readonly page: DevAppPageNormalizer
public readonly pathname: DevAppPathnameNormalizer
public readonly bundlePath: DevAppBundlePathNormalizer

constructor(appDir: string, extensions: ReadonlyArray<string>) {
this.page = new DevAppPageNormalizer(appDir, extensions)
this.pathname = new DevAppPathnameNormalizer(this.page)
this.bundlePath = new DevAppBundlePathNormalizer(this.page)
}
}
@@ -0,0 +1,33 @@
import {
DevPagesBundlePathNormalizer,
PagesBundlePathNormalizer,
} from './pages-bundle-path-normalizer'
import { PagesFilenameNormalizer } from './pages-filename-normalizer'
import { DevPagesPageNormalizer } from './pages-page-normalizer'
import { DevPagesPathnameNormalizer } from './pages-pathname-normalizer'

export class PagesNormalizers {
public readonly filename: PagesFilenameNormalizer
public readonly bundlePath: PagesBundlePathNormalizer

constructor(distDir: string) {
this.filename = new PagesFilenameNormalizer(distDir)
this.bundlePath = new PagesBundlePathNormalizer()

// You'd think that we'd require a `pathname` normalizer here, but for
// `/pages` we have to handle i18n routes, which means that we need to
// analyze the page path to determine the locale prefix and it's locale.
}
}

export class DevPagesNormalizers {
public readonly page: DevPagesPageNormalizer
public readonly pathname: DevPagesPathnameNormalizer
public readonly bundlePath: DevPagesBundlePathNormalizer

constructor(pagesDir: string, extensions: ReadonlyArray<string>) {
this.page = new DevPagesPageNormalizer(pagesDir, extensions)
this.pathname = new DevPagesPathnameNormalizer(pagesDir, extensions)
this.bundlePath = new DevPagesBundlePathNormalizer(this.page)
}
}
@@ -0,0 +1,36 @@
import { normalizePagePath } from '../../../../../shared/lib/page-path/normalize-page-path'
import { Normalizer } from '../../normalizer'
import { Normalizers } from '../../normalizers'
import { PrefixingNormalizer } from '../../prefixing-normalizer'
import { wrapNormalizerFn } from '../../wrap-normalizer-fn'

export class PagesBundlePathNormalizer extends Normalizers {
constructor() {
super([
// The bundle path should have the trailing `/index` stripped from
// it.
wrapNormalizerFn(normalizePagePath),
// The page should prefixed with `pages/`.
new PrefixingNormalizer('pages'),
])
}

public normalize(page: string): string {
return super.normalize(page)
}
}

export class DevPagesBundlePathNormalizer extends Normalizers {
constructor(pagesNormalizer: Normalizer) {
super([
// This should normalize the filename to a page.
pagesNormalizer,
// Normalize the app page to a pathname.
new PagesBundlePathNormalizer(),
])
}

public normalize(filename: string): string {
return super.normalize(filename)
}
}
@@ -0,0 +1,12 @@
import { SERVER_DIRECTORY } from '../../../../../shared/lib/constants'
import { PrefixingNormalizer } from '../../prefixing-normalizer'

export class PagesFilenameNormalizer extends PrefixingNormalizer {
constructor(distDir: string) {
super(distDir, SERVER_DIRECTORY)
}

public normalize(manifestFilename: string): string {
return super.normalize(manifestFilename)
}
}
@@ -0,0 +1,7 @@
import { AbsoluteFilenameNormalizer } from '../../absolute-filename-normalizer'

export class DevPagesPageNormalizer extends AbsoluteFilenameNormalizer {
constructor(pagesDir: string, extensions: ReadonlyArray<string>) {
super(pagesDir, extensions, 'pages')
}
}
@@ -0,0 +1,7 @@
import { AbsoluteFilenameNormalizer } from '../../absolute-filename-normalizer'

export class DevPagesPathnameNormalizer extends AbsoluteFilenameNormalizer {
constructor(pagesDir: string, extensions: ReadonlyArray<string>) {
super(pagesDir, extensions, 'pages')
}
}
@@ -1,8 +1,12 @@
import path from 'path'
import path from '../../../shared/lib/isomorphic/path'
import { Normalizer } from './normalizer'

export class PrefixingNormalizer implements Normalizer {
constructor(private readonly prefix: string) {}
private readonly prefix: string

constructor(...prefixes: ReadonlyArray<string>) {
this.prefix = path.posix.join(...prefixes)
}

public normalize(pathname: string): string {
return path.posix.join(this.prefix, pathname)
Expand Down
@@ -0,0 +1,10 @@
import { Normalizer } from './normalizer'

/**
* UnderscoreNormalizer replaces all instances of %5F with _.
*/
export class UnderscoreNormalizer implements Normalizer {
Copy link
Contributor

Choose a reason for hiding this comment

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

Java-ism strikes again 😂

public normalize(pathname: string): string {
return pathname.replace(/%5F/g, '_')
}
}