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(gatsby-plugin-page-creator): Add slugify option #28485

Merged
merged 6 commits into from
Dec 7, 2020
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
12 changes: 8 additions & 4 deletions packages/gatsby-page-utils/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -3,10 +3,12 @@
"version": "0.6.0-next.0",
"description": "Gatsby library that helps creating pages",
"main": "dist/index.js",
"types": "dist/index.d.ts",
"scripts": {
"build": "babel src --out-dir dist/ --ignore \"**/__tests__\" --extensions \".ts,.js\"",
"watch": "babel -w src --out-dir dist/ --ignore \"**/__tests__\" --extensions \".ts,.js\"",
"prepare": "cross-env NODE_ENV=production npm run build"
"build": "babel src --out-dir dist/ --ignore \"**/__tests__\" --extensions \".ts\"",
"typegen": "rimraf \"dist/**/*.d.ts\" && tsc --emitDeclarationOnly --declaration --declarationDir dist/",
"watch": "babel -w src --out-dir dist/ --ignore \"**/__tests__\" --extensions \".ts\"",
"prepare": "cross-env NODE_ENV=production npm run build && npm run typegen"
},
"keywords": [
"gatsby"
Expand Down Expand Up @@ -34,7 +36,9 @@
"@babel/core": "^7.12.3",
"@types/micromatch": "^4.0.1",
"babel-preset-gatsby-package": "^0.9.0-next.0",
"cross-env": "^7.0.2"
"cross-env": "^7.0.2",
"rimraf": "^3.0.2",
"typescript": "^3.9.7"
},
"files": [
"dist/"
Expand Down
2 changes: 1 addition & 1 deletion packages/gatsby-page-utils/src/ignore-path.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import { isMatch, Options as mmOptions } from "micromatch"

interface IPathIgnoreOptions {
export interface IPathIgnoreOptions {
patterns?: string | ReadonlyArray<string>
options?: mmOptions
}
Expand Down
2 changes: 1 addition & 1 deletion packages/gatsby-page-utils/src/index.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
export { validatePath } from "./validate-path"
export { createPath } from "./create-path"
export { ignorePath } from "./ignore-path"
export { ignorePath, IPathIgnoreOptions } from "./ignore-path"
export { watchDirectory } from "./watch-directory"
35 changes: 28 additions & 7 deletions packages/gatsby-plugin-page-creator/README.md
Original file line number Diff line number Diff line change
@@ -1,9 +1,9 @@
# gatsby-plugin-page-creator

Gatsby plugin that automatically creates pages from React components in specified directories. Gatsby
includes this plugin automatically in all sites for creating pages from components in `src/pages`. You can also leverage the [File System Route API](#TODO) to programmatically create pages from your data.
includes this plugin automatically in all sites for creating pages from components in `src/pages`. You can also leverage the [File System Route API](https://www.gatsbyjs.com/docs/file-system-route-api/) to programmatically create pages from your data.

You may include another instance of this plugin if you'd like to create additional "pages" directories.
You may include another instance of this plugin if you'd like to create additional "pages" directories or want to override the default usage.

With this plugin, _any_ file that lives in the specified pages folder (e.g. the default `src/pages`) or subfolders will be expected to export a React Component to generate a Page. The following files are automatically excluded:

Expand All @@ -25,6 +25,8 @@ To exclude custom patterns, see [Ignoring Specific Files](#ignoring-specific-fil

## How to use

Add the plugin to your `gatsby-config.js`:

```javascript
// gatsby-config.js

Expand All @@ -47,17 +49,36 @@ module.exports = {
path: `${__dirname}/src/settings/pages`,
},
},
// You can also overwrite the default behavior for src/pages
// This changes the page-creator instance used by Gatsby
{
resolve: `gatsby-plugin-page-creator`,
options: {
path: `${__dirname}/src/pages`,
ignore: [`foo-bar.js`],
},
},
],
}
```

## Options

The plugin supports options to ignore files and to pass options to the [`slugify`](https://github.com/sindresorhus/slugify) instance that is used in the File System Route API to create slugs.

| Option | Type | Description | Required |
| ------- | ---------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------ | -------- |
| path | `string` | Any file that lives inside this directory will be expected to export a React component to generate a page | true |
| ignore | `IPathIgnoreOptions ∣ string ∣ Array<string> ∣ null` | Ignore certain files inside the directory specified with `path` | false |
| slugify | `ISlugifyOptions` | Pass [options](https://github.com/sindresorhus/slugify#options) to the `slugify` instance that is used inside the File System Route API to generate the slug | false |

### Ignoring Specific Files

#### Shorthand

```javascript
// The following example will disable the `/blog` index page
The following example will disable the `/blog` index page:

```javascript
// gatsby-config.js
module.exports = {
plugins: [
Expand All @@ -75,13 +96,13 @@ module.exports = {
```

**NOTE**: The above code snippet will only stop the creation of the `/blog` page, which is defined as a React component.
This plugin does not affect programmatically generated pages from the [createPagesAPI](https://www.gatsbyjs.org/docs/node-apis/#createPages).
This plugin does not affect programmatically generated pages from the [createPages](https://www.gatsbyjs.com/docs/node-apis#createPages) API.

#### Ignore Options

```javascript
// The following example will ignore pages using case-insensitive matching
The following example will ignore pages using case-insensitive matching:

```javascript
// gatsby-config.js
module.exports = {
plugins: [
Expand Down
53 changes: 53 additions & 0 deletions packages/gatsby-plugin-page-creator/src/__tests__/derive-path.ts
Original file line number Diff line number Diff line change
Expand Up @@ -312,4 +312,57 @@ describe(`derive-path`, () => {
).derivedPath
).toEqual(`foo/muenchner-weisswuerstchen`)
})

it(`supports custom slugify options`, () => {
expect(
derivePath(
`foo/{Model.name}`,
{
name: `BAR and baz`,
},
reporter,
{ separator: `_` }
).derivedPath
).toEqual(`foo/bar_and_baz`)
expect(
derivePath(
`foo/{Model.name}`,
{
name: `Déjà Vu!`,
},
reporter,
{ lowercase: false }
).derivedPath
).toEqual(`foo/Deja-Vu`)
expect(
derivePath(
`foo/{Model.name}`,
{
name: `fooBar`,
},
reporter,
{ decamelize: false }
).derivedPath
).toEqual(`foo/foobar`)
expect(
derivePath(
`foo/{Model.name}`,
{
name: `this-is`,
},
reporter,
{ customReplacements: [[`this-is`, `the-way`]] }
).derivedPath
).toEqual(`foo/the-way`)
expect(
derivePath(
`foo/{Model.name}`,
{
name: `_foo_bar`,
},
reporter,
{ preserveLeadingUnderscore: true }
).derivedPath
).toEqual(`foo/_foo-bar`)
})
})
16 changes: 12 additions & 4 deletions packages/gatsby-plugin-page-creator/src/create-page-wrapper.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,11 @@
import { Actions, CreatePagesArgs } from "gatsby"
import { createPath, validatePath, ignorePath } from "gatsby-page-utils"
import {
createPath,
validatePath,
ignorePath,
IPathIgnoreOptions,
} from "gatsby-page-utils"
import { Options as ISlugifyOptions } from "@sindresorhus/slugify"
import { createClientOnlyPage } from "./create-client-only-page"
import { createPagesFromCollectionBuilder } from "./create-pages-from-collection-builder"
import systemPath from "path"
Expand All @@ -18,9 +24,10 @@ export function createPage(
filePath: string,
pagesDirectory: string,
actions: Actions,
ignore: Array<string>,
graphql: CreatePagesArgs["graphql"],
reporter: Reporter
reporter: Reporter,
ignore?: IPathIgnoreOptions | string | Array<string> | null,
slugifyOptions?: ISlugifyOptions
): void {
// Filter out special components that shouldn't be made into
// pages.
Expand All @@ -43,7 +50,8 @@ export function createPage(
absolutePath,
actions,
graphql,
reporter
reporter,
slugifyOptions
)
return
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
import { Actions, CreatePagesArgs } from "gatsby"
import { createPath } from "gatsby-page-utils"
import { Reporter } from "gatsby"
import { Options as ISlugifyOptions } from "@sindresorhus/slugify"
import { reverseLookupParams } from "./extract-query"
import { getMatchPath } from "./get-match-path"
import { getCollectionRouteParams } from "./get-collection-route-params"
Expand All @@ -11,13 +12,13 @@ import { collectionExtractQueryString } from "./collection-extract-query-string"
import { isValidCollectionPathImplementation } from "./is-valid-collection-path-implementation"
import { CODES, prefixId } from "./error-utils"

// TODO: Do we need the ignore argument?
Copy link
Contributor Author

Choose a reason for hiding this comment

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

export async function createPagesFromCollectionBuilder(
filePath: string,
absolutePath: string,
actions: Actions,
graphql: CreatePagesArgs["graphql"],
reporter: Reporter
reporter: Reporter,
slugifyOptions?: ISlugifyOptions
): Promise<void> {
if (isValidCollectionPathImplementation(absolutePath, reporter) === false) {
watchCollectionBuilder(absolutePath, ``, [], actions, reporter, () =>
Expand All @@ -26,7 +27,8 @@ export async function createPagesFromCollectionBuilder(
absolutePath,
actions,
graphql,
reporter
reporter,
slugifyOptions
)
)
return
Expand All @@ -43,7 +45,8 @@ export async function createPagesFromCollectionBuilder(
absolutePath,
actions,
graphql,
reporter
reporter,
slugifyOptions
)
)
return
Expand Down Expand Up @@ -78,7 +81,8 @@ ${errors.map(error => error.message).join(`\n`)}`.trim(),
absolutePath,
actions,
graphql,
reporter
reporter,
slugifyOptions
)
)

Expand All @@ -105,7 +109,12 @@ ${errors.map(error => error.message).join(`\n`)}`.trim(),
// the watcher will use this data to delete the pages if the query changes significantly.
const paths = nodes.map((node: Record<string, object>) => {
// URL path for the component and node
const { derivedPath, errors } = derivePath(filePath, node, reporter)
const { derivedPath, errors } = derivePath(
filePath,
node,
reporter,
slugifyOptions
)
const path = createPath(derivedPath)
// Params is supplied to the FE component on props.params
const params = getCollectionRouteParams(createPath(filePath), path)
Expand Down Expand Up @@ -150,7 +159,8 @@ ${errors.map(error => error.message).join(`\n`)}`.trim(),
absolutePath,
actions,
graphql,
reporter
reporter,
slugifyOptions
)
)
}
14 changes: 9 additions & 5 deletions packages/gatsby-plugin-page-creator/src/derive-path.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import _ from "lodash"
import slugify from "@sindresorhus/slugify"
import slugify, { Options as ISlugifyOptions } from "@sindresorhus/slugify"
import { Reporter } from "gatsby"
import {
extractFieldWithoutUnion,
Expand All @@ -17,7 +17,8 @@ const doubleForwardSlashes = /\/\/+/g
export function derivePath(
path: string,
node: Record<string, any>,
reporter: Reporter
reporter: Reporter,
slugifyOptions?: ISlugifyOptions
): { errors: number; derivedPath: string } {
// 0. Since this function will be called for every path times count of nodes the errors will be counted and then the calling function will throw the error once
let errors = 0
Expand Down Expand Up @@ -54,7 +55,7 @@ export function derivePath(
}

// 3.d Safely slugify all values (to keep URL structures) and remove any trailing slash
const value = stripTrailingSlash(safeSlugify(nodeValue))
const value = stripTrailingSlash(safeSlugify(nodeValue, slugifyOptions))

// 3.e replace the part of the slug with the actual value
modifiedPath = modifiedPath.replace(slugPart, value)
Expand All @@ -74,10 +75,13 @@ export function derivePath(
// If the node value is meant to be a slug, like `foo/bar`, the slugify
// function will remove the slashes. This is a hack to make sure the slashes
// stick around in the final url structuring
function safeSlugify(nodeValue: string): string {
function safeSlugify(
nodeValue: string,
slugifyOptions?: ISlugifyOptions
): string {
// The incoming GraphQL data can also be a number
const input = String(nodeValue)
const tempArr = input.split(`/`)

return tempArr.map(v => slugify(v)).join(`/`)
return tempArr.map(v => slugify(v, slugifyOptions)).join(`/`)
}
Loading