{"id":13500159,"url":"https://github.com/sunainapai/makesite","last_synced_at":"2025-05-15T04:00:18.949Z","repository":{"id":41254664,"uuid":"125613815","full_name":"sunainapai/makesite","owner":"sunainapai","description":"Simple, lightweight, and magic-free static site/blog generator for Python coders","archived":false,"fork":false,"pushed_at":"2023-03-04T18:35:06.000Z","size":43,"stargazers_count":1836,"open_issues_count":8,"forks_count":302,"subscribers_count":60,"default_branch":"master","last_synced_at":"2025-04-14T03:11:26.445Z","etag":null,"topics":[],"latest_commit_sha":null,"homepage":"","language":"Python","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/sunainapai.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":"CONTRIBUTING.md","funding":null,"license":"LICENSE.md","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}},"created_at":"2018-03-17T09:18:54.000Z","updated_at":"2025-04-06T10:30:47.000Z","dependencies_parsed_at":"2024-01-29T18:51:48.165Z","dependency_job_id":null,"html_url":"https://github.com/sunainapai/makesite","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/sunainapai%2Fmakesite","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/sunainapai%2Fmakesite/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/sunainapai%2Fmakesite/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/sunainapai%2Fmakesite/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/sunainapai","download_url":"https://codeload.github.com/sunainapai/makesite/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":254270640,"owners_count":22042858,"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":[],"created_at":"2024-07-31T22:00:51.967Z","updated_at":"2025-05-15T04:00:18.906Z","avatar_url":"https://github.com/sunainapai.png","language":"Python","funding_links":[],"categories":["资源列表","Python","静态站点生成器","Static Site Generator","Static Site Generator [🔝](#readme)","Uncategorized"],"sub_categories":["静态站点生成器","Uncategorized"],"readme":"makesite.py\n===========\n\nTake full control of your static website/blog generation by writing your\nown simple, lightweight, and magic-free static site generator in\nPython. That's right! Reinvent the wheel!\n\n[![View Source][SOURCE-BADGE]](makesite.py)\n[![View Demo][DEMO-BADGE]](https://tmug.github.io/makesite-demo)\n[![MIT License][LICENSE-BADGE]](LICENSE.md)\n\n[SOURCE-BADGE]: https://img.shields.io/badge/view-source-brightgreen.svg\n[DEMO-BADGE]: https://img.shields.io/badge/view-demo-brightgreen.svg\n[LICENSE-BADGE]: https://img.shields.io/badge/license-MIT-blue.svg\n\n\nContents\n--------\n\n* [Introduction](#introduction)\n* [But Why?](#but-why)\n* [Get Started](#get-started)\n* [The Code](#the-code)\n* [Layout](#layout)\n* [Content](#content)\n* [FAQ](#faq)\n* [Credits](#credits)\n* [License](#license)\n* [Support](#support)\n\n\nIntroduction\n------------\n\nThis repository contains the source code of an example website\ncontaining two static blogs and a few static pages. The website can be\ngenerated by running [makesite.py](makesite.py). The output looks like\n[this](https://tmug.github.io/makesite-demo). That's it!\n\nSo go ahead, fork this repository, replace the [content](content) with\nyour own, and generate your static website. It's that simple!\n\nYou are [free](LICENSE.md) to copy, use, and modify this project for\nyour blog or website, so go ahead and fork this repository and make it\nyour own project. Change the [layout](layout) if you wish to, improve\nthe [stylesheet](static/css/style.css) to suit your taste, enhance\n[makesite.py](makesite.py) if you need to, and develop your website/blog\njust the way you want it.\n\n\nBut Why?\n--------\n\nFor fun and profit! Okay, maybe not for profit, but hopefully for fun.\n\nHave you used a popular static site generator like Jekyll to generate\nyour blog? I have too. It is simple and great. But then did you yearn\nto use something even simpler to generate your blog? Do you like Python?\nPerhaps the thought of writing your own static site generator crossed\nyour mind but you thought it would be too much work? If you answered\n\"yes\" to these questions, then this project is for you.\n\nWith [makesite.py](makesite.py), you are in full control. There is no\nhidden magic! There is no need to read any documentation to understand\nhow it works. There is no need to learn how to write configuration files\nto produce some desired effect.\n\nWith [makesite.py](makesite.py):\n\n  - The code is the documentation.\n  - The code is the configuration.\n\nEverything is laid out as plain and simple Python code for you to read\nand enhance. It is less than 130 lines of code (excluding comments,\ndocstrings, and blank lines). It gets you off the ground pretty quickly.\nYou only need to execute `makesite.py`.\n\nYou can develop a decent website/blog within a few minutes and then you\ncan begin tinkering with the [source code](makesite.py), the\n[layout](layout), and the [stylesheet](static/css/style.css) to\ncustomize the look and feel of your website to your satisfaction.\n\n\nGet Started\n-----------\n\nThis section provides some quick steps to get you off the ground as\nquickly as possible.\n\n 1. For a quick demo on your local system, just enter this command:\n\n        make serve\n\n    If you don't have `make` but have Python 3.x, enter this command:\n\n        python3 makesite.py\n        cd _site\n        python3 -m http.server\n\n    Note: In some environments, you may need to use `python` instead of\n    `python3` to invoke Python 3.x.\n\n    If you only have Python 2.7, enter this command:\n\n        python makesite.py\n        cd _site\n        python -m SimpleHTTPServer\n\n    Then visit http://localhost:8000/. It should look like\n    [this](https://tmug.github.io/makesite-demo).\n\n    Note: You can run [makesite.py](makesite.py) with Python 2.7 or\n    Python 3.x.\n\n 2. You may see a few `Cannot render Markdown` warning messages in the\n    output of the previous command. This is due to the fact that an\n    example [blog](content/blog) in this project has a few posts written\n    in Markdown. To render them correctly, install the `commonmark`\n    package with this command:\n\n        pip install commonmark\n\n    Then try the previous step again.\n\n 3. For an Internet-facing website, you would be hosting the static\n    website/blog on a hosting service and/or with a web server such as\n    Apache HTTP Server, Nginx, etc. You probably only need to generate\n    the static files and know where the static files are and move them\n    to your hosting location.\n\n    If you have the `make` command, enter this command to generate your\n    website:\n\n        make site\n\n    If you don't have `make` but have `python3`, enter this command:\n\n        python3 makesite.py\n\n    Note: In some environments, you may need to use `python` instead of\n    `python3` to invoke Python 3.x.\n\n    If you only have `python`, enter this command:\n\n        python makesite.py\n\n    The `_site` directory contains the entire generated website. The\n    content of this directory may be copied to your website hosting\n    location.\n\n\nThe Code\n--------\n\nNow that you know how to generate the static website that comes with\nthis project, it is time to see what [makesite.py](makesite.py) does.\nYou probably don't really need to read the entire section. The source\ncode is pretty self-explanatory but just in case, you need a detailed\noverview of what it does, here are the details:\n\n 1. The `main()` function is the starting point of website generation.\n    It calls the other functions necessary to get the website generation\n    done.\n\n 2. First it creates a fresh new `_site` directory from scratch. All\n    files in the [static directory](static) are copied to this\n    directory. Later the static website is generated and written to this\n    directory.\n\n 3. Then it creates a `params` dictionary with some default parameters.\n    This dictionary is passed around to other functions. These other\n    functions would pick values from this dictionary to populate\n    placeholders in the layout template files.\n\n    Let us take the `subtitle` parameter for example. It is set\n    to our example website's fictitious brand name: \"Lorem Ipsum\". We\n    want each page to include this brand name as a suffix in the title.\n    For example, the [about page](https://tmug.github.io/makesite-demo/about/)\n    has \"About - Lorem Ipsum\" in its title. Now take a look at the\n    [page layout template](layout/page.html) that is used as the layout\n    for all pages in the static website. This layout file uses the\n    `{{ subtitle }}` syntax to denote that it is a placeholder that\n    should be populated while rendering the template.\n\n    Another interesting thing to note is that a content file can\n    override these parameters by defining its own parameters in the\n    content header. For example, take a look at the content file for\n    the [home page](content/_index.html). In its content header, i.e.,\n    the HTML comments at the top with key-value pairs, it defines a new\n    parameter named `title` and overrides the `subtitle` parameter.\n\n    We will discuss the syntax for placeholders and content headers\n    later. It is quite simple.\n\n 4. It then loads all the layout templates. There are 6 of them in this\n    project.\n\n      - [layout/page.html](layout/page.html): It contains the base\n        template that applies to all pages. It begins with\n        `\u003c!DOCTYPE html\u003e` and `\u003chtml\u003e`, and ends with `\u003c/html\u003e`. The\n        `{{ content }}` placeholder in this template is replaced with\n        the actual content of the page. For example, for the about page,\n        the `{{ content }}` placeholder is replaced with the the entire\n        content from [content/about.html](content/about.html). This is\n        done with the `make_pages()` calls further down in the code.\n\n      - [layout/post.html](layout/post.html): It contains the template\n        for the blog posts. Note that it does not begin with `\u003c!DOCTYPE\n        html\u003e` and does not contain the `\u003chtml\u003e` and `\u003c/html\u003e` tags.\n        This is not a complete standalone template. This template\n        defines only a small portion of the blog post pages that are\n        specific to blog posts.  It contains the HTML code and the\n        placeholders to display the title, publication date, and author\n        of blog posts.\n\n        This template must be combined with the\n        [page layout template](layout/page.html) to create the final\n        standalone template. To do so, we replace the `{{ content }}`\n        placeholder in the [page layout template](layout/page.html) with\n        the HTML code in the [post layout template](layout/post.html) to\n        get a final standalone template. This is done with the\n        `render()` calls further down in the code.\n\n        The resulting standalone template still has a `{{ content }}`\n        placeholder from the [post layout template](layout/post.html)\n        template.  This `{{ content }}` placeholder is then replaced\n        with the actual content from the [blog posts](content/blog).\n\n      - [layout/list.html](layout/list.html): It contains the template\n        for the blog listing page, the page that lists all the posts in\n        a blog in reverse chronological order. This template does not do\n        much except provide a title at the top and an RSS link at the\n        bottom.  The `{{ content }}` placeholder is populated with the\n        list of blog posts in reverse chronological order.\n\n        Just like the [post layout template](layout/post.html) , this\n        template must be combined with the\n        [page layout template](layout/page.html) to arrive at the final\n        standalone template.\n\n      - [layout/item.html](layout/item.html): It contains the template\n        for each blog post item in the blog listing page. The\n        `make_list()` function renders each blog post item with this\n        template and inserts them into the\n        [list layout template](layout/list.html) to create the blog\n        listing page.\n\n      - [layout/feed.xml](layout/feed.xml): It contains the XML template\n        for RSS feeds. The `{{ content }}` placeholder is populated with\n        the list of feed items.\n\n      - [layout/item.xml](layout/item.xml): It contains the XML template for\n        each blog post item to be included in the RSS feed. The\n        `make_list()` function renders each blog post item with this\n        template and inserts them into the\n        [layout/feed.xml](layout/feed.xml) template to create the\n        complete RSS feed.\n\n 5. After loading all the layout templates, it makes a `render()` call\n    to combine the [post layout template](layout/post.html) with the\n    [page layout template](layout/page.html) to form the final\n    standalone post template.\n\n    Similarly, it combines the [list layout template](layout/list.html)\n    template with the [page layout template](layout/page.html) to form\n    the final list template.\n\n 6. Then it makes two `make_pages()` calls to render the home page and a\n    couple of other site pages: the [contact page](content/contact.html)\n    and the [about page](content/about.html).\n\n 7. Then it makes two more `make_pages()` calls to render two blogs: one\n    that is named simply [blog](content/blog) and another that is named\n    [news](content/news).\n\n    Note that the `make_pages()` call accepts three positional\n    arguments:\n\n      - Path to content source files provided as a glob pattern.\n      - Output path template as a string.\n      - Layout template code as a string.\n\n    These three positional arguments are then followed by keyword\n    arguments. These keyword arguments are used as template parameters\n    in the output path template and the layout template to replace the\n    placeholders with their corresponding values.\n\n    As described in point 2 above, a content file can override these\n    parameters in its content header.\n\n 8. Then it makes two `make_list()` calls to render the blog listing\n    pages for the two blogs. These calls are very similar to the\n    `make_pages()` calls. There are only two things that are different\n    about the `make_list()` calls:\n\n      - There is no point in reading the same blog posts again that were\n        read by `make_pages()`, so instead of passing the path to\n        content source files, we feed a chronologically reverse-sorted\n        index of blog posts returned by `make_pages()` to `make_list()`.\n      - There is an additional argument to pass the\n        [item layout template](layout/item.html) as a string.\n\n 9. Finally it makes two more `make_list()` calls to generate the RSS\n    feeds for the two blogs. There is nothing different about these\n    calls than the previous ones except that we use the feed XML\n    templates here to generate RSS feeds.\n\nTo recap quickly, we create a `_site` directory to write the static site\ngenerated, define some default parameters, load all the layout\ntemplates, and then call `make_pages()` to render pages and blog posts\nwith these templates, call `make_list()` to render blog listing pages\nand RSS feeds. That's all!\n\nTake a look at how the `make_pages()` and `make_list()` functions are\nimplemented. They are very simple with less than 20 lines of code each.\nOnce you are comfortable with this code, you can begin modifying it to\nadd more blogs or reduce them. For example, you probably don't need a\nnews blog, so you may delete the `make_pages()` and `make_list()` calls\nfor `'news'` along with its content at [content/news](content/news).\n\n\nLayout\n------\n\nIn this project, the layout template files are located in the [layout\ndirectory](layout). But they don't necessarily have to be there. You can\nplace the layout files wherever you want and update\n[makesite.py](makesite.py) accordingly.\n\nThe source code of [makesite.py](makesite.py) that comes with this\nproject understands the notion of placeholders in the layout templates.\nThe template placeholders have the following syntax:\n\n    {{ \u003ckey\u003e }}\n\nAny whitespace before `{{`, around `\u003ckey\u003e`, and after `}}` is ignored.\nThe `\u003ckey\u003e` should be a valid Python identifier. Here is an example of\ntemplate placeholder:\n\n    {{ title }}\n\nThis is a very simple template mechanism that is implemented already in\nthe [makesite.py](makesite.py). For a simple website or blog, this\nshould be sufficient. If you need a more sophisticated template engine\nsuch as [Jinja2](http://jinja.pocoo.org/) or\n[Cheetah](https://pythonhosted.org/Cheetah/), you need to modify\n[makesite.py](makesite.py) to add support for it.\n\n\nContent\n-------\n\nIn this project, the content files are located in the [content\ndirectory](content). Most of the content files are written in HTML.\nHowever, the content files for the blog named [blog](content/blog) are\nwritten in Markdown.\n\nThe notion of headers in the content files is supported by\n[makesite.py](makesite.py). Each content file may begin with one or more\nconsecutive HTML comments that contain headers. Each header has the\nfollowing syntax:\n\n    \u003c!-- \u003ckey\u003e: \u003cvalue\u003e --\u003e\n\nAny whitespace before, after, and around the `\u003c!--`, `\u003ckey\u003e`, `:`,\n`\u003cvalue\u003e`, and `--\u003e` tokens are ignored. Here are some example headers:\n\n    \u003c!-- title: About --\u003e\n    \u003c!-- subtitle: Lorem Ipsum --\u003e\n    \u003c!-- author: Admin --\u003e\n\nIt looks for the headers at the top of every content file. As soon as\nsome non-header text is encountered, the rest of the content from that\npoint is not checked for headers.\n\nBy default, placeholders in content files are not populated during\nrendering. This behaviour is chosen so that you can write content freely\nwithout having to worry about makesite interfering with the content,\ni.e., you can write something like `{{ title }}` in the content and\nmakesite would leave it intact by default.\n\nHowever if you do want to populate the placeholders in a content file,\nyou need to specify a parameter named `render` with value of `yes`. This\ncan be done in two ways:\n\n  - Specify the parameter in a header in the content file in the\n    following manner:\n\n        \u003c!-- render: yes --\u003e\n\n  - Specify the parameter as a keyword argument in `make_pages` call.\n    For example:\n\n        blog_posts = make_pages('content/blog/*.md',\n                                '_site/blog/{{ slug }}/index.html',\n                                post_layout, blog='blog', render='yes',\n                                **params)\n\nFAQ\n---\n\nHere are some frequently asked questions along with answers to them:\n\n 1. Can you add feature X to this project?\n\n    I do not have any plans to add new features to this project. It is\n    intended to be as minimal and as simple as reasonably possible. This\n    project is meant to be a quick-starter-kit for developers who want\n    to develop their own static site generators. Someone who needs more\n    features is free to fork this project repository and customize the\n    project as per their needs in their own fork.\n\n 2. Can you add support for Jinja templates, YAML front matter, etc.?\n\n    I will not add or accept support for Jinja templates, YAML front\n    matter, etc. in this project. However, you can do so in your fork.\n    The reasons are explained in the first point.\n\n 3. Do you accept any new features from the contributors?\n\n    I do not accept any new features in this project. The reasons are\n    explained in the first point.\n\n 4. Do you accept bug fixes and improvements?\n\n    Yes, I accept bug fixes and minor improvements that do not increase\n    the scope and complexity of this project.\n\n 5. Are there any contribution guidelines?\n\n    Yes, please see [CONTRIBUTING.md](CONTRIBUTING.md).\n\n 6. How do I add my own copyright notice to the source code without\n    violating the terms of license while customizing this project in my\n    own fork?\n\n    This project is released under the terms of the MIT license. One of\n    the terms of the license is that the original copyright notice and\n    the license text must be preserved. However, at the same time, when\n    you edit and customize this project in your own fork, you hold the\n    copyright to your changes. To fulfill both conditions, please add\n    your own copyright notice above the original copyright notice and\n    clarify that your software is a derivative of the original.\n\n    Here is an example of such a notice where a person named J. Doe\n    wants to reserve all rights to their changes:\n\n        # Copyright (c) 2018-2019 J. Doe\n        # All rights reserved\n\n        # This software is a derivative of the original makesite.py.\n        # The license text of the original makesite.py is included below.\n\n    Anything similar to the above notice or something to this effect is\n    sufficient.\n\n\nCredits\n-------\n\nThanks to:\n\n  - [Susam Pal](https://github.com/susam) for the initial documentation\n    and the initial unit tests.\n  - [Keith Gaughan](https://github.com/kgaughan) for an improved\n    single-pass rendering of templates.\n\n\nLicense\n-------\n\nThis is free and open source software. You can use, copy, modify,\nmerge, publish, distribute, sublicense, and/or sell copies of it,\nunder the terms of the [MIT License](LICENSE.md).\n\nThis software is provided \"AS IS\", WITHOUT WARRANTY OF ANY KIND,\nexpress or implied. See the [MIT License](LICENSE.md) for details.\n\n\nSupport\n-------\n\nTo report bugs, suggest improvements, or ask questions, please visit\n\u003chttps://github.com/sunainapai/makesite/issues\u003e.\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fsunainapai%2Fmakesite","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fsunainapai%2Fmakesite","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fsunainapai%2Fmakesite/lists"}