{"id":22031098,"url":"https://github.com/russbaz/experimental-reparse-html","last_synced_at":"2025-05-07T12:22:36.109Z","repository":{"id":225191553,"uuid":"752921645","full_name":"RussBaz/experimental-reparse-html","owner":"RussBaz","description":"Swift Server Side HTML Only Templating Engine with Super Powers","archived":false,"fork":false,"pushed_at":"2024-10-22T11:01:25.000Z","size":289,"stargazers_count":10,"open_issues_count":0,"forks_count":0,"subscribers_count":2,"default_branch":"master","last_synced_at":"2025-04-19T00:36:13.148Z","etag":null,"topics":["html","swift","templating-engine"],"latest_commit_sha":null,"homepage":"","language":"Swift","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/RussBaz.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":"2024-02-05T05:30:10.000Z","updated_at":"2025-01-03T21:01:49.000Z","dependencies_parsed_at":"2024-03-07T05:41:56.183Z","dependency_job_id":"f071b450-903b-497a-ad9a-28ec363784bc","html_url":"https://github.com/RussBaz/experimental-reparse-html","commit_stats":null,"previous_names":["russbaz/experimental-reparse-html"],"tags_count":19,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/RussBaz%2Fexperimental-reparse-html","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/RussBaz%2Fexperimental-reparse-html/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/RussBaz%2Fexperimental-reparse-html/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/RussBaz%2Fexperimental-reparse-html/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/RussBaz","download_url":"https://codeload.github.com/RussBaz/experimental-reparse-html/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":252874362,"owners_count":21817811,"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":["html","swift","templating-engine"],"created_at":"2024-11-30T08:14:16.948Z","updated_at":"2025-05-07T12:22:36.089Z","avatar_url":"https://github.com/RussBaz.png","language":"Swift","readme":"# Reparse - HTML Server Side Templating Super Powers in Swift (Experimental)\n\n## Three Core Concepts\n\n1. Super Powerful and Flexible Syntax\n2. HTML Templating (and not a text templating)\n3. Templates are Compiled into the Swift Code\n\n## Example\n\nHere is an example of the `Reparse` syntax as used in the bundled `Example` project (with few lines removed for brevity):\n\n```html\n\u003cr-require name=\"context\" type=\"[String]\" label=\"superheroes\" /\u003e\n\u003cr-extend name=\"base\" /\u003e\n\u003cr-extend name=\"body\" /\u003e\n\n\u003cr-eval line='req.logger.info(\"Index Debug Message\")' /\u003e\n\n\u003cmain\u003e\n    \u003ch1\u003e\n        \u003cr-set name=\"class\" value=\" red\" append r-if=\"context.isEmpty\" /\u003e\n        Hello\n        \u003cr-include name=\"components.world\" r-if=\"!context.isEmpty\"\u003e\n            Ultra Heroes!\n        \u003c/r-include\u003e\n        \u003cr-block r-else\u003e World?\u003c/r-block\u003e\n    \u003c/h1\u003e\n    \u003col\u003e\n        \u003cli r-for-every=\"context\" r-with-item r-with-index\u003e\n            \u003cp\u003e\n                \u003cr-include name=\"components.hello-me\"\u003e\\(item)\u003c/r-include\u003e\n            \u003c/p\u003e\n            \u003cp\u003eIndex: \\(index)\u003c/p\u003e\n        \u003c/li\u003e\n        \u003cli r-else\u003eNo more heroes...\u003c/li\u003e\n    \u003c/ol\u003e\n\n    \u003cp\u003e\u003cr-value of=\"req.url.string\" /\u003e or \\(req.url.string)\u003c/p\u003e\n\u003c/main\u003e\n\n\u003ctitle r-add-to-slot=\"head\"\u003eHero List\u003c/title\u003e\n```\n\nHere is the default command to build the example pages manually: `swift run reparse ./Resources/Pages ./Sources/Example --parameters req:Request --imports Vapor`\n\nAnd then run the Example project with: `swift run ReparseExample`\n\n## How to Use\n\n### Installation\n\nIf you would like to use it in your own project, firstly, add it to your package dependencies like this:\n\n```swift\n.package(url: \"https://github.com/RussBaz/experimental-reparse-html.git\", from: \"0.0.19\"),\n```\n\nThen add the `ReparseRuntime` as a dependency to your target like this:\n\n```swift\n.product(name: \"ReparseRuntime\", package: \"experimental-reparse-html\"),\n```\n\n### As a Standalone Tool\n\nTo use it as a standalone tool, you would need to `git clone` this repo and then compile the tool like this: `swift build -c release`. Then copy the built tool form the build folder to anywhere you want and run it. Otherwise, you can just call `swift run reparse` from inside this project folder, providing valid arguments and options.\n\nHere is the help page for the tool:\n\n```\nUSAGE: reparse \u003clocation\u003e \u003cdestination\u003e [--file-name \u003cfile-name\u003e] [--file-extension \u003cfile-extension\u003e] [--enum-name \u003cenum-name\u003e] [--imports \u003cimports\u003e ...] [--parameters \u003cparameters\u003e ...] [--protocols \u003cprotocols\u003e ...] [--dry-run]\n\nARGUMENTS:\n  \u003clocation\u003e              The target data folder location.\n  \u003cdestination\u003e           The destination folder for the output file.\n\nOPTIONS:\n  --file-name \u003cfile-name\u003e Output file name (default: pages.swift)\n  --file-extension \u003cfile-extension\u003e\n                          The file extension to be searched for (default: html)\n  --enum-name \u003cenum-name\u003e The name of the generated enum (default: Pages)\n  --imports \u003cimports\u003e     List of global imports\n  --parameters \u003cparameters\u003e\n                          List of shared parameters (parameters to be added to\n                          every 'include' function) in a form of\n                          '[?][label:]name:type[=default]' where the optional\n                          parts are in square brackets. The question mark at the\n                          beginning indicates that the parameter will be\n                          overriden by a local requirement if it is present.\n  --protocols \u003cprotocols\u003e A list of protocols to apply to enums with render\n                          functions in them in a form of\n                          'name[:associatedName:associatedType]' where the\n                          optional parts are in square brackets. Optional part\n                          can be repeated any number of times.\n  --dry-run               Write the output to the console instead of file\n  -h, --help              Show help information.\n```\n\n### Command Plugin\n\nYou can also run it as a command plugin from the terminal. You only have to follow the installation instructions for it to be automatically available.\n\nTo list available plugins:\n\n```\nswift package plugin --list\n```\n\nTo use an automatic Vapor settings, use:\n\n```\nswift package plugin reparse --preset vapor\n```\n\nNOTE: Every Vapor based preset adds 'req: Request' as a first parameter to the generated functions.\n\nof if you are using `VaporHX`:\n\n```\nswift package plugin reparse --preset vaporhx\n```\n\nNOTE: If you are to use it with VaporHX, then you can only 'require' `context` variables because otherwise the genrated code will no longer conform to the `HXTemplateable` protocol, required for automatic use of generated templates.\n\nIn addition to to 'req: Request' coming from a Vapor preset, it also adds 'isPage: Bool' and 'context: Context' parameters. 'isPage' is true when the template is expected to return the whole page and it is false when a fragment of the page is expected (in-place update). Then 'Context' type is `EmptyContext` struct by default, provided by the VaporHX. However, if you specify the 'context' using a 'r-require' tag, then it will be replaced with your own type without breaking compatibility with the VaporHX.\n\nIf you do not use any preset, then all the relative routes would be pointing at the project root folder.\n\nAll the options you can pass are the same except the location and destination arguments are replaced by the following options:\n\n`--source` to specify a different root folder other than than the project root\n\n`--target` to pick a target from the package targets as a destination for the generated file\n\n`--destination` as a list of path components to provide a different output folder relative to the source root folder for the target (or project root if none selected)\n\n## Syntax\n\nAll special attributes and tags will be removed by the compilation and must not appear in the output of the render function. If they do, then it is a bug.\n\n### String Interpolation\n\nYou can use the standard swift syntax for string interpolation `\\(expression)` to insert any swift expression into a text part of the html template - inside the attribute values and the text between tags.\n\n### Control Attributes\n\nThere are a few types of control attributes and can be separated in 3 groups:\n\n#### Conditionals\n\nMost html tags can be equipped with one of the conditional control attributes:\n\n-   **r-if=\"condition\"**\n-   **r-else-if=\"condition\"**\n-   **r-else**\n\nIf the condition (which must be a valid Swift expression) is satisfied than the html tag and its contents are rendered.\n\nIf the condition is satisfied, then a special variable called `previousUnnamedIfTaken` will be set to true. Otherwise, it will be set to false.\n\nOptionally, you can save the result of the condition to a different variable using an additional attribute:\n\n-   **r-tag=\"tag-name\"**\n\n#### Loops\n\n-   **r-for-every=\"sequence\"** to repeat the tag for evey item in a sequence\n-   **r-with-item=\"optional name\"** to include the value of the item in the loop, optionally specifying an alternative name for such a variable. The default name is 'item'\n-   **r-with-index=\"optional name\"** to include the index of the item in the loop, optionally specifying an alternative name for such a variable. The default name is 'index'\n\n#### Slots\n\n-   **r-add-to-slot=\"slot-name\"**\n-   **r-replace-slot=\"slot-name\"**\n\n### Control Tags\n\n-   **`\u003cr-extend name=\"template-name\" /\u003e`** (must be before any tag other than r-require) to wrap the current template into a default slot of the specified template\n-   **`\u003cr-require label=\"optional-label\" name=\"var-name\" type=\"var-type\" default=\"optional-default\" /\u003e`** (must be before any tag other than r-extend) to define a variable that must be passed into the template from the caller\n-   **`\u003cr-require label=\"optional-label\" name=\"var-name\" type=\"var-type\" default=\"optional-default\" mutable /\u003e`** or if you need to remap it to a mutable variable of the same name\n-   or you can add `local-only` attribute to prevent it from being required in outer scopes. You have to provide your own argument for such a parameter in such a case. WARNING! This will disable all protocol conformance for the enums with `local-only` attributes.\n-   **`\u003cr-include name=\"template-name\" /\u003e`** to include another template or\n-   **`\u003cr-include name=\"template-name\"\u003e default slot \u003c/r-include\u003e`** to include a template with a default slot provided\n-   **`\u003cr-block\u003e some data \u003c/r-block\u003e`** to group some part of template, e.g. wrap some text with it and now you can apply control attributes to it.\n-   **`\u003cr-set name=\"attr-name\" value=\"attr-value\" /\u003e`** to replace an attribute in a preceding tag (skipping other set/unset tags) or\n-   **`\u003cr-set name=\"attr-name\" value=\"attr-value\" append /\u003e`** to append to it instead\n-   **`\u003cr-unset name=\"attr-name\" /\u003e`** to remove an atttribute from a preceding tag (skipping other set/unset tags)\n-   **`\u003cr-var name=\"var-name\" line=\"expression\" /\u003e`** to assign the result of an expression to a variable\n-   **`\u003cr-value of=\"name\" default=\"optional-val\" /\u003e`** to paste the value of the specified variable or the provided default value if the value was `nil`\n-   **`\u003cr-eval line=\"expression\" /\u003e`** to paste the expression as is into the generated code\n-   **`\u003cr-slot name=\"optional-name\" /\u003e`** to mark an area to be filled by the incoming outer slot. If no name is provided, it will be known as 'default' or\n-   **`\u003cr-slot name=\"optional-name\"\u003e default slot \u003c/r-slot\u003e`** to declare a slot and provide the default contents if no matching slot found in the incoming outer slots\n\n## How does it work?\n\nIt operates in two stages. There is the compilation stage and the runtime stage.\n\n### Compilation Stage\n\n1. All the templates are discovered, and their names and full paths are recorded. The names are derived from the relative path to the root folder and the name of file.\n2. For each template, a tokeniser converts a stream of characters into a stream of tokens.\n3. The tokens are now parsed into an abstract syntax tree.\n4. The syntax tree is flattened and transpiled into a series of commands for a file builder. References between files and other meta data are recorded.\n5. Function signatures are resolved for every template\n6. All individual command sequences for every template are combined into a single one based and executed.\n7. The final string is saved into the specified location.\n\n### Runtime Stage\n\n#### Rendering the Template\n\n1. The caller passes the paremters to the render function.\n2. The render function passes them to the include function of the specified template.\n3. The include function returns the line storage object with a render method.\n4. Then the render method is called and it will call its resolve method with empty outer slots.\n5. The returned sequence of text constants is then merged into a string and returned to the caller.\n\n#### Resolving the Template\n\n1. Calling the template will call an auto generated function that builds a simple internal model for each template by calling a very limited number of its commands. (done when the include method is called)\n2. The slots are passed into the template and the default slot is resolved. The slots passed into the template are referred as outer slots. (done when the resolve method is called)\n3. All slot declaration sites (including the inside of include commands) are recuresively replaced with either contents of outer slots (if found) and mark them for deletion, or replaces with the defaults. It does nothing if neither are found. Nested declarations are allowed but if the parent 'comsumes' the slot, then it will no longer appear to the nested declarations. Sibling declarations do not have this restriction.\n4. Inner slots are computed - the slots to be passed to included templates. All the commands affecting the slots are resolved.\n5. Default inner slots are computed for every include command and passed into the specified templates. Nested includes are allowed.\n6. Resolved inner templates are merged into the current template\n7. The contents, now consisting of text constant commands only, are returned to the caller.\n","funding_links":[],"categories":[],"sub_categories":[],"project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Frussbaz%2Fexperimental-reparse-html","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Frussbaz%2Fexperimental-reparse-html","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Frussbaz%2Fexperimental-reparse-html/lists"}