{"id":17117731,"url":"https://github.com/anderspitman/tuplates-py","last_synced_at":"2026-02-01T22:31:55.210Z","repository":{"id":81835980,"uuid":"321250041","full_name":"anderspitman/tuplates-py","owner":"anderspitman","description":"Commit valid code; not templates","archived":false,"fork":false,"pushed_at":"2024-09-11T14:58:30.000Z","size":30,"stargazers_count":3,"open_issues_count":0,"forks_count":0,"subscribers_count":2,"default_branch":"main","last_synced_at":"2025-04-10T19:05:36.658Z","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":null,"status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/anderspitman.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":"2020-12-14T06:05:40.000Z","updated_at":"2024-09-11T23:38:10.000Z","dependencies_parsed_at":null,"dependency_job_id":"3a724c58-c565-4c0c-861f-96d431b08d96","html_url":"https://github.com/anderspitman/tuplates-py","commit_stats":null,"previous_names":["anderspitman/tuplates-py"],"tags_count":6,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/anderspitman%2Ftuplates-py","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/anderspitman%2Ftuplates-py/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/anderspitman%2Ftuplates-py/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/anderspitman%2Ftuplates-py/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/anderspitman","download_url":"https://codeload.github.com/anderspitman/tuplates-py/tar.gz/refs/heads/main","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":248665157,"owners_count":21142118,"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-10-14T17:52:35.172Z","updated_at":"2026-02-01T22:31:55.203Z","avatar_url":"https://github.com/anderspitman.png","language":"Python","funding_links":[],"categories":[],"sub_categories":[],"readme":"# Introduction\n\nTale as old as time. I started developing a new static site. At first it was\njust a single page. But\nof course eventually I needed to add more pages and a header section with links to\nthem, and I needed that header section on every page. And of course HTML doesn't\nhave a way to\n[include another HTML file](https://css-tricks.com/the-simplest-ways-to-handle-html-includes/),\nbecause the popularity of JavaScript frameworks has slowed progress in\nother web APIs.\n\ntuplates is a simple way to template your code, without creating a\ndependency on your templating tool. Rather than replacing your template tags,\nit works by replacing lines between your template tags, are in\nsingle-line comments. This means that both\nyour template and code live together in your files. When you need to make a \nchange, you update the fragment and re-run tuplates, and it replaces the old\ncode. This way you can check your actual code into source control, rather than\nyour templates.\n\nThe name comes from \"update templates\".\n\n\n# Installation\n\nJust copy tuplates.py into your repo. Or rewrite it from scratch in your\nlanguage of choice. It's very simple.\n\n# Usage\n\nCreate a directory containing your fragments as files. You can\nname them whatever you want.\n\n```\nproject_directory/\n  index.html\n  index.js\n  templates/\n    header.html\n    footer.html\n    data.txt\n```\n\nInsert tuplate tags as comments in your code:\n\n```html\n\u003c!-- index.html --\u003e\n\u003c!doctype html\u003e\n\u003chtml\u003e\n  \u003chead\u003e\n    \u003cmeta charset=\"utf-8\"\u003e\n    \u003cmeta name=\"viewport\" content=\"width=device-width, initial-scale=1\" /\u003e\n    \u003ctitle\u003etuplates\u003c/title\u003e\n\n  \u003c/head\u003e\n\n  \u003cbody\u003e\n    \u003c!-- tuplate_start(templates/header.html) --\u003e\n    \u003c!-- tuplate_end() --\u003e\n\n    \u003cmain\u003e\n      \u003ch1\u003eMy Awesome Content\u003c/h1\u003e\n    \u003c/main\u003e\n\n    \u003c!-- tuplate_start(templates/footer.html) --\u003e\n    \u003c!-- tuplate_end() --\u003e\n  \u003c/body\u003e\n\n\u003c/html\u003e\n```\n\n```javascript\n// index.js\nconsole.log(\"Hi there\");\n\n// tuplate_start(data.js)\n// tuplate_end()\n\nconsole.log(data);\n```\n\nRun the `tuplates.py` script in the directory above the `templates` directory.\nIt will walk the directory and replace the lines between the tags with the\ntuplates.\n\nAfter running once, the source files might look something like this:\n\n```html\n\u003c!-- index.html --\u003e\n\u003c!doctype html\u003e\n\u003chtml\u003e\n  \u003chead\u003e\n    \u003cmeta charset=\"utf-8\"\u003e\n    \u003cmeta name=\"viewport\" content=\"width=device-width, initial-scale=1\" /\u003e\n    \u003ctitle\u003etuplates\u003c/title\u003e\n  \u003c/head\u003e\n\n  \u003cbody\u003e\n    \u003c!-- tuplate_start(templates/header.html) --\u003e\n    \u003c!-- tuplates/header.html --\u003e\n    \u003cdiv class='header'\u003e\n      \u003ch1\u003eMy Awesome Header\u003c/h1\u003e\n    \u003c/div\u003e\n    \u003c!-- tuplate_end() --\u003e\n\n    \u003cmain\u003e\n      \u003ch1\u003eMy Awesome Content\u003c/h1\u003e\n    \u003c/main\u003e\n\n    \u003c!-- tuplate_start(templates/footer.html) --\u003e\n    \u003c!-- tuplates/footer.html --\u003e\n    \u003cdiv class='footer'\u003e\n      \u003ch1\u003eAwesome Footer\u003c/h1\u003e\n    \u003c/div\u003e\n    \u003c!-- tuplate_end() --\u003e\n  \u003c/body\u003e\n\n\u003c/html\u003e\n```\n\n```javascript\n// index.js\nconsole.log(\"Hi there\");\n\n// tuplate_start(template/data.js)\n// tuplates/data.js\nconst data = {\n  a: 1,\n  b: 2,\n  c: 3,\n};\n// tuplate_end()\n\nconsole.log(data);\n```\n\nIf you re-run it again, the output doesn't change (unless you've changed\nthe source fragments), even though there's already\ncode between the tuplate tags. It simply replaces whatever is in there (if\nthere is anything). Just make sure your tuplate comments are on a line by\nthemselves.\n\nYou can also import from URLs:\n\n```javascript\n// leftpad.js\n// tuplate_start(https://cdn.jsdelivr.net/npm/left-pad@1.3.0/index.js)\n// tuplate_end()\n```\n\nbecomes:\n\n```javascript\n// leftpad.js\n// tuplate_start(https://cdn.jsdelivr.net/npm/left-pad@1.3.0/index.js)\n/* This program is free software. It comes without any warranty, to\n     * the extent permitted by applicable law. You can redistribute it\n     * and/or modify it under the terms of the Do What The Fuck You Want\n     * To Public License, Version 2, as published by Sam Hocevar. See\n     * http://www.wtfpl.net/ for more details. */\n'use strict';\nmodule.exports = leftPad;\n\nvar cache = [\n  '',\n  ' ',\n  '  ',\n  '   ',\n  '    ',\n  '     ',\n  '      ',\n  '       ',\n  '        ',\n  '         '\n];\n\nfunction leftPad (str, len, ch) {\n  // convert `str` to a `string`\n  str = str + '';\n  // `len` is the `pad`'s length now\n  len = len - str.length;\n  // doesn't need to pad\n  if (len \u003c= 0) return str;\n  // `ch` defaults to `' '`\n  if (!ch \u0026\u0026 ch !== 0) ch = ' ';\n  // convert `ch` to a `string` cuz it could be a number\n  ch = ch + '';\n  // cache common use cases\n  if (ch === ' ' \u0026\u0026 len \u003c 10) return cache[len] + str;\n  // `pad` starts with an empty string\n  var pad = '';\n  // loop\n  while (true) {\n    // add `ch` to `pad` if `len` is odd\n    if (len \u0026 1) pad += ch;\n    // divide `len` by 2, ditch the remainder\n    len \u003e\u003e= 1;\n    // \"double\" the `ch` so this operation count grows logarithmically on `len`\n    // each time `ch` is \"doubled\", the `len` would need to be \"doubled\" too\n    // similar to finding a value in binary search tree, hence O(log(n))\n    if (len) ch += ch;\n    // `len` is 0, exit the loop\n    else break;\n  }\n  // pad `str`!\n  return pad + str;\n}\n// tuplate_end()\n```\n\nIf name collisions are a problem, you can do things like this:\n\n```javascript\n// leftpad.js\nconst leftPadFunc = (function() {\n  const module = {};\n// tuplate_start(https://cdn.jsdelivr.net/npm/left-pad@1.3.0/index.js)\n// tuplate_end()\n  return leftPad;\n})();\n```\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fanderspitman%2Ftuplates-py","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fanderspitman%2Ftuplates-py","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fanderspitman%2Ftuplates-py/lists"}