{"id":13707787,"url":"https://github.com/cuttlebelle/cuttlebelle","last_synced_at":"2025-05-06T04:30:47.066Z","repository":{"id":21675955,"uuid":"93694524","full_name":"cuttlebelle/cuttlebelle","owner":"cuttlebelle","description":"The react static site generator that separates editing and code concerns","archived":false,"fork":false,"pushed_at":"2023-06-12T14:37:12.000Z","size":4624,"stargazers_count":545,"open_issues_count":18,"forks_count":28,"subscribers_count":15,"default_branch":"master","last_synced_at":"2024-04-14T13:10:02.324Z","etag":null,"topics":["content-management","nodejs","rapid-prototyping","react","reactjs","static-site-generator"],"latest_commit_sha":null,"homepage":"https://cuttlebelle.com/","language":"HTML","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"gpl-3.0","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/cuttlebelle.png","metadata":{"files":{"readme":"README.md","changelog":"CHANGELOG.md","contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null}},"created_at":"2017-06-08T01:23:04.000Z","updated_at":"2024-04-06T21:55:38.000Z","dependencies_parsed_at":"2024-01-17T08:59:42.529Z","dependency_job_id":null,"html_url":"https://github.com/cuttlebelle/cuttlebelle","commit_stats":null,"previous_names":[],"tags_count":0,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/cuttlebelle%2Fcuttlebelle","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/cuttlebelle%2Fcuttlebelle/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/cuttlebelle%2Fcuttlebelle/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/cuttlebelle%2Fcuttlebelle/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/cuttlebelle","download_url":"https://codeload.github.com/cuttlebelle/cuttlebelle/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":252621757,"owners_count":21777864,"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":["content-management","nodejs","rapid-prototyping","react","reactjs","static-site-generator"],"created_at":"2024-08-02T22:01:43.593Z","updated_at":"2025-05-06T04:30:47.058Z","avatar_url":"https://github.com/cuttlebelle.png","language":"HTML","funding_links":[],"categories":["HTML"],"sub_categories":[],"readme":"Cuttlebelle\n[![Build Status Travis](https://travis-ci.org/cuttlebelle/cuttlebelle.svg?branch=master)](https://travis-ci.org/cuttlebelle/cuttlebelle)\n[![Tweet](https://img.shields.io/twitter/url/http/shields.io.svg?style=social)](https://twitter.com/intent/tweet?url=https://cuttlebelle.com\u0026hashtags=react,reactjs,static-site-generator,jsx)\n===========\n\n**\u003cp align=\"center\"\u003eThe react static site generator with editing in mind\u003c/p\u003e**\n\n## \u003cp align=\"center\"\u003e[Visit our website](https://cuttlebelle.com/)\u003c/p\u003e\n\n[![Cuttlebelle](https://cuttlebelle.com/assets/img/cuttlebelle.jpg)](https://cuttlebelle.com/)\n\n## 🔥 Why yet another static site generator?\n\nAll static site generators I have used restrict you to use one layout per page. Todays webdesign needs have outgrown this and we often find ourself either\nadding code into our content pages (markdown files, liquid templates) or content into our code.\nThat makes updating and maintaining a page hard, especially for a non-technical content author.\n\nI needed a generator that can **separate content from code** as cleanly as possible while still staying a static site generator and as dynamic as possible.\n\n[React](https://facebook.github.io/react/) comes with the component paradigm and was exactly what I’m looking for.\n[JSX](https://facebook.github.io/react/docs/introducing-jsx.html) enables a very easy templating like way to write components while still keeping the power of\njavascript. **No more templating languages** that only do half of what you need. Use javascript to write your layouts.\n\n![Cuttlebelle markdown to jsx to html](https://raw.githubusercontent.com/cuttlebelle/cuttlebelle/master/assets/conversion.png)\n\n\n## Contents\n\n* [Install](#install)\n* [Requirements](#requirements)\n* [Getting started](#getting-started)\n* [Usage](#usage)\n* [Self-documenting](#self-documenting)\n* [Build](#build)\n* [Tests](#tests)\n* [Release History](#release-history)\n* [License](#license)\n\n\n----------------------------------------------------------------------------------------------------------------------------------------------------------------\n\n\n## Requirements\n\n\nTo use Cuttlebelle you need at least node `\u003e= 12`.\n\n\n**[⬆ back to top](#contents)**\n\n\n----------------------------------------------------------------------------------------------------------------------------------------------------------------\n\n\n## Install\n\n\n```shell\nnpm install cuttlebelle -g\n```\n\nThen run `cuttlebelle` in your working folder.\n\n\n💡Tip: I recommend [installing](#install) Cuttlebelle globally as it exposes the `cuttlebelle` command to your system.\nIf you for some reason want to install it locally, consider adding a npm script to your `package.json` to make\nrunning cuttlebelle easier:\n\n```diff\n{\n\t\"name\": \"your name\",\n\t\"version\": \"1.0.0\",\n\t\"description\": \"Your description\",\n\t\"main\": \"index.js\",\n\t\"scripts\": {\n+\t\t\"build\": \"cuttlebelle\",\n+\t\t\"watch\": \"cuttlebelle -w\",\n\t\t\"test\": \"echo \\\"Error: no test specified\\\" \u0026\u0026 exit 1\"\n\t},\n\t\"devDependencies\": {\n\t\t\"cuttlebelle\": \"^1.0.0\"\n\t}\n\t\"keywords\": [],\n\t\"author\": \"\",\n\t\"license\": \"ISC\"\n}\n```\n\nThen run `npm run build` to run cuttlebelle.\n\n\n**[⬆ back to top](#contents)**\n\n\n----------------------------------------------------------------------------------------------------------------------------------------------------------------\n\n\n## Getting started\n\nAfter [installing](#install) cuttlebelle, create a folders called `content` and `code` and start populating them.\nEach folder with an `index.yml` file will become an `index.html` in cuttlebelles generated pages.\n\n![Cuttlebelle files](https://raw.githubusercontent.com/cuttlebelle/cuttlebelle/master/assets/files.gif)\n\n\u003ctable\u003e\n\t\u003ctbody\u003e\n\t\t\u003ctr\u003e\n\t\t\t\u003cth\u003eYour content folder\u003c/th\u003e\n\t\t\t\u003cth\u003e\n\t\t\t\t\u0026nbsp;\u0026nbsp;\u0026nbsp;\u0026nbsp;\u0026nbsp;\u0026nbsp;\u0026nbsp;\u0026nbsp;\u0026nbsp;\u0026nbsp;\n\t\t\t\t\u0026nbsp;\u0026nbsp;\u0026nbsp;\u0026nbsp;\u0026nbsp;\u0026nbsp;\u0026nbsp;\u0026nbsp;\u0026nbsp;\u0026nbsp;\n\t\t\t\u003c/th\u003e\n\t\t\t\u003cth\u003eOutput\u003c/th\u003e\n\t\t\u003c/tr\u003e\n\t\t\u003ctr\u003e\n\t\t\t\u003ctd\u003e\n\n```shell\n.\n├── index\n│   ├── index.yml\n│   ├── partial1.md\n│   └── partial2.md\n├── page1\n│   ├── index.yml\n│   └── subpage1\n│       ├── index.yml\n│       ├── partial1.md\n│       └── partial2.md\n├── page2\n│   ├── index.yml\n│   ├── partial1.md\n│   └── partial2.md\n└── shared\n    ├── component1.md\n    └── component2.md\n```\n\n\u003c/td\u003e\n\u003ctd align=\"center\"\u003e → \u003c/td\u003e\n\u003ctd valign=\"top\"\u003e\n\n```shell\n.\n├── index.html\n├── page1\n│   ├── index.html\n│   └── subpage1\n│       └── index.html\n└── page2\n    └── index.html\n```\n\n\u003c/tr\u003e\n\t\u003c/tbody\u003e\n\u003c/table\u003e\n\nConsider this example to see how pages are constructed with partials and layouts:\n\nAn **index.yaml** page\n\n\u003ctable\u003e\n\t\u003ctbody\u003e\n\t\t\u003ctr\u003e\n\t\t\t\u003cth\u003eindex.yml\u003c/th\u003e\n\t\t\t\u003cth\u003e\u003c/th\u003e\n\t\t\t\u003cth\u003epage layout\u003c/th\u003e\n\t\t\u003c/tr\u003e\n\t\t\u003ctr\u003e\n\t\t\t\u003ctd valign=\"top\"\u003e\n\n```yaml\nlayout: page\ntitle: Homepage\nmain:\n  - header.md\n  - body.md\n```\n\n\u003c/td\u003e\n\u003ctd align=\"center\"\u003e + \u003c/td\u003e\n\u003ctd valign=\"top\"\u003e\n\n```jsx\nimport React from \"react\";\n\nexport default ({ title, main }) =\u003e (\n  \u003chtml\u003e\n  \u003chead\u003e\n    \u003ctitle\u003e{ title }\u003c/title\u003e\n  \u003c/head\u003e\n  \u003cbody\u003e\n    \u003cmain\u003e\n      { main }\n    \u003c/main\u003e\n  \u003c/body\u003e\n  \u003c/html\u003e\n);\n```\n\n\u003c/td\u003e\n\t\t\u003c/tr\u003e\n\t\u003c/tbody\u003e\n\u003c/table\u003e\n\nA **header.md** partial\n\n\u003ctable\u003e\n\t\u003ctbody\u003e\n\t\t\u003ctr\u003e\n\t\t\t\u003cth\u003epartial header.md\u003c/th\u003e\n\t\t\t\u003cth\u003e\u003c/th\u003e\n\t\t\t\u003cth\u003eheader layout\u003c/th\u003e\n\t\t\u003c/tr\u003e\n\t\t\u003ctr\u003e\n\t\t\t\u003ctd valign=\"top\"\u003e\n\n```markdown\n---\nlayout: header\nheadline: First post\nsub: Clear content separation\n---\n```\n\n\u003c/td\u003e\n\u003ctd align=\"center\"\u003e + \u003c/td\u003e\n\u003ctd valign=\"top\"\u003e\n\n```jsx\nimport React from \"react\";\n\nexport default ({ headline, sub }) =\u003e (\n  \u003cheader\u003e\n    \u003ch1 className=\"header__headline\"\u003e{ headline }\u003c/h1\u003e\n    {\n      sub\n        \u0026\u0026 \u003cp className=\"header__sub\"\u003e{ sub }\u003c/p\u003e\n    }\n  \u003c/header\u003e\n);\n```\n\n\u003c/td\u003e\n\t\t\u003c/tr\u003e\n\t\u003c/tbody\u003e\n\u003c/table\u003e\n\nA **body.md** partial\n\n\u003ctable\u003e\n\t\u003ctbody\u003e\n\t\t\u003ctr\u003e\n\t\t\t\u003cth\u003epartial body.md\u003c/th\u003e\n\t\t\t\u003cth\u003e\u003c/th\u003e\n\t\t\t\u003cth\u003ebody layout\u003c/th\u003e\n\t\t\u003c/tr\u003e\n\t\t\u003ctr\u003e\n\t\t\t\u003ctd valign=\"top\"\u003e\n\n```markdown\n---\nlayout: body\nheadline: First post\n---\n\n**Hello world**\n```\n\n\u003c/td\u003e\n\u003ctd align=\"center\"\u003e + \u003c/td\u003e\n\u003ctd valign=\"top\"\u003e\n\n```jsx\nimport React from \"react\";\n\nexport default ({ _body, headline }) =\u003e (\n  \u003carticle\u003e\n    \u003ch2\u003e{ headline }\u003c/h2\u003e\n    \u003cdiv className=\"body-text\"\u003e{ _body }\u003c/div\u003e\n  \u003c/article\u003e\n);\n```\n\n\u003c/td\u003e\n\t\t\u003c/tr\u003e\n\t\u003c/tbody\u003e\n\u003c/table\u003e\n\nWill give us this HTML\n\n\u003ctable\u003e\n\t\u003ctbody\u003e\n\t\t\u003ctr\u003e\n\t\t\t\u003cth\u003eresulting static HTML file\u003c/th\u003e\n\t\t\u003c/tr\u003e\n\t\t\u003ctr\u003e\n\t\t\t\u003ctd valign=\"top\"\u003e\n\n```html\n\u003c!DOCTYPE html\u003e\n\u003chtml\u003e\n\u003chead\u003e\n  \u003ctitle\u003eHomepage\u003c/title\u003e\n\u003c/head\u003e\n\n\u003cbody\u003e\n  \u003cmain\u003e\n    \u003cheader\u003e\n      \u003ch1 class=\"header__headline\"\u003eFirst post\u003c/h1\u003e\n      \u003cp class=\"header__sub\"\u003eClear content separation\u003c/p\u003e\n    \u003c/header\u003e\n    \u003carticle\u003e\n      \u003ch2\u003eFirst post\u003c/h2\u003e\n      \u003cdiv class=\"body-text\"\u003e\u003cstrong\u003eHello world\u003c/strong\u003e\u003c/div\u003e\n    \u003c/article\u003e\n  \u003c/main\u003e\n\u003c/body\u003e\n\n\u003c/html\u003e\n```\n\n\u003c/td\u003e\n\t\t\u003c/tr\u003e\n\t\u003c/tbody\u003e\n\u003c/table\u003e\n\n\n**[⬆ back to top](#contents)**\n\n\n----------------------------------------------------------------------------------------------------------------------------------------------------------------\n\n\n## Usage\n\n* [CLI](#cli)\n* [Your content](#your-content)\n* [Your assets](#your-assets)\n* [Your layout](#your-layout)\n* [Customizations](#customizations)\n\n\n### CLI\n\n![Cuttlebelle cli](https://raw.githubusercontent.com/cuttlebelle/cuttlebelle/master/assets/cuttlebelle.png)\n\n```shell\ncuttlebelle\n```\n\nThis will generate all pages into the `site` folder _(unless [specified otherwise](#customizations))_.\n\n\n#### Init\n\nFor when you’re just starting out on a new project use the `init` option. It will generate the folder structure for you and add some files for you to get\nstarted quickly.\n\n```shell\ncuttlebelle init\n```\n\n\n#### Watch\n\n![Cuttlebelle watch](https://raw.githubusercontent.com/cuttlebelle/cuttlebelle/master/assets/watch.png)\n\nYou can also run the highly optimized watch while adding content or developing your layouts.\n\n```\ncuttlebelle watch\n```\n\nThis command will first build your pages and then watch for changes in any of them.\n\nIt will dutifully only build the absolute minimum of pages once it detects a change somewhere. It is so eager to only build those pages that it thinks are\nrelevant that it misses sometimes. In cases where you add content from the `_pages` prop in one of your layouts for instance. I have added a new and somewhat\ngenius trick to catch cases like that.\n\n**Introducing the _double save_** \u003csup\u003eTM\u003c/sup\u003e\n\nIf you feel like the watch may have missed a page and you don’t want to leave your editor to complain about it to the watch, just save your file twice quickly\nlike a double click. The watch will detect the _double save_\u003csup\u003eTM\u003c/sup\u003e and generate all pages for you again.\n\n\n💡Tip: You can change the timeout of the watch to detect a double save via the `watchTimeout` [setting](#customizations).\n\n\n#### Watch flags\n\n##### No generator flag\n\nSometimes you may only want to start a watch and not rebuild all pages. For that use the `no-generate` option:\n\n```shell\ncuttlebelle watch --no-generate\n```\n\n\n##### Silent flag\n\nThe watch notifies you each time it encounters an error so you don’t have to watch the watch. You can disable that behavior via the silent option.\n\n```shell\ncuttlebelle watch --silent\n```\n\n\n#### Docs\n\nCuttlebelle comes with a nifty feature that will auto-document your layouts if you use the right propTypes and comments.\nRead more about how in the [self documenting](#self-documenting) section.\n\n\n```shell\ncuttlebelle docs\n```\n\n\n#### Version\n\nTo display the version of your cuttlebelle install all you have to do is run the version flag:\n\n```shell\ncuttlebelle --version\n```\n\n\n#### Help\n\n![Cuttlebelle help](https://raw.githubusercontent.com/cuttlebelle/cuttlebelle/master/assets/help.png)\n\nOf course there is also a help option. Just run it with the help flag:\n\n```shell\ncuttlebelle help\n```\n\n\n**[↑ back to Usage](#usage)**\n\n\n### Your content\n\nThe default folder structure divides content into the `content/` folder and the layout and component react files into the `code/` folder.\n\n```shell\n.\n├── content/           # The content folder\n│   ├── page1/         # Each folder represents a page and will be converted to `page1/index.html`\n│   │                  # 💡 As long as it contains an `index.yml` file.\n│   │\n│   ├── index/         # The index folder is treated as the homepage and converted to `/index.html`\n│   │\n│   └── page2/         # You can nest pages by nesting them in the folder structure\n│       │\n│       └── subpage1/  # As long as this folder has an `index.yml` file\n│                      # it will be converted to `page2/subpage1/index.html`\n│\n├── assets/            # The assets folder, every file you add here will be moved to your output.\n└── code/              # The `code` folder is where your layout lives\n```\n\n_(_ 💡 _All folders can be configured in your `package.json` file via the [cuttlebelle customizations](#customizations).)_\n\nNow let’s look into one folder:\n\n```shell\n.\n└── content\n    ├── page1\n    │   ├── index.yml      # This folder includes an `index.yml` file so it will be converted into\n    │   │                  # a page in the output of cuttlebelle.\n    │   ├── partial1.md    # The partials are all in markdown format and can have any name.\n    │   └── partial2.md    # You can reference partials from your index.yml or another partial.\n    │\n    └── shared             # A folder won’t be generated if it doesn’t have an `index.yml` file\n        ├── component1.md  # You can use such folders to share partials between pages\n        └── component2.md  # This is just a suggestion. Partials can live anywhere.\n```\n\n\n#### Your `index.yml`\n\nA typical `index.yml` file could look like this:\n\n```yaml\nlayout: page          # The layout of an `index.yml` defaults to `page` if it’s not set\ntitle: Homepage       # It’s always a good idea to give your page a title\nmain:                 # Defining an array in yaml\n  - feature-image.md  # This is a partial (because it ends with \".md\") and points to a markdown\n                      # file that exists\n  - cta.md\n  - contact-cards.md\n  - /shared/footer.md # This is also a partial but because it starts with a slash \"/\" the\n                      # location where this partial sits is relative to your content folder\n                      # and not the page folder you’re in.\nheader: header.md     # You can define a partial to a variable or to an array as seen above\ncontent: Hello world  # Or you can put any type of string into your yaml if you prefer.\n```\n\n_(_ 💡 _All variables that are defined inside a page are available as props under `{ _pages }` to all partials.)_\n\n\n#### Your partials\n\nAnd a typical `partial.md` file could look like this:\n\n```markdown\n---                                 # Each markdown file can have frontmatter\nlayout: cards                       # The power of cuttlebelle is each partial has it’s own layout\n                                    # The layout of partials defaults to `partial` if it’s not set\nheadline: Partial headline          # You can add any number of variables\ncards:                              # Even arrays\n  - id: ID1                         # Or objects\n    title: Card1\n    content: content for first card\n  - id: ID2\n    title: Card2\n    content: content for second card\n---\n\nContent\n\u003c!-- The content of the markdown file is exposed as { _body } to the props --\u003e\n```\n\n_(_ 💡 _Of course all variables are again available as props to the layout by their own name.)_\n\n\n**[↑ back to Usage](#usage)**\n\n\n### Your assets\n\nAll files included inside the `assets/` folder are moved to `site/assets/`. This is where you should keep your CSS, SVGs and images.\nJust create a prop inside your `index.yml` pages to include them into your pages:\n\n`content/index/index.yml`:\n\n```yaml\nlayout: layout/homepage\ntitle: Homepage\nstylesheet: homepage\nmain:\n  - /shared/header.md\n  - homepage.md\n  - /shared/footer.md\naside:\n  - nav.md\n  - callout.md\n```\n\n`code/layout/homepage.js`\n\n```jsx\nimport React from \"react\";\n\nexport default ({ title, stylesheet, title, main, aside }) =\u003e (\n\t\u003chtml\u003e\n\t\u003chead\u003e\n\t\t\u003ctitle\u003e{ title }\u003c/title\u003e\n\t\t{ stylesheet != undefined\n\t\t\t? ( \u003clink rel=\"stylesheet\" href={ `/assets/css/${ stylesheet }.css` } /\u003e )\n\t\t\t: null\n\t\t}\n\t\u003c/head\u003e\n\t\u003cbody\u003e\n\t\t\u003cmain\u003e\n\t\t\t\u003ch1\u003e{ title }\u003c/h1\u003e\n\t\t\t\u003cdiv\u003e{ main }\u003c/div\u003e\n\t\t\u003c/main\u003e\n\t\t\u003caside\u003e\n\t\t\t{ aside }\n\t\t\u003c/aside\u003e\n\t\u003c/body\u003e\n\t\u003c/html\u003e\n);\n```\n\n`/assets/homepage.css`\n\n```css\nmain {\n\tbackground: rebeccapurple;\n}\n\naside {\n\tbackground: hotpink;\n}\n```\n\n\n**[↑ back to Usage](#usage)**\n\n\n### Your layout\n\nThe layout are all [react](https://facebook.github.io/react/) components. You have to assign a layout to each page and partial. Each component will have a\nbunch of props exposed to it.\n\n\n#### A page layout\n\nA typical component for a page might look like this:\n\n```jsx\nimport React from \"react\";\n\nexport default ({ title, partials }) =\u003e (\n\t\u003chtml\u003e\n\t\u003chead\u003e\n\t\t\u003ctitle\u003e{ title }\u003c/title\u003e\n\t\u003c/head\u003e\n\t\u003cbody\u003e\n\t\t\u003cmain\u003e\n\t\t\t\u003ch1\u003e{ title }\u003c/h1\u003e\n\t\t\t\u003cdiv\u003e{ partials }\u003c/div\u003e\n\t\t\u003c/main\u003e\n\t\u003c/body\u003e\n\t\u003c/html\u003e\n);\n```\n\n\n#### A partial layout\n\nA typical component for a partial might look like this:\n\n```jsx\nimport React from \"react\";\n\nexport default ({ _body, title }) =\u003e (\n\t\u003carticle\u003e\n\t\t\u003ch2\u003e{ title }\u003c/h2\u003e\n\t\t\u003cdiv\u003e{ _body }\u003c/div\u003e\n\t\u003c/article\u003e\n);\n```\n\n_(_ 💡 _You can access the page your partial was called in via: `props._pages[ props._ID ]`.)_\n\n\n#### Props\n\nA file will receive the following props:\n\n| prop name      | description                                                                           | Example                                       |\n|----------------|---------------------------------------------------------------------------------------|-----------------------------------------------|\n| `_ID`          | The ID of the current page                                                            | `props._ID`                                   |\n| `_self`        | The relative path to the content file; can be md or yaml file                         | `props._self`                                 |\n| `_isDocs`      | A boolean value, `true` in docs context only                                          | `props._isDocs`                               |\n| `_parents`     | An array of all parent pages IDs                                                      | `props._parents`                              |\n| `_body`        | The body of your markdown file (empty for `index.yml` files)                          | `props._body`                                 |\n| `_pages`       | An object of all pages and their props; with ID as key                                | `props._pages.map()`                          |\n| `_nav`         | A nested object of your site structure                                                | `Object.keys( props._nav ).map()`             |\n| `_globalProp`  | A prop that can be set globally from the `package.json`                               | `props._globalProp`                           |\n| `_storeSet`    | You can set data to persist between react components by setting them with this helper | `props._storeSet({ variable: \"value\" })`      |\n| `_store`       | To get that data just call this prop function                                         | `props._store()`                              |\n| `_relativeURL` | A helper function to make an absolute URL relative                                    | `props._relativeURL( URL, yourLocation )`     |\n| `_parseMD`     | A helper function to parse markdown into HTML                                         | `props._parseMD('Your **markdown**!')`        |\n| `_parseYaml`   | A helper function to parse yaml into an object                                        | `props._parseYaml('test:\\n  - one\\n  - two')` |\n| `_parseReact`  | A helper function to parse a react component into a string                            | `props._parseReact( \u003cComponent prop=\"hi\"/\u003e )` |\n\nPlus all other variables declared inside the file either as `frontmatter` or in the `yaml` files.\n\n\n#### Async data fetching\n\nIf you require data from an API or some other async operations you can use the static `getInitialProps` method in your component:\n\n```jsx\nimport React, { Component } from 'react';\n\nclass GetData extends Component {\n\tstatic async getInitialProps( props ) {\n\t\tconst data = await FetchMyDataFromSomewhere( props._ID );\n\t\treturn { data };\n\t}\n\n\trender() {\n\t\treturn (\n\t\t\t\u003cdiv\u003e\n\t\t\t\tMy Data: { this.props.data }\n\t\t\t\u003c/div\u003e\n\t\t);\n\t}\n}\n\nexport default GetData;\n```\n\n`getInitialProps` will be executed before we render the HTML and whatever you return from it will be passed into your react component as a prop.\nThe function will have access to all props of that partial as well.\n\n_(_ 💡 _Make sure you return an object so that you can find your prop again.)_\n\n\n**[↑ back to Usage](#usage)**\n\n\n### Customizations\n\nCuttlebelle can be customized via your own `package.json` file.\n\n_(_ 💡 _You can generate it via `npm init` if you don’t have `package.json`.)_\n\nSee below all configuration with default values:\n\n```diff\n{\n\t\"name\": \"your name\",\n\t\"version\": \"1.0.0\",\n\t\"description\": \"Your description\",\n\t\"main\": \"index.js\",\n\t\"scripts\": {\n\t\t\"test\": \"echo \\\"Error: no test specified\\\" \u0026\u0026 exit 1\"\n\t},\n+\t\"cuttlebelle\": {\n+\t\t\"folder\": {\n+\t\t\t\"content\": \"/content/\",\n+\t\t\t\"code\": \"/code/\",\n+\t\t\t\"site\": \"/site/\",\n+\t\t\t\"docs\": \"/docs/\",\n+\t\t\t\"assets\": \"/assets/\",\n+\t\t\t\"index\": \"index\",\n+\t\t\t\"homepage\": \"index\"\n+\t\t},\n+\t\t\"layouts\": {\n+\t\t\t\"page\": \"page\",\n+\t\t\t\"partial\": \"partial\"\n+\t\t},\n+\t\t\"site\": {\n+\t\t\t\"root\": \"/\",\n+\t\t\t\"doctype\": \"\u003c!DOCTYPE html\u003e\",\n+\t\t\t\"redirectReact\": true,\n+\t\t\t\"markdownRenderer\": \"\",\n+\t\t\t\"watchTimeout\": 400,\n+\t\t\t\"browserSync\": {},\n+\t\t\t\"globalProp\": {},\n+\t\t},\n+\t\t\"docs\": {\n+\t\t\t\"root\": \"files/\",\n+\t\t\t\"index\": \".template/docs/layout/index.js\",\n+\t\t\t\"category\": \".template/docs/layout/category.js\",\n+\t\t\t\"IDProp\": \"page2\",\n+\t\t\t\"navProp\": {},\n+\t\t\t\"pagesProp\": {}\n+\t\t}\n+\t},\n\t\"keywords\": [],\n\t\"author\": \"\",\n\t\"license\": \"ISC\"\n}\n```\n\nA breakdown:\n\n```js\n\"cuttlebelle\": {                  // The cuttlebelle object\n  \"folder\": {                     // Where we can adjust folder/file names\n    \"content\": \"content/\",        // Where does your content live?\n    \"code\": \"code/\",              // Where do your react layouts live?\n    \"assets\": \"assets/\",          // Where do your assets live?\n    \"site\": \"site/\",              // Where do you want to generate your static site to?\n    \"docs\": \"docs\",               // Where do you want to generate the docs to?\n    \"index\": \"index\",             // What is the name of the file we look for to generate pages?\n    \"homepage\": \"index\"           // What should the index folder be named?\n  },\n  \"layouts\": {                    // Your layout settings\n    \"page\": \"page\",               // What is the default layout for pages?\n    \"partial\": \"partial\"          // What is the default layout for partials?\n  },\n  \"site\": {                       // General settings\n    \"root\": \"/\",                  // What should cuttlebelle append to links?\n    \"doctype\": \"\u003c!DOCTYPE html\u003e\", // What doctype string do you want to add?\n    \"redirectReact\": true,        // You can disable redirecting `import` calls to the locally\n                                  // installed react instance of cuttlebelle rather than your\n                                  // local folder.\n    \"markdownRenderer\": \"\",       // A path to a file that `module.exports` an Marked.Renderer()\n                                  // object. Learn more about it here:\n                                  // https://github.com/chjj/marked#renderer\n                                  // The only addition is the `preparse` key that will be run\n                                  // before we go into the markdown parsing\n    \"watchTimeout\": 400,          // This is the time in milliseconds the watch waits\n                                  // to detect a double saves action\n    \"browserSync\": {},            // You can overwrite the browserSync options here\n                                  // https://www.browsersync.io/docs/options\n    \"globalProp\": {}              // A global prop that can be set here accessible for all pages\n  },\n  \"docs\": {                                          // Docs settings\n    \"root\": \"files/\",                                // What is the root folder called where all\n                                                     // categories are generated in\n    \"index\": \".template/docs/layout/index.js\",       // The path to the index layout file\n    \"category\": \".template/docs/layout/category.js\", // The path to the category layout file\n                                                     // All following settings are the default props\n                                                     // each component is given for the example\n\n                                                     // The following props are important so we\n                                                     // can generate the docs example:\n    \"IDProp\": \"page2\",                               // The _ID prop\n    \"selfProp\": \"body.md\",                           // The _self prop\n    \"navProp\": {                                     // The _nav prop\n      \"index\": {\n        \"page1\": \"page1\",\n        \"page2\": {\n          \"page2/nested\": \"page2/nested\"\n        },\n        \"page3\": \"page3\"\n      }\n    },\n    \"pagesProp\": {                                   // The _pages prop\n      \"page1\": {\n        \"_url\": \"/page1\",\n        \"title\": \"Page 1\"\n      },\n      \"page2\": {\n        \"_url\": \"/page2\",\n        \"title\": \"Page 2\"\n      },\n      \"page2/nested\": {\n        \"_url\": \"/page2/nested\",\n        \"title\": \"Nested in page 2\"\n      },\n      \"page3\": {\n        \"_url\": \"/page3\",\n        \"title\": \"Page 3\"\n      },\n      \"index\": {\n        \"_url\": \"/\",\n        \"title\": \"Homepage\"\n      }\n    }\n  }\n}\n```\n\n\n**[⬆ back to top](#contents)**\n\n\n----------------------------------------------------------------------------------------------------------------------------------------------------------------\n\n\n## Self-documenting\n\nBecause you now can separate the content flow from the development flow you will still need to communicate what partials and layouts the content authors have\nto their disposal and how they might use it.\n\nCuttlebelle has a built in feature that will generate documentation for your components automatically as long as you use\n[PropTypes](https://facebook.github.io/react/docs/typechecking-with-proptypes.html) and a comment above them that reflects the `yaml`.\n\n```jsx\nCards.propTypes = {\n  /**\n   * level: \"2\"\n   */\n  level: PropTypes.oneOf([ '1', '2', '3', '4', '5', '6' ]).isRequired,\n\n  /**\n   * hero: true\n   */\n  hero: PropTypes.bool,\n\n  /**\n   * cards:\n   *   - title: Card 1\n   *     content: Content for card 1\n   *     href: http://link/to\n   *   - title: Card 2\n   *     content: Content for card 2\n   *     href: http://link/to\n   *   - title: Card 3\n   *     content: Content for card 3\n   *     href: http://link/to\n   *   - title: Card 4\n   *     content: Content for card 4\n   *     href: http://link/to\n   */\n  cards: PropTypes.arrayOf(\n    PropTypes.shape({\n      title: PropTypes.string.isRequired,\n      content: PropTypes.string.isRequired,\n      href: PropTypes.string,\n    })\n  ).isRequired,\n};\n```\n\nYou can also hide a component from the docs by adding the `@disable-docs` to the main comment before declaring your component:\n\n```jsx\nimport PropTypes from 'prop-types';\nimport React from \"react\";\n\n/**\n * Hiding this component from the docs\n *\n * @disable-docs\n */\nconst Hidden = ({ _body, title }) =\u003e (\n  \u003carticle className={`globalheader`}\u003e\n    \u003ch1\u003e{ title }\u003c/h1\u003e\n    { _body }\n  \u003c/article\u003e\n);\n\nHidden.propTypes = {\n  /**\n   * title: Welcome\n   */\n  title: PropTypes.string.isRequired,\n\n  /**\n   * _body: (text)(7)\n   */\n  _body: PropTypes.node.isRequired,\n};\n\nexport default Hidden;\n```\n\nOnce all your components have those comments cuttlebelle can generate the docs for you. All you have to do it run:\n\n```shell\ncuttlebelle docs\n```\n\nThe docs will be generated by default in the `docs/` folder of your project.\n\n\n### Supported PropTypes\n\n- PropTypes.array\n- PropTypes.bool\n- PropTypes.number\n- PropTypes.object\n- PropTypes.string\n- PropTypes.node\n- PropTypes.element\n- PropTypes.oneOfType\n- PropTypes.arrayOf\n- PropTypes.objectOf\n- PropTypes.shape\n- PropTypes.any\n\n### PropTypes that can’t be passed with YAML\n\n- PropTypes.func\n- PropTypes.symbol\n- PropTypes.instanceOf\n\n\n**[⬆ back to top](#contents)**\n\n\n----------------------------------------------------------------------------------------------------------------------------------------------------------------\n\n\n## Build\n\n**We use Yarn on this project to ensure deterministic builds across node versions**\n\nTo contribute to this still young project you need to install it’s dependencies and run a watch to transpile the files.\n\n```shell\nyarn install\nyarn run watch\n```\n\n_(_ 💡 _Please look at the coding style and work with it, not against it :smile:.)_\n\n\n**[⬆ back to top](#contents)**\n\n\n----------------------------------------------------------------------------------------------------------------------------------------------------------------\n\n\n## Tests\n\nCuttlebelle is being automatically tested on the below systems and node versions.\n\nhttps://travis-ci.org/cuttlebelle/cuttlebelle.svg?branch=master\n\n| OS        | Node version | Node version | Node version |\n|-----------|--------------|--------------|--------------|\n| `Linux`   | `~8`         | `~10`         | `~12`       |\n| `OSX`     | `~8`         | `~10`         | `~12`       |\n\nI got an [end-to-end test script](https://github.com/cuttlebelle/cuttlebelle/blob/master/tests/tester.js) that compares fixtures to what cuttlebelle\ngenerates. In each of those folders I test for [specific things](https://github.com/cuttlebelle/cuttlebelle/blob/master/tests/tester.js#L30) and make sure\nthe checksum of the generated files match the fixtures. In addition to that I created as many\n[unit tests](https://github.com/cuttlebelle/cuttlebelle/tree/master/tests/__unit__) as I can via [Jest](https://facebook.github.io/jest/).\n[Flow](https://flow.org/) types are also checked where they are specified.\n\n- `yarn run test` to run all tests\n- `yarn run test:end-to-end` will run the end-to-end test only\n- `yarn run test:unit-test` will run the unit test only\n- `yarn run test:flow` will run the flow type test\n- `yarn run test:detail` will give you coverage infos for the unit tests\n- `yarn run test:watch` will spin up the jest watch\n\n\n**[⬆ back to top](#contents)**\n\n\n----------------------------------------------------------------------------------------------------------------------------------------------------------------\n\n\n## Release History\n\nSee [CHANGELOG](https://github.com/cuttlebelle/cuttlebelle/blob/master/CHANGELOG.md).\n\n**[⬆ back to top](#contents)**\n\n\n----------------------------------------------------------------------------------------------------------------------------------------------------------------\n\n\n## License\n\nCopyright (c) Dominik Wilkowski. Licensed under [GNU-GPLv3](https://raw.githubusercontent.com/cuttlebelle/cuttlebelle/master/LICENSE).\n\n\n**[⬆ back to top](#contents)**\n\n# };\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fcuttlebelle%2Fcuttlebelle","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fcuttlebelle%2Fcuttlebelle","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fcuttlebelle%2Fcuttlebelle/lists"}