├── .gitattributes ├── .gitignore ├── .gitlab-ci.yml ├── .travis.yml ├── LICENSE ├── META6.json ├── README.md ├── bin └── uzu ├── lib ├── Uzu.rakumod └── Uzu │ ├── CLI.rakumod │ ├── Config.rakumod │ ├── HTTP.rakumod │ ├── LiveReload.rakumod │ ├── Logger.rakumod │ ├── Render.rakumod │ ├── Utilities.rakumod │ └── Watch.rakumod ├── logotype └── logo_32x32.png ├── resources ├── mustache │ ├── pages │ │ └── index.mustache │ ├── partials │ │ ├── footer.mustache │ │ └── head.mustache │ └── themes │ │ └── default │ │ └── layout.mustache └── tt │ ├── pages │ └── index.tt │ ├── partials │ ├── footer.tt │ └── head.tt │ └── themes │ └── default │ └── layout.tt ├── t ├── 00-basics.t ├── example_project_mustache │ ├── bad_file.txt │ ├── bad_folder │ │ └── empty_file │ ├── config.yml │ ├── i18n │ │ ├── blog │ │ │ └── fiji │ │ │ │ └── en.yml │ │ ├── en.yml │ │ └── ja.yml │ ├── pages │ │ ├── about.mustache │ │ ├── blog │ │ │ ├── fiji.mustache │ │ │ └── index.mustache │ │ ├── deepembed.mustache │ │ ├── embedded.mustache │ │ ├── empty.mustache │ │ ├── excludeme.mustache │ │ ├── index.mustache │ │ ├── layout.mustache │ │ ├── nolayout.mustache │ │ ├── related.mustache │ │ ├── site_details.json.mustache │ │ └── themepartial.mustache │ ├── partials │ │ ├── deep1.mustache │ │ ├── deep2.mustache │ │ ├── deep3.mustache │ │ ├── embedded.mustache │ │ ├── embedded1.mustache │ │ ├── embedded2.mustache │ │ ├── footer.mustache │ │ ├── head.mustache │ │ ├── home.mustache │ │ ├── jumbotron.mustache │ │ ├── link.mustache │ │ ├── navigation.mustache │ │ ├── profiles.mustache │ │ ├── related.mustache │ │ ├── see_also.mustache │ │ ├── special │ │ │ └── part.mustache │ │ └── usetheme.mustache │ ├── public │ │ └── robots.txt │ └── themes │ │ ├── default │ │ ├── assets │ │ │ └── img │ │ │ │ └── logo.png │ │ ├── layout.mustache │ │ └── partials │ │ │ └── usetheme.mustache │ │ └── summer2017 │ │ ├── assets │ │ └── img │ │ │ └── logo.png │ │ ├── layout.mustache │ │ └── partials │ │ └── usetheme.mustache ├── example_project_tt │ ├── bad_file.txt │ ├── bad_folder │ │ └── empty_file │ ├── config.yml │ ├── i18n │ │ ├── blog │ │ │ └── fiji │ │ │ │ └── en.yml │ │ ├── en.yml │ │ └── ja.yml │ ├── pages │ │ ├── about.tt │ │ ├── blog │ │ │ ├── fiji.tt │ │ │ └── index.tt │ │ ├── deepembed.tt │ │ ├── embedded.tt │ │ ├── empty.tt │ │ ├── excludeme.tt │ │ ├── index.tt │ │ ├── layout.tt │ │ ├── nolayout.tt │ │ ├── related.tt │ │ ├── site_details.json.tt │ │ └── themepartial.tt │ ├── partials │ │ ├── deep1.tt │ │ ├── deep2.tt │ │ ├── deep3.tt │ │ ├── embedded1.tt │ │ ├── embedded2.tt │ │ ├── footer.tt │ │ ├── head.tt │ │ ├── home.tt │ │ ├── jumbotron.tt │ │ ├── navigation.tt │ │ ├── profiles.tt │ │ └── usetheme.tt │ ├── public │ │ └── robots.txt │ └── themes │ │ ├── .default.mustache.swp │ │ ├── default │ │ ├── assets │ │ │ └── img │ │ │ │ └── logo.png │ │ ├── layout.tt │ │ └── partials │ │ │ └── usetheme.tt │ │ └── summer2017 │ │ ├── assets │ │ └── img │ │ │ └── logo.png │ │ ├── layout.tt │ │ └── partials │ │ └── usetheme.tt ├── expected_mustache │ ├── about-ja.html │ ├── about.html │ ├── blog │ │ ├── fiji-ja.html │ │ ├── fiji.html │ │ ├── index-ja.html │ │ └── index.html │ ├── deepembed-ja.html │ ├── deepembed.html │ ├── embedded-ja.html │ ├── embedded.html │ ├── empty-ja.html │ ├── empty.html │ ├── img │ │ └── logo.png │ ├── index-ja.html │ ├── index.html │ ├── nolayout-ja.html │ ├── nolayout.html │ ├── related-ja.html │ ├── related.html │ ├── robots.txt │ ├── site_details-ja.json │ ├── site_details.json │ ├── summer2017 │ │ ├── layout-ja.html │ │ └── layout.html │ ├── themepartial-ja.html │ └── themepartial.html ├── expected_tt │ ├── about-ja.html │ ├── about.html │ ├── blog │ │ ├── fiji-ja.html │ │ ├── fiji.html │ │ ├── index-ja.html │ │ └── index.html │ ├── deepembed-ja.html │ ├── deepembed.html │ ├── embedded-ja.html │ ├── embedded.html │ ├── img │ │ └── logo.png │ ├── index-ja.html │ ├── index.html │ ├── nolayout-ja.html │ ├── nolayout.html │ ├── related-ja.html │ ├── related.html │ ├── robots.txt │ ├── site_details-ja.json │ ├── site_details.json │ ├── summer2017 │ │ ├── layout-ja.html │ │ └── layout.html │ ├── themepartial-ja.html │ └── themepartial.html ├── lib │ ├── Local.rakumod │ └── LocalX.rakumod └── serve │ ├── build │ ├── blog │ │ └── fiji.html │ └── index.html │ ├── config-multi-port-increment.yml │ ├── config-multi.yml │ ├── config.yml │ ├── i18n │ └── en.yml │ ├── pages │ └── index.tt │ ├── partials │ └── blank.tt │ ├── public │ └── robots.txt │ ├── summer2017 │ ├── layout-ja.html │ └── layout.html │ └── themes │ ├── default │ └── layout │ │ └── index.tt │ └── summer2017 │ └── layout.tt └── xt ├── 01-serve.t ├── 02-build-tt.t ├── 03-build-mustache.t └── 04-build-warnings.t /.gitattributes: -------------------------------------------------------------------------------- 1 | /bin/uzu gitlab-language=perl 2 | /lib/*.pm6 gitlab-language=perl 3 | *.pl6 gitlab-language=perl 4 | /t/*.t gitlab-language=perl 5 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | build 2 | .precomp 3 | -------------------------------------------------------------------------------- /.gitlab-ci.yml: -------------------------------------------------------------------------------- 1 | image: rakudo-star:latest 2 | 3 | before_script: 4 | - zef update 5 | - zef install --depsonly --/test . 6 | - zef --/test install . 7 | test: 8 | script: 9 | - prove -ve 'perl6 -Ilib' 10 | only: 11 | - master 12 | - dev 13 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: perl6 2 | sudo: false 3 | perl6: 4 | - 2020.08 5 | install: 6 | - rakudobrew build-zef 7 | - zef --/test install . 8 | - RAKULIB="./t/lib" prove --exec raku -r -Ilib xt/ 9 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The Artistic License 2.0 2 | 3 | Copyright (c) 2000-2015, The Perl Foundation. 4 | 5 | Everyone is permitted to copy and distribute verbatim copies 6 | of this license document, but changing it is not allowed. 7 | 8 | Preamble 9 | 10 | This license establishes the terms under which a given free software 11 | Package may be copied, modified, distributed, and/or redistributed. 12 | The intent is that the Copyright Holder maintains some artistic 13 | control over the development of that Package while still keeping the 14 | Package available as open source and free software. 15 | 16 | You are always permitted to make arrangements wholly outside of this 17 | license directly with the Copyright Holder of a given Package. If the 18 | terms of this license do not permit the full use that you propose to 19 | make of the Package, you should contact the Copyright Holder and seek 20 | a different licensing arrangement. 21 | 22 | Definitions 23 | 24 | "Copyright Holder" means the individual(s) or organization(s) 25 | named in the copyright notice for the entire Package. 26 | 27 | "Contributor" means any party that has contributed code or other 28 | material to the Package, in accordance with the Copyright Holder's 29 | procedures. 30 | 31 | "You" and "your" means any person who would like to copy, 32 | distribute, or modify the Package. 33 | 34 | "Package" means the collection of files distributed by the 35 | Copyright Holder, and derivatives of that collection and/or of 36 | those files. A given Package may consist of either the Standard 37 | Version, or a Modified Version. 38 | 39 | "Distribute" means providing a copy of the Package or making it 40 | accessible to anyone else, or in the case of a company or 41 | organization, to others outside of your company or organization. 42 | 43 | "Distributor Fee" means any fee that you charge for Distributing 44 | this Package or providing support for this Package to another 45 | party. It does not mean licensing fees. 46 | 47 | "Standard Version" refers to the Package if it has not been 48 | modified, or has been modified only in ways explicitly requested 49 | by the Copyright Holder. 50 | 51 | "Modified Version" means the Package, if it has been changed, and 52 | such changes were not explicitly requested by the Copyright 53 | Holder. 54 | 55 | "Original License" means this Artistic License as Distributed with 56 | the Standard Version of the Package, in its current version or as 57 | it may be modified by The Perl Foundation in the future. 58 | 59 | "Source" form means the source code, documentation source, and 60 | configuration files for the Package. 61 | 62 | "Compiled" form means the compiled bytecode, object code, binary, 63 | or any other form resulting from mechanical transformation or 64 | translation of the Source form. 65 | 66 | 67 | Permission for Use and Modification Without Distribution 68 | 69 | (1) You are permitted to use the Standard Version and create and use 70 | Modified Versions for any purpose without restriction, provided that 71 | you do not Distribute the Modified Version. 72 | 73 | 74 | Permissions for Redistribution of the Standard Version 75 | 76 | (2) You may Distribute verbatim copies of the Source form of the 77 | Standard Version of this Package in any medium without restriction, 78 | either gratis or for a Distributor Fee, provided that you duplicate 79 | all of the original copyright notices and associated disclaimers. At 80 | your discretion, such verbatim copies may or may not include a 81 | Compiled form of the Package. 82 | 83 | (3) You may apply any bug fixes, portability changes, and other 84 | modifications made available from the Copyright Holder. The resulting 85 | Package will still be considered the Standard Version, and as such 86 | will be subject to the Original License. 87 | 88 | 89 | Distribution of Modified Versions of the Package as Source 90 | 91 | (4) You may Distribute your Modified Version as Source (either gratis 92 | or for a Distributor Fee, and with or without a Compiled form of the 93 | Modified Version) provided that you clearly document how it differs 94 | from the Standard Version, including, but not limited to, documenting 95 | any non-standard features, executables, or modules, and provided that 96 | you do at least ONE of the following: 97 | 98 | (a) make the Modified Version available to the Copyright Holder 99 | of the Standard Version, under the Original License, so that the 100 | Copyright Holder may include your modifications in the Standard 101 | Version. 102 | 103 | (b) ensure that installation of your Modified Version does not 104 | prevent the user installing or running the Standard Version. In 105 | addition, the Modified Version must bear a name that is different 106 | from the name of the Standard Version. 107 | 108 | (c) allow anyone who receives a copy of the Modified Version to 109 | make the Source form of the Modified Version available to others 110 | under 111 | 112 | (i) the Original License or 113 | 114 | (ii) a license that permits the licensee to freely copy, 115 | modify and redistribute the Modified Version using the same 116 | licensing terms that apply to the copy that the licensee 117 | received, and requires that the Source form of the Modified 118 | Version, and of any works derived from it, be made freely 119 | available in that license fees are prohibited but Distributor 120 | Fees are allowed. 121 | 122 | 123 | Distribution of Compiled Forms of the Standard Version 124 | or Modified Versions without the Source 125 | 126 | (5) You may Distribute Compiled forms of the Standard Version without 127 | the Source, provided that you include complete instructions on how to 128 | get the Source of the Standard Version. Such instructions must be 129 | valid at the time of your distribution. If these instructions, at any 130 | time while you are carrying out such distribution, become invalid, you 131 | must provide new instructions on demand or cease further distribution. 132 | If you provide valid instructions or cease distribution within thirty 133 | days after you become aware that the instructions are invalid, then 134 | you do not forfeit any of your rights under this license. 135 | 136 | (6) You may Distribute a Modified Version in Compiled form without 137 | the Source, provided that you comply with Section 4 with respect to 138 | the Source of the Modified Version. 139 | 140 | 141 | Aggregating or Linking the Package 142 | 143 | (7) You may aggregate the Package (either the Standard Version or 144 | Modified Version) with other packages and Distribute the resulting 145 | aggregation provided that you do not charge a licensing fee for the 146 | Package. Distributor Fees are permitted, and licensing fees for other 147 | components in the aggregation are permitted. The terms of this license 148 | apply to the use and Distribution of the Standard or Modified Versions 149 | as included in the aggregation. 150 | 151 | (8) You are permitted to link Modified and Standard Versions with 152 | other works, to embed the Package in a larger work of your own, or to 153 | build stand-alone binary or bytecode versions of applications that 154 | include the Package, and Distribute the result without restriction, 155 | provided the result does not expose a direct interface to the Package. 156 | 157 | 158 | Items That are Not Considered Part of a Modified Version 159 | 160 | (9) Works (including, but not limited to, modules and scripts) that 161 | merely extend or make use of the Package, do not, by themselves, cause 162 | the Package to be a Modified Version. In addition, such works are not 163 | considered parts of the Package itself, and are not subject to the 164 | terms of this license. 165 | 166 | 167 | General Provisions 168 | 169 | (10) Any use, modification, and distribution of the Standard or 170 | Modified Versions is governed by this Artistic License. By using, 171 | modifying or distributing the Package, you accept this license. Do not 172 | use, modify, or distribute the Package, if you do not accept this 173 | license. 174 | 175 | (11) If your Modified Version has been derived from a Modified 176 | Version made by someone other than you, you are nevertheless required 177 | to ensure that your Modified Version complies with the requirements of 178 | this license. 179 | 180 | (12) This license does not grant you the right to use any trademark, 181 | service mark, tradename, or logo of the Copyright Holder. 182 | 183 | (13) This license includes the non-exclusive, worldwide, 184 | free-of-charge patent license to make, have made, use, offer to sell, 185 | sell, import and otherwise transfer the Package with respect to any 186 | patent claims licensable by the Copyright Holder that are necessarily 187 | infringed by the Package. If you institute patent litigation 188 | (including a cross-claim or counterclaim) against any party alleging 189 | that the Package constitutes direct or contributory patent 190 | infringement, then this Artistic License to you shall terminate on the 191 | date that such litigation is filed. 192 | 193 | (14) Disclaimer of Warranty: 194 | THE PACKAGE IS PROVIDED BY THE COPYRIGHT HOLDER AND CONTRIBUTORS "AS 195 | IS" AND WITHOUT ANY EXPRESS OR IMPLIED WARRANTIES. THE IMPLIED 196 | WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, OR 197 | NON-INFRINGEMENT ARE DISCLAIMED TO THE EXTENT PERMITTED BY YOUR LOCAL 198 | LAW. UNLESS REQUIRED BY LAW, NO COPYRIGHT HOLDER OR CONTRIBUTOR WILL 199 | BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, OR CONSEQUENTIAL 200 | DAMAGES ARISING IN ANY WAY OUT OF THE USE OF THE PACKAGE, EVEN IF 201 | ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 202 | -------------------------------------------------------------------------------- /META6.json: -------------------------------------------------------------------------------- 1 | { 2 | "perl" : "6.d", 3 | "name" : "Uzu", 4 | "license" : "Artistic-2.0", 5 | "version" : "0.3.6", 6 | "description" : "Uzu is a static site generator with built-in web server, file modification watcher, live reload, i18n, themes, multi-page support, inject external data via local Raku module, and external pre/post command execution.", 7 | "tags" : [ "web", "static-site-generator" ], 8 | "provides" : { 9 | "Uzu" : "lib/Uzu.rakumod", 10 | "Uzu::Config" : "lib/Uzu/Config.rakumod", 11 | "Uzu::CLI" : "lib/Uzu/CLI.rakumod", 12 | "Uzu::HTTP" : "lib/Uzu/HTTP.rakumod", 13 | "Uzu::LiveReload" : "lib/Uzu/LiveReload.rakumod", 14 | "Uzu::Logger" : "lib/Uzu/Logger.rakumod", 15 | "Uzu::Render" : "lib/Uzu/Render.rakumod", 16 | "Uzu::Utilities" : "lib/Uzu/Utilities.rakumod", 17 | "Uzu::Watch" : "lib/Uzu/Watch.rakumod" 18 | }, 19 | "authors" : [ "Sam Morrison" ], 20 | "depends" : [ 21 | "File::Directory::Tree", 22 | "File::Find", 23 | "File::Temp", 24 | "Terminal::ANSIColor", 25 | "Template6", 26 | "Template::Mustache", 27 | "Test::Output", 28 | "HTTP::Server::Tiny", 29 | "YAMLish" 30 | ], 31 | "test-depends" : [ "Test::Output" ], 32 | "resources" : [ 33 | "mustache/pages/index.mustache", 34 | "mustache/partials/footer.mustache", 35 | "mustache/partials/head.mustache", 36 | "mustache/themes/default/layout.mustache", 37 | "tt/pages/index.tt", 38 | "tt/partials/footer.tt", 39 | "tt/partials/head.tt", 40 | "tt/themes/default/layout.tt" 41 | ], 42 | "source-url" : "https://github.com/scmorrison/uzu.git" 43 | } 44 | -------------------------------------------------------------------------------- /bin/uzu: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env raku 2 | 3 | use v6; 4 | use Uzu::CLI; 5 | 6 | # vim: ft=raku 7 | -------------------------------------------------------------------------------- /lib/Uzu.rakumod: -------------------------------------------------------------------------------- 1 | use v6; 2 | 3 | unit module Uzu:ver<0.3.6>:auth; 4 | 5 | # License 6 | # 7 | # This module is licensed under the same license as Perl6 itself. 8 | # Artistic License 2.0. 9 | # 10 | # Copyright 2020 Sam Morrison. 11 | 12 | # vim: ft=perl6 13 | -------------------------------------------------------------------------------- /lib/Uzu/CLI.rakumod: -------------------------------------------------------------------------------- 1 | use Uzu::Config; 2 | use Uzu::HTTP; 3 | use Uzu::Render; 4 | use Uzu::Watch; 5 | use Terminal::ANSIColor; 6 | 7 | my %*SUB-MAIN-OPTS = :named-anywhere; 8 | 9 | sub USAGE is export { 10 | say q:to/END/; 11 | Usage: 12 | uzu init - Initialize new project 13 | uzu webserver - Start local web server 14 | uzu build - Render all templates to build 15 | uzu clear - Delete build directory and all of its contents 16 | uzu watch - Start web server and re-render 17 | build on template modification 18 | uzu version - Print uzu version and exit 19 | 20 | Optional arguments: 21 | 22 | --config= - Specify a custom config file 23 | Default is `config` 24 | 25 | e.g. uzu --config=path/to/config.yml init 26 | 27 | --no-livereload - Disable livereload when 28 | running uzu watch. 29 | 30 | --clear - Delete build directory before 31 | render when running with build. 32 | 33 | --page-filter - Restrict build to pages starting 34 | from this directory 35 | 36 | --theme - Limit build / watch to single theme 37 | 38 | e.g. uzu --theme=default build 39 | END 40 | } 41 | 42 | multi MAIN( 43 | 'config', 44 | Str :$config = 'config.yml' 45 | ) is export { 46 | say Uzu::Config::from-file( 47 | config_file => $config.IO); 48 | } 49 | 50 | multi MAIN( 51 | 'webserver', 52 | Str :$config = 'config.yml' 53 | ) is export { 54 | say "ctrl+c / command+c to stop"; 55 | await Uzu::HTTP::web-server 56 | Uzu::Config::from-file( 57 | config_file => $config.IO 58 | ); 59 | } 60 | 61 | multi MAIN( 62 | 'build', 63 | Str :$config = 'config.yml', 64 | Str :$page-filter = '', 65 | Str :$theme, 66 | Bool :$clear = False 67 | ) is export { 68 | my %config = Uzu::Config::from-file 69 | config_file => $config.IO, 70 | page_filter => $page-filter, 71 | single_theme => $theme, 72 | no_livereload => True; 73 | if $clear { 74 | Uzu::Render::clear %config; 75 | } 76 | Uzu::Render::build %config; 77 | } 78 | 79 | multi MAIN( 80 | 'clear', 81 | Str :$config = 'config.yml' 82 | ) is export { 83 | Uzu::Render::clear 84 | Uzu::Config::from-file( 85 | config_file => $config.IO, 86 | no_livereload => True); 87 | } 88 | 89 | multi MAIN( 90 | 'watch', 91 | Str :$config = 'config.yml', 92 | Str :$page-filter = '', 93 | Str :$theme, 94 | Bool :$no-livereload = False 95 | ) is export { 96 | Uzu::Watch::start( 97 | Uzu::Config::from-file( 98 | config_file => $config.IO, 99 | page_filter => $page-filter, 100 | single_theme => $theme, 101 | no_livereload => $no-livereload 102 | )); 103 | } 104 | 105 | multi MAIN( 106 | 'init', 107 | Str :$config = 'config.yml' 108 | ) is export { 109 | 110 | # config file exists, exit 111 | return say "Please run uzu init from an empty directory." unless elems(dir '.') eq 0; 112 | 113 | my Bool $continue = False; 114 | my Str $site_name; 115 | my Str $language; 116 | my Str $template_engine = 'mustache'; 117 | 118 | until $continue 119 | && $site_name !~~ '' 120 | && $template_engine ~~ 'mustache'|'tt' 121 | && so $language ~~ /^<[a..z]> ** 2..2 $/ { 122 | 123 | if $site_name ~~ '' { 124 | say colored "Site name must not be blank.\n", "bold white on_red"; 125 | } 126 | 127 | if $template_engine && $template_engine !~~ 'mustache'|'tt' { 128 | say colored "Template engine must be mustache or tt.\n", "bold white on_red"; 129 | } 130 | 131 | if $language && so $language !~~ /^<[a..z]> ** 2..2 $/ { 132 | say colored "Language must be two character abreviation (e.g. en, ja).\n", "bold white on_red"; 133 | } 134 | 135 | say "Uzu project initialization"; 136 | $site_name = prompt("Please enter site name: ") ~ ''; 137 | $template_engine = prompt("Please enter template engine (mustache / tt): "); 138 | $language = prompt("Please enter language (e.g. en, ja): "); 139 | my $confirmation = qq:to/EOF/; 140 | You have entered following: 141 | 142 | Site name: {$site_name} 143 | Templage engine: {$template_engine} 144 | Language: {$language} 145 | 146 | Enter y to continue 147 | EOF 148 | $continue = prompt($confirmation) ~~ 'y'|'Y'; 149 | } 150 | 151 | if Uzu::Config::init 152 | config_file => $config.IO, 153 | site_name => $site_name, 154 | language => $language||'en', 155 | template_engine => $template_engine||'mustache' { 156 | say "Config [$config] successfully created."; 157 | } 158 | } 159 | 160 | multi MAIN('version') is export { 161 | use Uzu; 162 | say "uzu {Uzu.^ver}"; 163 | } 164 | -------------------------------------------------------------------------------- /lib/Uzu/Config.rakumod: -------------------------------------------------------------------------------- 1 | use YAMLish; 2 | 3 | unit module Uzu::Config; 4 | 5 | sub valid-project-folder-structure( 6 | @template_dirs 7 | --> Bool() 8 | ) { 9 | @template_dirs.grep({ !$_.IO.e }).&{ 10 | if $_ { 11 | note "Project directory missing: \n * {$_.join: "\n * "}"; 12 | exit 1; 13 | } 14 | }(); 15 | } 16 | 17 | sub parse-config( 18 | IO::Path :$config_file 19 | --> Map() 20 | ) { 21 | unless $config_file.IO.f { 22 | note "Config file [{$config_file}] not found. Please run uzu init to generate."; 23 | exit 1; 24 | } 25 | 26 | try { 27 | 28 | CATCH { 29 | default { 30 | note "Invalid config yaml file [{$config_file}]"; 31 | note .Str; 32 | exit 1; 33 | } 34 | } 35 | 36 | my %config = load-yaml slurp($config_file) when $config_file.IO.f; 37 | # Normalize themes 38 | %config = %config.List.hyper.map(-> $theme { 39 | $theme ~~ Iterable ?? $theme.head !! $theme; 40 | }).List; 41 | 42 | # Collect non-core variables into :site 43 | %config = flat(grep({ 44 | # Exclude core vars 45 | # from site hash 46 | .key !(elem) ['exclude', 47 | 'exclude_pages', 48 | 'extended', 49 | 'host', 50 | 'language', 51 | 'i18n_scheme', 52 | 'port', 53 | 'project_root', 54 | 'template_engine', 55 | 'theme', 56 | 'themes', 57 | 'pre_command', 58 | 'post_command'] 59 | }, %config)).Hash; 60 | 61 | return %config; 62 | } 63 | } 64 | 65 | sub build-dir-exists(@seen, $dir) { 66 | if so @seen (cont) $dir { 67 | note "Cannot render multiple themes to the same build directory [{$dir}]" when so @seen (cont) $dir; 68 | exit 1; 69 | } 70 | } 71 | 72 | sub safe-build-dir-check($dir, :$project_root) { 73 | if $dir.IO ~~ $*HOME.IO|$project_root.IO { 74 | note "Build directory [{$dir.IO.path}] cannot be {$*HOME} or project root [{$project_root}]."; 75 | exit 1; 76 | } 77 | } 78 | 79 | sub themes-config( 80 | Str :$single_theme, # optional, only config this theme 81 | IO::Path :$themes_dir, # project themes dir 82 | Str :$theme, # yaml theme: 83 | :@themes, # yaml themes: 84 | IO::Path :$build_dir, # default build dir 85 | Int :$port, # default port 86 | List :$exclude_pages, # default exclude pages 87 | IO::Path :$project_root # project root 88 | ) { 89 | # Always use single theme if yaml `theme:` variable set 90 | return %{ 91 | "{$theme||'default'}" => %{ 92 | theme_dir => $themes_dir.IO.child("$theme"||'default'), 93 | build_dir => $build_dir, 94 | port => ($port||3000), 95 | exclude_pages => $exclude_pages}, 96 | } when defined $theme and $theme !~~ ""; 97 | 98 | # Keep track of build dirs to avoid 99 | # reusing the same build dir for 100 | # multiple themes. 101 | my IO::Path @seen_build_dirs; 102 | 103 | # We can to keep incrementing the 104 | # port to assign to the next theme 105 | # if a port is not defined for the 106 | # theme or the defined port is 107 | # already used. 108 | my Int $working_port = $port; 109 | my %themes; 110 | 111 | # ... otherwise use hash 112 | for hyper @themes -> $theme { 113 | 114 | given $theme { 115 | 116 | # Single theme, $theme = theme name 117 | when Str { 118 | 119 | # When user passes --theme to cli only configure that theme or skip 120 | next when $single_theme.defined && $single_theme !~~ $theme; 121 | 122 | my Int $theme_port = $working_port; 123 | # Increment next port 124 | ++$working_port; 125 | 126 | %themes{"{$theme||'default'}"} = %{ 127 | theme_dir => $themes_dir.IO.child($theme||'default'), 128 | build_dir => $build_dir.IO.child($theme||'default'), 129 | port => $theme_port, 130 | exclude_pages => $exclude_pages 131 | } 132 | } 133 | 134 | when Pair { 135 | 136 | my Str $theme_name = $theme.key; 137 | my Hash $theme_config = $theme.value; 138 | my IO::Path $theme_dir = $themes_dir.IO.child($theme_name); 139 | 140 | # When user passes --theme to cli only configure that theme or skip 141 | next when $single_theme.defined && $single_theme !~~ $theme_name; 142 | 143 | do { 144 | note "Theme directory [{$theme_name}] does not exist. Skipping."; 145 | next; 146 | } unless $theme_dir.IO.e; 147 | 148 | # Single theme build dir 149 | my IO::Path $theme_build_dir = do if @themes.elems eq 1 { 150 | $theme_config:exists ?? $theme_config.IO !! $build_dir; 151 | } else { # Mutiple themes build dir 152 | my $theme_build_dir = $theme_config ?? $theme_config !! $build_dir.IO.child($theme_name); 153 | build-dir-exists @seen_build_dirs, $theme_build_dir; 154 | push @seen_build_dirs, $theme_build_dir.IO; 155 | safe-build-dir-check $theme_build_dir.IO, :$project_root; 156 | $theme_build_dir.IO; 157 | } 158 | 159 | # Single theme port 160 | my Int $theme_port = do if @themes.elems eq 1 { 161 | $theme_config:exists ?? $theme_config.Int !! $working_port; 162 | } else { 163 | # Multiple themes port 164 | if $theme_config:exists && $theme_config > $working_port { 165 | $working_port = $theme_config.Int; 166 | } 167 | $theme_config:exists ?? $theme_config.Int !! $working_port; 168 | } 169 | 170 | # Increment next port 171 | ++$working_port; 172 | 173 | %themes{"{$theme_name}"} = %{ 174 | theme_dir => $theme_dir, 175 | build_dir => $theme_build_dir, 176 | port => $theme_port, 177 | exclude_pages => $theme_config 178 | } 179 | } 180 | } 181 | } 182 | return %themes; 183 | } 184 | 185 | sub run-extended($module) { 186 | return %{} unless $module; 187 | my %extended = %{}; 188 | try { 189 | require ::($module); 190 | if ::($module) !~~ Failure && defined &::($module)::context { 191 | say "Loading from extended [$module]"; 192 | my $extended = &::($module)::context(); 193 | if $extended ~~ Hash { 194 | %extended = $extended; 195 | } else { 196 | say "Routine {$module}::context() does not return Hash"; 197 | } 198 | } else { 199 | say "Extended [$module] is missing context()"; 200 | } 201 | CATCH { 202 | default { 203 | say "Unable to load $module"; 204 | } 205 | } 206 | } 207 | return %extended; 208 | } 209 | 210 | our sub from-file( 211 | IO::Path :$config_file = 'config.yml'.IO, 212 | Str :$page_filter = '', 213 | Str :$single_theme, 214 | Bool :$no_livereload = False 215 | ) { 216 | 217 | # Gemeral config 218 | my %_config = parse-config(config_file => $config_file); 219 | my $project_root = "{%_config||$*CWD}".subst('~', $*HOME).IO; 220 | my %extended = do if %_config:exists { 221 | run-extended(%_config); 222 | } 223 | 224 | my %config = %{ 225 | project_root => $project_root, 226 | language => [%_config.flat], 227 | i18n_scheme => (%_config||'suffix'), 228 | extended => %extended, 229 | _extended => &{ 230 | %_config:exists ?? run-extended(%_config) !! %{}; 231 | }, 232 | refresh_extended => (%_config:exists && %_config.starts-with('f'|'F') ?? False !! True), 233 | site => %_config, 234 | 235 | # Network 236 | host => %_config||'0.0.0.0', 237 | port => (%_config:exists ?? %_config.Int !! 3000), 238 | 239 | # Paths 240 | build_dir => $project_root.IO.child('build'), 241 | i18n_dir => $project_root.IO.child('i18n'), 242 | themes_dir => $project_root.IO.child('themes'), 243 | assets_dir => $project_root.IO.child('themes').child("{%_config||'default'}").child('assets'), 244 | theme_dir => $project_root.IO.child('themes').child("{%_config||'default'}"), 245 | pages_watch_dir => $project_root.IO.child('pages').child($page_filter)||$project_root.IO.child('pages'), 246 | pages_dir => $project_root.IO.child('pages'), 247 | partials_dir => $project_root.IO.child('partials'), 248 | public_dir => $project_root.IO.child('public'), 249 | 250 | # Misc. 251 | template_extensions => %{ tt => ['tt'], mustache => ['ms', 'mustache'] }, 252 | exclude_pages => (%_config||[]), 253 | exclude => (%_config||[]), 254 | omit_html_ext => (so %_config:exists||False), 255 | no_livereload => $no_livereload, 256 | config_file => $config_file, 257 | single_theme => $single_theme, 258 | 259 | # Pre/post build commands 260 | pre_command => (%_config||''), 261 | post_command => (%_config||'') 262 | 263 | } 264 | 265 | # Template / layout 266 | %config = %config.IO.child('layout'); 267 | %config = [ 268 | %config, 269 | %config, 270 | %config 271 | ]; 272 | %config = ( %_config ∈ %config.keys ?? %_config !! 'tt' ); 273 | %config = [ |%config{%config}, 'html', 'yml']; 274 | 275 | # Themes config 276 | %config = themes-config( 277 | # Render single theme? ignore multi config 278 | single_theme => %config, 279 | themes_dir => %config, 280 | build_dir => %config, 281 | # Serve port 282 | port => %config, 283 | exclude_pages => %config, 284 | project_root => %config, 285 | # Single theme default 286 | theme => %_config||'', 287 | # Mutli themes options 288 | themes => %_config||[], 289 | ); 290 | 291 | # Confirm all template directories exist 292 | # before continuing. 293 | valid-project-folder-structure(%config); 294 | 295 | # We want to stop everything if the project root ~~ $*HOME or 296 | # the build dir ~~ project root. This would have bad side-effects 297 | safe-build-dir-check(%config.IO, project_root => %config.IO); 298 | 299 | # Merged config as output 300 | return %config; 301 | } 302 | 303 | our sub init( 304 | IO::Path :$config_file = 'config.yml'.IO, 305 | Str :$site_name = 'New Uzu Project', 306 | Str :$template_engine = 'mustache', 307 | Str :$language = 'en', 308 | Str :$theme = 'default' 309 | --> Bool 310 | ) { 311 | my %config = 312 | name => $site_name, 313 | language => $language, 314 | theme => $theme, 315 | template_engine => $template_engine; 316 | 317 | my IO::Path $theme_dir = "themes".IO.child($theme); 318 | my List $template_dirs = ( 319 | "i18n".IO, 320 | "pages".IO, 321 | "partials".IO, 322 | "public".IO, 323 | $theme_dir.IO.child('assets') 324 | ); 325 | 326 | # Copy template files 327 | my %templates = 328 | pages => ['index'], 329 | partials => ['footer','head'], 330 | themes => ['layout']; 331 | 332 | # Create project directories 333 | $template_dirs.map( -> $dir { mkdir $dir }); 334 | 335 | %templates.kv.map: -> $root, @files { 336 | for @files -> $file { 337 | my $target_filename = "{$file}.{$template_engine}"; 338 | my $target_path = 339 | $root ~~ 'themes' 340 | ?? 'default'.IO.child($target_filename) 341 | !! $target_filename; 342 | my $source_filename = 343 | $root ~~ 'themes' 344 | ?? "{$template_engine}/{$root}/default/{$file}.{$template_engine}" 345 | !! "{$template_engine}/{$root}/{$file}.{$template_engine}"; 346 | 347 | spurt $root.IO.child($target_path), slurp(%?RESOURCES{$source_filename}.IO); 348 | } 349 | } 350 | 351 | # Save default language yaml 352 | spurt "i18n".IO.child("{$language}.yml"), "---\nsite_name: $site_name\n"; 353 | 354 | # Write config file 355 | my Str $config_yaml = S:g /'...'// given save-yaml(%config); 356 | my IO::Path $config_out = S:g /'~'/$*HOME/ given $config_file; 357 | return spurt $config_out, $config_yaml; 358 | } 359 | -------------------------------------------------------------------------------- /lib/Uzu/HTTP.rakumod: -------------------------------------------------------------------------------- 1 | use HTTP::Server::Tiny:ver<0.0.2>; 2 | use Uzu::Logger; 3 | 4 | unit module Uzu::HTTP; 5 | 6 | our sub web-server( 7 | %config 8 | --> Array 9 | ) { 10 | my Promise @servers; 11 | 12 | for %config.List -> $theme_config { 13 | 14 | push @servers, start { 15 | 16 | my $theme_name = $theme_config.key; 17 | my %theme = $theme_config.value; 18 | my $build_dir = %theme; 19 | my $port = %theme; 20 | my $ct_json = 'Content-Type' => 'application/json'; 21 | my $ct_text = 'Content-Type' => 'text/plain'; 22 | 23 | # Use for triggering reload staging when reload is triggered 24 | my $reload = Channel.new; 25 | 26 | HTTP::Server::Tiny.new(host => %config, :$port).run(sub (%env) { 27 | 28 | given %env { 29 | # When accessed, sets $reload to True 30 | # Routes 31 | when '/reload' { 32 | $reload.send(True); 33 | logger "GET /reload [$theme_name]"; 34 | return 200, [$ct_json], ['{ "reload": "Staged" }']; 35 | } 36 | 37 | # If $reload is True, return a JSON doc 38 | # instructing uzu/js/live.js to reload the 39 | # browser. 40 | when '/live' { 41 | return 200, [$ct_json], ['{ "reload": "True" }'] if $reload.poll; 42 | return 200, [$ct_json], ['{ "reload": "False" }']; 43 | } 44 | 45 | # Include live.js that starts polling /live 46 | # for reload instructions 47 | when '/uzu/js/live.js' { 48 | logger "GET /uzu/js/live.js [$theme_name]"; 49 | my Str $livejs = q:to|END|; 50 | // Uzu live-reload 51 | function live() { 52 | var xhttp = new XMLHttpRequest(); 53 | xhttp.onreadystatechange = function() { 54 | if (xhttp.readyState == 4 && xhttp.status == 200) { 55 | var resp = JSON.parse(xhttp.responseText); 56 | if (resp.reload == 'True') { 57 | document.location.reload(); 58 | }; 59 | }; 60 | }; 61 | xhttp.open("GET", "/live", true); 62 | xhttp.send(); 63 | setTimeout(live, 1000); 64 | } 65 | setTimeout(live, 1000); 66 | END 67 | 68 | return 200, [$ct_json], [$livejs]; 69 | } 70 | 71 | default { 72 | 73 | my $file = $_; 74 | 75 | # Trying to access files outside of build path 76 | return 400, [$ct_text], ['Invalid path'] if $file.match('..'); 77 | 78 | # Handle HTML without file extension 79 | my $index = 'index' ~ (%config ?? '' !! '.html'); 80 | 81 | my IO::Path $path = do given $file { 82 | when '/' { 83 | $build_dir.IO.child($index) 84 | } 85 | when so * ~~ / '/' $ / { 86 | $build_dir.IO.child($file.split('?')[0].IO.child($index)) 87 | } 88 | default { 89 | $build_dir.IO.child($file.split('?')[0]) 90 | } 91 | } 92 | 93 | given $path { 94 | 95 | when !*.IO.e { 96 | # Invalid path 97 | logger "GET $file (not found) [$theme_name]"; 98 | return 400, [$ct_text], ['Invalid path']; 99 | } 100 | 101 | default { 102 | # Return any valid paths 103 | my %ct = detect-content-type($path); 104 | 105 | logger "GET $file [$theme_name]"; 106 | 107 | # HTML 108 | if %ct ~~ 'text/html;charset=UTF-8' { 109 | return 200, ['Content-Type' => %ct ], [ 110 | process-livereload( 111 | content => slurp($path), 112 | no_livereload => %config)]; 113 | } 114 | # UTF8 text 115 | return 200, ['Content-Type' => %ct ], [slurp($path)] unless %ct; 116 | # Binary 117 | return 201, ['Content-Type' => %ct ], [slurp($path, :bin)]; 118 | } 119 | } 120 | } 121 | 122 | logger "uzu serves [http://localhost:{$port}] for theme [$theme_name]"; 123 | } 124 | 125 | }); # /http server 126 | } 127 | } 128 | 129 | return @servers; 130 | } 131 | 132 | our sub wait-port( 133 | int $port, 134 | Str $host = '0.0.0.0', 135 | :$sleep = 0.1, 136 | int :$times = 600 137 | ) is export { 138 | LOOP: for 1..$times { 139 | try { 140 | my $sock = IO::Socket::INET.new(:host($host), :port($port)); 141 | $sock.close; 142 | 143 | CATCH { default { sleep $sleep; next LOOP } } 144 | } 145 | return True; 146 | } 147 | 148 | die "$host:$port doesn't open in {$sleep*$times} sec."; 149 | } 150 | 151 | our sub inet-request( 152 | Str $req, 153 | int $port, 154 | $host='0.0.0.0' 155 | ) is export { 156 | my $client = IO::Socket::INET.new(:host($host), :port($port)); 157 | my $data = ''; 158 | try { 159 | $client.print($req); 160 | sleep .5; 161 | while my $d = $client.recv { 162 | $data ~= $d; 163 | } 164 | $client.close; 165 | CATCH { default {} } 166 | } 167 | return $data; 168 | } 169 | 170 | #| Inject livereload JS 171 | our sub process-livereload( 172 | Str :$content, 173 | Bool :$no_livereload 174 | --> Str 175 | ) { 176 | return '' when !$content.defined; 177 | unless $no_livereload { 178 | # Add livejs if live-reload enabled (default) 179 | my Str $livejs = ''; 180 | if $content ~~ /''/ { 181 | return S/''/$livejs\n<\/body>/ given $content; 182 | } else { 183 | return $content ~ "\n$livejs"; 184 | } 185 | } 186 | return $content; 187 | } 188 | 189 | # From Bailador 190 | sub detect-content-type( 191 | IO::Path $file 192 | ) { 193 | my %mapping = ( 194 | appcache => %{ bin => False, type => 'text/cache-manifest' }, 195 | atom => %{ bin => False, type => 'application/atom+xml' }, 196 | bin => %{ bin => True, type => 'application/octet-stream' }, 197 | css => %{ bin => False, type => 'text/css' }, 198 | eot => %{ bin => True, type => 'application/vnd.ms-fontobject' }, 199 | gif => %{ bin => True, type => 'image/gif' }, 200 | gz => %{ bin => True, type => 'application/x-gzip' }, 201 | htm => %{ bin => False, type => 'text/html' }, 202 | html => %{ bin => False, type => 'text/html;charset=UTF-8' }, 203 | '' => %{ bin => False, type => 'text/html;charset=UTF-8' }, 204 | ico => %{ bin => True, type => 'image/x-icon' }, 205 | jpeg => %{ bin => True, type => 'image/jpeg' }, 206 | jpg => %{ bin => True, type => 'image/jpeg' }, 207 | js => %{ bin => False, type => 'application/javascript' }, 208 | json => %{ bin => False, type => 'application/json;charset=UTF-8' }, 209 | mp3 => %{ bin => True, type => 'audio/mpeg' }, 210 | mp4 => %{ bin => True, type => 'video/mp4' }, 211 | ogg => %{ bin => True, type => 'audio/ogg' }, 212 | ogv => %{ bin => True, type => 'video/ogg' }, 213 | otf => %{ bin => True, type => 'application/x-font-opentype' }, 214 | pdf => %{ bin => True, type => 'application/pdf' }, 215 | png => %{ bin => True, type => 'image/png' }, 216 | rss => %{ bin => False, type => 'application/rss+xml' }, 217 | sfnt => %{ bin => True, type => 'application/font-sfnt' }, 218 | svg => %{ bin => True, type => 'image/svg+xml' }, 219 | ttf => %{ bin => True, type => 'application/x-font-truetype' }, 220 | txt => %{ bin => False, type => 'text/plain;charset=UTF-8' }, 221 | webm => %{ bin => True, type => 'video/webm' }, 222 | woff => %{ bin => True, type => 'application/font-woff' }, 223 | woff2 => %{ bin => True, type => 'application/font-woff' }, 224 | xml => %{ bin => False, type => 'application/xml' }, 225 | zip => %{ bin => True, type => 'application/zip' }, 226 | pm => %{ bin => False, type => 'application/x-perl' }, 227 | pm6 => %{ bin => False, type => 'application/x-perl' }, 228 | pl => %{ bin => False, type => 'application/x-perl' }, 229 | pl6 => %{ bin => False, type => 'application/x-perl' }, 230 | p6 => %{ bin => False, type => 'application/x-perl' }, 231 | ); 232 | 233 | my $ext = $file.extension.lc; 234 | return %mapping{$ext} if %mapping{$ext}:exists; 235 | return %mapping; 236 | } 237 | -------------------------------------------------------------------------------- /lib/Uzu/LiveReload.rakumod: -------------------------------------------------------------------------------- 1 | use Uzu::HTTP; 2 | 3 | unit module Uzu::LiveReload; 4 | 5 | our sub reload-browser( 6 | %config, 7 | --> Bool() 8 | ) { 9 | unless %config { 10 | for %config -> $theme_config { 11 | my %theme = $theme_config.values.head; 12 | my $theme_port = %theme; 13 | 14 | Uzu::HTTP::inet-request 15 | "GET /reload HTTP/1.0\r\nContent-length: 0\r\n\r\n", 16 | %theme, %config; 17 | } 18 | } 19 | } 20 | 21 | -------------------------------------------------------------------------------- /lib/Uzu/Logger.rakumod: -------------------------------------------------------------------------------- 1 | unit module Uzu::Logger; 2 | 3 | our sub start( 4 | Supplier $log = Supplier.new 5 | --> Block 6 | ) { 7 | start { 8 | react { 9 | whenever $log.Supply { say $_ } 10 | } 11 | } 12 | 13 | return -> $message, $l = $log { 14 | $l.emit: $message; 15 | } 16 | } 17 | 18 | sub logger($message) is export { 19 | state $logger; 20 | $logger = Uzu::Logger::start() unless $logger; 21 | return &$logger($message); 22 | } 23 | -------------------------------------------------------------------------------- /lib/Uzu/Utilities.rakumod: -------------------------------------------------------------------------------- 1 | use File::Find; 2 | 3 | unit module Uzu::Utilities; 4 | 5 | our sub copy-dir( 6 | IO::Path $source, 7 | IO::Path $target, 8 | :@exclude = [] 9 | --> Bool 10 | ) is export { 11 | my @files = find(dir => $source, exclude => / <@exclude> /); 12 | for @files { 13 | next unless so $_.IO.d && $_.path !~~ /@exclude/ ; 14 | mkdir $_.path.subst($source.path, $target.path); 15 | } 16 | for @files { 17 | next unless so $_.IO.f && $_.path !~~ /@exclude/ ; 18 | copy $_, $_.path.subst($source.path, $target.path); 19 | } 20 | return True; 21 | } 22 | 23 | our sub rm-dir( 24 | IO::Path $dir 25 | --> Bool 26 | ) is export { 27 | return unless $dir.IO.d; 28 | if $*SPEC ~~ 'Win32' { 29 | so shell "rmdir $dir /s /q"; 30 | } else { 31 | so shell "rm -rf $dir"; 32 | } 33 | } 34 | 35 | # 36 | # build-category-uri: convert YAML dict 37 | # into list of URIs for each branch. 38 | # 39 | 40 | proto sub build-category-uri(|) is export {*} 41 | multi sub build-category-uri(Str $item) { 42 | return $item; 43 | } 44 | multi sub build-category-uri(Str $parent, Str $item) { 45 | return "$parent/$item"; 46 | } 47 | multi sub build-category-uri(Str $parent, Hash $items) { 48 | map -> $k, $item { 49 | build-category-uri "$parent/$k", $item; 50 | }, kv $items; 51 | } 52 | multi sub build-category-uri(Hash $items) { 53 | map -> $parent, $item { 54 | build-category-uri $parent, $item; 55 | }, kv $items; 56 | } 57 | multi sub build-category-uri(Str $parent, Array $items) { 58 | map -> $item { 59 | build-category-uri $parent, $item; 60 | }, $items.List; 61 | } 62 | multi sub build-category-uri(Array $dict, :$cat_label = 'categories') { 63 | "/$cat_label/" <<~<< (map -> $item { 64 | build-category-uri $item; 65 | }, $dict.List).flat; 66 | } 67 | 68 | # 69 | # build-category-toc-html: generate bulleted list from 70 | # category hash. 71 | # 72 | 73 | proto sub build-category-toc-html(|) is export {*} 74 | multi sub build-category-toc-html( 75 | Str $item, 76 | Str :$breadcrumb 77 | --> Str 78 | ) { 79 | "
  • {$item}
  • "; 80 | } 81 | multi sub build-category-toc-html( 82 | Pair $item, 83 | Str :$breadcrumb 84 | --> Str 85 | ) { 86 | my $bc = "{$breadcrumb}/{$item.key}"; 87 | ['
  • ', "{$item.key}", build-category-toc-html($item.value, breadcrumb => $bc), '
  • '].join(''); 88 | } 89 | multi sub build-category-toc-html( 90 | Iterable $items, 91 | Str :$breadcrumb 92 | --> Str 93 | ) { 94 | ['
      ', (map -> $item { 95 | build-category-toc-html($item, breadcrumb => $breadcrumb) }, $items 96 | )].join(''); 97 | } 98 | multi sub build-category-toc-html( 99 | Hash $items, 100 | Str :$breadcrumb 101 | --> Str 102 | ) { 103 | [(map -> $k, $v { 104 | my $bc = $k ~~ Str ?? "{$breadcrumb}/{$k}" !! $breadcrumb; 105 | "
    • {$k}", build-category-toc-html($v.flat, breadcrumb => $bc) 106 | }, kv $items), '
    ', ''].join(''); 107 | } 108 | 109 | sub date-hash(--> Hash) is export { 110 | my %date = do with DateTime.new(now) { 111 | hh-mm-ss => .hh-mm-ss, 112 | utc => .utc, 113 | day-of-month => .day-of-month, 114 | weekday-of-month => .weekday-of-month, 115 | is-leap-year => .is-leap-year, 116 | day-of-year => .day-of-year, 117 | week-year => .week-year, 118 | day-of-week => .day-of-week, 119 | daycount => .daycount, 120 | week-number => .week-number, 121 | days-in-month => .days-in-month, 122 | week => .week, 123 | yyyy-mm-dd => .yyyy-mm-dd, 124 | hour => .hour, 125 | minute => .minute, 126 | second => .second, 127 | year => .year, 128 | month => .month, 129 | day => .day, 130 | timezone => .timezone 131 | } 132 | return %date; 133 | } 134 | -------------------------------------------------------------------------------- /lib/Uzu/Watch.rakumod: -------------------------------------------------------------------------------- 1 | use Uzu::HTTP; 2 | use Uzu::LiveReload; 3 | use Uzu::Logger; 4 | use Uzu::Render; 5 | use File::Find; 6 | use Terminal::ANSIColor; 7 | 8 | unit module Uzu::Watch; 9 | 10 | sub find-dirs( 11 | IO::Path $p 12 | --> Slip 13 | ) { 14 | slip ($p.IO, slip find :dir($p.path), :type); 15 | } 16 | 17 | sub file-change-monitor( 18 | List $dirs 19 | --> Supply 20 | ) { 21 | 22 | supply { 23 | my &watch-dir = -> $p { 24 | whenever IO::Notification.watch-path($p.path) -> $c { 25 | if $c.event ~~ FileRenamed && $c.path.IO ~~ :d { 26 | find-dirs($c.path).map(watch-dir $_); 27 | } 28 | emit $c; 29 | } 30 | } 31 | watch-dir(~$_.path) for $dirs.map: { find-dirs $_ }; 32 | } 33 | } 34 | 35 | sub build-and-reload( 36 | %config, 37 | --> Bool 38 | ) { 39 | Uzu::Render::build %config; 40 | Uzu::LiveReload::reload-browser(%config); 41 | } 42 | 43 | sub user-input( 44 | %config, 45 | Array :$servers, 46 | --> Bool 47 | ) { 48 | loop { 49 | logger colored "`r enter` to [rebuild]\n" ~ \ 50 | "`c enter` to [clear] build directory and rebuild\n" ~ \ 51 | "`q enter` to [quit]", "bold green on_blue"; 52 | given prompt('') { 53 | when 'r' { 54 | logger colored "Rebuild triggered", "bold green on_blue"; 55 | build-and-reload %config; 56 | } 57 | when 'c' { 58 | logger colored "Clear build directory and rebuild triggered", "bold green on_blue"; 59 | Uzu::Render::clear %config; 60 | build-and-reload %config; 61 | } 62 | when 'q'|'quit' { 63 | exit 1; 64 | } 65 | } 66 | } 67 | } 68 | 69 | our sub start( 70 | %config, 71 | --> Bool 72 | ) { 73 | # Initialize build 74 | logger "Initial build"; 75 | Uzu::Render::build %config; 76 | 77 | # Track time delta between File events. 78 | # Some editors trigger more than one event per 79 | # edit. 80 | my List $exts = %config; 81 | my List $dirs = ( 82 | |%config, 83 | |%config.map({ $_.values.head, $_.values.head.IO.child('partials') }).flat 84 | ).grep(*.IO.e).List; 85 | 86 | $dirs.map(-> $dir { 87 | logger "Starting watch on {$dir.subst("{$*CWD}/", '')}"; 88 | }); 89 | 90 | # Start server 91 | my @servers = Uzu::HTTP::web-server %config; 92 | 93 | # Keep track of the last render timestamp 94 | state Instant $last_run = now; 95 | 96 | # Watch directories for modifications 97 | start { 98 | react { 99 | whenever file-change-monitor($dirs) -> $e { 100 | # Make sure the file change is a 101 | # known extension; don't re-render too fast 102 | if so $exts (cont) $e.path.IO.extension and (now - $last_run) > 2 { 103 | logger colored "Change detected [{$e.path()}]", "bold green on_blue"; 104 | build-and-reload %config; 105 | $last_run = now; 106 | } 107 | } 108 | } 109 | } 110 | 111 | # Listen for keyboard input 112 | user-input %config, :@servers; 113 | } 114 | 115 | -------------------------------------------------------------------------------- /logotype/logo_32x32.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/scmorrison/uzu/40139f63a5b7da6ccd4ef64b2fd8a11d9fc53889/logotype/logo_32x32.png -------------------------------------------------------------------------------- /resources/mustache/pages/index.mustache: -------------------------------------------------------------------------------- 1 | --- 2 | title: Home 3 | --- 4 |

    Welcome to Uzu

    5 | -------------------------------------------------------------------------------- /resources/mustache/partials/footer.mustache: -------------------------------------------------------------------------------- 1 | 2 | 3 | -------------------------------------------------------------------------------- /resources/mustache/partials/head.mustache: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | {{ title }} | {{ i18n.site_name }} 6 | 7 | 8 | -------------------------------------------------------------------------------- /resources/mustache/themes/default/layout.mustache: -------------------------------------------------------------------------------- 1 | {{> head }} 2 | {{> content }} 3 | {{> footer }} 4 | -------------------------------------------------------------------------------- /resources/tt/pages/index.tt: -------------------------------------------------------------------------------- 1 | --- 2 | title: Home 3 | --- 4 |

    Welcome to Uzu

    5 | -------------------------------------------------------------------------------- /resources/tt/partials/footer.tt: -------------------------------------------------------------------------------- 1 | 2 | 3 | -------------------------------------------------------------------------------- /resources/tt/partials/head.tt: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | [% title %] | [% i18n.site_name %] 6 | 7 | 8 | -------------------------------------------------------------------------------- /resources/tt/themes/default/layout.tt: -------------------------------------------------------------------------------- 1 | [% INCLUDE "head" %] 2 | [% INCLUDE "content" %] 3 | [% INCLUDE "footer" %] 4 | -------------------------------------------------------------------------------- /t/00-basics.t: -------------------------------------------------------------------------------- 1 | use v6; 2 | 3 | use Test; 4 | use Uzu; 5 | 6 | # Additional tests in xt/ 7 | 8 | # use 9 | use-ok('Uzu'); 10 | 11 | done-testing; 12 | -------------------------------------------------------------------------------- /t/example_project_mustache/bad_file.txt: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/scmorrison/uzu/40139f63a5b7da6ccd4ef64b2fd8a11d9fc53889/t/example_project_mustache/bad_file.txt -------------------------------------------------------------------------------- /t/example_project_mustache/bad_folder/empty_file: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/scmorrison/uzu/40139f63a5b7da6ccd4ef64b2fd8a11d9fc53889/t/example_project_mustache/bad_folder/empty_file -------------------------------------------------------------------------------- /t/example_project_mustache/config.yml: -------------------------------------------------------------------------------- 1 | --- 2 | name: uzu-tests 3 | language: 4 | - en 5 | - ja 6 | themes: 7 | - default 8 | - summer2017: 9 | port: 4333 10 | exclude_pages: 11 | - excludeme 12 | template_engine: mustache 13 | url: https://gitlab.com/samcns/uzu 14 | author: Sam Morrison 15 | exclude: 16 | - bad_folder 17 | - bad_file.txt 18 | pre_command: "echo 'pre-command test'" 19 | post_command: "echo 'post-command test'" 20 | -------------------------------------------------------------------------------- /t/example_project_mustache/i18n/blog/fiji/en.yml: -------------------------------------------------------------------------------- 1 | --- 2 | title: "Fiji Vacation 2017" 3 | date: "2017/07/19" 4 | body: Our trip to Fiji 5 | -------------------------------------------------------------------------------- /t/example_project_mustache/i18n/en.yml: -------------------------------------------------------------------------------- 1 | --- 2 | company: Sam Morrison 3 | site_name: Uzu Test Project 4 | copyright: "2016 Sam Morrison" 5 | -------------------------------------------------------------------------------- /t/example_project_mustache/i18n/ja.yml: -------------------------------------------------------------------------------- 1 | --- 2 | company: サム・モリソン 3 | site_name: 渦テストプロジェクト 4 | copyright: "2017 サム・モリソン" 5 | -------------------------------------------------------------------------------- /t/example_project_mustache/pages/about.mustache: -------------------------------------------------------------------------------- 1 | --- 2 | title: About Us 3 | author: Sam Morrison 4 | categories: 5 | - travel 6 | - vacation 7 | --- 8 |
    9 |
    10 |

    About Us

    11 |
    12 |
    13 | 14 | {{> profiles }} 15 | 16 | -------------------------------------------------------------------------------- /t/example_project_mustache/pages/blog/fiji.mustache: -------------------------------------------------------------------------------- 1 | --- 2 | title: Fiji Vacation 3 | author: Sam Morrison 4 | categories: 5 | - travel: 6 | - 2017: 7 | - august 8 | - summer: 9 | - family 10 | - sailing 11 | - fiji 12 | - vacation 13 | --- 14 |

    {{ i18n.title }}

    15 |
    16 | Date: {{ i18n.date }}
    17 | Body:
    18 | {{ i18n.body }} 19 |
    20 | -------------------------------------------------------------------------------- /t/example_project_mustache/pages/blog/index.mustache: -------------------------------------------------------------------------------- 1 |

    Blog index

    2 | -------------------------------------------------------------------------------- /t/example_project_mustache/pages/deepembed.mustache: -------------------------------------------------------------------------------- 1 | --- 2 | msg: too deep 3 | nolayout: true 4 | --- 5 | {{> deep1 }} 6 | -------------------------------------------------------------------------------- /t/example_project_mustache/pages/embedded.mustache: -------------------------------------------------------------------------------- 1 | --- 2 | msg: I am embedded 3 | nolayout: true 4 | --- 5 | {{> embedded1 }} 6 | -------------------------------------------------------------------------------- /t/example_project_mustache/pages/empty.mustache: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/scmorrison/uzu/40139f63a5b7da6ccd4ef64b2fd8a11d9fc53889/t/example_project_mustache/pages/empty.mustache -------------------------------------------------------------------------------- /t/example_project_mustache/pages/excludeme.mustache: -------------------------------------------------------------------------------- 1 | I should not be rendered for summer2017 2 | -------------------------------------------------------------------------------- /t/example_project_mustache/pages/index.mustache: -------------------------------------------------------------------------------- 1 | --- 2 | title: 'Welcome to Uzu' 3 | date: '2017/07/16' 4 | author: Sam Morrison 5 | theme_handbook: True 6 | --- 7 | {{ page.id }} {{ page.extension }} {{ page.path }} 8 | {{> jumbotron }} 9 | 10 | {{> home }} 11 | 12 |

    13 | Title: {{ title }}
    14 | Date: {{ date }} 15 |

    16 | -------------------------------------------------------------------------------- /t/example_project_mustache/pages/layout.mustache: -------------------------------------------------------------------------------- 1 | --- 2 | nolayout: true 3 | --- 4 | {{ layout.special }} 5 | -------------------------------------------------------------------------------- /t/example_project_mustache/pages/nolayout.mustache: -------------------------------------------------------------------------------- 1 | --- 2 | nolayout: True 3 | --- 4 | Do not render me with a layout. 5 | -------------------------------------------------------------------------------- /t/example_project_mustache/pages/related.mustache: -------------------------------------------------------------------------------- 1 | --- 2 | author: Page Author 3 | related_pages: 4 | - page: about 5 | title: Let's use this title 6 | - page: blog/fiji 7 | - page: https://www.perl6.org 8 | title: The Perl 6 Programming Language 9 | author: Perl 6 10 | other_pages: 11 | - page: blog/fiji 12 | - page: https://modules.perl6.org 13 | title: Perl 6 Modules Directory 14 | author: Perl 6 15 | - page: about 16 | going_down: 17 | - something: important 18 | - deep_pages: 19 | - page: blog/fiji 20 | - page: about 21 | - page: https://github.com/scmorrison/uzu/ 22 | author: Sam Morrison 23 | title: Uzu is a static site generator 24 | - deeper_still: 25 | - deeper_pages: 26 | - page: index 27 | - page: about 28 | - page: blog/fiji 29 | categories: 30 | - title: General tasks 31 | category_pages: 32 | - page: http://www.perl6.org 33 | title: Perl 6 34 | - page: index 35 | - page: blog/fiji 36 | - page: about 37 | - title: Other tasks 38 | category_pages: 39 | - page: about 40 | - page: index 41 | - title: Even more tasks 42 | category_pages: 43 | - page: blog/fiji 44 | - page: about 45 | --- 46 | related pages 47 |
      48 | {{#related_pages}} 49 |
    • 50 | {{ title }} [{{ author }}] 51 |
    • 52 | {{/related_pages}} 53 |
    54 | 55 | other pages 56 |
      57 | {{#other_pages}} 58 |
    • 59 | {{ title }} [{{ author }}] 60 |
    • 61 | {{/other_pages}} 62 |
    63 | 64 | deep pages 65 |
      66 | {{#going_down}} 67 | {{#deep_pages}} 68 |
    • 69 | {{ title }} [{{ author }}] 70 |
    • 71 | {{/deep_pages}} 72 | {{/going_down}} 73 |
    74 | 75 | deeper pages 76 |
      77 | {{#going_down}} 78 | {{#deeper_still}} 79 | {{#deeper_pages}} 80 |
    • 81 | {{ title }} [{{ author }} 82 |
    • 83 | {{/deeper_pages}} 84 | {{/deeper_still}} 85 | {{/going_down}} 86 |
    87 | 88 | categories 89 |

    90 | {{#categories}} 91 | {{ title }} 92 |

      93 | {{#category_pages}} 94 |
    • {{ title }} [{{ author }}
    • 95 | {{/category_pages}} 96 |
    97 | {{/categories}} 98 |

    99 | -------------------------------------------------------------------------------- /t/example_project_mustache/pages/site_details.json.mustache: -------------------------------------------------------------------------------- 1 | { 2 | "site_name": "{{ i18n.site_name }}" 3 | } 4 | -------------------------------------------------------------------------------- /t/example_project_mustache/pages/themepartial.mustache: -------------------------------------------------------------------------------- 1 | {{> usetheme }} 2 | -------------------------------------------------------------------------------- /t/example_project_mustache/partials/deep1.mustache: -------------------------------------------------------------------------------- 1 | --- 2 | bear: mountain 3 | --- 4 | {{> deep2 }} 5 | -------------------------------------------------------------------------------- /t/example_project_mustache/partials/deep2.mustache: -------------------------------------------------------------------------------- 1 | --- 2 | hawaii: five-o 3 | dog: 'set in deep2 dog' 4 | --- 5 | {{ bear }} 6 | {{> deep3 }} 7 | -------------------------------------------------------------------------------- /t/example_project_mustache/partials/deep3.mustache: -------------------------------------------------------------------------------- 1 | --- 2 | dog: 'override for deep3' 3 | --- 4 | {{ msg }} 5 | {{ hawaii }} 6 | {{ bear }} 7 | {{ dog }} 8 | -------------------------------------------------------------------------------- /t/example_project_mustache/partials/embedded.mustache: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /t/example_project_mustache/partials/embedded1.mustache: -------------------------------------------------------------------------------- 1 | {{> embedded2 }} 2 | -------------------------------------------------------------------------------- /t/example_project_mustache/partials/embedded2.mustache: -------------------------------------------------------------------------------- 1 |

    {{ msg }}

    2 | -------------------------------------------------------------------------------- /t/example_project_mustache/partials/footer.mustache: -------------------------------------------------------------------------------- 1 |
    2 |

    {{ i18n.copyright }}

    3 | About Us 4 |
    5 | -------------------------------------------------------------------------------- /t/example_project_mustache/partials/head.mustache: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | {{ i18n.site_name }} 5 | 6 | 7 | 13 | 14 | -------------------------------------------------------------------------------- /t/example_project_mustache/partials/home.mustache: -------------------------------------------------------------------------------- 1 |
    2 | 3 |
    4 |
    5 |

    Heading

    6 |

    Donec id elit non mi porta gravida at eget metus. Fusce dapibus, tellus ac cursus commodo, tortor mauris condimentum nibh, ut fermentum massa justo sit amet risus. Etiam porta sem malesuada magna mollis euismod. Donec sed odio dui.

    7 |

    View details »

    8 |
    9 |
    10 |

    Heading

    11 |

    Donec id elit non mi porta gravida at eget metus. Fusce dapibus, tellus ac cursus commodo, tortor mauris condimentum nibh, ut fermentum massa justo sit amet risus. Etiam porta sem malesuada magna mollis euismod. Donec sed odio dui.

    12 |

    View details »

    13 |
    14 |
    15 |

    Heading

    16 |

    Donec sed odio dui. Cras justo odio, dapibus ac facilisis in, egestas eget quam. Vestibulum id ligula porta felis euismod semper. Fusce dapibus, tellus ac cursus commodo, tortor mauris condimentum nibh, ut fermentum massa justo sit amet risus.

    17 |

    View details »

    18 |
    19 |
    20 | 21 |
    22 | -------------------------------------------------------------------------------- /t/example_project_mustache/partials/jumbotron.mustache: -------------------------------------------------------------------------------- 1 | 2 |
    3 |
    4 |

    Hello, world!

    5 |

    This is a template for a simple marketing or informational website. It includes a large callout called a jumbotron and three supporting pieces of content. Use it as a starting point to create something more unique.

    6 |

    Learn more »

    7 |
    8 |
    9 | 10 | -------------------------------------------------------------------------------- /t/example_project_mustache/partials/link.mustache: -------------------------------------------------------------------------------- 1 | {{#see_also_pages}} 2 |
  • {{ url }}
  • 3 | {{/see_also_pages}} 4 | -------------------------------------------------------------------------------- /t/example_project_mustache/partials/navigation.mustache: -------------------------------------------------------------------------------- 1 | 26 | -------------------------------------------------------------------------------- /t/example_project_mustache/partials/profiles.mustache: -------------------------------------------------------------------------------- 1 |
    2 | 3 |
    4 |
    5 |

    CEO

    6 |

    Donec id elit non mi porta gravida at eget metus. Fusce dapibus, tellus ac cursus commodo, tortor mauris condimentum nibh, ut fermentum massa justo sit amet risus. Etiam porta sem malesuada magna mollis euismod. Donec sed odio dui.

    7 |

    View details »

    8 |
    9 |
    10 |

    CTO

    11 |

    Donec id elit non mi porta gravida at eget metus. Fusce dapibus, tellus ac cursus commodo, tortor mauris condimentum nibh, ut fermentum massa justo sit amet risus. Etiam porta sem malesuada magna mollis euismod. Donec sed odio dui.

    12 |

    View details »

    13 |
    14 |
    15 |

    CFO

    16 |

    Donec sed odio dui. Cras justo odio, dapibus ac facilisis in, egestas eget quam. Vestibulum id ligula porta felis euismod semper. Fusce dapibus, tellus ac cursus commodo, tortor mauris condimentum nibh, ut fermentum massa justo sit amet risus.

    17 |

    View details »

    18 |
    19 |
    20 | 21 |
    22 | -------------------------------------------------------------------------------- /t/example_project_mustache/partials/related.mustache: -------------------------------------------------------------------------------- 1 |
      2 | {{#related_pages}} 3 |
    • 4 | {{ title }} [{{ author }}] 5 |
    • 6 | {{/related_pages}} 7 |
    8 | -------------------------------------------------------------------------------- /t/example_project_mustache/partials/see_also.mustache: -------------------------------------------------------------------------------- 1 |
      2 | {{#see_also_pages}} 3 | {{> link }} 4 | {{/see_also_pages}} 5 |
    6 | -------------------------------------------------------------------------------- /t/example_project_mustache/partials/special/part.mustache: -------------------------------------------------------------------------------- 1 | I am special 2 | -------------------------------------------------------------------------------- /t/example_project_mustache/partials/usetheme.mustache: -------------------------------------------------------------------------------- 1 | I am not the partial you are looking for. 2 | -------------------------------------------------------------------------------- /t/example_project_mustache/public/robots.txt: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/scmorrison/uzu/40139f63a5b7da6ccd4ef64b2fd8a11d9fc53889/t/example_project_mustache/public/robots.txt -------------------------------------------------------------------------------- /t/example_project_mustache/themes/default/assets/img/logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/scmorrison/uzu/40139f63a5b7da6ccd4ef64b2fd8a11d9fc53889/t/example_project_mustache/themes/default/assets/img/logo.png -------------------------------------------------------------------------------- /t/example_project_mustache/themes/default/layout.mustache: -------------------------------------------------------------------------------- 1 | --- 2 | root_url: "https://www.perl6.org" 3 | separator: ':::' 4 | url_postfix: '@-@' 5 | special: I am in the default layout 6 | --- 7 | 8 | 9 | {{> head }} 10 | 11 |
    12 | {{> navigation }} 13 | {{> content }} 14 | {{> footer }} 15 |
    16 | 17 | 18 | -------------------------------------------------------------------------------- /t/example_project_mustache/themes/default/partials/usetheme.mustache: -------------------------------------------------------------------------------- 1 | You found me. 2 | -------------------------------------------------------------------------------- /t/example_project_mustache/themes/summer2017/assets/img/logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/scmorrison/uzu/40139f63a5b7da6ccd4ef64b2fd8a11d9fc53889/t/example_project_mustache/themes/summer2017/assets/img/logo.png -------------------------------------------------------------------------------- /t/example_project_mustache/themes/summer2017/layout.mustache: -------------------------------------------------------------------------------- 1 | --- 2 | root_url: "https://www.perl6.org" 3 | separator: ':::' 4 | url_postfix: '@-@' 5 | special: I am in the summer2017 layout 6 | --- 7 | 8 | 9 | {{> head }} 10 | 11 |
    12 | {{> navigation }} 13 | {{> content }} 14 | {{> footer }} 15 |
    16 | 17 | 18 | -------------------------------------------------------------------------------- /t/example_project_mustache/themes/summer2017/partials/usetheme.mustache: -------------------------------------------------------------------------------- 1 | You found me. 2 | -------------------------------------------------------------------------------- /t/example_project_tt/bad_file.txt: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/scmorrison/uzu/40139f63a5b7da6ccd4ef64b2fd8a11d9fc53889/t/example_project_tt/bad_file.txt -------------------------------------------------------------------------------- /t/example_project_tt/bad_folder/empty_file: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/scmorrison/uzu/40139f63a5b7da6ccd4ef64b2fd8a11d9fc53889/t/example_project_tt/bad_folder/empty_file -------------------------------------------------------------------------------- /t/example_project_tt/config.yml: -------------------------------------------------------------------------------- 1 | --- 2 | name: uzu-testing 3 | language: 4 | - en 5 | - ja 6 | themes: 7 | - default 8 | - summer2017: 9 | port: 4333 10 | exclude_pages: 11 | - excludeme 12 | url: https://gitlab.com/samcns/uzu 13 | author: Sam Morrison 14 | exclude: 15 | - bad_folder 16 | - bad_file.txt 17 | pre_command: "echo 'pre-command test'" 18 | post_command: "echo 'post-command test'" 19 | extended: 'Local' 20 | -------------------------------------------------------------------------------- /t/example_project_tt/i18n/blog/fiji/en.yml: -------------------------------------------------------------------------------- 1 | --- 2 | title: "Fiji Vacation 2017" 3 | date: "2017/07/19" 4 | body: Our trip to Fiji 5 | -------------------------------------------------------------------------------- /t/example_project_tt/i18n/en.yml: -------------------------------------------------------------------------------- 1 | --- 2 | company: Sam Morrison 3 | site_name: Uzu Test Project 4 | copyright: "2016 Sam Morrison" 5 | -------------------------------------------------------------------------------- /t/example_project_tt/i18n/ja.yml: -------------------------------------------------------------------------------- 1 | --- 2 | company: サム・モリソン 3 | site_name: 渦テストプロジェクト 4 | copyright: "2017 サム・モリソン" 5 | -------------------------------------------------------------------------------- /t/example_project_tt/pages/about.tt: -------------------------------------------------------------------------------- 1 | --- 2 | title: About Us 3 | author: Sam Morrison 4 | --- 5 |
    6 |
    7 |

    About Us

    8 |
    9 |
    10 | 11 | [% INCLUDE "profiles" %] 12 | 13 | -------------------------------------------------------------------------------- /t/example_project_tt/pages/blog/fiji.tt: -------------------------------------------------------------------------------- 1 | --- 2 | title: Fiji Vacation 2017 3 | author: Sam Morrison 4 | categories: 5 | - travel 6 | - fiji 7 | - vacation 8 | --- 9 |

    [% i18n.title %]

    10 |
    11 | Date: [% i18n.date %]
    12 | Body:
    13 | [% i18n.body %] 14 |
    15 | -------------------------------------------------------------------------------- /t/example_project_tt/pages/blog/index.tt: -------------------------------------------------------------------------------- 1 |

    Blog index

    2 | -------------------------------------------------------------------------------- /t/example_project_tt/pages/deepembed.tt: -------------------------------------------------------------------------------- 1 | --- 2 | msg: too deep 3 | nolayout: true 4 | --- 5 | [% INCLUDE "deep1" %] 6 | -------------------------------------------------------------------------------- /t/example_project_tt/pages/embedded.tt: -------------------------------------------------------------------------------- 1 | --- 2 | msg: I am embedded 3 | nolayout: true 4 | --- 5 | [% INCLUDE "embedded1" %] 6 | -------------------------------------------------------------------------------- /t/example_project_tt/pages/empty.tt: -------------------------------------------------------------------------------- 1 | [% INCLUDE "nothing" %] 2 | -------------------------------------------------------------------------------- /t/example_project_tt/pages/excludeme.tt: -------------------------------------------------------------------------------- 1 | I should not be rendered for summer2017 2 | -------------------------------------------------------------------------------- /t/example_project_tt/pages/index.tt: -------------------------------------------------------------------------------- 1 | --- 2 | title: Welcome to Uzu 3 | date: "2017/07/16" 4 | --- 5 | [% page.id %] [% page.extension %] [% page.path %] 6 | [% INCLUDE "jumbotron" %] 7 | 8 | [% INCLUDE "home" %] 9 | 10 | -------------------------------------------------------------------------------- /t/example_project_tt/pages/layout.tt: -------------------------------------------------------------------------------- 1 | --- 2 | nolayout: true 3 | --- 4 | [% layout.special %] 5 | -------------------------------------------------------------------------------- /t/example_project_tt/pages/nolayout.tt: -------------------------------------------------------------------------------- 1 | --- 2 | nolayout: True 3 | --- 4 | Do not render me with a layout. 5 | -------------------------------------------------------------------------------- /t/example_project_tt/pages/related.tt: -------------------------------------------------------------------------------- 1 | --- 2 | author: Page Author 3 | related_pages: 4 | - page: about 5 | title: Let's use this title 6 | - page: blog/fiji 7 | - page: https://www.perl6.org 8 | title: The Perl 6 Programming Language 9 | author: Perl 6 10 | other_pages: 11 | - page: blog/fiji 12 | - page: https://modules.perl6.org 13 | title: Perl 6 Modules Directory 14 | author: Perl 6 15 | - page: about 16 | going_down: 17 | - something: important 18 | - deep_pages: 19 | - page: blog/fiji 20 | - page: about 21 | - page: https://github.com/scmorrison/uzu/ 22 | author: Sam Morrison 23 | title: Uzu is a static site generator 24 | - deeper_still: 25 | - title: Deeper title 26 | - deeper_pages: 27 | - page: index 28 | - page: about 29 | - page: blog/fiji 30 | categories: 31 | - general_tasks: 32 | - title: General tasks 33 | category_pages: 34 | - page: http://www.perl6.org 35 | title: Perl 6 36 | - page: index 37 | - page: blog/fiji 38 | - page: about 39 | - other_tasks: 40 | - title: Other tasks 41 | category_pages: 42 | - page: about 43 | - page: index 44 | - more_tasks: 45 | - title: Even more tasks 46 | category_pages: 47 | - page: blog/fiji 48 | - page: about 49 | --- 50 | related pages 51 |
      52 | [% for page in related_pages %] 53 |
    • 54 | [% page.title %] [[% page.author %]] 55 |
    • 56 | [% end %] 57 |
    58 | 59 | other pages 60 |
      61 | [% for page in other_pages %] 62 |
    • 63 | [% page.title %] [[% page.author %]] 64 |
    • 65 | [% end %] 66 |
    67 | 68 | deep pages 69 |
      70 | [% for page in going_down.deep_pages %] 71 |
    • 72 | [% page.title %] [[% page.author %]] 73 |
    • 74 | [% end %] 75 |
    76 | 77 | deeper still 78 |

    79 | [% going_down.deeper_still.title %] 80 |

      81 | [% for page in going_down.deeper_still.deeper_pages %] 82 |
    • 83 | [% page.title %] [[% page.author %]] 84 |
    • 85 | [% end %] 86 |
    87 |

    88 | 89 | categories 90 |

    91 | [% categories.general_tasks.title %] 92 |

      93 | [% for page in categories.general_tasks.category_pages %] 94 |
    • 95 | [% page.title %] [[% page.author %]] 96 |
    • 97 | [% end %] 98 |
    99 |

    100 | 101 |

    102 | [% categories.other_tasks.title %] 103 |

      104 | [% for page in categories.other_tasks.category_pages %] 105 |
    • 106 | [% page.title %] [[% page.author %]] 107 |
    • 108 | [% end %] 109 |
    110 |

    111 | 112 |

    113 | [% categories.more_tasks.title %] 114 |

      115 | [% for page in categories.more_tasks.category_pages %] 116 |
    • 117 | [% page.title %] [[% page.author %]] 118 |
    • 119 | [% end %] 120 |
    121 |

    122 | -------------------------------------------------------------------------------- /t/example_project_tt/pages/site_details.json.tt: -------------------------------------------------------------------------------- 1 | --- 2 | open: "{" 3 | close: "}" 4 | --- 5 | [% open %] 6 | "site_name": "[% i18n.site_name %]" 7 | 8 | [% close %] 9 | -------------------------------------------------------------------------------- /t/example_project_tt/pages/themepartial.tt: -------------------------------------------------------------------------------- 1 | [% INCLUDE "usetheme" %] 2 | -------------------------------------------------------------------------------- /t/example_project_tt/partials/deep1.tt: -------------------------------------------------------------------------------- 1 | --- 2 | bear: mountain 3 | --- 4 | [% INCLUDE "deep2" %] 5 | -------------------------------------------------------------------------------- /t/example_project_tt/partials/deep2.tt: -------------------------------------------------------------------------------- 1 | --- 2 | hawaii: five-o 3 | dog: 'set in deep2 dog' 4 | --- 5 | [% bear %] 6 | [% INCLUDE "deep3" %] 7 | -------------------------------------------------------------------------------- /t/example_project_tt/partials/deep3.tt: -------------------------------------------------------------------------------- 1 | --- 2 | dog: 'override for deep3' 3 | --- 4 | [% msg %] 5 | [% hawaii %] 6 | [% bear %] 7 | [% dog %] 8 | -------------------------------------------------------------------------------- /t/example_project_tt/partials/embedded1.tt: -------------------------------------------------------------------------------- 1 | [% INCLUDE "embedded2" %] 2 | -------------------------------------------------------------------------------- /t/example_project_tt/partials/embedded2.tt: -------------------------------------------------------------------------------- 1 | [% msg %] 2 | -------------------------------------------------------------------------------- /t/example_project_tt/partials/footer.tt: -------------------------------------------------------------------------------- 1 |
    2 |

    [% i18n.copyright %]

    3 | About Us 4 |
    5 | -------------------------------------------------------------------------------- /t/example_project_tt/partials/head.tt: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | [% title %] - [% i18n.site_name %] 5 | 6 | 7 | 13 | 14 | -------------------------------------------------------------------------------- /t/example_project_tt/partials/home.tt: -------------------------------------------------------------------------------- 1 |
    2 | 3 |
    4 |
    5 |

    Heading

    6 |

    Donec id elit non mi porta gravida at eget metus. Fusce dapibus, tellus ac cursus commodo, tortor mauris condimentum nibh, ut fermentum massa justo sit amet risus. Etiam porta sem malesuada magna mollis euismod. Donec sed odio dui.

    7 |

    View details »

    8 |
    9 |
    10 |

    Heading

    11 |

    Donec id elit non mi porta gravida at eget metus. Fusce dapibus, tellus ac cursus commodo, tortor mauris condimentum nibh, ut fermentum massa justo sit amet risus. Etiam porta sem malesuada magna mollis euismod. Donec sed odio dui.

    12 |

    View details »

    13 |
    14 |
    15 |

    Heading

    16 |

    Donec sed odio dui. Cras justo odio, dapibus ac facilisis in, egestas eget quam. Vestibulum id ligula porta felis euismod semper. Fusce dapibus, tellus ac cursus commodo, tortor mauris condimentum nibh, ut fermentum massa justo sit amet risus.

    17 |

    View details »

    18 |
    19 |
    20 | 21 |
    22 | -------------------------------------------------------------------------------- /t/example_project_tt/partials/jumbotron.tt: -------------------------------------------------------------------------------- 1 | 2 |
    3 |
    4 |

    Hello, world!

    5 |

    This is a template for a simple marketing or informational website. It includes a large callout called a jumbotron and three supporting pieces of content. Use it as a starting point to create something more unique.

    6 |

    Learn more »

    7 |
    8 |
    9 | 10 | -------------------------------------------------------------------------------- /t/example_project_tt/partials/navigation.tt: -------------------------------------------------------------------------------- 1 | 26 | -------------------------------------------------------------------------------- /t/example_project_tt/partials/profiles.tt: -------------------------------------------------------------------------------- 1 |
    2 | 3 |
    4 |
    5 |

    CEO

    6 |

    Donec id elit non mi porta gravida at eget metus. Fusce dapibus, tellus ac cursus commodo, tortor mauris condimentum nibh, ut fermentum massa justo sit amet risus. Etiam porta sem malesuada magna mollis euismod. Donec sed odio dui.

    7 |

    View details »

    8 |
    9 |
    10 |

    CTO

    11 |

    Donec id elit non mi porta gravida at eget metus. Fusce dapibus, tellus ac cursus commodo, tortor mauris condimentum nibh, ut fermentum massa justo sit amet risus. Etiam porta sem malesuada magna mollis euismod. Donec sed odio dui.

    12 |

    View details »

    13 |
    14 |
    15 |

    CFO

    16 |

    Donec sed odio dui. Cras justo odio, dapibus ac facilisis in, egestas eget quam. Vestibulum id ligula porta felis euismod semper. Fusce dapibus, tellus ac cursus commodo, tortor mauris condimentum nibh, ut fermentum massa justo sit amet risus.

    17 |

    View details »

    18 |
    19 |
    20 | 21 |
    22 | -------------------------------------------------------------------------------- /t/example_project_tt/partials/usetheme.tt: -------------------------------------------------------------------------------- 1 | I am not the partial you are looking for. 2 | -------------------------------------------------------------------------------- /t/example_project_tt/public/robots.txt: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/scmorrison/uzu/40139f63a5b7da6ccd4ef64b2fd8a11d9fc53889/t/example_project_tt/public/robots.txt -------------------------------------------------------------------------------- /t/example_project_tt/themes/.default.mustache.swp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/scmorrison/uzu/40139f63a5b7da6ccd4ef64b2fd8a11d9fc53889/t/example_project_tt/themes/.default.mustache.swp -------------------------------------------------------------------------------- /t/example_project_tt/themes/default/assets/img/logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/scmorrison/uzu/40139f63a5b7da6ccd4ef64b2fd8a11d9fc53889/t/example_project_tt/themes/default/assets/img/logo.png -------------------------------------------------------------------------------- /t/example_project_tt/themes/default/layout.tt: -------------------------------------------------------------------------------- 1 | 2 | 3 | [% INCLUDE "head" %] 4 | 5 | [% INCLUDE "navigation" %] 6 | [% INCLUDE "content" %] 7 | [% INCLUDE "footer" %] 8 |
    9 | 10 | 11 | -------------------------------------------------------------------------------- /t/example_project_tt/themes/default/partials/usetheme.tt: -------------------------------------------------------------------------------- 1 | You found me. 2 | -------------------------------------------------------------------------------- /t/example_project_tt/themes/summer2017/assets/img/logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/scmorrison/uzu/40139f63a5b7da6ccd4ef64b2fd8a11d9fc53889/t/example_project_tt/themes/summer2017/assets/img/logo.png -------------------------------------------------------------------------------- /t/example_project_tt/themes/summer2017/layout.tt: -------------------------------------------------------------------------------- 1 | --- 2 | root_url: "https://www.perl6.org" 3 | separator: ':::' 4 | url_postfix: '@-@' 5 | special: I am in the summer2017 layout 6 | --- 7 | 8 | 9 | [% INCLUDE "head" %] 10 | 11 |
    12 | [% INCLUDE "navigation" %] 13 | [% INCLUDE "content" %] 14 | [% INCLUD "footer" %] 15 |
    16 | 17 | 18 | -------------------------------------------------------------------------------- /t/example_project_tt/themes/summer2017/partials/usetheme.tt: -------------------------------------------------------------------------------- 1 | You found me. 2 | -------------------------------------------------------------------------------- /t/expected_mustache/about-ja.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 渦テストプロジェクト 7 | 8 | 9 | 15 | 16 | 17 |
    18 | 43 |
    44 |
    45 |

    About Us

    46 |
    47 |
    48 | 49 |
    50 | 51 |
    52 |
    53 |

    CEO

    54 |

    Donec id elit non mi porta gravida at eget metus. Fusce dapibus, tellus ac cursus commodo, tortor mauris condimentum nibh, ut fermentum massa justo sit amet risus. Etiam porta sem malesuada magna mollis euismod. Donec sed odio dui.

    55 |

    View details »

    56 |
    57 |
    58 |

    CTO

    59 |

    Donec id elit non mi porta gravida at eget metus. Fusce dapibus, tellus ac cursus commodo, tortor mauris condimentum nibh, ut fermentum massa justo sit amet risus. Etiam porta sem malesuada magna mollis euismod. Donec sed odio dui.

    60 |

    View details »

    61 |
    62 |
    63 |

    CFO

    64 |

    Donec sed odio dui. Cras justo odio, dapibus ac facilisis in, egestas eget quam. Vestibulum id ligula porta felis euismod semper. Fusce dapibus, tellus ac cursus commodo, tortor mauris condimentum nibh, ut fermentum massa justo sit amet risus.

    65 |

    View details »

    66 |
    67 |
    68 | 69 |
    70 | 71 |
    72 |

    2017 サム・モリソン

    73 | About Us 74 |
    75 |
    76 | 77 | 78 | -------------------------------------------------------------------------------- /t/expected_mustache/about.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | Uzu Test Project 7 | 8 | 9 | 15 | 16 | 17 |
    18 | 43 |
    44 |
    45 |

    About Us

    46 |
    47 |
    48 | 49 |
    50 | 51 |
    52 |
    53 |

    CEO

    54 |

    Donec id elit non mi porta gravida at eget metus. Fusce dapibus, tellus ac cursus commodo, tortor mauris condimentum nibh, ut fermentum massa justo sit amet risus. Etiam porta sem malesuada magna mollis euismod. Donec sed odio dui.

    55 |

    View details »

    56 |
    57 |
    58 |

    CTO

    59 |

    Donec id elit non mi porta gravida at eget metus. Fusce dapibus, tellus ac cursus commodo, tortor mauris condimentum nibh, ut fermentum massa justo sit amet risus. Etiam porta sem malesuada magna mollis euismod. Donec sed odio dui.

    60 |

    View details »

    61 |
    62 |
    63 |

    CFO

    64 |

    Donec sed odio dui. Cras justo odio, dapibus ac facilisis in, egestas eget quam. Vestibulum id ligula porta felis euismod semper. Fusce dapibus, tellus ac cursus commodo, tortor mauris condimentum nibh, ut fermentum massa justo sit amet risus.

    65 |

    View details »

    66 |
    67 |
    68 | 69 |
    70 | 71 |
    72 |

    2016 Sam Morrison

    73 | About Us 74 |
    75 |
    76 | 77 | 78 | -------------------------------------------------------------------------------- /t/expected_mustache/blog/fiji-ja.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 渦テストプロジェクト 7 | 8 | 9 | 15 | 16 | 17 |
    18 | 43 |

    Fiji Vacation 2017

    44 |
    45 | Date: 2017/07/19
    46 | Body:
    47 | Our trip to Fiji 48 |
    49 |
    50 |

    2017 サム・モリソン

    51 | About Us 52 |
    53 |
    54 | 55 | 56 | -------------------------------------------------------------------------------- /t/expected_mustache/blog/fiji.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | Uzu Test Project 7 | 8 | 9 | 15 | 16 | 17 |
    18 | 43 |

    Fiji Vacation 2017

    44 |
    45 | Date: 2017/07/19
    46 | Body:
    47 | Our trip to Fiji 48 |
    49 |
    50 |

    2016 Sam Morrison

    51 | About Us 52 |
    53 |
    54 | 55 | 56 | -------------------------------------------------------------------------------- /t/expected_mustache/blog/index-ja.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 渦テストプロジェクト 7 | 8 | 9 | 15 | 16 | 17 |
    18 | 43 |

    Blog index

    44 |
    45 |

    2017 サム・モリソン

    46 | About Us 47 |
    48 |
    49 | 50 | 51 | -------------------------------------------------------------------------------- /t/expected_mustache/blog/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | Uzu Test Project 7 | 8 | 9 | 15 | 16 | 17 |
    18 | 43 |

    Blog index

    44 |
    45 |

    2016 Sam Morrison

    46 | About Us 47 |
    48 |
    49 | 50 | 51 | -------------------------------------------------------------------------------- /t/expected_mustache/deepembed-ja.html: -------------------------------------------------------------------------------- 1 | mountain 2 | too deep 3 | five-o 4 | mountain 5 | -------------------------------------------------------------------------------- /t/expected_mustache/deepembed.html: -------------------------------------------------------------------------------- 1 | mountain 2 | too deep 3 | five-o 4 | mountain 5 | override for deep3 6 | -------------------------------------------------------------------------------- /t/expected_mustache/embedded-ja.html: -------------------------------------------------------------------------------- 1 |

    I am embedded

    2 | -------------------------------------------------------------------------------- /t/expected_mustache/embedded.html: -------------------------------------------------------------------------------- 1 |

    I am embedded

    2 | -------------------------------------------------------------------------------- /t/expected_mustache/empty-ja.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 渦テストプロジェクト 7 | 8 | 9 | 15 | 16 | 17 |
    18 | 43 |
    44 |

    2017 サム・モリソン

    45 | About Us 46 |
    47 |
    48 | 49 | 50 | -------------------------------------------------------------------------------- /t/expected_mustache/empty.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | Uzu Test Project 7 | 8 | 9 | 15 | 16 | 17 |
    18 | 43 |
    44 |

    2016 Sam Morrison

    45 | About Us 46 |
    47 |
    48 | 49 | 50 | -------------------------------------------------------------------------------- /t/expected_mustache/img/logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/scmorrison/uzu/40139f63a5b7da6ccd4ef64b2fd8a11d9fc53889/t/expected_mustache/img/logo.png -------------------------------------------------------------------------------- /t/expected_mustache/index-ja.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 渦テストプロジェクト 7 | 8 | 9 | 15 | 16 | 17 |
    18 | 43 | 44 |
    45 |
    46 |

    Hello, world!

    47 |

    This is a template for a simple marketing or informational website. It includes a large callout called a jumbotron and three supporting pieces of content. Use it as a starting point to create something more unique.

    48 |

    Learn more »

    49 |
    50 |
    51 | 52 | 53 |
    54 | 55 |
    56 |
    57 |

    Heading

    58 |

    Donec id elit non mi porta gravida at eget metus. Fusce dapibus, tellus ac cursus commodo, tortor mauris condimentum nibh, ut fermentum massa justo sit amet risus. Etiam porta sem malesuada magna mollis euismod. Donec sed odio dui.

    59 |

    View details »

    60 |
    61 |
    62 |

    Heading

    63 |

    Donec id elit non mi porta gravida at eget metus. Fusce dapibus, tellus ac cursus commodo, tortor mauris condimentum nibh, ut fermentum massa justo sit amet risus. Etiam porta sem malesuada magna mollis euismod. Donec sed odio dui.

    64 |

    View details »

    65 |
    66 |
    67 |

    Heading

    68 |

    Donec sed odio dui. Cras justo odio, dapibus ac facilisis in, egestas eget quam. Vestibulum id ligula porta felis euismod semper. Fusce dapibus, tellus ac cursus commodo, tortor mauris condimentum nibh, ut fermentum massa justo sit amet risus.

    69 |

    View details »

    70 |
    71 |
    72 | 73 |
    74 | 75 |

    76 | Title: Welcome to Uzu
    77 | Date: 2017/07/16 78 |

    79 |
    80 |

    2017 サム・モリソン

    81 | About Us 82 |
    83 |
    84 | 85 | 86 | -------------------------------------------------------------------------------- /t/expected_mustache/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | Uzu Test Project 7 | 8 | 9 | 15 | 16 | 17 |
    18 | 43 | index html index.html 44 | 45 |
    46 |
    47 |

    Hello, world!

    48 |

    This is a template for a simple marketing or informational website. It includes a large callout called a jumbotron and three supporting pieces of content. Use it as a starting point to create something more unique.

    49 |

    Learn more »

    50 |
    51 |
    52 | 53 | 54 |
    55 | 56 |
    57 |
    58 |

    Heading

    59 |

    Donec id elit non mi porta gravida at eget metus. Fusce dapibus, tellus ac cursus commodo, tortor mauris condimentum nibh, ut fermentum massa justo sit amet risus. Etiam porta sem malesuada magna mollis euismod. Donec sed odio dui.

    60 |

    View details »

    61 |
    62 |
    63 |

    Heading

    64 |

    Donec id elit non mi porta gravida at eget metus. Fusce dapibus, tellus ac cursus commodo, tortor mauris condimentum nibh, ut fermentum massa justo sit amet risus. Etiam porta sem malesuada magna mollis euismod. Donec sed odio dui.

    65 |

    View details »

    66 |
    67 |
    68 |

    Heading

    69 |

    Donec sed odio dui. Cras justo odio, dapibus ac facilisis in, egestas eget quam. Vestibulum id ligula porta felis euismod semper. Fusce dapibus, tellus ac cursus commodo, tortor mauris condimentum nibh, ut fermentum massa justo sit amet risus.

    70 |

    View details »

    71 |
    72 |
    73 | 74 |
    75 | 76 |

    77 | Title: Welcome to Uzu
    78 | Date: 2017/07/16 79 |

    80 |
    81 |

    2016 Sam Morrison

    82 | About Us 83 |
    84 |
    85 | 86 | 87 | -------------------------------------------------------------------------------- /t/expected_mustache/nolayout-ja.html: -------------------------------------------------------------------------------- 1 | Do not render me with a layout. 2 | -------------------------------------------------------------------------------- /t/expected_mustache/nolayout.html: -------------------------------------------------------------------------------- 1 | Do not render me with a layout. 2 | -------------------------------------------------------------------------------- /t/expected_mustache/related-ja.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 渦テストプロジェクト 7 | 8 | 9 | 15 | 16 | 17 |
    18 | 43 | related pages 44 | 55 | 56 | other pages 57 | 68 | 69 | deep pages 70 | 81 | 82 | deeper pages 83 | 94 | 95 | categories 96 |

    97 | General tasks 98 |

    104 | Other tasks 105 | 109 | Even more tasks 110 | 114 |

    115 |
    116 |

    2017 サム・モリソン

    117 | About Us 118 |
    119 |
    120 | 121 | 122 | -------------------------------------------------------------------------------- /t/expected_mustache/related.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | Uzu Test Project 7 | 8 | 9 | 15 | 16 | 17 |
    18 | 43 | related pages 44 | 55 | 56 | other pages 57 | 68 | 69 | deep pages 70 | 81 | 82 | deeper pages 83 | 94 | 95 | categories 96 |

    97 | General tasks 98 |

    104 | Other tasks 105 | 109 | Even more tasks 110 | 114 |

    115 |
    116 |

    2016 Sam Morrison

    117 | About Us 118 |
    119 |
    120 | 121 | 122 | -------------------------------------------------------------------------------- /t/expected_mustache/robots.txt: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/scmorrison/uzu/40139f63a5b7da6ccd4ef64b2fd8a11d9fc53889/t/expected_mustache/robots.txt -------------------------------------------------------------------------------- /t/expected_mustache/site_details-ja.json: -------------------------------------------------------------------------------- 1 | { 2 | "site_name": "渦テストプロジェクト" 3 | } 4 | -------------------------------------------------------------------------------- /t/expected_mustache/site_details.json: -------------------------------------------------------------------------------- 1 | { 2 | "site_name": "Uzu Test Project" 3 | } 4 | -------------------------------------------------------------------------------- /t/expected_mustache/summer2017/layout-ja.html: -------------------------------------------------------------------------------- 1 | I am in the summer2017 layout 2 | -------------------------------------------------------------------------------- /t/expected_mustache/summer2017/layout.html: -------------------------------------------------------------------------------- 1 | I am in the summer2017 layout 2 | -------------------------------------------------------------------------------- /t/expected_mustache/themepartial-ja.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 渦テストプロジェクト 7 | 8 | 9 | 15 | 16 | 17 |
    18 | 43 | You found me. 44 |
    45 |

    2017 サム・モリソン

    46 | About Us 47 |
    48 |
    49 | 50 | 51 | -------------------------------------------------------------------------------- /t/expected_mustache/themepartial.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | Uzu Test Project 7 | 8 | 9 | 15 | 16 | 17 |
    18 | 43 | You found me. 44 |
    45 |

    2016 Sam Morrison

    46 | About Us 47 |
    48 |
    49 | 50 | 51 | -------------------------------------------------------------------------------- /t/expected_tt/about-ja.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | About Us - 渦テストプロジェクト 7 | 8 | 9 | 15 | 16 | 17 | 18 | 43 | 44 |
    45 |
    46 |

    About Us

    47 |
    48 |
    49 |
    50 | 51 |
    52 |
    53 |

    CEO

    54 |

    Donec id elit non mi porta gravida at eget metus. Fusce dapibus, tellus ac cursus commodo, tortor mauris condimentum nibh, ut fermentum massa justo sit amet risus. Etiam porta sem malesuada magna mollis euismod. Donec sed odio dui.

    55 |

    View details »

    56 |
    57 |
    58 |

    CTO

    59 |

    Donec id elit non mi porta gravida at eget metus. Fusce dapibus, tellus ac cursus commodo, tortor mauris condimentum nibh, ut fermentum massa justo sit amet risus. Etiam porta sem malesuada magna mollis euismod. Donec sed odio dui.

    60 |

    View details »

    61 |
    62 |
    63 |

    CFO

    64 |

    Donec sed odio dui. Cras justo odio, dapibus ac facilisis in, egestas eget quam. Vestibulum id ligula porta felis euismod semper. Fusce dapibus, tellus ac cursus commodo, tortor mauris condimentum nibh, ut fermentum massa justo sit amet risus.

    65 |

    View details »

    66 |
    67 |
    68 | 69 |
    70 | 71 | 72 | 73 |
    74 |

    2017 サム・モリソン

    75 | About Us 76 |
    77 | 78 |
    79 | 80 | 81 | -------------------------------------------------------------------------------- /t/expected_tt/about.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | About Us - Uzu Test Project 7 | 8 | 9 | 15 | 16 | 17 | 18 | 43 | 44 |
    45 |
    46 |

    About Us

    47 |
    48 |
    49 |
    50 | 51 |
    52 |
    53 |

    CEO

    54 |

    Donec id elit non mi porta gravida at eget metus. Fusce dapibus, tellus ac cursus commodo, tortor mauris condimentum nibh, ut fermentum massa justo sit amet risus. Etiam porta sem malesuada magna mollis euismod. Donec sed odio dui.

    55 |

    View details »

    56 |
    57 |
    58 |

    CTO

    59 |

    Donec id elit non mi porta gravida at eget metus. Fusce dapibus, tellus ac cursus commodo, tortor mauris condimentum nibh, ut fermentum massa justo sit amet risus. Etiam porta sem malesuada magna mollis euismod. Donec sed odio dui.

    60 |

    View details »

    61 |
    62 |
    63 |

    CFO

    64 |

    Donec sed odio dui. Cras justo odio, dapibus ac facilisis in, egestas eget quam. Vestibulum id ligula porta felis euismod semper. Fusce dapibus, tellus ac cursus commodo, tortor mauris condimentum nibh, ut fermentum massa justo sit amet risus.

    65 |

    View details »

    66 |
    67 |
    68 | 69 |
    70 | 71 | 72 | 73 |
    74 |

    2016 Sam Morrison

    75 | About Us 76 |
    77 | 78 |
    79 | 80 | 81 | -------------------------------------------------------------------------------- /t/expected_tt/blog/fiji-ja.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | Fiji Vacation 2017 - 渦テストプロジェクト 7 | 8 | 9 | 15 | 16 | 17 | 18 | 43 | 44 |

    Fiji Vacation 2017

    45 |
    46 | Date: 2017/07/19
    47 | Body:
    48 | Our trip to Fiji 49 |
    50 | 51 |
    52 |

    2017 サム・モリソン

    53 | About Us 54 |
    55 | 56 |
    57 | 58 | 59 | -------------------------------------------------------------------------------- /t/expected_tt/blog/fiji.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | Fiji Vacation 2017 - Uzu Test Project 7 | 8 | 9 | 15 | 16 | 17 | 18 | 43 | 44 |

    Fiji Vacation 2017

    45 |
    46 | Date: 2017/07/19
    47 | Body:
    48 | Our trip to Fiji 49 |
    50 | 51 |
    52 |

    2016 Sam Morrison

    53 | About Us 54 |
    55 | 56 |
    57 | 58 | 59 | -------------------------------------------------------------------------------- /t/expected_tt/blog/index-ja.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | title - 渦テストプロジェクト 7 | 8 | 9 | 15 | 16 | 17 | 18 | 43 | 44 |

    Blog index

    45 | 46 |
    47 |

    2017 サム・モリソン

    48 | About Us 49 |
    50 | 51 |
    52 | 53 | 54 | -------------------------------------------------------------------------------- /t/expected_tt/blog/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | title - Uzu Test Project 7 | 8 | 9 | 15 | 16 | 17 | 18 | 43 | 44 |

    Blog index

    45 | 46 |
    47 |

    2016 Sam Morrison

    48 | About Us 49 |
    50 | 51 |
    52 | 53 | 54 | -------------------------------------------------------------------------------- /t/expected_tt/deepembed-ja.html: -------------------------------------------------------------------------------- 1 | mountain too deep five-o mountain 2 | 3 | 4 | 5 | -------------------------------------------------------------------------------- /t/expected_tt/deepembed.html: -------------------------------------------------------------------------------- 1 | mountain too deep five-o mountainoverride for deep3 2 | 3 | 4 | 5 | -------------------------------------------------------------------------------- /t/expected_tt/embedded-ja.html: -------------------------------------------------------------------------------- 1 | I am embedded 2 | 3 | 4 | -------------------------------------------------------------------------------- /t/expected_tt/embedded.html: -------------------------------------------------------------------------------- 1 | I am embedded 2 | 3 | 4 | -------------------------------------------------------------------------------- /t/expected_tt/img/logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/scmorrison/uzu/40139f63a5b7da6ccd4ef64b2fd8a11d9fc53889/t/expected_tt/img/logo.png -------------------------------------------------------------------------------- /t/expected_tt/index-ja.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | Welcome to Uzu - 渦テストプロジェクト 7 | 8 | 9 | 15 | 16 | 17 | 18 | 43 | 44 | 45 |
    46 |
    47 |

    Hello, world!

    48 |

    This is a template for a simple marketing or informational website. It includes a large callout called a jumbotron and three supporting pieces of content. Use it as a starting point to create something more unique.

    49 |

    Learn more »

    50 |
    51 |
    52 | 53 | 54 |
    55 | 56 |
    57 |
    58 |

    Heading

    59 |

    Donec id elit non mi porta gravida at eget metus. Fusce dapibus, tellus ac cursus commodo, tortor mauris condimentum nibh, ut fermentum massa justo sit amet risus. Etiam porta sem malesuada magna mollis euismod. Donec sed odio dui.

    60 |

    View details »

    61 |
    62 |
    63 |

    Heading

    64 |

    Donec id elit non mi porta gravida at eget metus. Fusce dapibus, tellus ac cursus commodo, tortor mauris condimentum nibh, ut fermentum massa justo sit amet risus. Etiam porta sem malesuada magna mollis euismod. Donec sed odio dui.

    65 |

    View details »

    66 |
    67 |
    68 |

    Heading

    69 |

    Donec sed odio dui. Cras justo odio, dapibus ac facilisis in, egestas eget quam. Vestibulum id ligula porta felis euismod semper. Fusce dapibus, tellus ac cursus commodo, tortor mauris condimentum nibh, ut fermentum massa justo sit amet risus.

    70 |

    View details »

    71 |
    72 |
    73 | 74 |
    75 | 76 | 77 | 78 |
    79 |

    2017 サム・モリソン

    80 | About Us 81 |
    82 | 83 |
    84 | 85 | 86 | -------------------------------------------------------------------------------- /t/expected_tt/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | Welcome to Uzu - Uzu Test Project 7 | 8 | 9 | 15 | 16 | 17 | 18 | 43 | 44 | index html index.html 45 |
    46 |
    47 |

    Hello, world!

    48 |

    This is a template for a simple marketing or informational website. It includes a large callout called a jumbotron and three supporting pieces of content. Use it as a starting point to create something more unique.

    49 |

    Learn more »

    50 |
    51 |
    52 | 53 | 54 |
    55 | 56 |
    57 |
    58 |

    Heading

    59 |

    Donec id elit non mi porta gravida at eget metus. Fusce dapibus, tellus ac cursus commodo, tortor mauris condimentum nibh, ut fermentum massa justo sit amet risus. Etiam porta sem malesuada magna mollis euismod. Donec sed odio dui.

    60 |

    View details »

    61 |
    62 |
    63 |

    Heading

    64 |

    Donec id elit non mi porta gravida at eget metus. Fusce dapibus, tellus ac cursus commodo, tortor mauris condimentum nibh, ut fermentum massa justo sit amet risus. Etiam porta sem malesuada magna mollis euismod. Donec sed odio dui.

    65 |

    View details »

    66 |
    67 |
    68 |

    Heading

    69 |

    Donec sed odio dui. Cras justo odio, dapibus ac facilisis in, egestas eget quam. Vestibulum id ligula porta felis euismod semper. Fusce dapibus, tellus ac cursus commodo, tortor mauris condimentum nibh, ut fermentum massa justo sit amet risus.

    70 |

    View details »

    71 |
    72 |
    73 | 74 |
    75 | 76 | 77 | 78 |
    79 |

    2016 Sam Morrison

    80 | About Us 81 |
    82 | 83 |
    84 | 85 | 86 | -------------------------------------------------------------------------------- /t/expected_tt/nolayout-ja.html: -------------------------------------------------------------------------------- 1 | Do not render me with a layout. 2 | -------------------------------------------------------------------------------- /t/expected_tt/nolayout.html: -------------------------------------------------------------------------------- 1 | Do not render me with a layout. 2 | -------------------------------------------------------------------------------- /t/expected_tt/related-ja.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | title - 渦テストプロジェクト 7 | 8 | 9 | 15 | 16 | 17 | 18 | 43 | 44 | related pages 45 | 56 | 57 | other pages 58 | 69 | 70 | deep pages 71 | 82 | 83 | deeper still 84 |

    Deeper title 85 |

    96 |

    97 | 98 | categories 99 |

    General tasks 100 |

    114 |

    115 | 116 |

    Other tasks 117 |

    125 |

    126 | 127 |

    Even more tasks 128 |

    136 |

    137 | 138 |
    139 |

    2017 サム・モリソン

    140 | About Us 141 |
    142 | 143 |
    144 | 145 | 146 | -------------------------------------------------------------------------------- /t/expected_tt/related.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | title - Uzu Test Project 7 | 8 | 9 | 15 | 16 | 17 | 18 | 43 | 44 | related pages 45 | 56 | 57 | other pages 58 | 69 | 70 | deep pages 71 | 82 | 83 | deeper still 84 |

    Deeper title 85 |

    96 |

    97 | 98 | categories 99 |

    General tasks 100 |

    114 |

    115 | 116 |

    Other tasks 117 |

    125 |

    126 | 127 |

    Even more tasks 128 |

    136 |

    137 | 138 |
    139 |

    2016 Sam Morrison

    140 | About Us 141 |
    142 | 143 |
    144 | 145 | 146 | -------------------------------------------------------------------------------- /t/expected_tt/robots.txt: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/scmorrison/uzu/40139f63a5b7da6ccd4ef64b2fd8a11d9fc53889/t/expected_tt/robots.txt -------------------------------------------------------------------------------- /t/expected_tt/site_details-ja.json: -------------------------------------------------------------------------------- 1 | { 2 | "site_name": "渦テストプロジェクト" 3 | } 4 | -------------------------------------------------------------------------------- /t/expected_tt/site_details.json: -------------------------------------------------------------------------------- 1 | { 2 | "site_name": "Uzu Test Project" 3 | } 4 | -------------------------------------------------------------------------------- /t/expected_tt/summer2017/layout-ja.html: -------------------------------------------------------------------------------- 1 | I am in the summer2017 layout 2 | -------------------------------------------------------------------------------- /t/expected_tt/summer2017/layout.html: -------------------------------------------------------------------------------- 1 | I am in the summer2017 layout 2 | -------------------------------------------------------------------------------- /t/expected_tt/themepartial-ja.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | title - 渦テストプロジェクト 7 | 8 | 9 | 15 | 16 | 17 | 18 | 43 | 44 | You found me. 45 | 46 | 47 |
    48 |

    2017 サム・モリソン

    49 | About Us 50 |
    51 | 52 |
    53 | 54 | 55 | -------------------------------------------------------------------------------- /t/expected_tt/themepartial.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | title - Uzu Test Project 7 | 8 | 9 | 15 | 16 | 17 | 18 | 43 | 44 | You found me. 45 | 46 | 47 |
    48 |

    2016 Sam Morrison

    49 | About Us 50 |
    51 | 52 | 53 | 54 | 55 | -------------------------------------------------------------------------------- /t/lib/Local.rakumod: -------------------------------------------------------------------------------- 1 | unit module Local; 2 | 3 | our sub context(--> Hash) { 4 | return %{ 5 | extended => "post-command test" 6 | } 7 | } 8 | -------------------------------------------------------------------------------- /t/lib/LocalX.rakumod: -------------------------------------------------------------------------------- 1 | unit module LocalX; 2 | -------------------------------------------------------------------------------- /t/serve/build/blog/fiji.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | Fiji Vacation 2017 4 | 5 | 6 | 2017/07/18
    7 | Our trip to Fiji 8 | 9 | 10 | -------------------------------------------------------------------------------- /t/serve/build/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | Test 4 | 5 | 6 | Uzu test html 7 | 8 | 9 | -------------------------------------------------------------------------------- /t/serve/config-multi-port-increment.yml: -------------------------------------------------------------------------------- 1 | --- 2 | name: uzu-tests 3 | language: 4 | - en 5 | port: 4000 6 | themes: 7 | - default 8 | - summer2017 9 | url: https://gitlab.com/samcns/uzu 10 | project_root: t/serve 11 | host: "127.0.0.1" 12 | -------------------------------------------------------------------------------- /t/serve/config-multi.yml: -------------------------------------------------------------------------------- 1 | --- 2 | name: uzu-tests 3 | language: 4 | - en 5 | port: 3334 6 | themes: 7 | - default 8 | - summer2017: 9 | port: 3335 10 | url: https://gitlab.com/samcns/uzu 11 | project_root: t/serve 12 | host: "127.0.0.1" 13 | -------------------------------------------------------------------------------- /t/serve/config.yml: -------------------------------------------------------------------------------- 1 | --- 2 | name: uzu-tests 3 | language: 4 | - en 5 | theme: default 6 | url: https://gitlab.com/samcns/uzu 7 | project_root: t/serve 8 | host: "127.0.0.1" 9 | port: 3333 10 | -------------------------------------------------------------------------------- /t/serve/i18n/en.yml: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/scmorrison/uzu/40139f63a5b7da6ccd4ef64b2fd8a11d9fc53889/t/serve/i18n/en.yml -------------------------------------------------------------------------------- /t/serve/pages/index.tt: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/scmorrison/uzu/40139f63a5b7da6ccd4ef64b2fd8a11d9fc53889/t/serve/pages/index.tt -------------------------------------------------------------------------------- /t/serve/partials/blank.tt: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/scmorrison/uzu/40139f63a5b7da6ccd4ef64b2fd8a11d9fc53889/t/serve/partials/blank.tt -------------------------------------------------------------------------------- /t/serve/public/robots.txt: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/scmorrison/uzu/40139f63a5b7da6ccd4ef64b2fd8a11d9fc53889/t/serve/public/robots.txt -------------------------------------------------------------------------------- /t/serve/summer2017/layout-ja.html: -------------------------------------------------------------------------------- 1 | I am in the summer2017 layout 2 | -------------------------------------------------------------------------------- /t/serve/summer2017/layout.html: -------------------------------------------------------------------------------- 1 | I am in the summer2017 layout 2 | -------------------------------------------------------------------------------- /t/serve/themes/default/layout/index.tt: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/scmorrison/uzu/40139f63a5b7da6ccd4ef64b2fd8a11d9fc53889/t/serve/themes/default/layout/index.tt -------------------------------------------------------------------------------- /t/serve/themes/summer2017/layout.tt: -------------------------------------------------------------------------------- 1 | --- 2 | root_url: "https://www.perl6.org" 3 | separator: ':::' 4 | url_postfix: '@-@' 5 | special: I am in the summer2017 layout 6 | --- 7 | 8 | 9 | [% INCLUDE "head" %] 10 | 11 |
    12 | [% INCLUDE "navigation" %] 13 | [% INCLUDE "content" %] 14 | [% INCLUD "footer" %] 15 |
    16 | 17 | 18 | -------------------------------------------------------------------------------- /xt/01-serve.t: -------------------------------------------------------------------------------- 1 | use v6; 2 | 3 | use Test; 4 | use Test::Output; 5 | use Uzu::Config; 6 | use Uzu::HTTP; 7 | 8 | my $root = $*CWD; 9 | my $host = '127.0.0.1'; 10 | my $port = 3333; 11 | 12 | my $output = output-from { 13 | Uzu::Config::from-file( 14 | config_file => $root.IO.child('t').child('serve').child('config.yml'), 15 | no_livereload => True).&Uzu::HTTP::web-server() 16 | } 17 | say $output if %*ENV; 18 | 19 | subtest { 20 | # Wait for server to come online 21 | ok Uzu::HTTP::wait-port($port, times => 600), 'spawned development web server'; 22 | }, 'Single theme'; 23 | 24 | subtest { 25 | my $html_test = q:to/END/; 26 | 27 | 28 | Test 29 | 30 | 31 | Uzu test html 32 | 33 | 34 | END 35 | 36 | my $results = Uzu::HTTP::inet-request("GET /index.html HTTP/1.0\r\nContent-length: 0\r\n\r\n", $port); 37 | ok $results ~~ / $html_test /, 'served HTML match'; 38 | }, 'Top-level page'; 39 | 40 | subtest { 41 | my $html_test = q:to/END/; 42 | 43 | 44 | Fiji Vacation 2017 45 | 46 | 47 | 2017/07/18
    48 | Our trip to Fiji 49 | 50 | 51 | END 52 | 53 | my $results = Uzu::HTTP::inet-request("GET /blog/fiji.html HTTP/1.0\r\nContent-length: 0\r\n\r\n", $port); 54 | ok $results ~~ / $html_test /, 'served nested page HTML match'; 55 | }, 'Nested page'; 56 | 57 | subtest { 58 | my $t1_output = output-from { 59 | try { 60 | Uzu::HTTP::web-server( 61 | Uzu::Config::from-file( 62 | config_file => $root.IO.child('t').child('serve').child('config-multi.yml'), 63 | no_livereload => True) 64 | ); 65 | } 66 | } 67 | say $t1_output if %*ENV; 68 | 69 | # Wait for server to come online 70 | my $t1_default_server = Uzu::HTTP::wait-port(3334, times => 600); 71 | my $t1_summer2017_server = Uzu::HTTP::wait-port(3335, times => 600); 72 | 73 | ok ($t1_default_server && $t1_summer2017_server), 'spawned development web server'; 74 | }, 'Multi-theme 1'; 75 | 76 | subtest { 77 | my $t2_output = output-from { 78 | try { 79 | Uzu::HTTP::web-server( 80 | Uzu::Config::from-file( 81 | config_file => $root.IO.child('t').child('serve').child('config-multi-port-increment.yml'), 82 | no_livereload => True) 83 | ); 84 | } 85 | } 86 | say $t2_output if %*ENV; 87 | 88 | # Wait for server to come online 89 | my $t2_default_server = Uzu::HTTP::wait-port(4000, times => 600); 90 | my $t2_summer2017_server = Uzu::HTTP::wait-port(4001, times => 600); 91 | 92 | ok ($t2_default_server && $t2_summer2017_server), 'auto-increment port number when not defined in config'; 93 | }, 'Multi-theme 2'; 94 | 95 | done-testing; 96 | 97 | # vim: ft=perl6 98 | -------------------------------------------------------------------------------- /xt/02-build-tt.t: -------------------------------------------------------------------------------- 1 | use v6; 2 | 3 | use Test; 4 | use Test::Output; 5 | use Uzu::Config; 6 | use Uzu::Render; 7 | use Uzu::Utilities; 8 | use File::Temp; 9 | 10 | # Source project files 11 | my $test_root = $*CWD.IO.child('t'); 12 | 13 | subtest { 14 | my $source_root = $test_root.IO.child('example_project_tt'); 15 | # Setup tmp project root 16 | my $tmp_root = tempdir; 17 | 18 | # Copy all example project files to tmp project root 19 | copy-dir $source_root, $tmp_root.IO; 20 | rm-dir $tmp_root.IO.child('build'); 21 | 22 | # Add tmp path to project config 23 | my $config_path = $tmp_root.IO.child('config.yml'); 24 | my $config_file = slurp $config_path; 25 | spurt $config_path, $config_file ~ "project_root: {$tmp_root.path}\n"; 26 | 27 | # Set config file path 28 | my $config = Uzu::Config::from-file config_file => $config_path, :no_livereload; 29 | 30 | # Generate HTML from templates 31 | my $stdout = stdout-from { 32 | try { 33 | Uzu::Render::build $config; 34 | } 35 | } 36 | say $stdout if %*ENV; 37 | 38 | # Did we generate the build directory? 39 | my $tmp_build_path = $tmp_root.IO.child('build').child('default').path; 40 | ok $tmp_build_path.IO.e, 'build directory created'; 41 | 42 | # Did we copy the assets folder contents? 43 | is $tmp_build_path.IO.child('img').child('logo.png').IO.e, True, 'assets folder contents copied'; 44 | 45 | # Did we copy the pulic folder contents? 46 | is $tmp_build_path.IO.child('robots.txt').IO.e, True, 'public folder contents copied'; 47 | 48 | # Generated HTML looks good? 49 | my $t4_expected_html = slurp $test_root.IO.child('expected_tt').child('index.html'); 50 | my $t4_generated_html = slurp $tmp_build_path.IO.child('index.html'); 51 | is $t4_generated_html, $t4_expected_html, '[Template6] rendered HTML matches test'; 52 | 53 | # Generated nested HTML looks good? 54 | my $t5_expected_html = slurp $test_root.IO.child('expected_tt').child('blog').child('fiji.html'); 55 | my $t5_generated_html = slurp $tmp_build_path.IO.child('blog').child('fiji.html'); 56 | is $t5_generated_html, $t5_expected_html, '[Template6] rendered nested HTML matches test'; 57 | 58 | # Generated *_pages links exposed 59 | my $t6_expected_html = slurp $test_root.IO.child('expected_tt').child('related.html'); 60 | my $t6_generated_html = slurp $tmp_build_path.IO.child('related.html'); 61 | is $t6_generated_html, $t6_expected_html, '[Template6] expose and utilize *_pages dict variables'; 62 | 63 | # Use i18n language in uri for non-default languages 64 | my $t7_expected_html = slurp $test_root.IO.child('expected_tt').child('related-ja.html'); 65 | my $t7_generated_html = slurp $tmp_build_path.IO.child('related-ja.html'); 66 | is $t7_generated_html, $t7_expected_html, '[Template6] i18n language in uri for non-default languages'; 67 | 68 | # Use theme partial 69 | my $t8_expected_html = slurp $test_root.IO.child('expected_tt').child('themepartial.html'); 70 | my $t8_generated_html = slurp $tmp_build_path.IO.child('themepartial.html'); 71 | is $t8_generated_html, $t8_expected_html, '[Template6] use theme partial'; 72 | 73 | # Rebuild page when related page modified 74 | my $t9_generated_pre_modified = $tmp_build_path.IO.child('related.html').modified; 75 | my $t9_related_page = $tmp_root.IO.child('pages').child('about.tt'); 76 | spurt $t9_related_page, slurp($t9_related_page); 77 | my $stdout9 = stdout-from { 78 | try { 79 | Uzu::Render::build $config; 80 | } 81 | } 82 | say $stdout9 if %*ENV; 83 | 84 | my $t9_generated_post_modified = $tmp_build_path.IO.child('related.html').modified; 85 | ok $t9_generated_post_modified > $t9_generated_pre_modified, '[Template6] modifying a related page triggers page rebuild'; 86 | 87 | # Modifying an unrelated partial does not trigger page rebuild 88 | my $t10_generated_pre_modified = $tmp_build_path.IO.child('related.html').modified; 89 | my $t10_unrelated_partial = $tmp_root.IO.child('partials').child('usetheme.tt'); 90 | spurt $t10_unrelated_partial, slurp($t10_unrelated_partial); 91 | my $stdout10 = stdout-from { Uzu::Render::build $config }; 92 | say $stdout10 if %*ENV; 93 | my $t10_generated_post_modified = $tmp_build_path.IO.child('related.html').modified; 94 | ok $t10_generated_post_modified == $t10_generated_pre_modified, '[Template6] modifying an unrelated partial does not trigger page rebuild'; 95 | 96 | # Disable theme layout from page yaml 97 | my $t11_expected_html = slurp $test_root.IO.child('expected_tt').child('nolayout.html'); 98 | my $t11_generated_html = slurp $tmp_build_path.IO.child('nolayout.html'); 99 | is $t11_generated_html, $t11_expected_html, '[Template6] disable theme layout from page yaml'; 100 | 101 | # Embedded partials can access page vars 102 | my $t12_expected_html = slurp $test_root.IO.child('expected_tt').child('embedded.html'); 103 | my $t12_generated_html = slurp $tmp_build_path.IO.child('embedded.html'); 104 | is $t12_generated_html, $t12_expected_html, '[Template6] embedded partials can access page vars'; 105 | 106 | # Deeply embedded partials can access page vars 107 | my $t13_expected_html = slurp $test_root.IO.child('expected_tt').child('deepembed.html'); 108 | my $t13_generated_html = slurp $tmp_build_path.IO.child('deepembed.html'); 109 | is $t13_generated_html, $t13_expected_html, '[Template6] deeply embedded partials can access page vars'; 110 | 111 | my $t14_expected_html = slurp $test_root.IO.child('expected_tt').child('summer2017').child('layout.html'); 112 | my $t14_generated_html = slurp $tmp_root.IO.child('build').child('summer2017').child('layout.html'); 113 | is $t14_generated_html, $t14_expected_html, '[Template6] multi-theme build with layout specific varibles'; 114 | 115 | my $t15_excluded_page = $tmp_root.IO.child('build').child('summer2017').child('excludeme.html').IO.e; 116 | nok $t15_excluded_page, '[Template6] multi-theme exclude page for theme via config'; 117 | 118 | my $t16_excluded_dir = so $tmp_root.IO.child('build').child('bad_folder').IO.e; 119 | nok $t16_excluded_dir, '[Template6] exclude directory from build via config'; 120 | 121 | my $t17_excluded_file = so $tmp_root.IO.child('build').child('bad_file.txt').IO.e; 122 | nok $t17_excluded_file, '[Template6] exclude file from build via config'; 123 | 124 | my $t18_pre_command = so $stdout10.contains('pre-command test'); 125 | ok $t18_pre_command, '[Template6] pre_command via config'; 126 | 127 | my $t19_post_command = so $stdout10.contains('post-command test'); 128 | ok $t19_post_command, '[Template6] post_command via config'; 129 | 130 | }, 'Rendering [tt]'; 131 | 132 | subtest { 133 | 134 | my $source_root = $test_root.IO.child('example_project_tt'); 135 | # Setup tmp project root 136 | my $tmp_root = tempdir; 137 | 138 | # Copy all example project files to tmp project root 139 | copy-dir $source_root, $tmp_root.IO; 140 | rm-dir $tmp_root.IO.child('build'); 141 | 142 | # Add tmp path to project config 143 | my $config_path = $tmp_root.IO.child('config.yml'); 144 | my $config_file = slurp $config_path; 145 | spurt $config_path, $config_file ~ "project_root: $tmp_root\ni18n_scheme: 'directory'\n"; 146 | 147 | # Set config file path 148 | my $config = Uzu::Config::from-file config_file => $config_path, :no_livereload; 149 | 150 | # Generate HTML from templates 151 | my $stdout = stdout-from { 152 | try { 153 | Uzu::Render::build $config; 154 | } 155 | } 156 | say $stdout if %*ENV; 157 | 158 | my $tmp_build_path = $tmp_root.IO.child('build').child('default').path; 159 | ok $tmp_build_path.IO.child('ja').child('related.html').e, '[Template6] i18n language for non-default languages (scheme: directory)'; 160 | 161 | }, 'Rendering i18n scheme directory [tt]'; 162 | 163 | done-testing; 164 | 165 | # vim: ft=perl6 166 | -------------------------------------------------------------------------------- /xt/03-build-mustache.t: -------------------------------------------------------------------------------- 1 | use v6; 2 | 3 | use Test; 4 | use Test::Output; 5 | use Uzu::Config; 6 | use Uzu::Render; 7 | use Uzu::Utilities; 8 | use File::Temp; 9 | 10 | # Source project files 11 | my $test_root = $*CWD.IO.child('t'); 12 | 13 | subtest { 14 | my $source_root = $test_root.IO.child('example_project_mustache'); 15 | # Setup tmp project root 16 | my $tmp_root = tempdir; 17 | 18 | # Copy all example project files to tmp project root 19 | copy-dir $source_root, $tmp_root.IO; 20 | rm-dir $tmp_root.IO.child('build'); 21 | 22 | # Add tmp path to project config 23 | my $config_path = $tmp_root.IO.child('config.yml'); 24 | my $config_file = slurp $config_path; 25 | spurt $config_path, $config_file ~ "\nproject_root: $tmp_root\n"; 26 | 27 | # Set config file path 28 | my $config = Uzu::Config::from-file config_file => $config_path, :no_livereload; 29 | 30 | # Generate HTML from templates 31 | my $stdout = output-from { 32 | try { 33 | Uzu::Render::build $config; 34 | } 35 | } 36 | say $stdout if %*ENV; 37 | 38 | my $tmp_build_path = $tmp_root.IO.child('build').child('default').path; 39 | 40 | # Generated HTML looks good? 41 | my $t1_expected_html = slurp $test_root.IO.child('expected_mustache').child('index.html'); 42 | my $t1_generated_html = slurp $tmp_build_path.IO.child('index.html'); 43 | is $t1_generated_html, $t1_expected_html, '[Mustache] rendered HTML matches test'; 44 | 45 | # Generated nested HTML looks good? 46 | my $t2_expected_html = slurp $test_root.IO.child('expected_mustache').child('blog').child('fiji.html'); 47 | my $t2_generated_html = slurp $tmp_build_path.IO.child('blog').child('fiji.html'); 48 | is $t2_generated_html, $t2_expected_html, '[Mustache] rendered nested HTML matches test'; 49 | 50 | # Generated *_pages links exposed 51 | my $t3_expected_html = slurp $test_root.IO.child('expected_mustache').child('related.html'); 52 | my $t3_generated_html = slurp $tmp_build_path.IO.child('related.html'); 53 | is $t3_generated_html, $t3_expected_html, '[Mustache] expose and utilize *_pages dict variables'; 54 | 55 | # Use i18n language in uri for non-default languages 56 | my $t4_expected_html = slurp $test_root.IO.child('expected_mustache').child('related-ja.html'); 57 | my $t4_generated_html = slurp $tmp_build_path.IO.child('related-ja.html'); 58 | is trim($t4_generated_html), trim($t4_expected_html), '[Mustache] i18n language in uri for non-default languages'; 59 | 60 | # Use theme partial 61 | my $t5_expected_html = slurp $test_root.IO.child('expected_mustache').child('themepartial.html'); 62 | my $t5_generated_html = slurp $tmp_build_path.IO.child('themepartial.html'); 63 | is $t5_generated_html, $t5_expected_html, '[Mustache] use theme partial'; 64 | 65 | # Rebuild page when related page modified 66 | my $t6_generated_pre_modified = $tmp_build_path.IO.child('related.html').modified; 67 | my $t6_related_page = $tmp_root.IO.child('pages').child('about.mustache'); 68 | spurt $t6_related_page, slurp($t6_related_page); 69 | my $stdout6 = stdout-from { 70 | try { 71 | Uzu::Render::build $config; 72 | } 73 | } 74 | say $stdout6 if %*ENV; 75 | my $t6_generated_post_modified = $tmp_build_path.IO.child('related.html').modified; 76 | ok $t6_generated_post_modified > $t6_generated_pre_modified, '[Mustache] modifying a related page triggers page rebuild'; 77 | 78 | # Modifying an unrelated partial does not trigger page rebuild 79 | my $t7_generated_pre_modified = $tmp_build_path.IO.child('related.html').modified; 80 | my $t7_unrelated_partial = $tmp_root.IO.child('partials').child('usetheme.mustache'); 81 | spurt $t7_unrelated_partial, slurp($t7_unrelated_partial); 82 | my $stdout7 = stdout-from { 83 | try { 84 | Uzu::Render::build $config; 85 | } 86 | } 87 | say $stdout7 if %*ENV; 88 | my $t7_generated_post_modified = $tmp_build_path.IO.child('related.html').modified; 89 | ok $t7_generated_post_modified == $t7_generated_pre_modified, '[Mustache] modifying an unrelated partial does not trigger page rebuild'; 90 | 91 | # Disable theme layout from page yaml 92 | my $t8_expected_html = slurp $test_root.IO.child('expected_mustache').child('nolayout.html'); 93 | my $t8_generated_html = slurp $tmp_build_path.IO.child('nolayout.html'); 94 | is $t8_generated_html, $t8_expected_html, '[Mustache] disable theme layout from page yaml'; 95 | 96 | # Embedded partials can access page vars 97 | my $t9_expected_html = slurp $test_root.IO.child('expected_mustache').child('embedded.html'); 98 | my $t9_generated_html = slurp $tmp_build_path.IO.child('embedded.html'); 99 | is $t9_generated_html, $t9_expected_html, '[Mustache] embedded partials can access page vars'; 100 | 101 | # Deeply embedded partials can access page vars 102 | my $t10_expected_html = slurp $test_root.IO.child('expected_mustache').child('deepembed.html'); 103 | my $t10_generated_html = slurp $tmp_build_path.IO.child('deepembed.html'); 104 | is $t10_generated_html, $t10_expected_html, '[Mustache] deeply embedded partials can access page vars'; 105 | 106 | my $t11_expected_html = slurp $test_root.IO.child('expected_mustache').child('summer2017').child('layout.html'); 107 | my $t11_generated_html = slurp $tmp_root.IO.child('build').child('summer2017').child('layout.html'); 108 | is $t11_generated_html, $t11_expected_html, '[Mustache] multi-theme build with layout specific varibles'; 109 | 110 | my $t12_excluded_page = $tmp_root.IO.child('build').child('summer2017').child('excludeme.html').IO.e; 111 | nok $t12_excluded_page, '[Mustache] multi-theme exclude page for theme via config'; 112 | 113 | my $t13_excluded_dir = so $tmp_root.IO.child('build').child('bad_folder').IO.e; 114 | nok $t13_excluded_dir, '[Mustache] exclude directory from build via config'; 115 | 116 | my $t14_excluded_file = so $tmp_root.IO.child('build').child('bad_file.txt').IO.e; 117 | nok $t14_excluded_file, '[Mustache] exclude file from build via config'; 118 | 119 | my $t15_pre_command = so $stdout7.contains('pre-command test'); 120 | ok $t15_pre_command, '[Mustache] pre_command via config'; 121 | 122 | my $t16_post_command = so $stdout7.contains('post-command test'); 123 | ok $t16_post_command, '[Mustache] post_command via config'; 124 | 125 | }, 'Rendering [Mustache]'; 126 | 127 | subtest { 128 | 129 | my $source_root = $test_root.IO.child('example_project_mustache'); 130 | # Setup tmp project root 131 | my $tmp_root = tempdir; 132 | 133 | # Copy all example project files to tmp project root 134 | copy-dir $source_root, $tmp_root.IO; 135 | rm-dir $tmp_root.IO.child('build'); 136 | 137 | # Add tmp path to project config 138 | my $config_path = $tmp_root.IO.child('config.yml'); 139 | my $config_file = slurp $config_path; 140 | spurt $config_path, $config_file ~ "project_root: $tmp_root\ni18n_scheme: 'directory'\n"; 141 | 142 | # Set config file path 143 | my $config = Uzu::Config::from-file config_file => $config_path, :no_livereload; 144 | 145 | # Generate HTML from templates 146 | my $stdout = stdout-from { 147 | try { 148 | Uzu::Render::build $config; 149 | } 150 | } 151 | say $stdout if %*ENV; 152 | 153 | my $tmp_build_path = $tmp_root.IO.child('build').child('default').path; 154 | ok $tmp_build_path.IO.child('ja').child('related.html').e, '[Mustache] i18n language for non-default languages (scheme: directory)'; 155 | 156 | }, 'Rendering i18n scheme directory [Mustache]'; 157 | done-testing; 158 | 159 | # vim: ft=perl6 160 | -------------------------------------------------------------------------------- /xt/04-build-warnings.t: -------------------------------------------------------------------------------- 1 | use v6; 2 | 3 | use Test; 4 | use Test::Output; 5 | use Uzu::Config; 6 | use Uzu::Render; 7 | use Uzu::Utilities; 8 | use File::Temp; 9 | 10 | # Source project files 11 | my $test_root = $*CWD.IO.child('t'); 12 | 13 | subtest { 14 | my $source_root = $test_root.IO.child('example_project_tt'); 15 | 16 | # Setup tmp project root 17 | my $tmp_root = tempdir; 18 | 19 | # Copy all example project files to tmp project root 20 | copy-dir $source_root, $tmp_root.IO; 21 | 22 | # Add tmp path to project config 23 | my $config_path = $tmp_root.IO.child('config.yml'); 24 | my $config_file = slurp $config_path; 25 | 26 | # Intejntionally attempt to load nonexisting module, use tmp_root 27 | spurt $config_path, $config_file.subst('Local', 'LocalX') ~ "project_root: $tmp_root\n"; 28 | 29 | # Expect a warning when i18n yaml is invalid 30 | my $yaml = q:to/END/; 31 | --- 32 | company: Sam Morrison 33 | site_name: Uzu Test Project 34 | # Need to quote strings that start with numbers 35 | copyright: 2016 Sam Morrison 36 | @can't start a key with @ 37 | END 38 | 39 | # Save to tmp_build_path i18n yaml file 40 | spurt $tmp_root.IO.child('i18n').child('en.yml'), $yaml; 41 | 42 | # Do not die when theme layout template is missing 43 | unlink $tmp_root.IO.child('themes').child('default').child('layout.tt'); 44 | 45 | my $stdout = output-from { 46 | try { 47 | Uzu::Render::build 48 | Uzu::Config::from-file( config_file => $config_path, :no_livereload ); 49 | } 50 | } 51 | note $stdout if %*ENV; 52 | 53 | # Test warnings 54 | like $stdout, / 'Rendered page [empty] is empty' /, 'empty page template warning to stdout'; 55 | like $stdout, / 'Invalid i18n yaml file' /, 'invalid i18n yaml warning to stdout'; 56 | like $stdout, / 'Theme [default] does not contain a layout template' /, 'theme layout template is missing warning to stdout'; 57 | like $stdout, / 'Extended [LocalX] is missing context()' /, 'extended library is missing subroutine context()'; 58 | 59 | }, 'Warnings'; 60 | 61 | done-testing; 62 | 63 | # vim: ft=perl6 64 | --------------------------------------------------------------------------------