Skip to content

Commit

Permalink
feat(gatsby-plugin-page-creator): Add slugify option (#28485)
Browse files Browse the repository at this point in the history
  • Loading branch information
LekoArts authored Dec 7, 2020
1 parent 493fd73 commit 721f42a
Show file tree
Hide file tree
Showing 9 changed files with 166 additions and 43 deletions.
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?
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

0 comments on commit 721f42a

Please sign in to comment.