{"id":16922125,"url":"https://github.com/julien-marcou/static-html","last_synced_at":"2025-04-11T16:51:12.893Z","repository":{"id":44197977,"uuid":"383603277","full_name":"Julien-Marcou/static-html","owner":"Julien-Marcou","description":"A simple, lightweight and fast static HTML website generator which makes use of Template Literals","archived":false,"fork":false,"pushed_at":"2023-02-25T11:04:50.000Z","size":145,"stargazers_count":10,"open_issues_count":0,"forks_count":1,"subscribers_count":1,"default_branch":"main","last_synced_at":"2025-03-25T12:46:10.715Z","etag":null,"topics":["builder","generator","html","render","static","template","website"],"latest_commit_sha":null,"homepage":"","language":"TypeScript","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/Julien-Marcou.png","metadata":{"files":{"readme":"README.md","changelog":null,"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,"dei":null,"publiccode":null,"codemeta":null}},"created_at":"2021-07-06T21:35:20.000Z","updated_at":"2024-05-01T15:27:31.000Z","dependencies_parsed_at":"2025-02-19T20:42:39.060Z","dependency_job_id":null,"html_url":"https://github.com/Julien-Marcou/static-html","commit_stats":null,"previous_names":[],"tags_count":4,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Julien-Marcou%2Fstatic-html","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Julien-Marcou%2Fstatic-html/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Julien-Marcou%2Fstatic-html/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Julien-Marcou%2Fstatic-html/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/Julien-Marcou","download_url":"https://codeload.github.com/Julien-Marcou/static-html/tar.gz/refs/heads/main","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":248442983,"owners_count":21104311,"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":["builder","generator","html","render","static","template","website"],"created_at":"2024-10-13T19:54:12.789Z","updated_at":"2025-04-11T16:51:12.883Z","avatar_url":"https://github.com/Julien-Marcou.png","language":"TypeScript","funding_links":[],"categories":[],"sub_categories":[],"readme":"\u003cp align=\"center\"\u003e\n  \u003cimg src=\"https://raw.githubusercontent.com/Julien-Marcou/static-html/main/logo.png\" alt=\"\"\u003e\n\u003c/p\u003e\n\n# Static-Html\n\n[![NPM Package](https://img.shields.io/npm/v/static-html?label=release\u0026color=%23cd2620\u0026logo=npm)](https://www.npmjs.com/package/static-html)\n[![GitHub Repository](https://img.shields.io/github/stars/Julien-Marcou/static-html?color=%23f5f5f5\u0026logo=github)](https://github.com/Julien-Marcou/static-html)\n\n![Downloads per Month](https://img.shields.io/npm/dm/static-html)\n![Gzip Size](https://img.shields.io/bundlephobia/minzip/static-html?label=gzip%20size)\n![MIT License](https://img.shields.io/npm/l/static-html)\n\nStatic-Html is a simple, lightweight and fast static HTML website generator which makes use of [Template Literals](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Template_literals) to let you render JavaScript data and expressions into HTML templates.\n\nStatic-Html is not a binding engine as it do not keep track of your data or expressions to update portions of your templates dynamically, templates are only render once.\n\nIf your looking for a more advanced rendering engine with dynamic data binding, I suggest you take a look at [Lit](https://lit.dev/) on which this project is inspired.\n\n## Install\n\n```shell\nnpm install static-html\n```\n\n## Table of Contents\n\n- [Getting Started](#getting-started)\n- [Minimal Project Example](#minimal-project-example)\n- [HTML Tag \u0026 Syntax Highlighting](#html-tag--syntax-highlighting)\n- [Building your Website](#building-your-website)\n- [Assets](#assets)\n  - [Asset Revisions with Content Hash](#asset-revisions-with-content-hash)\n  - [External Assets](#external-assets)\n- [Static Data Rendering](#static-data-rendering)\n- [Dynamic Data Rendering](#dynamic-data-rendering)\n- [Special Variables](#special-variables)\n  - [Page URL \u0026 Active Page](#page-url--active-page)\n  - [Page Scripts](#page-scripts)\n- [Development Stack](#development-stack)\n- [Deploy to Production](#deploy-to-production)\n- [URL Redirections](#url-redirections)\n\n## Getting Started\n\nAfter installing `static-html`, make sure you are in the root folder of your project, then run this command :\n\n```shell\nnpx static init\n```\n\nIt will create the default structure for your project so you are ready to go. \n\nThe `src` folder contains all the source files needed to build your static website.\n\nThe `src/website.json` file contains the configurations for your static website and its content must match this interface :\n\n```typescript\n{\n  // The title of the website\n  \"title\": string,\n\n  // The description of the website\n  \"description\"?: string,\n\n  // The keywords of the website\n  \"keywords\"?: Array\u003cstring\u003e,\n\n  // The pages of the website\n  \"pages\"?: Array\u003c{\n\n    // The name of the page (used for the url and the corresponding page's template)\n    \"name\": string,\n\n    // The title of the page\n    \"title\": string,\n\n    // The static data of the page (see the \"Static Data Rendering\" section)\n    \"data\"?: Record\u003cstring, any\u003e,\n\n    // Scripts to attach before the closing \u003c/body\u003e (see the \"Page Scripts\" section)\n    \"scripts\"?: Array\u003cstring\u003e,\n  }\u003e,\n  \n  // Assets which need revisions for cache busting (see the \"Asset Revisions with Content Hash\" section)\n  \"assetRevisions\"?: Array\u003c{\n    \"key\": string,\n    \"source\": string,\n    \"target\": string,\n  }\u003e,\n\n  // Assets that are out of the \"src\" folder (see the \"External Assets\" section)\n  \"externalAssets\"?: Array\u003c{\n    \"source\": string,\n    \"target\": string,\n  }\u003e,\n}\n```\n\nThe `src/template.ts` file contains the common HTML template for all your pages and its content must match this interface :\n\n```typescript\nimport { Page, Website } from 'static-html';\n\n/**\n * @param content - The rendered content for the current page\n * @param page - The current page configuration \u0026 data\n * @param website - The website configuration\n * @return The fully rendered page\n */\nexport default (content: string, page: Page, website: Website) =\u003e string | Promise\u003cstring\u003e;\n```\n\nThe `src/pages` folder contains HTML templates for each of your pages, so that each of them have a `src/pages/{pageName}.ts` file and the corresponding entry inside the `src/website.json`.\n\nEach `src/pages/{pageName}.ts` file content must match this interface :\n\n```typescript\nimport { Page, Website } from 'static-html';\n\n/**\n * @param data - The data for the current page\n * @param page - The current page configuration \u0026 data\n * @param website - The website configuration\n * @return The rendered content for the current page\n */\nexport default (data: Record\u003cstring, any\u003e, page: Page, website: Website) =\u003e string | Promise\u003cstring\u003e;\n```\n\n## Minimal Project Example\n\nHere is the bare minimum configuration that you need in order to create a static website with a single `index` page :\n\n```\n📂 my-website/\n├── 📂 src/\n│   ├── 📂 pages/\n│   │   └── 📄 index.ts\n│   ├── 📄 template.ts\n│   └── 📄 website.json\n└── 📄 package.json\n```\n\n```jsonc\n// src/website.json\n{\n  \"title\": \"My website\",\n  \"pages\": [\n    {\n      \"name\": \"index\",\n      \"title\": \"Homepage\"\n    }\n  ]\n}\n```\n\n```typescript\n// src/template.ts\nexport default (content, page, website) =\u003e\n\n`\u003c!doctype html\u003e\n\u003chtml lang=\"en\"\u003e\n\u003chead\u003e\n  \u003cmeta charset=\"utf-8\"\u003e\n  \u003ctitle\u003e${website.title} - ${page.title}\u003c/title\u003e\n\u003c/head\u003e\n\u003cbody\u003e\n  ${content}\n\u003c/body\u003e`;\n```\n\n```typescript\n// src/pages/index.ts\nexport default () =\u003e\n\n`\u003ch1\u003eWelcome to my website\u003c/h1\u003e`;\n```\n\n## HTML Tag \u0026 Syntax Highlighting\n\nAlthough this is not mandatory, I highly suggest you to prefix all your Template Literals with the `html` tag provided by `static-html`.\n\n[Tagged Template Literals](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Template_literals#tagged_templates) allow for custom parsing of the given template.\n\nBut it's mostly usefull as it will also allow you to enable syntax highlighting and language support for HTML inside of your Template Literals by installing the corresponding extension to your code editor (e.g. `lit-html` for VSCode).\n\n```typescript\nexport default () =\u003e\n\n`\u003ch1\u003eWelcome to my website\u003c/h1\u003e`;\n```\n\nWill become : \n\n```typescript\nimport { html } from 'static-html';\n\nexport default () =\u003e\n\nhtml`\u003ch1\u003eWelcome to my website\u003c/h1\u003e`;\n```\n\nThe only difference between our `html` tagged template literal and a non-tagged template literal is the parsing of `array` items.\n\nWith a non-tagged template literal, items are joined with a comma, while `html` tagged template literal will join them with an empty string.\n\nIt's very handy when you have an array of items that you want to render, as you only need to map() each items to the desired HTML output :\n\n```typescript\nimport { html } from 'static-html';\n\nconst items = [1, 2, 3];\n\nexport default () =\u003e html`\n\u003cul\u003e\n  ${items.map(item =\u003e html`\n    \u003cli\u003e\n      Item ${i}\n    \u003c/li\u003e\n  `)}\n\u003c/ul\u003e`;\n```\n\nWill render : \n\n```html\n\u003cul\u003e\n  \u003cli\u003e\n    Item 1\n  \u003c/li\u003e\n  \u003cli\u003e\n    Item 2\n  \u003c/li\u003e\n  \u003cli\u003e\n    Item 3\n  \u003c/li\u003e\n\u003c/ul\u003e\n```\n\nInstead of : \n\n```html\n\u003cul\u003e\n  \u003cli\u003e\n    Item 1\n  \u003c/li\u003e\n  ,\n  \u003cli\u003e\n    Item 2\n  \u003c/li\u003e\n  ,\n  \u003cli\u003e\n    Item 3\n  \u003c/li\u003e\n\u003c/ul\u003e\n```\n\nIf for some reason you want the original behavior of non-tagged template literals, and keep syntax highlighting, you can define your own `html` tag using the native `String.raw` tag (identical behavior to non-tagged template literal), instead of using the one defined by `static-html` :\n\n```typescript\nconst html = String.raw;\n\nexport default () =\u003e html`\u003ch1\u003eTitle\u003c/h1\u003e`;\n```\n\n## Building your Website\n\n```shell\nnpx static build\n```\n\nWill generate your static website into the `dist` folder :\n\n```\n📂 my-website/dist/\n└── 📄 index.html\n```\n\n```html\n\u003c!-- dist/index.html --\u003e\n\u003c!doctype html\u003e\n\u003chtml lang=\"en\"\u003e\n\u003chead\u003e\n  \u003cmeta charset=\"utf-8\"\u003e\n  \u003ctitle\u003eMy website - Homepage\u003c/title\u003e\n\u003c/head\u003e\n\u003cbody\u003e\n  \u003ch1\u003eWelcome to my website\u003c/h1\u003e\n\u003c/body\u003e\n```\n\n## Assets\n\nYou can add assets to your website, that will automatically be copied from the `src/assets` folder to the `dist` folder :\n\n```\n📂 my-website/src/assets/\n├── 📂 css/\n│   └── 📄 style.css\n├── 📂 img/\n│   └── 📄 logo.svg\n└── 📄 favicon.ico\n```\n\nWill generate :\n\n```\n📂 my-website/dist/\n├── 📂 css/\n│   └── 📄 style.css\n├── 📂 img/\n│   └── 📄 logo.svg\n└── 📄 favicon.ico\n```\n\n### Asset Revisions with Content Hash\n\nIf you have some assets that frequently change and can end up being cached for too long by browsers (e.g. your `style.css` file), you can generate a `content hash` for these files in order to force browsers to reload them when they have changed :\n\n```jsonc\n// src/website.json\n{\n  // ...\n  \"assetRevisions\": [\n    {\n      \"key\": \"style\", // Will be used in the HTML template to retrieve the generated file\n      \"source\": \"/css/style.css\", // The file must be located inside the `src/assets` folder\n      \"target\": \"/css/style.{contentHash}.css\"\n    }\n  ]\n}\n```\n\n```\n📂 my-website/src/assets/css/\n└── 📄 style.css\n```\n\nWill generate (keep in mind the hash will change each time the content of the file changes) :\n\n```shell\n📂 my-website/dist/css/\n└── 📄 style.1454857a471481dc40384d3fb9f2c9b7.css\n```\n\nThen in your HTML template, you can retrieve the path to the asset revision like this :\n\n```typescript\n// src/template.ts\nimport { html } from 'static-html';\n\nexport default (website, page, content) =\u003e\n\nhtml`\u003c!doctype html\u003e\n\u003chtml lang=\"en\"\u003e\n\u003chead\u003e\n  \u003cmeta charset=\"utf-8\"\u003e\n  \u003ctitle\u003e${website.title} - ${page.title}\u003c/title\u003e\n  \u003clink rel=\"icon\" type=\"image/x-icon\" href=\"/favicon.ico\"\u003e\n  \u003clink rel=\"stylesheet\" href=\"${website.assets.style.url}\"\u003e\n\u003c/head\u003e\n\u003cbody\u003e\n  ${content}\n\u003c/body\u003e`;\n```\n\nWich will give you :\n\n```html\n\u003c!-- dist/index.html --\u003e\n\u003c!doctype html\u003e\n\u003chtml lang=\"en\"\u003e\n\u003chead\u003e\n  \u003cmeta charset=\"utf-8\"\u003e\n  \u003ctitle\u003eMy website - Homepage\u003c/title\u003e\n  \u003clink rel=\"icon\" type=\"image/x-icon\" href=\"/favicon.ico\"\u003e\n  \u003clink rel=\"stylesheet\" href=\"/css/style.1454857a471481dc40384d3fb9f2c9b7.css\"\u003e\n\u003c/head\u003e\n\u003cbody\u003e\n  \u003ch1\u003eWelcome to my website\u003c/h1\u003e\n\u003c/body\u003e\n```\n\nKeep in mind that only the assets with revision can be accessed through the `website.assets` dictionnary.\n\n### External Assets\n\nIn addition to the `src/assets` folder, you can require other assets to be copied to the `dist` folder, from anywhere inside your project's root folder (e.g. if you want to use an asset from a node module) :\n\n```jsonc\n// src/website.json\n{\n  // ...\n  \"externalAssets\": [\n    {\n      \"source\": \"/node_modules/bootstrap/bootstrap.css\", // The file must be located inside your project's root folder\n      \"target\": \"/css/bootstrap.css\"\n    }\n  ]\n}\n```\n\n```\n📂 my-website/node_modules/bootstrap/\n└── 📄 bootstrap.css\n```\n\nWill generate:\n\n```shell\n📂 my-website/dist/css/\n└── 📄 bootstrap.css\n```\n\n## Static Data Rendering\n\nFor each pages, you can add `data` that will be made available to your page template :\n\n```jsonc\n// src/website.json\n{\n  // ...\n  \"pages\": [\n    {\n      \"name\": \"index\",\n      \"title\": \"Homepage\",\n      // Will be given as the first arguments of `src/pages/index.ts`\n      \"data\": {\n        \"defaultColor\": \"#0f0\",\n        \"colors\": [\n          {\n            \"value\": \"#f00\",\n            \"label\": \"Red\"\n          },\n          {\n            \"value\": \"#0f0\",\n            \"label\": \"Green\"\n          },\n          {\n            \"value\": \"#00f\",\n            \"label\": \"Blue\"\n          }\n        ]\n      }\n    }\n  ]\n}\n```\n\n```typescript\n// src/pages/index.ts\nimport { html } from 'static-html';\n\n// Destructuring the data object so it's simpler to use\nexport default ({defaultColor, colors}) =\u003e\n\nhtml`\u003ch1\u003eWelcome to my website\u003c/h1\u003e\n\n\u003cform\u003e\n  \u003ch2\u003eWhat is your favorite color?\u003c/h2\u003e\n\n  ${colors.map(color =\u003e html`\n    \u003clabel\u003e\n      ${color.label}\n      \u003cinput\n        type=\"checkbox\"\n        value=\"${color.value}\"\n        ${color.value === defaultColor ? 'checked' : ''}\u003e\n    \u003c/label\u003e\n  `)}\n\n  \u003cbutton\u003eSubmit\u003c/button\u003e\n\u003c/form\u003e`;\n```\n\nWich will give you :\n\n```html\n\u003c!-- dist/index.html --\u003e\n\u003c!doctype html\u003e\n\u003chtml lang=\"en\"\u003e\n\u003chead\u003e\n  \u003cmeta charset=\"utf-8\"\u003e\n  \u003ctitle\u003eMy website - Homepage\u003c/title\u003e\n\u003c/head\u003e\n\u003cbody\u003e\n  \u003ch1\u003eWelcome to my website\u003c/h1\u003e\n\n  \u003cform\u003e\n    \u003ch2\u003eWhat is your favorite color?\u003c/h2\u003e\n\n    \u003clabel\u003e\n      Red\n      \u003cinput type=\"checkbox\" value=\"#f00\"\u003e\n    \u003c/label\u003e\n\n    \u003clabel\u003e\n      Green\n      \u003cinput type=\"checkbox\" value=\"#0f0\" checked\u003e\n    \u003c/label\u003e\n\n    \u003clabel\u003e\n      Blue\n      \u003cinput type=\"checkbox\" value=\"#00f\"\u003e\n    \u003c/label\u003e\n\n    \u003cbutton\u003eSubmit\u003c/button\u003e\n  \u003c/form\u003e\n\u003c/body\u003e\n```\n\n## Dynamic Data Rendering\n\nFor each page, if you have data that must be resolved before being renderer (e.g. calling an API), you can create the corresponding `{pageName}.ts` file inside the `src/resolvers` folder.\n\nEach `src/resolvers/{pageName}.ts` file content must match this interface :\n\n```typescript\nimport { Page } from 'static-html';\n\n/**\n * @param page - The current page configuration \u0026 data\n */\nexport default (page: Page) =\u003e void | Promise\u003cvoid\u003e;\n```\n\nHere is a quick example :\n\n```typescript\n// src/resolvers/{pageName}.ts\n\nfunction async getPhotos() {\n  // ...\n}\n\nexport default async (page) =\u003e {\n  page.data.photos = await getPhotos();\n}\n```\n\nThe page's `data` will then be made available to your page template :\n\n```typescript\n// src/pages/{pageName}.ts\nimport { html } from 'static-html';\n\nexport default ({photos}) =\u003e\n\nhtml`\u003cul\u003e\n  ${photos.map(photo =\u003e html`\n    \u003cli\u003e\n      \u003cimg src=\"${photo.src}\" alt=\"${photo.atl}\"\u003e\n    \u003c/li\u003e\n  `)}\n\u003c/ul\u003e`;\n```\n\n## Special Variables\n\nSome special variables are available through the `page` \u0026 `website` arguments that are given to `src/template.ts` \u0026 `src/pages/{pageName}.ts` files.\n\nSome useful variables you may already have seen :\n\n- `website.title`\n- `website.description`\n- `website.keywords`\n- `page.title`\n\n### Page URL \u0026 Active Page\n\nIf you want to change the content of the template based on which page is currently rendered (e.g. add an `active` class on the menu link corresponding to the current page), you can use :\n\n- `website.pages.{pageName}.url`\n- `website.pages.{pageName}.isActive`\n\nWhere `{pageName}` is the camel case version of the page's name defined in the `src/website.json`.\n\nHere is a quick example :\n\n```typescript\n// src/template.ts\nimport { html } from 'static-html';\n\nexport default (content, page, website) =\u003e\n\nhtml`\u003c!doctype html\u003e\n\u003chtml lang=\"en\"\u003e\n\u003chead\u003e\n  \u003cmeta charset=\"utf-8\"\u003e\n  \u003ctitle\u003e${website.title} - ${page.title}\u003c/title\u003e\n\u003c/head\u003e\n\u003cbody\u003e\n  \u003cnav\u003e\n    \u003cul\u003e\n      \u003cli${website.pages.index.isActive ? ' class=\"active\"' : ''}\u003e\n        \u003ca href=\"${website.pages.index.url}\"\u003e\n          Home\n        \u003c/a\u003e\n      \u003c/li\u003e\n      \u003cli${website.pages.termsOfService.isActive ? ' class=\"active\"' : ''}\u003e\n        \u003ca href=\"${website.pages.termsOfService.url}\"\u003e\n          Terms of Service\n        \u003c/a\u003e\n      \u003c/li\u003e\n      \u003cli${website.pages.cookiePolicy.isActive ? ' class=\"active\"' : ''}\u003e\n        \u003ca href=\"${website.pages.cookiePolicy.url}\"\u003e\n          Cookie Policy\n        \u003c/a\u003e\n      \u003c/li\u003e\n    \u003c/ul\u003e\n  \u003c/nav\u003e\n  ${content}\n\u003c/body\u003e`;\n```\n\nand :\n\n```jsonc\n// src/website.json\n{\n  \"title\": \"My website\",\n  \"pages\": [\n    {\n      \"name\": \"index\",\n      \"title\": \"Homepage\"\n    },\n    {\n      \"name\": \"terms-of-service\",\n      \"title\": \"Our Terms of Service\" \n    },\n    {\n      \"name\": \"cookie-policy\",\n      \"title\": \"Our Cookie Policy\" \n    }\n  ]\n}\n```\n\nWill render (for the `index` page) :\n\n```html\n\u003c!-- dist/index.html --\u003e\n\u003c!doctype html\u003e\n\u003chtml lang=\"en\"\u003e\n\u003chead\u003e\n  \u003cmeta charset=\"utf-8\"\u003e\n  \u003ctitle\u003eMy website - Homepage\u003c/title\u003e\n\u003c/head\u003e\n\u003cbody\u003e\n  \u003cnav\u003e\n    \u003cul\u003e\n      \u003cli class=\"active\"\u003e\n        \u003ca href=\"/\"\u003e\n          Home\n        \u003c/a\u003e\n      \u003c/li\u003e\n      \u003cli\u003e\n        \u003ca href=\"/terms-of-service\"\u003e\n          Terms of Service\n        \u003c/a\u003e\n      \u003c/li\u003e\n      \u003cli\u003e\n        \u003ca href=\"/cookie-policy\"\u003e\n          Cookie Policy\n        \u003c/a\u003e\n      \u003c/li\u003e\n    \u003c/ul\u003e\n  \u003c/nav\u003e\n  \u003ch1\u003eWelcome to my website\u003c/h1\u003e\n\u003c/body\u003e`;\n```\n\n### Page Scripts\n\nYou can include JS `scripts` for a specific page like that :\n\n```jsonc\n// src/website.json\n{\n  // ...\n  \"pages\": [\n    {\n      \"name\": \"index\",\n      \"title\": \"Homepage\",\n      \"scripts\": [\n        // The files must be located inside the `src/assets` folder\n        \"/js/my-script.js\",\n        \"/js/another-script.js\"\n      ]\n    }\n  ]\n}\n```\n\n```typescript\n// src/template.ts\nimport { html } from 'static-html';\n\nexport default (website, page, content) =\u003e\n\nhtml`\u003c!doctype html\u003e\n\u003chtml lang=\"en\"\u003e\n\u003chead\u003e\n  \u003cmeta charset=\"utf-8\"\u003e\n  \u003ctitle\u003e${website.title} - ${page.title}\u003c/title\u003e\n\u003c/head\u003e\n\u003cbody\u003e\n  ${content}\n  ${page.scripts}\n\u003c/body\u003e`;\n```\n\nWich will give you :\n\n```html\n\u003c!-- dist/index.html --\u003e\n\u003c!doctype html\u003e\n\u003chtml lang=\"en\"\u003e\n\u003chead\u003e\n  \u003cmeta charset=\"utf-8\"\u003e\n  \u003ctitle\u003eMy website - Homepage\u003c/title\u003e\n\u003c/head\u003e\n\u003cbody\u003e\n  \u003ch1\u003eWelcome to my website\u003c/h1\u003e\n  \u003cscript src=\"/js/my-script.js\"\u003e\u003c/script\u003e\n  \u003cscript src=\"/js/another-script.js\"\u003e\u003c/script\u003e\n\u003c/body\u003e\n```\n\n## Development Stack\n\n```shell\nnpx static watch\n```\n\nWill automatically rebuild your website when there is a change to any file inside the `src` folder.\n\n```shell\nnpx static serve\n```\n\nWill do the same as the previous command, but it will also create a local server for the `dist` folder at `http://localhost:4200` and will automatically reload your browser after each rebuild.\n\nYou can also change the port of local server using the `--port` option :\n\n```shell\nnpx static serve --port 8080\n```\n\n## Deploy to Production\n\nWith static websites, you want to rely on the browser's cache system, where the browser will try to keep in memory resources previously requested as long as they have not changed.\n\nBecause of that, you don't want to overwrite your production files when they didn't changed (overwriting a file when the content hasn't changed will still change the `last modified` date and may force the browser to clear its cache for this file).\n\n```shell\nnpx static deploy \u003cproduction-directory\u003e\n```\n\nThis command will ask you if you want to continue before automatically deploying the content of the `dist` folder to the `production-directory` folder whitout overwritting files that have not changed.\n\nIt will also removed stale files and directories so the content of the `production-directory` folder exactly matches the content of the `dist` folder.\n\nYou can also skip all interactions with the prompt using the `--no-interaction` option (e.g. if you use a CRON to regularly deploy your static website) :\n\n```shell\nnpx static deploy \u003cproduction-directory\u003e --no-interaction\n```\n\nAfter a sucessful deployement, you can use the following command to clean the `dist` folder (does the same as `rm -rf dist`) as it is not needed anymore :\n\n```shell\nnpx static clean\n```\n\n## URL Redirections\n\nAltough you will need a `.htaccess` on your production server if you don't want the `.html` extension in the URLs, the local developement server will automatically make the index page accessible through the `/` url (i.e. `http://localhost:4200/` instead of `http://localhost:4200/index.html`), and all other pages will be made accessible without the `.html` extension (e.g `http://localhost:4200/page` instead of `http://localhost:4200/page.html`).\n\nThe recommended `.htaccess` is :\n\n```apacheconf\n# src/assets/.htaccess\nRewriteEngine On\n\n# Redirect \"/page.html\" to \"/page\" (only if \"/page.html\" exists)\nRewriteCond %{REQUEST_FILENAME} -f\nRewriteCond %{THE_REQUEST} /(.+)\\.html [NC]\nRewriteRule ^(.+)\\.html$ /$1 [NC,R=301,L]\n\n# Redirect \"/index\" to \"/\"\nRewriteRule ^index$ / [NC,R=301,L]\n\n# Load \"/page.html\" when requesting \"/page\" (only if \"/page.html\" exists)\nRewriteCond %{DOCUMENT_ROOT}%{REQUEST_URI}.html -f\nRewriteRule ^ /%{REQUEST_URI}.html [QSA,L]\n```\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fjulien-marcou%2Fstatic-html","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fjulien-marcou%2Fstatic-html","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fjulien-marcou%2Fstatic-html/lists"}