{"id":13618768,"url":"https://github.com/modiimedia/contentful-hugo","last_synced_at":"2025-09-10T22:39:11.984Z","repository":{"id":37821097,"uuid":"180189330","full_name":"modiimedia/contentful-hugo","owner":"modiimedia","description":"A CLI tool that pulls data from Contentful and turns it into markdown files for Hugo and other static site generators. It also includes an express server that can be used for local development and content previews","archived":false,"fork":false,"pushed_at":"2024-04-24T00:59:28.000Z","size":4134,"stargazers_count":38,"open_issues_count":1,"forks_count":9,"subscribers_count":3,"default_branch":"master","last_synced_at":"2024-04-25T00:48:34.669Z","etag":null,"topics":["contentful","headless-cms","hugo","jamstack","javascript","static-site","static-site-generator"],"latest_commit_sha":null,"homepage":"","language":"TypeScript","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":null,"status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/modiimedia.png","metadata":{"files":{"readme":"readme.md","changelog":null,"contributing":null,"funding":null,"license":null,"code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null}},"created_at":"2019-04-08T16:27:09.000Z","updated_at":"2024-05-31T20:31:59.181Z","dependencies_parsed_at":"2024-04-24T00:28:30.862Z","dependency_job_id":"9b945693-2ec0-4efb-bb29-961ac13b188b","html_url":"https://github.com/modiimedia/contentful-hugo","commit_stats":{"total_commits":634,"total_committers":8,"mean_commits":79.25,"dds":"0.35962145110410093","last_synced_commit":"b12396a4d6eaff1956719a53113bdf60a24b2781"},"previous_names":[],"tags_count":34,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/modiimedia%2Fcontentful-hugo","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/modiimedia%2Fcontentful-hugo/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/modiimedia%2Fcontentful-hugo/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/modiimedia%2Fcontentful-hugo/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/modiimedia","download_url":"https://codeload.github.com/modiimedia/contentful-hugo/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":238777879,"owners_count":19528813,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["contentful","headless-cms","hugo","jamstack","javascript","static-site","static-site-generator"],"created_at":"2024-08-01T21:00:30.282Z","updated_at":"2025-09-10T22:39:11.976Z","avatar_url":"https://github.com/modiimedia.png","language":"TypeScript","funding_links":[],"categories":[":star: Awesome Utilities"],"sub_categories":[],"readme":"# Contentful Hugo\n\n[![Codacy Badge](https://app.codacy.com/project/badge/Grade/c24aacec450c44c1a81ac78d234838b0)](https://www.codacy.com/gh/ModiiMedia/contentful-hugo/dashboard?utm_source=github.com\u0026utm_medium=referral\u0026utm_content=ModiiMedia/contentful-hugo\u0026utm_campaign=Badge_Grade)\n\nThis is a CLI tool that pulls data from Contentful CMS and turns it into Markdown or YAML files for use with a static site generator. It can be used with any static site generator that uses Markdown with YAML frontmatter, but it has some features that are specific to [Hugo](https://gohugo.io). It also includes a simple Express server that can can recieve webhooks from Contentful to retrigger get and delete commands (useful when running a preview environment).\n\n![Screenshot of Contentful Hugo](screenshot.png)\n\n## Features\n\n- Outputs to Markdown, YAML, or JSON files\n- Singleton support\n- Rich text field support\n- Multilingual support\n- Default shortcodes for rich text content\n- Automatic asset field resolution\n- Customizable linked entry resolution\n- Content filters\n- Content Preview API support\n- Field name and field value overrides\n- Custom field support\n- Server mode to receive webhook triggers from Contentful\n\n## Table of Contents\n\n- [Prerequisites](#prerequisites)\n- [Installation](#installation)\n- [Usage](#usage)\n- [Configuration](#configuration)\n    - [Environenment Variables](#environment-variables)\n    - [Config File(s)](#config-file)\n        - [Example JS Config](#example-javascript-config)\n        - [Example YAML Config](#example-yaml-config)\n        - [Config Fields](#config-fields)\n        - [Advanced Config Examples](#advanced-config-examples)\n        - [Config Autocomplete](#config-file-autocomplete)\n    - [Gitignore Setup](#gitignore-setup)\n- [Expected Output](#expected-output)\n    - [Standard Fields](#default-metadata-fields-and-date-field)\n    - [Richtext Fields](#rich-text-as-main-content)\n    - [Resolving Reference Fields](#the-resolve-entries-parameter)\n    - [Overriding Field Names \u0026 Field Values](#the-overrides-parameter)\n    - [Filtering Entries Within a Content Type](#the-filters-parameter)\n    - [Adding Custom Fields To Frontmatter](#adding-custom-fields-to-frontmatter)\n- [Guides](#guides)\n    - [Getting live updates from Contentful on localhost](guides/localhost-live-updates.md)\n- [Known Issues](#known-issues)\n\n## Prerequisites\n\nInstall [Node.js](https://nodejs.org) (Minimum supported version is Node v18)\n\n## Installation\n\nwith NPM\n\n```bash\n# local install\nnpm install contentful-hugo\n\n# global install\nnpm install contentful-hugo -g\n```\n\nwith PNPM\n\n```bash\n# local install\npnpm install contentful-hugo\n\n# global install\npnpm install contentful-hugo -g\n```\n\n## Usage\n\n### Terminal Commands\n\nComplete [configuration](#configuration) then run the following command(s) in the terminal\n\n#### When Installed Globally\n\n```powershell\n## initialize the directory\ncontentful-hugo --init\n\n## fetch content from contentful\ncontentful-hugo [flags]\n```\n\n#### When Installed Locally\n\n```powershell\nnpx contentful-hugo --init\nnpx contentful-hugo [flags]\n\n# or when using pnpm\npnpm contentful-hugo --init\npnpm contentful-hugo [flags]\n```\n\n### Flags\n\n| flag      | aliases | description                                                                                              |\n| --------- | ------- | -------------------------------------------------------------------------------------------------------- |\n| --init    |         | Initialize the directory. Generates a config file and default shortcodes for Contentful rich text fields |\n| --preview | -P      | Runs in preview mode, which pulls both published and unpublished entries from Contentful                 |\n| --wait    | -W      | Wait for the specified number of milliseconds before pulling data from Contentful.                       |\n| --config  | -C      | Specify the path to a config file.                                                                       |\n| --server  | -S      | Run in server mode to receive webhooks from Contentful                                                   |\n| --port    |         | Specify port for server mode (Default 1414)                                                              |\n| --clean   |         | Delete any directories specified in singleTypes and repeatableTypes                                      |\n| --help    |         | Show help                                                                                                |\n| --version |         | Show version number                                                                                      |\n\n#### Multiple Flags Example\n\n```powershell\ncontentful-hugo --wait=2000 --preview --config=\"my_custom_config.js\"\n\n# or\n\ncontentful-hugo --wait 2000 --preview --config my_custom_config.js\n```\n\n### Example Package.json\n\n```JSON\n{\n  \"name\": \"my-hugo-project\",\n  \"scripts\": {\n    \"dev\": \"contentful-hugo --preview \u0026\u0026 hugo server\",\n    \"build\": \"contentful-hugo \u0026\u0026 hugo --minify\"\n  }\n}\n```\n\nIn this example when you run `npm run dev` it will first use contentful-hugo to pull Contentful data then start hugo server. In the same way when you do the command `npm run build` it will first use contentful-hugo to pull Contentful data then run `hugo --minify` to build a minified version of your hugo site.\n\n### Error Messages\n\nTrying to use this package before completing configuration will return an error in the console\n\n```shell\nError: There is an error in your config file, or it does't exits.\nCheck your config for errors or run \"contentful-hugo --init\" to create a config file.\n```\n\n## Configuration\n\n### Environment Variables\n\nBy default this library will look for the following environment variables. You can also override these values with the config file. (See [config](#config-file))\n\n- CONTENTFUL_SPACE\n- CONTENTFUL_TOKEN\n- CONTENTFUL_PREVIEW_TOKEN\n\n**.env File:**\n\nTo declare these environment variables create a `.env` file in the root directory of your project.\n\n```TOML\nCONTENTFUL_SPACE = '\u003cspace-id\u003e'\nCONTENTFUL_TOKEN = '\u003ccontent-accessToken\u003e'\n\n# optional but required for preview mode\nCONTENTFUL_PREVIEW_TOKEN = '\u003cpreview-accessToken\u003e'\n```\n\nYou can also declare the environment variables in the command line\n\n**Powershell:**\n\n```powershell\n$env:CONTENTFUL_SPACE=\"\u003ccontentful_space_id\u003e\"\n$env:CONTENTFUL_TOKEN=\"\u003ccontentful_acessToken\u003e\"\n$env:CONTENTFUL_PREVIEW_TOKEN=\"\u003ccontentful_preview_accessToken\u003e\"\n```\n\n**Bash:**\n\n```bash\nexport CONTENTFUL_SPACE=\"\u003ccontentful_space_id\u003e\"\nexport CONTENTFUL_TOKEN=\"\u003ccontentful_accessToken\u003e\"\nexport CONTENTFUL_PREVIEW_TOKEN=\"\u003ccontentful_preview_accessToken\u003e\"\n```\n\n### Config File\n\nBefore getting started, you will need to create a config file in the root of your repository. Contentful-hugo by default will search for the following files as a config.\n\n- `contentful-hugo.config.ts`\n- `contentful-hugo.config.js`\n- `contentful-hugo.config.yaml`\n- `contentful-hugo.yaml`\n- `contentful-settings.yaml`\n\nYou can also specify a custom config file using the `--config` flag. (Javascript or YAML config files are the only currently accepted filetypes)\n\n#### Example Typescript Config\n\n```ts\nimport { defineConfig } from 'contentful-hugo';\n\nexport default defineConfig({\n    // fetches from default locale if left blank\n    locales: ['en-US', 'fr-FR'],\n\n    contentful: {\n        // defaults to CONTENTFUL_SPACE env variable\n        space: 'space-id',\n        // defaults to CONTENTFUL_TOKEN env variable\n        token: 'content-deliver-token',\n        // defaults to CONTENTFUL_PREVIEW_TOKEN env variable\n        previewToken: 'content-preview-token',\n        // defaults to \"master\"\n        environment: 'master',\n        // defaults to 1000\n        pageSize: 150,\n    },\n\n    singleTypes: [\n        {\n            id: 'homepage',\n            directory: 'content',\n            fileName: '_index',\n        },\n        {\n            id: 'siteSettings',\n            directory: 'data',\n            fileName: 'settings',\n            fileExtension: 'yaml', // default is md\n        },\n    ],\n\n    repeatableTypes: [\n        {\n            id: 'posts',\n            directory: 'content/posts',\n            mainContent: 'content',\n            resolveEntries: {\n                categories: 'fields.slug',\n                author: 'fields.name',\n                relatedPosts: 'sys.id',\n            },\n        },\n        {\n            id: 'seoFields',\n            isHeadless: true,\n            directory: 'content/seo-fields',\n            customFields: {\n                // these fields will be added to the frontmatter\n                myCustomField: 'myCustomFieldVal',\n                myOtherCustomField: (entry) =\u003e {\n                    return entry.fields.whatever;\n                },\n            },\n        },\n        {\n            id: 'reviews',\n            directory: 'content/reviews',\n            mainContent: 'reviewBody',\n        },\n        {\n            id: 'category',\n            directory: 'content/categories',\n            isTaxonomy: true,\n        },\n    ],\n\n    staticContent: [\n        {\n            inputDir: 'static_content',\n            outputDir: 'content',\n        },\n    ],\n});\n```\n\n##### Using a Javascript Config\n\nA javascript config is pretty much the same as a typescript config.\n\n```js\nimport { defineConfig } from 'contentful-hugo';\n\nexport default defineConfig({\n    // stuff goes here\n});\n```\n\nCommonJS syntax _should_ also work (I don't actually test against this maybe it works maybe it doesn't)\n\n```js\nconst { defineConfig } = require('contentful-hugo');\n\nmodule.exports = defineConfig({\n    // stuff goes here\n});\n```\n\n#### Example YAML Config\n\n```yaml\n# contentful-hugo.config.yaml\n\nlocales: # fetches from default locale if left blank\n    - en-US\n    - fr-FR\n\ncontentful:\n    space: 'space-id' # defaults to CONTENTFUL_SPACE env variable\n    token: 'content-deliver-token' # defaults to  CONTENTFUL_TOKEN env variable\n    previewToken: 'content-preview-token' # defaults to  CONTENTFUL_PREVIEW_TOKEN env variable\n    environment: 'master' # defaults to \"master\"\n    pageSize: 150 # defaults to 1000\n\nsingleTypes:\n    # fetches only the most recently updated entry in a particular content type\n    # Generated file will be named after the fileName setting\n\n    - id: homepage\n      directory: content\n      fileName: _index\n\n    - id: siteSettings\n      directory: data\n      fileName: settings\n      fileExtension: yaml # default is md\n\nrepeatableTypes:\n    # fetches all the entries of a content type and places them in a directory.\n    # Generated files will be named after their Entry ID in Contentful.\n\n    - id: posts\n      directory: content/posts\n      fileExtension: md\n      mainContent: content\n      resolveEntries: # resolves a reference or asset field to a specific property\n          categories: fields.slug\n          author: fields.name\n          relatedPosts: sys.id\n\n    - id: seoFields\n      isHeadless: true\n      directory: content/seo-fields\n      customFields:\n          # will be added to the frontmatter\n          myCustomFields: 'myCustomFieldValue'\n\n    - id: reviews\n      directory: content/reviews\n      mainContent: reviewBody\n\n    - id: staff\n      isHeadless: true\n      directory: content/staff\n\n    - id: category\n      directory: content/categories\n      isTaxonomy: true\n```\n\n#### **Config Fields**\n\n##### \u003cu\u003e**Contentful Options**\u003c/u\u003e\n\n| field        | required | description                                                                                  |\n| ------------ | -------- | -------------------------------------------------------------------------------------------- |\n| space        | optional | Contentful Space ID (Defaults to CONTENTFUL_SPACE environment variable if not set)           |\n| token        | optional | Content delivery token (Defaults to CONTENTFUL_TOKEN environment variable if not set)        |\n| previewToken | optional | Content preview token (Defaults to CONTENTFUL_PREVIEW_TOKEN environment variable if not set) |\n| environment  | optional | Contentful environment ID (Defaults to \"master\" if not set)                                  |\n| pageSize | optional | Configure how many entries to fetch per page when querying the API. (Defaults to 1000 if not set) |\n\n##### \u003cu\u003e**Single Type Options**\u003c/u\u003e\n\n| field          | required | description                                                                                                                                                                                                          |\n| -------------- | -------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |\n| id             | required | Contentful content type ID                                                                                                                                                                                           |\n| directory      | required | Directory where you want the file(s) to be generated                                                                                                                                                                 |\n| fileName       | required | Name of the file generated                                                                                                                                                                                           |\n| fileExtension  | optional | Can be \"md\", \"yml\", \"yaml\", or \"json\" (defaults to \"md\")                                                                                                                                                             |\n| mainContent    | optional | Field ID for field you want to be the main Markdown content. (Can be a markdown, richtext, or string field)                                                                                                          |\n| type           | optional | Manually set value for \"type\" field in the frontmatter (see [hugo docs](https://gohugo.io/content-management/types/))                                                                                                |\n| resolveEntries | optional | Resolve the specified reference fields and/or asset fields to one of it's properties parameter                                                                                                                       |\n| overrides      | optional | Do custom overrides for field values or field names                                                                                                                                                                  |\n| filters        | optional | Accepts an object of Contentful search parameters to filter results. See [Contentful docs](https://www.contentful.com/developers/docs/references/content-delivery-api/#/reference/search-parameters/select-operator) |\n| ignoreLocales  | optional | Ignore localization settings and only pull from the default locale (defaults to false)                                                                                                                               |\n| customFields   | optional | Accepts an object of fields and values. The values can be a standard static value or a function that accepts the Contentful entry as a parameter and returns a value                                                 |\n\n##### \u003cu\u003e**Repeatable Type Options**\u003c/u\u003e\n\n| field                     | required | description                                                                                                                                                                                                                                |\n| ------------------------- | -------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ |\n| id                        | required | Contentful content type ID                                                                                                                                                                                                                 |\n| directory                 | required | Directory where you want the files to be generated                                                                                                                                                                                         |\n| fileName                  | optional | Entry property that will dicatate the filename. (By default this will be `sys.id`)                                                                                                                                                         |\n| fileExtension             | optional | Can be \"md\", \"yml\", \"yaml\", or \"json\" (defaults to \"md\")                                                                                                                                                                                   |\n| isHeadless                | optional | Turns all entries in a content type into headless leaf bundles (see [hugo docs](https://gohugo.io/content-management/page-bundles/#headless-bundle)). Cannot be set to true when isTaxonomy is set to true.                                |\n| isTaxonomy (Experimental) | optional | Organize entries in file structure allowing for custom taxonomy metadata (see [hugo docs](https://gohugo.io/content-management/taxonomies/#add-custom-metadata-a-taxonomy-or-term)). Cannot be set to true when isHeadless is set to true. |\n| mainContent               | optional | Field ID for field you want to be the main markdown content. (Can be a markdown, richtext, or string field)                                                                                                                                |\n| type                      | optional | Manually set value for \"type\" field in the frontmatter (see [hugo docs](https://gohugo.io/content-management/types/))                                                                                                                      |\n| resolveEntries            | optional | Resolve the specified reference fields and/or asset fields to one of it's properties                                                                                                                                                       |\n| overrides                 | optional | Do custom overrides for field values or field names                                                                                                                                                                                        |\n| filters                   | optional | Accepts an object of Contentful search parameters to filter results. See [Contentful docs](https://www.contentful.com/developers/docs/references/content-delivery-api/#/reference/search-parameters/select-operator)                       |\n| ignoreLocales             | optional | Ignore localization settings and only pull from the default locale (defaults to false)                                                                                                                                                     |\n| customFields              | optional | Accepts an object of fields and values. The values can be a standard static value or a function that accepts the Contentful entry as a parameter and returns a value                                                                       |\n\n##### \u003cu\u003e**Localization Options**\u003c/u\u003e\n\nThe config also has a `locales` field that allows you to specify what locales you want to pull from. This field can take an array of strings, an array of objects, or a combination.\n\nBy default locale specific file extensions will be used for multiple translations.\n\n```js\nconst { defineConfig } = require('contentful-hugo');\n// produce en-us.md and fr-fr.md files\nmodule.exports = defineConfig({\n    locales: ['en-US', 'fr-FR'];\n    // rest of config\n})\n\n// produce en.md and fr.md files\nmodule.exports = defineConfig({\n    locales: [\n        {\n            code: 'en-US',\n            mapTo: 'en'\n        },\n        {\n            code: 'fr-FR',\n            mapTo: 'fr'\n        }\n    ]\n    // rest of config\n})\n\n// produce en-us.md files and fr.md files\nmodule.exports = defineConfig({\n    locales: [\n        'en-US',\n        {\n            code: 'fr-FR',\n            mapTo: 'fr'\n        }\n    ]\n    // rest of config\n})\n```\n\nAfter configuring locales in Contentful Hugo you will need to update your Hugo config to account for these locales. Consult the [Hugo docs](https://gohugo.io/content-management/multilingual/) for more details.\n\n```toml\n# config.toml\n\n[languages]\n    [languages.en-us]\n    #language settings\n    [languages.fr-fr]\n    #language settings\n```\n\n###### Locale Specific Directories\n\nThere are sometimes cases where you will want to place content in a directory based on it's locale rather than using a file extension based translation. In order to do this you simple include `[locale]` inside your directory file path.\n\nWhen using locale specific directories the locale specific file extensions (i.e. `en.md` or `fr.md`) get dropped\n\n```js\nconst { defineConfig } = require('contentful-hugo');\n\nmodule.exports = defineConfig({\n    locales: ['en', 'fr']\n    singleTypes: [\n        {\n            id: 'settings',\n            fileName: 'settings',\n            fileExtension: 'yaml',\n            directory: 'data/[locale]'\n            /*\n                produces:\n                - data/en/settings.yaml\n                - data/fr/settings.yaml\n            */\n        }\n    ]\n    repeatableTypes: [\n        {\n            id: 'post',\n            directory: 'content/[locale]/post',\n            /*\n                produces:\n                - content/en/post/[entryId].md\n                - content/fr/post/[entryId].md\n            */\n        },\n    ],\n});\n```\n\n##### \u003cu\u003e**Static Content Options**\u003c/u\u003e\n\nThe recommended setup for Contentful Hugo is to have your content (usually `./content`) and data (usually `./data`) directories ignored in version control. This is because contentful-hugo will generate these directories at build time. However, this creates trouble for instances where you have pages that are not managed in Contentful and aren't generated at build time by another source.\n\nTo deal with this problem Contentful-Hugo has a `staticContent` parameter. This paramter accepts an input directory (`inputDir`) that can be commited to git, and an output directory (`outputDir`) which would be your standard content or data directory. All items in the inputDir will get copied into the outputDir at build time and will retain their folder structure.abs\n\nFor example in the config below `./static_content/posts/my-post.md` will get copied to `./content/posts/my-post.md`, and `./static_data/global-settings.yaml` will be copied to `./data/global-settings.yaml`.\n\n```js\nconst { defineConfig } = require('contentful-hugo');\n\nmodule.exports = defineConfig({\n    // rest of config\n    staticContent: [\n        {\n            // all items in ./static_content will be copied to ./content\n            inputDir: 'static_content',\n            outputDir: 'content',\n        },\n        {\n            // all items in ./static_data will be copied to ./data\n            inputDir: 'static_data',\n            outputDir: 'data',\n        },\n    ],\n});\n```\n\nContentful-Hugo will also watch for file changes in the inputDir's while running in server mode.\n\n#### Advanced Config Examples\n\n##### Dynamically Changing Tokens\n\nHere is an example of dynamically change the `token`, `previewToken`, and `environment` options depending on any arbitrary condition.\n\n```javascript\n// contentful-hugo.config.js\nconst { defineConfig } = require('contentful-hugo');\nrequire('dotenv').config(); // assuming you have \"dotenv\" in your dependencies\n\nconst myMasterToken = process.env.CONTENTFUL_MASTER_TOKEN;\nconst myMasterPreviewToken = process.env.CONTENTFUL_MASTER_PREVIEW_TOKEN;\nconst myStagingToken = process.env.CONTENTFUL_STAGING_TOKEN;\nconst myStagingPreviewToken = process.env.CONTENTFUL_STAGING_PREVIEW_TOKEN;\n\n// set some condition\nconst isStaging = true || false;\n\nmodule.exports = defineConfig({\n    contentful: {\n        space: 'my-space-id',\n        token: isStaging ? myStagingToken : myMasterToken,\n        preview: isStaging ? myStagingPreviewToken : myMasterPreviewToken,\n        environment: isStaging ? 'staging' : 'master',\n    },\n    // rest of config\n});\n```\n\n##### Overriding Fields and Field Values\n\n```js\nconst { defineConfig } = require('contentful-hugo');\n// contentful-hugo.config.js\n\nmodule.exports = defineConfig({\n    repeatableTypes: [\n        {\n            id: \"trips\",\n            directory: \"content/trips\"\n            overrides: {\n                // change the url field name to \"slug\"\n                url: {\n                    fieldName: \"slug\"\n                }\n                /*\n                    rename \"distanceInKilometers\" to \"distanceInMiles\"\n                    and change the field value from km to mi\n                */\n                distanceInKilometers: {\n                    fieldName: \"distanceInMiles\",valueTransformer: (val) =\u003e {\n                        if(typeof val === 'number') {\n                            return val * 0.621371\n                        }\n                        return 0\n                    }\n                }\n            }\n        }\n    ]\n})\n\n// ALTERNATIVE SYNTAX\nmodule.exports = defineConfig({\n    repeatableTypes: [\n        {\n            id: \"trips\",\n            directory: \"content/trips\"\n            overrides: [\n            {\n                field: \"url\",\n                options: {\n                    // change the url field name to \"slug\" in frontmatter\n                    fieldName: \"slug\"\n                }\n            },\n            {\n                field: \"distanceInKilometers\",\n                options: {\n                    // rename \"distanceInKilometers\" to \"distanceInMiles\"\n                    fieldName: \"distanceInMiles\",\n                    // convert distance to miles and output the result in frontmatter\n                    valueTransformer: (val) =\u003e {\n                        if(typeof val === 'number') {\n                            return val * 0.621371\n                        }\n                        return 0\n                    }\n                }\n            }]\n        }\n    ]\n})\n```\n\n#### Config File Autocomplete\n\nFor JS config files you can use the `defineConfig` helper or you can import the `ContentfulHugoConfig` type.\n\n```js\n//////////// OPTION 1 ////////////\nconst { defineConfig } = require('contentful-hugo');\n\nmodule.exports = defineConfig({\n    // config goes here\n});\n\n//////////// OPTION 2 ////////////\n/**\n * @type {import('contentful-hugo').ContentfulHugoConfig}\n */\nmodule.exports = {\n    // rest of config\n};\n```\n\n### Gitignore Setup\n\nExample `.gitignore` setup\n\n```bash\n# general stuff\n.env\nnode_modules\npublic\nresources\n\n# Contenful Hugo stuff\n# temp folder that contentful uses to track files\n.contentful-hugo\n# since content and data is coming from Contentful\n# usually you'll want to ignore those directories\ncontent\ndata\n```\n\n## Expected Output\n\nFiles will be generated in the directory specified in the config file. Front matter will be in YAML format. Files of single types will be named after fileName specified in the config file. Files of repeatable types will be named after their entry ID in Contenful, which makes it easy to link files together.\n\n### Default Metadata Fields and Date Field\n\nThe following fields will always appear in your frontmatter:\n\n```yaml\ndate: # defaults to sys.createdAt unless you have a field with the id \"date\" then it get's overwritten\nsys:\n    id: # the entry id\n    updatedAt: # the last time this entry was updated in Contentful\n    createdAt: # when the entry was created in Contentful\n    revision: # the revision number\n    space: # the space id\n    contentType: # the content type id\n```\n\n### Asset Information\n\nAssets like images and videos come with some extra information that makes it easy to implement things like alt text or layouts that rely on knowing the image dimensions. The fields are as follows:\n\n```yaml\nassetFieldName:\n    assetType: # indicates the asset mime type such as image/png, video/mp4, audio/mp3, ect.\n    url: # url of the asset\n    title: # title of the asset written in Contentful\n    description: # description of the asset written in Contentful\n    width: # width of the asset (images only)\n    height: # height of the asset (images only)\n```\n\nIf you're using Hugo you can access the information like below:\n\n```html\n\u003cimg\n    src=\"{{ .Params.assetFieldName.url }}\"\n    width=\"{{ .Params.assetFieldName.width }}\"\n/\u003e\n```\n\nFor images you can append parameters to the asset url in order to make use of Contentful's [images api](https://www.contentful.com/developers/docs/references/images-api/#/introduction)\n\n```html\n\u003cimg\n    src=\"{{ .Params.assetFieldname.url }}?w=200\u0026h=200\u0026fit=fill\"\n    width=\"200\"\n    height=\"200\"\n/\u003e\n```\n\nThis same information will also appear in asset arrays like a gallery:\n\n```yaml\nmyGallery:\n    - assetType: 'image/jpg'\n      url: '//link-to-image.jpg'\n      title: 'Image 1'\n      description: 'Image 1 Description'\n      width: 500\n      height: 500\n    - assetType: 'image/jpg'\n      url: '//link-to-image-2.jpg'\n      title: 'Image 2'\n      description: 'Image 2 Description'\n      width: 1920\n      height: 1080\n```\n\n### Entries\n\nLinked entries will include fields for it's id and it's content type id.\n\n```yaml\nlinkedEntry:\n    id: \u003ccontentful-entry-id\u003e\n    typeId: \u003ccontent-type-ID\u003e\n\n#example with array of linked entries\n\nrelatedArticles:\n    - id: '41UFfIhszbS1kh95bomMj7'\n      typeId: 'articles'\n    - id: '85UFfIhsacS1kh71bpqMj7'\n      typeId: 'articles'\n```\n\n#### Accessing Linked Entry Data\n\nAll files are named after their entry id in Contentful making it easy to retrieve it using `.Site.GetPage` in Hugo\n\n```go\n// if you have access to the \"Page\" object\n{{ with .Site.GetPage \"\u003cpath-to-file\u003e/\u003centry-id\u003e\" }}\n    {{ .Title }}\n{{ end }}\n\n// if you don't have access to the \"Page\" object\n// for example in a nested partial\n{{ with site.GetPage \"\u003cpath-to-file\u003e/\u003centry-id\u003e\" }}\n    {{ .Title }}\n{{ end }}\n```\n\n**Simple example**\n\n```go\n{{ with .Params.myEntryField }}\n    {{ $pagePage := print \"path/to/entryDir/\" .id }}\n    {{ with site.GetPage $pagePath }}\n        {{ .Title }}\n        {{ .Params.someOtherField }}\n    {{ end }}\n{{ end }}\n```\n\nRelevant Documentation:\n\n- [GetPage Method](https://gohugo.io/functions/getpage/#readout)\n- [Getting the Site object from a partial](https://gohugo.io/variables/site/#get-the-site-object-from-a-partial)\n\n### Rich Text As Main Content\n\nA rich text field that is set as the \"mainContent\" for a content type will be rendered as markdown for Hugo.\n\nDynamic content such as `embedded-entry-blocks` are rendered as shortcodes with parameters included that can be used to fetch the necessary data.\n\n```md\n\u003c!-- example embedded entry --\u003e\n\u003c!-- you can use the id, contentType, and parentContentType parameters to fetch the desired data --\u003e\n\n{{\u003c contentful-hugo/embedded-entry id=\"nTLo2ffSJJp5QrnrO5IU9\" contentType=\"gallery\" parentContentType=\"post\" \u003e}}\n```\n\nBefore fetching rich text data make sure you have run `contentful-hugo --init` so that you will have all the rich text shortcodes. Once you have these shortcodes you can extend and modify them to suit your needs.\n\nThe list of rich text short codes includes:\n\n- contentful-hugo/asset-hyperlink.html\n- contentful-hugo/embedded-asset.html\n- contentful-hugo/embedded-entry.html\n- contentful-hugo/entry-hyperlink.html\n- contentful-hugo/inline-entry.html\n\nBy default the richtext short codes will show a notification for an unconfigured item.\n\n![Unconfigured Embedded Entry Block](images/unconfigured-embedded-entry.PNG)\n\nYou can customize them by navigating to `layouts/shortcodes/contentful-hugo/{shortcode-name}.html`\n\n### Rich Text In FrontMatter\n\nA Rich text field will produce nested arrays mirroring the JSON structure that they have in the API. Each node will need to be looped through and produce HTML depending on the nodeType field.\n\n```yaml\nrichTextField:\n    - nodeType: 'paragraph'\n      data: {}\n      content:\n          - data: {}\n            marks: []\n            value: 'This is a simple paragraph.'\n            nodeType: 'text'\n    - nodeType: 'paragraph'\n      data: {}\n      content:\n          - data: {}\n            marks: []\n            value: 'This is a paragraph with '\n            nodeType: 'text'\n          - data: {}\n            marks:\n                - type: 'italic'\n            value: 'italicized text.'\n            nodeType: 'text'\n    - nodeType: 'embedded-asset-block'\n      data:\n          assetType: 'image/jpeg'\n          url: '//images.ctfassets.net/some-image-url.jpg'\n          title: 'Image title will appear here'\n          description: 'Image description will appear here'\n          width: 1920\n          height: 1080\n      content: []\n```\n\nIn addition a plaintext version of the field will be generated using the field ID appended with \"\\_plaintext\". This allows you to quickly fetch the text by itself without any of the other data. A simple use case would be using the plaintext output to automatically generate a meta description for a webpage.\n\n```yaml\nrichTextField_plaintext: 'This is a simple paragraph. This is a paragraph with italicized text.'\n```\n\n### The Resolve Entries Parameter\n\nThe resolve entries option let's you specify a property from a referenced entry or asset to resolve that field value to. For example say you have a `category` content type that is referenced in `posts`. Normally contentful-hugo will give the following result\n\n```yaml\ncategory:\n    id: some-entry-id\n    contentType: category\n```\n\nWhile this makes it easy to find the category, this format does not allow you to use Hugo's built in taxonomy features. With the `resolveEntries` parameter you can remedy this.\n\n```js\n// from the config file\nmodule.exports = defineConfig({\n    repeatableTypes: [\n        id: 'posts',\n        directory: 'content/posts',\n        resolveEntries: {\n            category: 'fields.slug'\n        }\n        // alternative syntax\n        resolveEntries: [\n            {\n                field: 'category',\n                resolveTo: 'fields.slug',\n            },\n        ],\n    ]\n})\n```\n\nNow the category field will only display the slug as the value.\n\n```yaml\ncategory: my-category-slug\n```\n\nThe resolve entries feature works with both reference fields and asset fields, as well as multiple reference and multiple asset fields.\n\n### The Overrides Parameter\n\nOverrides can be used to modify field names and field values.\n\nHere's a simple example of changing a field name from \"url\" to \"videoUrl\"\n\n```js\nrepeatableTypes: [\n    {\n        id: 'youtubeVideo',\n        directory: 'content/_youtubeVideo',\n        isHeadless: true,\n        overrides: {\n            // the name of the url field to videoUrl\n            url: {\n                fieldName: 'videoUrl'\n            }\n        }\n        // alternative syntax\n        overrides: [\n            {\n                field: 'url',\n                options: {\n                    fieldName: 'videoUrl',\n                },\n            },\n        ],\n    },\n];\n```\n\n`overrides` also has a `valueTransformer` options that allows you to manipulate the field data that will appear in frontmatter. `valueTransformer` takes a method that has the field value as a parameter and then returns the final result that will appear in the frontmatter. (Be aware that since `valueTransformer` must be a method this option will only work in javascript config files)\n\nHere's an example where we change the field name from \"url\" to \"videoId\" and then we use the `valueTransformer` to extract the video id from the url and then place it in the frontmatter.\n\n```js\nrepeatableTypes: [\n    {\n        id: 'youtubeVideo',\n        directory: 'content/_youtubeVideo',\n        isHeadless: true,\n        overrides: {\n            url: {\n                fieldName: 'videoId',\n                // \"value\" is whatever value is currently saved in the field.\n                // in this case it's a url for a youtube video\n                valueTransformer: (value) =\u003e {\n                    if (!value) {\n                            return null;\n                        }\n                    const url = new URL(value);\n                    // extract the video id from the url and return it\n                    return url.searchParams.get('v');\n                }\n            }\n        }\n        // alternative syntax\n        overrides: [\n            {\n                field: 'url',\n                options: {\n                    fieldName: 'videoId',\n                    valueTransformer: (value) =\u003e {\n                        // transform the value\n                    },\n                },\n            },\n        ],\n    },\n];\n```\n\nWhen using the `valueTransformer` option on fields that contain arrays make sure to loop through the value when manipulating it.\n\n```js\nrepeatabledTypes: [\n    {\n        id: 'post',\n        directory: 'content/posts',\n        overrides: {\n            authors: {\n                valueTransformer: (authorRefs) =\u003e {\n                    const authors = [];\n                    for (const ref of authorRefs) {\n                        // get the name, photo, and bio of the author\n                        // and add it to the array\n                        authors.push({\n                            name: ref.fields.name,\n                            photo: ref.fields.photo.fields.file.url,\n                            bio: ref.fields.bio,\n                        });\n                    }\n                    return authors;\n                },\n            },\n        },\n    },\n];\n```\n\nNow the `authors` field will look like this:\n\n```yaml\nauthors:\n    - name: Some Name\n      photo: //images.cfassets.net/path-to-photo.jpg\n      bio: some bio text\n    - name: Some other name\n      photo: //images.cfassets.net/path-to-photo.jpg\n      bio: some other bio text\n```\n\nAs you can see this can be used to produce similar results to the `resolveEntries` parameter, but `resolveEntries` can only return one property while with overrides you can do whatever you want with the field values.\n\n### The Filters Parameter\n\nYou can use to `filters` option to enter search parameters allowing you to filter entries based on some of their properties. For more info on Contentful search parameters visit their [docs](https://www.contentful.com/developers/docs/references/content-delivery-api/#/reference/search-parameters).\n\nBe aware that the following search parameters will be ignored `content_type`, `skip`, `order`, `limit`\n\n#### Examples:\n\n```js\nmodule.exports = defineConfig({\n    singleTypes: [\n        // get a homepage with a specific entryId\n        {\n            id: 'homepage',\n            directory: 'content',\n            fileName: '_index',\n            filters: {\n                'sys.id': 'my-homepace-id'\n            }\n        }\n    ]\n    repeatableTypes: [\n        // only get events that start after 01/01/2020\n        {\n            id: 'events',\n            directory: 'content/events',\n            filters: {\n                'fields.startDate[gte]': '2020-01-01T00:00:00Z',\n            },\n        },\n        // get posts where author is \"John Doe\" and contains the tag \"flowers\"\n        {\n            id: 'posts',\n            directory: 'content/posts',\n            filters: {\n                'fields.author': 'John Doe',\n                'fields.tags': 'flowers'\n            },\n        },\n    ];\n})\n```\n\n### Adding Custom Fields To Frontmatter\n\nYou can use the `customFields` parameter to add additional fields to your entries. The config for custom fields can be a static value or a method that accepts a Contentful entry as a parameter and returns a value.\n\n### Examples\n\nLet's say we have an author content type with the following fields:\n\n- firstName\n- lastName\n- slug\n\nHere's an example config:\n\n```js\nmodule.exports = defineConfig({\n    // rest of config\n    repeatableTypes: [\n        {\n            id: 'author',\n            directory: 'content/authors',\n            customFields: {\n                // both \"myCustomField\" and \"fullName\"\n                // will be appended to the frontmatter for author entries\n                myCustomField: 'myCustomFieldValue',\n                fullName: (entry) =\u003e {\n                    const { firstName, lastName } = entry.fields;\n                    return `${firstName} ${lastName}`;\n                },\n            },\n        },\n    ],\n});\n```\n\nHere's what that config will result in\n\n```yaml\n---\nfirstName: 'John'\nlastName: 'Doe'\nslug: 'john-doe'\nmyCustomField: 'myCustomFieldValue' # custom field\nfullName: 'John Doe' # custom field\n---\n```\n\nYou could also use this for Hugo specific fields like [Build Options](https://gohugo.io/content-management/build-options/#readout)\n\n```js\n// prevent a content type from appearing in list pages\n{\n    customFields: {\n        _build: {\n            render: 'alway',\n            list: 'never',\n            publishResources: true\n        }\n    }\n}\n\n// prevent a content type from rendering a single page\n{\n    customFields: {\n        _build: {\n            render: 'never',\n            list: 'always',\n            publishResources: true\n        }\n    }\n}\n```\n\n## Guides\n\n- [Getting live Contentful updates on Localhost with Contentful-Hugo and Ngrok](guides/localhost-live-updates.md)\n\n## Known Issues\n\nThese are some known issues.\n\n- **Date \u0026 Time Field w/o Timezone**: Date fields that include time but do not have a specified timezone will have a timezone set based on whatever machine the script is run on. So using a date field in contentful with this setting could lead to unexpected results when formatting dates. Date fields that don't include time (ex: YYYY-MM-DD) are not effected by this.\n- **Fetching Data Before Contentful CDN Updates**: Sometimes when triggering a build from a webhook, it won't always get the latest data. This is because it sometimes takes a couple seconds for the latest data to get distributed across Contentful's CDN. If you run into this issue add the the `--wait` flag to your script. Here's an example where we wait an additional 6 seconds `contentful-hugo --wait=6000`.\n- **Hugo --server Rendering Issues**: If you have fields that where multiple different files are referenced such as a rich text field that references other entries Hugo's default server mode may not rerender everything. To fix this run `hugo server --disableFastRender`\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fmodiimedia%2Fcontentful-hugo","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fmodiimedia%2Fcontentful-hugo","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fmodiimedia%2Fcontentful-hugo/lists"}