` tags in the HTML output. For example, this input:
378 |
379 | * Bird
380 | * Magic
381 |
382 | will turn into:
383 |
384 |
442 |
443 |
444 | It's worth noting that it's possible to trigger an ordered list by
445 | accident, by writing something like this:
446 |
447 | 1986. What a great season.
448 |
449 | In other words, a *number-period-space* sequence at the beginning of a
450 | line. To avoid this, you can backslash-escape the period:
451 |
452 | 1986\. What a great season.
453 |
454 |
455 |
456 | Code Blocks
457 |
458 | Pre-formatted code blocks are used for writing about programming or
459 | markup source code. Rather than forming normal paragraphs, the lines
460 | of a code block are interpreted literally. Markdown wraps a code block
461 | in both `` and `` tags.
462 |
463 | To produce a code block in Markdown, simply indent every line of the
464 | block by at least 4 spaces or 1 tab. For example, given this input:
465 |
466 | This is a normal paragraph:
467 |
468 | This is a code block.
469 |
470 | Markdown will generate:
471 |
472 | This is a normal paragraph:
473 |
474 | This is a code block.
475 |
476 |
477 | One level of indentation -- 4 spaces or 1 tab -- is removed from each
478 | line of the code block. For example, this:
479 |
480 | Here is an example of AppleScript:
481 |
482 | tell application "Foo"
483 | beep
484 | end tell
485 |
486 | will turn into:
487 |
488 | Here is an example of AppleScript:
489 |
490 | tell application "Foo"
491 | beep
492 | end tell
493 |
494 |
495 | A code block continues until it reaches a line that is not indented
496 | (or the end of the article).
497 |
498 | Within a code block, ampersands (`&`) and angle brackets (`<` and `>`)
499 | are automatically converted into HTML entities. This makes it very
500 | easy to include example HTML source code using Markdown -- just paste
501 | it and indent it, and Markdown will handle the hassle of encoding the
502 | ampersands and angle brackets. For example, this:
503 |
504 |
507 |
508 | will turn into:
509 |
510 | <div class="footer">
511 | © 2004 Foo Corporation
512 | </div>
513 |
514 |
515 | Regular Markdown syntax is not processed within code blocks. E.g.,
516 | asterisks are just literal asterisks within a code block. This means
517 | it's also easy to use Markdown to write about Markdown's own syntax.
518 |
519 |
520 |
521 | Horizontal Rules
522 |
523 | You can produce a horizontal rule tag (` `) by placing three or
524 | more hyphens, asterisks, or underscores on a line by themselves. If you
525 | wish, you may use spaces between the hyphens or asterisks. Each of the
526 | following lines will produce a horizontal rule:
527 |
528 | * * *
529 |
530 | ***
531 |
532 | *****
533 |
534 | - - -
535 |
536 | ---------------------------------------
537 |
538 |
539 | * * *
540 |
541 | Span Elements
542 |
543 | Links
544 |
545 | Markdown supports two style of links: *inline* and *reference*.
546 |
547 | In both styles, the link text is delimited by [square brackets].
548 |
549 | To create an inline link, use a set of regular parentheses immediately
550 | after the link text's closing square bracket. Inside the parentheses,
551 | put the URL where you want the link to point, along with an *optional*
552 | title for the link, surrounded in quotes. For example:
553 |
554 | This is [an example](http://example.com/ "Title") inline link.
555 |
556 | [This link](http://example.net/) has no title attribute.
557 |
558 | Will produce:
559 |
560 | This is
561 | an example inline link.
562 |
563 | This link has no
564 | title attribute.
565 |
566 | If you're referring to a local resource on the same server, you can
567 | use relative paths:
568 |
569 | See my [About](/about/) page for details.
570 |
571 | Reference-style links use a second set of square brackets, inside
572 | which you place a label of your choosing to identify the link:
573 |
574 | This is [an example][id] reference-style link.
575 |
576 | You can optionally use a space to separate the sets of brackets:
577 |
578 | This is [an example] [id] reference-style link.
579 |
580 | Then, anywhere in the document, you define your link label like this,
581 | on a line by itself:
582 |
583 | [id]: http://example.com/ "Optional Title Here"
584 |
585 | That is:
586 |
587 | * Square brackets containing the link identifier (optionally
588 | indented from the left margin using up to three spaces);
589 | * followed by a colon;
590 | * followed by one or more spaces (or tabs);
591 | * followed by the URL for the link;
592 | * optionally followed by a title attribute for the link, enclosed
593 | in double or single quotes, or enclosed in parentheses.
594 |
595 | The following three link definitions are equivalent:
596 |
597 | [foo]: http://example.com/ "Optional Title Here"
598 | [foo]: http://example.com/ 'Optional Title Here'
599 | [foo]: http://example.com/ (Optional Title Here)
600 |
601 | **Note:** There is a known bug in Markdown.pl 1.0.1 which prevents
602 | single quotes from being used to delimit link titles.
603 |
604 | The link URL may, optionally, be surrounded by angle brackets:
605 |
606 | [id]: "Optional Title Here"
607 |
608 | You can put the title attribute on the next line and use extra spaces
609 | or tabs for padding, which tends to look better with longer URLs:
610 |
611 | [id]: http://example.com/longish/path/to/resource/here
612 | "Optional Title Here"
613 |
614 | Link definitions are only used for creating links during Markdown
615 | processing, and are stripped from your document in the HTML output.
616 |
617 | Link definition names may consist of letters, numbers, spaces, and
618 | punctuation -- but they are *not* case sensitive. E.g. these two
619 | links:
620 |
621 | [link text][a]
622 | [link text][A]
623 |
624 | are equivalent.
625 |
626 | The *implicit link name* shortcut allows you to omit the name of the
627 | link, in which case the link text itself is used as the name.
628 | Just use an empty set of square brackets -- e.g., to link the word
629 | "Google" to the google.com web site, you could simply write:
630 |
631 | [Google][]
632 |
633 | And then define the link:
634 |
635 | [Google]: http://google.com/
636 |
637 | Because link names may contain spaces, this shortcut even works for
638 | multiple words in the link text:
639 |
640 | Visit [Daring Fireball][] for more information.
641 |
642 | And then define the link:
643 |
644 | [Daring Fireball]: http://daringfireball.net/
645 |
646 | Link definitions can be placed anywhere in your Markdown document. I
647 | tend to put them immediately after each paragraph in which they're
648 | used, but if you want, you can put them all at the end of your
649 | document, sort of like footnotes.
650 |
651 | Here's an example of reference links in action:
652 |
653 | I get 10 times more traffic from [Google] [1] than from
654 | [Yahoo] [2] or [MSN] [3].
655 |
656 | [1]: http://google.com/ "Google"
657 | [2]: http://search.yahoo.com/ "Yahoo Search"
658 | [3]: http://search.msn.com/ "MSN Search"
659 |
660 | Using the implicit link name shortcut, you could instead write:
661 |
662 | I get 10 times more traffic from [Google][] than from
663 | [Yahoo][] or [MSN][].
664 |
665 | [google]: http://google.com/ "Google"
666 | [yahoo]: http://search.yahoo.com/ "Yahoo Search"
667 | [msn]: http://search.msn.com/ "MSN Search"
668 |
669 | Both of the above examples will produce the following HTML output:
670 |
671 | I get 10 times more traffic from Google than from
673 | Yahoo
674 | or MSN .
675 |
676 | For comparison, here is the same paragraph written using
677 | Markdown's inline link style:
678 |
679 | I get 10 times more traffic from [Google](http://google.com/ "Google")
680 | than from [Yahoo](http://search.yahoo.com/ "Yahoo Search") or
681 | [MSN](http://search.msn.com/ "MSN Search").
682 |
683 | The point of reference-style links is not that they're easier to
684 | write. The point is that with reference-style links, your document
685 | source is vastly more readable. Compare the above examples: using
686 | reference-style links, the paragraph itself is only 81 characters
687 | long; with inline-style links, it's 176 characters; and as raw HTML,
688 | it's 234 characters. In the raw HTML, there's more markup than there
689 | is text.
690 |
691 | With Markdown's reference-style links, a source document much more
692 | closely resembles the final output, as rendered in a browser. By
693 | allowing you to move the markup-related metadata out of the paragraph,
694 | you can add links without interrupting the narrative flow of your
695 | prose.
696 |
697 |
698 | Emphasis
699 |
700 | Markdown treats asterisks (`*`) and underscores (`_`) as indicators of
701 | emphasis. Text wrapped with one `*` or `_` will be wrapped with an
702 | HTML `` tag; double `*`'s or `_`'s will be wrapped with an HTML
703 | `` tag. E.g., this input:
704 |
705 | *single asterisks*
706 |
707 | _single underscores_
708 |
709 | **double asterisks**
710 |
711 | __double underscores__
712 |
713 | will produce:
714 |
715 | single asterisks
716 |
717 | single underscores
718 |
719 | double asterisks
720 |
721 | double underscores
722 |
723 | You can use whichever style you prefer; the lone restriction is that
724 | the same character must be used to open and close an emphasis span.
725 |
726 | Emphasis can be used in the middle of a word:
727 |
728 | un*frigging*believable
729 |
730 | But if you surround an `*` or `_` with spaces, it'll be treated as a
731 | literal asterisk or underscore.
732 |
733 | To produce a literal asterisk or underscore at a position where it
734 | would otherwise be used as an emphasis delimiter, you can backslash
735 | escape it:
736 |
737 | \*this text is surrounded by literal asterisks\*
738 |
739 |
740 |
741 | Code
742 |
743 | To indicate a span of code, wrap it with backtick quotes (`` ` ``).
744 | Unlike a pre-formatted code block, a code span indicates code within a
745 | normal paragraph. For example:
746 |
747 | Use the `printf()` function.
748 |
749 | will produce:
750 |
751 | Use the printf()
function.
752 |
753 | To include a literal backtick character within a code span, you can use
754 | multiple backticks as the opening and closing delimiters:
755 |
756 | ``There is a literal backtick (`) here.``
757 |
758 | which will produce this:
759 |
760 | There is a literal backtick (`) here.
761 |
762 | The backtick delimiters surrounding a code span may include spaces --
763 | one after the opening, one before the closing. This allows you to place
764 | literal backtick characters at the beginning or end of a code span:
765 |
766 | A single backtick in a code span: `` ` ``
767 |
768 | A backtick-delimited string in a code span: `` `foo` ``
769 |
770 | will produce:
771 |
772 | A single backtick in a code span: `
773 |
774 | A backtick-delimited string in a code span: `foo`
775 |
776 | With a code span, ampersands and angle brackets are encoded as HTML
777 | entities automatically, which makes it easy to include example HTML
778 | tags. Markdown will turn this:
779 |
780 | Please don't use any `` tags.
781 |
782 | into:
783 |
784 | Please don't use any <blink>
tags.
785 |
786 | You can write this:
787 |
788 | `—` is the decimal-encoded equivalent of `—`.
789 |
790 | to produce:
791 |
792 | —
is the decimal-encoded
793 | equivalent of —
.
794 |
795 |
796 |
797 | Images
798 |
799 | Admittedly, it's fairly difficult to devise a "natural" syntax for
800 | placing images into a plain text document format.
801 |
802 | Markdown uses an image syntax that is intended to resemble the syntax
803 | for links, allowing for two styles: *inline* and *reference*.
804 |
805 | Inline image syntax looks like this:
806 |
807 | 
808 |
809 | 
810 |
811 | That is:
812 |
813 | * An exclamation mark: `!`;
814 | * followed by a set of square brackets, containing the `alt`
815 | attribute text for the image;
816 | * followed by a set of parentheses, containing the URL or path to
817 | the image, and an optional `title` attribute enclosed in double
818 | or single quotes.
819 |
820 | Reference-style image syntax looks like this:
821 |
822 | ![Alt text][id]
823 |
824 | Where "id" is the name of a defined image reference. Image references
825 | are defined using syntax identical to link references:
826 |
827 | [id]: url/to/image "Optional title attribute"
828 |
829 | As of this writing, Markdown has no syntax for specifying the
830 | dimensions of an image; if this is important to you, you can simply
831 | use regular HTML ` ` tags.
832 |
833 |
834 | * * *
835 |
836 |
837 | Miscellaneous
838 |
839 | Automatic Links
840 |
841 | Markdown supports a shortcut style for creating "automatic" links for URLs and email addresses: simply surround the URL or email address with angle brackets. What this means is that if you want to show the actual text of a URL or email address, and also have it be a clickable link, you can do this:
842 |
843 |
844 |
845 | Markdown will turn this into:
846 |
847 | http://example.com/
848 |
849 | Automatic links for email addresses work similarly, except that
850 | Markdown will also perform a bit of randomized decimal and hex
851 | entity-encoding to help obscure your address from address-harvesting
852 | spambots. For example, Markdown will turn this:
853 |
854 |
855 |
856 | into something like this:
857 |
858 | address@exa
861 | mple.com
862 |
863 | which will render in a browser as a clickable link to "address@example.com".
864 |
865 | (This sort of entity-encoding trick will indeed fool many, if not
866 | most, address-harvesting bots, but it definitely won't fool all of
867 | them. It's better than nothing, but an address published in this way
868 | will probably eventually start receiving spam.)
869 |
870 |
871 |
872 | Backslash Escapes
873 |
874 | Markdown allows you to use backslash escapes to generate literal
875 | characters which would otherwise have special meaning in Markdown's
876 | formatting syntax. For example, if you wanted to surround a word
877 | with literal asterisks (instead of an HTML `` tag), you can use
878 | backslashes before the asterisks, like this:
879 |
880 | \*literal asterisks\*
881 |
882 | Markdown provides backslash escapes for the following characters:
883 |
884 | \ backslash
885 | ` backtick
886 | * asterisk
887 | _ underscore
888 | {} curly braces
889 | [] square brackets
890 | () parentheses
891 | # hash mark
892 | + plus sign
893 | - minus sign (hyphen)
894 | . dot
895 | ! exclamation mark
896 |
--------------------------------------------------------------------------------
/docs/contents/getting-started/about.md:
--------------------------------------------------------------------------------
1 | ---
2 | title: "About"
3 | slug: "about"
4 | date: "2017-06-02T15:00:31+08:00"
5 | ---
6 |
7 | # What is Wasp?
8 |
9 | 
10 | [](https://github.com/icyleaf/wasp/blob/master/LICENSE)
11 | [](https://circleci.com/gh/icyleaf/wasp)
12 |
13 | A Static Site Generator written in [Crystal](http://crystal-lang.org/).
14 |
15 | ## Documents
16 |
17 | Read it [Online](https://icyleaf.github.io/wasp/) or [install](https://crystal-lang.org/docs/installation/) crystal-lang and clone the project, then to run:
18 |
19 | ```
20 | $ make
21 | $ ./bin/wasp server -s docs --verbose
22 | Using config file: /Users/icyleaf/Development/crystal/wasp/docs
23 | Generating static files to /Users/icyleaf/Development/crystal/wasp/docs/public
24 | Write to /Users/icyleaf/Development/crystal/wasp/docs/public/guide/getting-started/index.html
25 | Write to /Users/icyleaf/Development/crystal/wasp/docs/public/guide/install/index.html
26 | Write to /Users/icyleaf/Development/crystal/wasp/docs/public/guide/intro/index.html
27 | Total in 55.375 ms
28 | Watch changes in '/Users/icyleaf/Development/crystal/wasp/docs/{config.yml,contents/**/*.md,layouts/**/*.html,static/**/*}'
29 | Web Server is running at http://localhost:8624/ (bind address 127.0.0.1)
30 | Press Ctrl+C to stop
31 | ```
32 |
33 | ## Inspires
34 |
35 | - [hugo](https://github.com/spf13/hugo)
36 | - [journey](https://github.com/kabukky/journey)
37 | - [dingo](https://github.com/dingoblog/dingo)
38 |
39 | ## Contributing
40 |
41 | 1. [Fork me](https://github.com/icyleaf/wasp/fork)
42 | 2. Create your feature branch (git checkout -b my-new-feature)
43 | 3. Commit your changes (git commit -am 'Add some feature')
44 | 4. Push to the branch (git push origin my-new-feature)
45 | 5. Create a new Pull Request
46 |
47 | ## Contributors
48 |
49 | - [icyleaf](https://github.com/icyleaf) - creator, maintainer
50 |
--------------------------------------------------------------------------------
/docs/contents/getting-started/install.md:
--------------------------------------------------------------------------------
1 | ---
2 | title: "Installing Wasp"
3 | slug: "install"
4 | date: "2017-06-01T15:00:31+08:00"
5 | ---
6 |
7 | We can't provide pre-built binaries currently, Wasp is written in Crystal with support for multiple platforms except Windows.
8 |
9 | ## Installing from source
10 |
11 | > Preqeuisite tools for downloading and building source code
12 |
13 | - [Git](http://git-scm.com/)
14 | - [Crystal](https://crystal-lang.org/) 0.26.0+
15 |
16 | ```
17 | $ git clone https://github.com/icyleaf/wasp.git && cd wasp
18 | $ make build
19 | $ sudo cp bin/wasp /usr/local/bin/wasp
20 | ```
21 |
--------------------------------------------------------------------------------
/docs/layouts/404.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 | {{ site.title }}
11 |
12 | {% include "partial/header.html" %}
13 |
14 |
15 |
16 | {% include "partial/menu.html" %}
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
Page not found.
26 |
27 |
28 |
29 |
30 |
40 |
41 |
42 | {% include "partial/footer.html" %}
43 |
44 |
45 |
--------------------------------------------------------------------------------
/docs/layouts/_default/single.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 | {{ page.title }} | {{ site.title }}
10 |
11 | {% include "partial/header.html" %}
12 |
13 |
14 |
15 |
16 | {% include "partial/menu.html" %}
17 |
18 |
19 |
20 |
30 |
31 |
32 |
33 |
{{ page.content | safe }}
34 |
35 |
36 |
37 |
38 |
39 | {% include "partial/footer.html" %}
40 |
41 |
42 |
--------------------------------------------------------------------------------
/docs/layouts/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 | {{ site.title }}
11 |
12 | {% include "partial/header.html" %}
13 |
14 |
15 |
16 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
32 |
33 |
34 |
35 |
36 |
37 |
38 |
39 |
40 |
41 |
42 |
43 |
44 |
flash_on
45 |
Speeds up development
46 |
47 |
We did most of the heavy lifting for you to provide a default stylings that incorporate our custom components. Additionally, we refined animations and transitions to provide a smoother experience for developers.
48 |
49 |
50 |
51 |
52 |
53 |
group
54 |
User Experience Focused
55 |
56 |
By utilizing elements and principles of Material Design, we were able to create a framework that incorporates components and animations that provide more feedback to users. Additionally, a single underlying responsive system across all platforms allow for a more unified user experience.
57 |
58 |
59 |
60 |
61 |
62 |
settings
63 |
Easy to work with
64 |
65 |
We have provided detailed documentation as well as specific code examples to help new users get started. We are also always open to feedback and can answer any questions a user may have about Materialize.
66 |
67 |
68 |
69 |
70 |
71 |
72 |
82 |
83 |
84 |
85 |
86 |
87 |
88 |
89 |
Contributing
90 |
91 | Fork it ( https://github.com/icyleaf/wasp/fork )
92 | Create your feature branch (git checkout -b my-new-feature)
93 | Commit your changes (git commit -am 'Add some feature')
94 | Push to the branch (git push origin my-new-feature)
95 | Create a new Pull Request
96 |
97 |
98 |
99 |
100 |
101 |
102 |
103 |
113 |
114 |
147 |
148 | {% include "partial/footer.html" %}
149 |
150 |
151 |
--------------------------------------------------------------------------------
/docs/layouts/partial/footer.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
--------------------------------------------------------------------------------
/docs/layouts/partial/header.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
--------------------------------------------------------------------------------
/docs/layouts/partial/menu.html:
--------------------------------------------------------------------------------
1 |
6 |
7 |
8 |
9 | Wasp
10 |
11 |
12 |
13 | {% for menu in pages %}
14 |
15 |
16 |
17 | {% endfor %}
18 |
19 |
20 |
21 |
22 |
--------------------------------------------------------------------------------
/docs/static/background1.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/icyleaf/wasp/2c0710547608a59b1f5fb9ced1384af258305916/docs/static/background1.jpg
--------------------------------------------------------------------------------
/docs/static/background2.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/icyleaf/wasp/2c0710547608a59b1f5fb9ced1384af258305916/docs/static/background2.jpg
--------------------------------------------------------------------------------
/docs/static/background3.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/icyleaf/wasp/2c0710547608a59b1f5fb9ced1384af258305916/docs/static/background3.jpg
--------------------------------------------------------------------------------
/docs/static/css/style.css:
--------------------------------------------------------------------------------
1 | /* Custom Stylesheet */
2 | /**
3 | * Use this file to override Materialize files so you can update
4 | * the core Materialize files in the future
5 | *
6 | * Made By MaterializeCSS.com
7 | */
8 |
9 | nav ul a,
10 | nav .brand-logo {
11 | color: #444;
12 | }
13 |
14 | header, main {
15 | padding-left: 150px;
16 | }
17 |
18 | p {
19 | line-height: 2rem;
20 | color: rgba(0,0,0,0.71);
21 | }
22 |
23 | blockquote {
24 | color: rgba(0,0,0,0.71);
25 | border-left: 5px solid #f9a825;
26 | }
27 |
28 | .button-collapse {
29 | color: #26a69a;
30 | }
31 |
32 | .parallax-container {
33 | min-height: 380px;
34 | line-height: 0;
35 | height: auto;
36 | color: rgba(255,255,255,.9);
37 | }
38 | .parallax-container .section {
39 | width: 100%;
40 | }
41 |
42 | @media only screen and (max-width : 992px) {
43 | .parallax-container .section {
44 | position: absolute;
45 | top: 40%;
46 | }
47 | #index-banner .section {
48 | top: 10%;
49 | }
50 | }
51 |
52 | @media only screen and (max-width : 600px) {
53 | #index-banner .section {
54 | top: 0;
55 | }
56 | }
57 |
58 | /*#content-body h1, #content-body h2, #content-body h3, #content-body h4, #content-body h5 {
59 | color: #f9a825;
60 | }*/
61 |
62 | #content-body p {
63 | font-size: 1.25rem;
64 | font-weight: 300;
65 | margin-bottom: 30px;
66 | }
67 |
68 | .icon-block {
69 | padding: 0 15px;
70 | }
71 | .icon-block .material-icons {
72 | font-size: inherit;
73 | }
74 |
75 | footer.page-footer {
76 | margin: 0;
77 | }
--------------------------------------------------------------------------------
/docs/static/fonts/roboto/Roboto-Bold.woff:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/icyleaf/wasp/2c0710547608a59b1f5fb9ced1384af258305916/docs/static/fonts/roboto/Roboto-Bold.woff
--------------------------------------------------------------------------------
/docs/static/fonts/roboto/Roboto-Bold.woff2:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/icyleaf/wasp/2c0710547608a59b1f5fb9ced1384af258305916/docs/static/fonts/roboto/Roboto-Bold.woff2
--------------------------------------------------------------------------------
/docs/static/fonts/roboto/Roboto-Light.woff:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/icyleaf/wasp/2c0710547608a59b1f5fb9ced1384af258305916/docs/static/fonts/roboto/Roboto-Light.woff
--------------------------------------------------------------------------------
/docs/static/fonts/roboto/Roboto-Light.woff2:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/icyleaf/wasp/2c0710547608a59b1f5fb9ced1384af258305916/docs/static/fonts/roboto/Roboto-Light.woff2
--------------------------------------------------------------------------------
/docs/static/fonts/roboto/Roboto-Medium.woff:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/icyleaf/wasp/2c0710547608a59b1f5fb9ced1384af258305916/docs/static/fonts/roboto/Roboto-Medium.woff
--------------------------------------------------------------------------------
/docs/static/fonts/roboto/Roboto-Medium.woff2:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/icyleaf/wasp/2c0710547608a59b1f5fb9ced1384af258305916/docs/static/fonts/roboto/Roboto-Medium.woff2
--------------------------------------------------------------------------------
/docs/static/fonts/roboto/Roboto-Regular.woff:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/icyleaf/wasp/2c0710547608a59b1f5fb9ced1384af258305916/docs/static/fonts/roboto/Roboto-Regular.woff
--------------------------------------------------------------------------------
/docs/static/fonts/roboto/Roboto-Regular.woff2:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/icyleaf/wasp/2c0710547608a59b1f5fb9ced1384af258305916/docs/static/fonts/roboto/Roboto-Regular.woff2
--------------------------------------------------------------------------------
/docs/static/fonts/roboto/Roboto-Thin.woff:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/icyleaf/wasp/2c0710547608a59b1f5fb9ced1384af258305916/docs/static/fonts/roboto/Roboto-Thin.woff
--------------------------------------------------------------------------------
/docs/static/fonts/roboto/Roboto-Thin.woff2:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/icyleaf/wasp/2c0710547608a59b1f5fb9ced1384af258305916/docs/static/fonts/roboto/Roboto-Thin.woff2
--------------------------------------------------------------------------------
/docs/static/js/init.js:
--------------------------------------------------------------------------------
1 | (function($){
2 | $(function(){
3 |
4 | $('.button-collapse').sideNav();
5 | $('.parallax').parallax();
6 |
7 | }); // end of document ready
8 | })(jQuery); // end of jQuery name space
--------------------------------------------------------------------------------
/shard.lock:
--------------------------------------------------------------------------------
1 | version: 1.0
2 | shards:
3 | callback:
4 | github: mosop/callback
5 | version: 0.6.3
6 |
7 | cli:
8 | github: mosop/cli
9 | version: 0.7.0
10 |
11 | crinja:
12 | github: straight-shoota/crinja
13 | version: 0.3.0
14 |
15 | markd:
16 | github: icyleaf/markd
17 | version: 0.1.1
18 |
19 | optarg:
20 | github: mosop/optarg
21 | version: 0.5.8
22 |
23 | popcorn:
24 | github: icyleaf/popcorn
25 | version: 0.2.0
26 |
27 | string_inflection:
28 | github: mosop/string_inflection
29 | version: 0.2.1
30 |
31 | terminal-ui:
32 | github: icyleaf/terminal-ui.cr
33 | version: 0.1.2
34 |
35 | totem:
36 | github: icyleaf/totem
37 | commit: e122f16c77d6dcbaefc88b20f8d8e58a6b232760
38 |
39 |
--------------------------------------------------------------------------------
/shard.yml:
--------------------------------------------------------------------------------
1 | name: wasp
2 | version: 0.1.1
3 |
4 | authors:
5 | - icyleaf
6 |
7 | targets:
8 | wasp:
9 | main: src/wasp.cr
10 |
11 | dependencies:
12 | markd:
13 | github: icyleaf/markd
14 | version: ~> 0.1.1
15 | totem:
16 | github: icyleaf/totem
17 | # version: ~> 0.4.0
18 | branch: master
19 | terminal-ui:
20 | github: icyleaf/terminal-ui.cr
21 | version: ~> 0.1.0
22 | crinja:
23 | github: straight-shoota/crinja
24 | version: ~> 0.3.0
25 | cli:
26 | github: mosop/cli
27 | version: ~> 0.7.0
28 |
29 | crystal: 0.26.1
30 |
31 | license: MIT
32 |
--------------------------------------------------------------------------------
/spec/fixtures/configs/config.yml:
--------------------------------------------------------------------------------
1 | title: "Wasp"
2 | subtitle: "A Static Site Generator"
3 | description: "Wasp is a Static Site Generator written in Crystal."
4 | timezone: "Asia/Shanghai"
5 |
6 | base_url: "https://icyleaf.github.io/wasp/"
7 | # theme: "nest"
8 |
9 | permalink: ":section/:title/"
10 | ugly_url: false
11 |
--------------------------------------------------------------------------------
/spec/fixtures/configs/config_with_social.yml:
--------------------------------------------------------------------------------
1 | title: "Wasp"
2 | subtitle: "A Static Site Generator"
3 | description: "Wasp is a Static Site Generator written in Crystal."
4 | timezone: "Asia/Shanghai"
5 |
6 | base_url: "https://icyleaf.github.io/wasp/"
7 | # theme: "nest"
8 |
9 | # avaiabled in basic/marked
10 | markdown: marked
11 |
12 | permalink: ":section/:title/"
13 | ugly_url: false
14 |
15 | social:
16 | twitter: icyleaf
17 | facebook: icyleaf
18 | instagram: icyleaf
19 |
--------------------------------------------------------------------------------
/spec/spec_helper.cr:
--------------------------------------------------------------------------------
1 | ENV["WASP_SPEC_RUNNING"] = "true"
2 |
3 | require "spec"
4 | require "../src/wasp"
5 |
--------------------------------------------------------------------------------
/spec/wasp/configuration_spec.cr:
--------------------------------------------------------------------------------
1 | require "../spec_helper"
2 |
3 | private def load_config(file : String)
4 | path = File.expand_path("../../fixtures/configs", __FILE__)
5 | Wasp::Configuration.configure(File.join(path, file))
6 | end
7 |
8 | describe Wasp::Configuration do
9 | describe "#initialize" do
10 | it "should returns same as YAML::Any" do
11 | config = load_config("config.yml")
12 |
13 | config["title"].should eq "Wasp"
14 | config["subtitle"].should eq "A Static Site Generator"
15 | config["description"].should eq "Wasp is a Static Site Generator written in Crystal."
16 | config["timezone"].should eq "Asia/Shanghai"
17 | config["base_url"].should eq "https://icyleaf.github.io/wasp/"
18 | config["permalink"].should eq ":section/:title/"
19 | config["ugly_url"].should be_false
20 | end
21 | end
22 |
23 | describe "#mapping" do
24 | it "should mapping full config to a struct" do
25 | config = load_config("config.yml")
26 | site = config.mapping(Wasp::Configuration::SiteStruct)
27 | site.title.should eq "Wasp"
28 | end
29 |
30 | it "should mapping a key of config to a struct" do
31 | config = load_config("config_with_social.yml")
32 | social = config.mapping(Wasp::Configuration::SocialStruct, "social")
33 | if s = social
34 | s.twitter.should eq "icyleaf"
35 | end
36 | end
37 |
38 | it "throws an exception if not exists the key" do
39 | config = load_config("config_with_social.yml")
40 | expect_raises Totem::MappingError do
41 | config.mapping(Wasp::Configuration::SocialStruct, "null")
42 | end
43 | end
44 | end
45 | end
46 |
--------------------------------------------------------------------------------
/spec/wasp/filesystem/content_file_spec.cr:
--------------------------------------------------------------------------------
1 | require "../../spec_helper"
2 |
3 | describe Wasp::FileSystem::ContentFile do
4 | end
5 |
--------------------------------------------------------------------------------
/spec/wasp/filesystem/front_matter_spec.cr:
--------------------------------------------------------------------------------
1 | require "../../spec_helper"
2 |
3 | private def empty_front_matter
4 | Wasp::FileSystem::FrontMatter.new("", "Asia/Shanghai")
5 | end
6 |
7 | describe Wasp::FileSystem::FrontMatter do
8 | describe "parse" do
9 | it "works with initialize with empty string" do
10 | empty_front_matter.should be_a(Wasp::FileSystem::FrontMatter)
11 | end
12 |
13 | it "raise exception without YAML data" do
14 | expect_raises Wasp::FrontMatterParseError do
15 | Wasp::FileSystem::FrontMatter.parse("not-yaml-data", "Asia/Shanghai")
16 | end
17 | end
18 | end
19 |
20 | describe "gets empty" do
21 | it "return empty with title" do
22 | m = empty_front_matter
23 | m.title.should eq("")
24 | end
25 |
26 | it "return empty with missing key" do
27 | m = empty_front_matter
28 | m.not_found_key.should eq("")
29 | end
30 |
31 | it "return unix time since 1970" do
32 | m = empty_front_matter
33 | m.date.should be_a(Time)
34 | m.date.year.should eq(1970)
35 | m.date.month.should eq(1)
36 | m.date.day.should eq(1)
37 | m.date.hour.should eq(0)
38 | m.date.minute.should eq(0)
39 | m.date.second.should eq(0)
40 | end
41 | end
42 |
43 | describe "gets" do
44 | it "should get each key from yaml data" do
45 | text = <<-YAML
46 | ---
47 | title: "Getting Started"
48 | slug: "getting-started"
49 | date: "2017-05-01T15:00:31+08:00"
50 | categories: Guide
51 | tags:
52 | - documents
53 | - install
54 | author: [icyleaf, "Wang Shen"]
55 | YAML
56 |
57 | m = Wasp::FileSystem::FrontMatter.new(text, "Asia/Shanghai")
58 | m.title.should eq("Getting Started")
59 | m.slug.should eq("getting-started")
60 | m.date.should eq(Time.parse("2017-05-01T15:00:31+08:00", Wasp::FileSystem::FrontMatter::WASP_DATE_FORMAT, Time::Location.load("Asia/Shanghai")))
61 | m.categories.should eq(["Guide"])
62 | m.tags.should eq(["documents", "install"])
63 | m.draft?.should be_false
64 |
65 | m.author.should eq(["icyleaf", "Wang Shen"])
66 | m.author.not_nil!.size.should eq(2) # because YAML::Type alias include Nil :(
67 | end
68 |
69 | it "tags accept string" do
70 | m = Wasp::FileSystem::FrontMatter.new("---\ntags: crystal", "Asia/Shanghai")
71 | m.tags.should eq(["crystal"])
72 | end
73 |
74 | it "tags accept empty string" do
75 | m = Wasp::FileSystem::FrontMatter.new("---\ntags: ", "Asia/Shanghai")
76 | m.tags.should eq([] of String)
77 | end
78 |
79 | it "tags accept array" do
80 | m = Wasp::FileSystem::FrontMatter.new("---\ntags: \n - crystal\n - \"ruby\"", "Asia/Shanghai")
81 | m.tags.should eq(["crystal", "ruby"])
82 | end
83 |
84 | it "tags returns empty without string or array" do
85 | m = Wasp::FileSystem::FrontMatter.new("---\ntags:\n crystal : Crystal", "Asia/Shanghai")
86 | m.tags.should eq([] of String)
87 | end
88 |
89 | it "categories accept string" do
90 | m = Wasp::FileSystem::FrontMatter.new("---\ncategories: crystal", "Asia/Shanghai")
91 | m.categories.should eq(["crystal"])
92 | end
93 |
94 | it "categories accept empty string" do
95 | m = Wasp::FileSystem::FrontMatter.new("---\ncategories: ", "Asia/Shanghai")
96 | m.categories.should eq([] of String)
97 | end
98 |
99 | it "categories accept array" do
100 | m = Wasp::FileSystem::FrontMatter.new("---\ncategories: \n - crystal\n - \"ruby\"", "Asia/Shanghai")
101 | m.categories.should eq(["crystal", "ruby"])
102 | end
103 |
104 | it "categories returns empty without string or array" do
105 | m = Wasp::FileSystem::FrontMatter.new("---\ncategories:\n crystal : Crystal", "Asia/Shanghai")
106 | m.categories.should eq([] of String)
107 | end
108 | end
109 | end
110 |
--------------------------------------------------------------------------------
/spec/wasp_spec.cr:
--------------------------------------------------------------------------------
1 | require "./spec_helper"
2 |
3 | describe Wasp do
4 | it "should has a name" do
5 | Wasp::NAME.should eq "Wasp"
6 | end
7 |
8 | it "should has a version" do
9 | Wasp::VERSION.should_not eq ""
10 | end
11 |
12 | it "should has a description" do
13 | Wasp::DESC.should_not eq ""
14 | end
15 | end
16 |
--------------------------------------------------------------------------------
/src/wasp.cr:
--------------------------------------------------------------------------------
1 | require "./wasp/*"
2 |
3 | unless ENV.has_key?("WASP_SPEC_RUNNING")
4 | args = if ARGV.size > 0
5 | ARGV
6 | else
7 | %w(--help)
8 | end
9 |
10 | Wasp::Command.run args
11 | end
12 |
--------------------------------------------------------------------------------
/src/wasp/command.cr:
--------------------------------------------------------------------------------
1 | require "cli"
2 | require "terminal-ui"
3 |
4 | module Wasp
5 | class GlobalOptions < Cli::Command
6 | class Options
7 | string %w(-s --source), var: "string", default: ".", desc: "the source path of site to read"
8 | string %w(-o --output), var: "string", desc: "the path of generate to write"
9 | bool "--verbose", default: false, desc: "verbose output"
10 | bool "--verboseLog", default: false, desc: "verbose logging timestamp"
11 |
12 | help
13 | end
14 |
15 | def run
16 | Terminal::UI.instance.logger.level = Logger::DEBUG if args.verbose?
17 | ENV["WASP_SHOW_TIMESTAMP"] = "true" if args.verboseLog?
18 | end
19 | end
20 |
21 | class Command < Cli::Supercommand
22 | class Help
23 | title "Wasp is a tool of static site generator, used to build your site."
24 | header "Complete documentation is available at http://github.com/icyleaf/wasp/."
25 | footer "Use 'wasp [command] --help' for more information about a command."
26 | end
27 |
28 | class Options
29 | string %w(-s --source), var: "string", desc: "the source path of site to read"
30 | string %w(-o --output), var: "string", desc: "the path of generate to write"
31 | bool "--verbose", default: false, desc: "verbose output"
32 | bool "--verboseLog", default: false, desc: "verbose logging timestamp"
33 |
34 | help
35 | end
36 | end
37 | end
38 |
39 | require "./commands/*"
40 |
--------------------------------------------------------------------------------
/src/wasp/commands/build.cr:
--------------------------------------------------------------------------------
1 | require "file_utils"
2 | require "yaml"
3 |
4 | module Wasp
5 | class Command < Cli::Supercommand
6 | command "b", aliased: "build"
7 |
8 | class Build < GlobalOptions
9 | class Help
10 | caption "Build markdown to static files"
11 | end
12 |
13 | class Options
14 | string %w(-b --baseURL), var: "string", desc: "hostname (and path) to the root, e.g. http://icyleaf.com/"
15 | help
16 | end
17 |
18 | def run
19 | elapsed_time do
20 | options = Hash(String, String).new.tap do |obj|
21 | obj["base_url"] = args.baseURL if args.baseURL?
22 | end
23 |
24 | generator = Wasp::Generator.new args.source, options
25 | Terminal::UI.verbose "Using config file: #{generator.context.source_path}/config.yml"
26 | Terminal::UI.verbose "Generating static files to #{generator.context.public_path}"
27 | generator.run
28 | end
29 | end
30 |
31 | private def elapsed_time
32 | started_at = Time.now
33 | yield
34 | Terminal::UI.message("Total in #{(Time.now - started_at).total_milliseconds} ms")
35 | end
36 | end
37 | end
38 | end
39 |
--------------------------------------------------------------------------------
/src/wasp/commands/config.cr:
--------------------------------------------------------------------------------
1 | module Wasp
2 | class Command < Cli::Supercommand
3 | command "c", aliased: "config"
4 |
5 | class Config < GlobalOptions
6 | class Help
7 | caption "Print site configuration"
8 | end
9 |
10 | def run
11 | path = args.source? ? args.source : "."
12 | config = Configuration.configure(path)
13 | config.each do |k, v|
14 | puts "#{k}: #{v}"
15 | end
16 | rescue e : NotFoundFileError
17 | Terminal::UI.error e.message
18 | end
19 | end
20 | end
21 | end
22 |
--------------------------------------------------------------------------------
/src/wasp/commands/init.cr:
--------------------------------------------------------------------------------
1 | module Wasp
2 | class Command < Cli::Supercommand
3 | command "i", aliased: "init"
4 |
5 | class Init < GlobalOptions
6 | class Help
7 | caption "Initialize a new site"
8 | end
9 |
10 | def run
11 | super
12 |
13 | Terminal::UI.important "To be continue"
14 | end
15 | end
16 | end
17 | end
18 |
--------------------------------------------------------------------------------
/src/wasp/commands/new.cr:
--------------------------------------------------------------------------------
1 | module Wasp
2 | class Command < Cli::Supercommand
3 | command "n", aliased: "new"
4 |
5 | class New < GlobalOptions
6 | class Help
7 | caption "Create a new content(post, page etc)"
8 | end
9 |
10 | def run
11 | super
12 |
13 | Terminal::UI.important "To be continue"
14 | end
15 | end
16 | end
17 | end
18 |
--------------------------------------------------------------------------------
/src/wasp/commands/server.cr:
--------------------------------------------------------------------------------
1 | require "http/server"
2 | require "../server/handlers/*"
3 |
4 | module Wasp
5 | class Command < Cli::Supercommand
6 | command "s", aliased: "server"
7 |
8 | class Server < GlobalOptions
9 | class Help
10 | caption "Run a web server"
11 | end
12 |
13 | class Options
14 | string %w(-b --baseURL), var: "string", default: "/", desc: "hostname (and path) to the root, e.g. http://icyleaf.com/"
15 | string "--bindHost", var: "string", default: "127.0.0.1", desc: "interface to which the server will bind"
16 | string "--watchInterval", var: "int", default: "1", desc: "seconds to wait between watch filesystem"
17 | string %w(-p --port), var: "int", default: "8624", desc: "port on which the server will listen"
18 | bool %w(-w --watch), not: "-W", default: true, desc: "watch filesystem for changes and recreate as needed"
19 | help
20 | end
21 |
22 | def run
23 | super
24 |
25 | # TODO: it does not works with localhost:8624 in HTTP::Server
26 | base_url = args.baseURL? ? args.baseURL : "/"
27 | build_args = ["--source", args.source, "--baseURL", base_url]
28 | Build.run(build_args)
29 |
30 | port = args.port
31 | public_path = if args.source?
32 | File.join(args.source, "public")
33 | else
34 | "public"
35 | end
36 |
37 | handlers = [
38 | Wasp::StaticSiteHandler.new(public_path),
39 | ] of HTTP::Handler
40 |
41 | if args.watch?
42 | source_path = File.expand_path(args.source)
43 | watcher = Watcher.new(source_path)
44 |
45 | puts ""
46 | Terminal::UI.verbose "Watch changes in '#{source_path}/{#{watcher.rules.join(",")}}'"
47 |
48 | livereload_hanlder = Wasp::LiveReloadHandler.new(public_path, port.to_i) do |ws|
49 | ws.on_message do |message|
50 | if message.includes?("\"command\":\"hello\"")
51 | ws.send({
52 | "command" => "hello",
53 | "protocols" => [
54 | "http://livereload.com/protocols/official-7",
55 | ],
56 | "serverName": "Wasp",
57 | }.to_json)
58 | end
59 | end
60 |
61 | spawn do
62 | loop do
63 | watcher.watch_changes do |file, status|
64 | Terminal::UI.message "File #{status}: #{file}"
65 | Build.run(build_args)
66 |
67 | ws.send({
68 | "command" => "reload",
69 | "path": file,
70 | "liveCSS": true,
71 | }.to_json)
72 | end
73 |
74 | sleep args.watchInterval.to_i
75 | end
76 | end
77 | end
78 |
79 | handlers.insert(0, livereload_hanlder)
80 | end
81 |
82 | Terminal::UI.message "Web Server is running at http://localhost:#{args.port}/ (bind address #{args.bindHost})"
83 | Terminal::UI.message "Press Ctrl+C to stop"
84 | server = HTTP::Server.new(handlers: handlers)
85 | server.listen args.bindHost, port.to_i
86 | end
87 | end
88 | end
89 | end
90 |
--------------------------------------------------------------------------------
/src/wasp/commands/version.cr:
--------------------------------------------------------------------------------
1 | module Wasp
2 | class Command < Cli::Supercommand
3 | command "v", aliased: "version"
4 |
5 | class Version < GlobalOptions
6 | class Help
7 | caption "Print version of Wasp"
8 | end
9 |
10 | def run
11 | super
12 |
13 | Terminal::UI.message "#{Wasp::NAME} - #{Wasp::DESC} v#{Wasp::VERSION} in Crystal v#{Crystal::VERSION}"
14 | end
15 | end
16 | end
17 | end
18 |
--------------------------------------------------------------------------------
/src/wasp/configuration.cr:
--------------------------------------------------------------------------------
1 | require "totem"
2 |
3 | module Wasp
4 | class Configuration
5 | include Totem::ConfigBuilder
6 |
7 | build do
8 | config_type "yaml"
9 | end
10 |
11 | def app_info
12 | {
13 | "name": Wasp::NAME,
14 | "version": Wasp::VERSION,
15 | "crystal": Crystal::VERSION,
16 | }
17 | end
18 |
19 | struct SiteStruct
20 | include YAML::Serializable
21 |
22 | property title : String
23 | property subtitle : String
24 | property description : String
25 | property base_url : String
26 | property timezone : String
27 | property permalink : String
28 | property ugly_url : Bool
29 | end
30 |
31 | struct SocialStruct
32 | include YAML::Serializable
33 |
34 | property twitter : String
35 | property facebook : String
36 | property instagram : String
37 | end
38 |
39 | struct AppStruct
40 | property name : String = Wasp::NAME
41 | property version : String = Wasp::VERSION
42 | property crystal : String = Crystal::VERSION
43 | end
44 | end
45 | end
46 |
--------------------------------------------------------------------------------
/src/wasp/exception.cr:
--------------------------------------------------------------------------------
1 | module Wasp
2 | class Error < Exception; end
3 |
4 | class NotFoundFileError < Error; end
5 |
6 | class ConfigDecodeError < Error; end
7 |
8 | class FrontMatterParseError < Error; end
9 |
10 | class MissingFrontMatterError < Error; end
11 | end
12 |
--------------------------------------------------------------------------------
/src/wasp/filesystem.cr:
--------------------------------------------------------------------------------
1 | module Wasp
2 | class FileSystem
3 | end
4 | end
5 |
6 | require "./filesystem/*"
7 |
--------------------------------------------------------------------------------
/src/wasp/filesystem/content_file.cr:
--------------------------------------------------------------------------------
1 | require "./front_matter"
2 | require "markd"
3 | require "uri"
4 |
5 | class Wasp::FileSystem
6 | struct ContentFile
7 | getter content
8 |
9 | @content : String
10 |
11 | FRONT_MATTER_REGEX = /^(---\s*\n.*?\n?)^(---\s*$\n?)/m
12 |
13 | def initialize(file : String, @site_config : Configuration)
14 | @file = File.expand_path(file)
15 | @name = File.basename(@file)
16 |
17 | text = File.read(@file)
18 | if text =~ FRONT_MATTER_REGEX
19 | @front_matter = FrontMatter.new($1, @site_config["timezone"].as_s)
20 | else
21 | raise MissingFrontMatterError.new("Not found metadata in " + @file)
22 | end
23 |
24 | @content = Markd.to_html(text.gsub(FRONT_MATTER_REGEX, ""))
25 | end
26 |
27 | def section
28 | sections = @file.split("/")
29 | start_index = sections.index("contents").not_nil!
30 | sections.delete_at(start_index + 1, (sections.size - start_index - 2)).join("/")
31 | end
32 |
33 | def filename
34 | @name.chomp(File.extname(@name))
35 | end
36 |
37 | def summary(limit = 300)
38 | # TODO: process to pure text
39 | @content[0..limit] + " »"
40 | end
41 |
42 | def link(ugly_url = "false")
43 | ugly_url = @site_config["ugly_url"]? || ugly_url.to_s
44 | File.join(@site_config["base_url"].to_s, permalink(ugly_url))
45 | end
46 |
47 | def permalink(ugly_url = "false")
48 | sections = [] of String
49 | (@site_config["permalink"]? || ":filename").to_s.split("/").each do |section|
50 | next if section.empty?
51 |
52 | sections << permalink_section(section).to_s
53 | end
54 |
55 | uri = sections.join("/")
56 | uri = uri + ".html" if ugly_url == "true"
57 | uri
58 | end
59 |
60 | forward_missing_to @front_matter
61 |
62 | def to_h
63 | @front_matter.to_h.merge({
64 | "summary" => Totem::Any.new(summary),
65 | "content" => Totem::Any.new(@content),
66 | "permalink" => Totem::Any.new(permalink),
67 | "link" => Totem::Any.new(link),
68 | })
69 | end
70 |
71 | private def permalink_section(uri)
72 | return "" if !uri || uri.empty?
73 |
74 | case uri
75 | when ":year"
76 | @front_matter.date.year
77 | when ":month"
78 | @front_matter.date.month
79 | when ":day"
80 | @front_matter.date.day
81 | when ":title", ":slug"
82 | @front_matter.slug ? @front_matter.slug : URI.escape(@front_matter.title.downcase.gsub(" ", "-"))
83 | when ":section"
84 | section
85 | else
86 | # such as :filename or others
87 | filename
88 | end
89 | end
90 | end
91 | end
92 |
--------------------------------------------------------------------------------
/src/wasp/filesystem/front_matter.cr:
--------------------------------------------------------------------------------
1 | class Wasp::FileSystem
2 | struct FrontMatter
3 | WASP_DATE_FORMAT = "%Y-%m-%dT%H:%M:%S%:z"
4 |
5 | @inner : Totem::Config
6 |
7 | def self.parse(text : String, timezone : String)
8 | self.new(text, timezone)
9 | end
10 |
11 | def initialize(text : String, @timezone : String)
12 | @inner = if text.empty?
13 | Totem.new
14 | else
15 | begin
16 | Totem.from_yaml(text)
17 | rescue TypeCastError
18 | raise FrontMatterParseError.new("can not parse front matter from yaml string")
19 | end
20 | end
21 | end
22 |
23 | def title
24 | @inner["title"]?.to_s
25 | end
26 |
27 | def date
28 | Time.parse(@inner.fetch("date", "1970-01-01T00:00:00+00:00").to_s, WASP_DATE_FORMAT, Time::Location.load(@timezone))
29 | end
30 |
31 | def slug
32 | @inner["slug"]?.to_s
33 | end
34 |
35 | def tags
36 | find_array_value("tags")
37 | end
38 |
39 | def categories
40 | find_array_value("categories")
41 | end
42 |
43 | def draft?
44 | @inner.fetch("draft", "false").as_bool
45 | end
46 |
47 | def to_h
48 | @inner.set_defaults({
49 | "date" => date,
50 | "tags" => tags,
51 | "categories" => categories,
52 | })
53 |
54 | @inner.to_h
55 | end
56 |
57 | def dup
58 | end
59 |
60 | forward_missing_to @inner
61 |
62 | macro method_missing(call)
63 | @inner.fetch({{ call.name.id.stringify }}, "")
64 |
65 | # TODO: i don't know why this not works
66 | # case object = @inner.fetch({{ call.name.id.stringify }}, "")
67 | # when Nil
68 | # object.to_s
69 | # when String
70 | # object.as(YAML::Any)
71 | # when Array
72 | # puts {{ call.name.id.stringify }}
73 | # puts object.class
74 | # object.as(Array(YAML::Any))
75 | # when Hash
76 | # object.as(Hash(YAML::Any, YAML::Any))
77 | # else
78 | # object
79 | # end
80 | end
81 |
82 | private def find_array_value(key : String)
83 | empty_array = Array(String).new
84 | return empty_array unless object = @inner[key]?
85 |
86 | case object
87 | when .as_s?
88 | [object.as_s]
89 | when .as_a?
90 | object.as_a.map(&.as_s)
91 | else
92 | empty_array
93 | end
94 | end
95 | end
96 | end
97 |
--------------------------------------------------------------------------------
/src/wasp/generator.cr:
--------------------------------------------------------------------------------
1 | require "./generator/*"
2 |
3 | module Wasp
4 | class Generator
5 | getter context : Context
6 |
7 | @handlers : Array(Handler)
8 |
9 | def initialize(source_path : String, options = {} of String => String, handlers = [] of Handler)
10 | @context = Context.new source_path, options
11 | @handlers = default_handers.concat handlers
12 | end
13 |
14 | def run
15 | 0.upto(@handlers.size - 2) { |i| @handlers[i].next = @handlers[i + 1] }
16 | @handlers.first.call @context
17 | end
18 |
19 | private def default_handers
20 | [Contents.new.as(Handler)]
21 | end
22 | end
23 | end
24 |
--------------------------------------------------------------------------------
/src/wasp/generator/context.cr:
--------------------------------------------------------------------------------
1 | module Wasp
2 | class Generator
3 | class Context
4 | CONTENTS_PATH = "contents"
5 | LAYOUTS_PATH = "layouts"
6 | PUBLIC_PATH = "public"
7 | STATIC_PATH = "static"
8 |
9 | property source_path : String
10 | property site_config : Configuration
11 | # property fs : FileSystem
12 | property pages : Array(FileSystem::ContentFile)
13 |
14 | def initialize(@source_path : String, options = {} of String => String)
15 | @site_config = load_and_merge_config(@source_path, options)
16 | @pages = pages
17 | end
18 |
19 | def app_info
20 | {
21 | "name" => Wasp::NAME,
22 | "version" => Wasp::VERSION,
23 | }
24 | end
25 |
26 | def pages
27 | Array(FileSystem::ContentFile).new.tap do |files|
28 | Dir.glob(File.join(contents_path, "**", "*.md")).each do |file|
29 | files << FileSystem::ContentFile.new(file, @site_config)
30 | end
31 | end.sort_by(&.date).reverse
32 | end
33 |
34 | {% for method in @type.constants %}
35 | def {{ method.stringify.downcase.id }}
36 | path_to({{ method.id }})
37 | end
38 | {% end %}
39 |
40 | private def path_to(path)
41 | File.join(@source_path, path)
42 | end
43 |
44 | private def load_and_merge_config(source_path : String, options : Hash(String, String))
45 | config = Configuration.configure(source_path)
46 | options.each do |k, v|
47 | config.set(k, v)
48 | end
49 | config
50 | end
51 | end
52 | end
53 | end
54 |
--------------------------------------------------------------------------------
/src/wasp/generator/handler.cr:
--------------------------------------------------------------------------------
1 | module Wasp
2 | class Generator
3 | module Handler
4 | property next : Handler | Nil
5 |
6 | abstract def call(context : Wasp::Generator::Context)
7 |
8 | def call_next(context : Wasp::Generator::Context)
9 | if next_handler = @next
10 | next_handler.call context
11 | end
12 | end
13 | end
14 | end
15 | end
16 |
17 | require "./handlers/*"
18 |
--------------------------------------------------------------------------------
/src/wasp/generator/handlers/contents.cr:
--------------------------------------------------------------------------------
1 | require "file_utils"
2 | require "crinja"
3 |
4 | class Wasp::Generator
5 | class Contents
6 | include Handler
7 |
8 | PAGES = ["index.html", "404.html"]
9 |
10 | def call(context)
11 | generate_assets context
12 | generate_pages context
13 |
14 | call_next context
15 | end
16 |
17 | private def generate_assets(context)
18 | public_path = context.public_path
19 |
20 | source_static_path = context.static_path
21 | public_static_path = File.join public_path, Context::STATIC_PATH
22 |
23 | FileUtils.rm_rf(public_path) if Dir.exists?(public_path)
24 | FileUtils.mkdir_p public_path
25 | FileUtils.cp_r source_static_path, public_static_path
26 | end
27 |
28 | private def generate_pages(context)
29 | template_env = Crinja.new
30 | template_env.loader = Crinja::Loader::FileSystemLoader.new(context.layouts_path)
31 |
32 | variables = {
33 | "site" => context.site_config.to_h,
34 | "wasp" => context.app_info,
35 | "pages" => context.pages.map(&.to_h),
36 | }
37 |
38 | PAGES.each do |page|
39 | desc_file = File.join context.public_path, page
40 | generate_page page, desc_file, template_env, variables
41 | end
42 |
43 | context.pages.each do |content|
44 | source_file = "_default/single.html"
45 | desc_file = File.join(context.public_path, content.permalink, "index.html")
46 |
47 | generate_page(source_file, desc_file, template_env, variables) do |variables|
48 | Terminal::UI.verbose("Write to #{desc_file}")
49 |
50 | variables["page"] = content.to_h
51 | variables
52 | end
53 | end
54 | end
55 |
56 | private def generate_page(source_file, desc_file, template_env, variables)
57 | template = template_env.get_template source_file
58 | File.write desc_file, template.render(variables)
59 | end
60 |
61 | private def generate_page(source_file, desc_file, template_env, variables)
62 | template = template_env.get_template source_file
63 | variables = yield variables
64 |
65 | desc_path = File.dirname desc_file
66 | FileUtils.mkdir_p desc_path unless Dir.exists?(desc_path)
67 |
68 | File.write desc_file, template.render(variables)
69 | end
70 |
71 | private def template_file(path)
72 | file = File.open(path)
73 | Liquid::Template.parse(file)
74 | end
75 | end
76 | end
77 |
--------------------------------------------------------------------------------
/src/wasp/server/handlers/livereload_handler.cr:
--------------------------------------------------------------------------------
1 | module Wasp
2 | class LiveReloadHandler < HTTP::WebSocketHandler
3 | def initialize(public_path : String, @port : Int32, &@proc : HTTP::WebSocket, HTTP::Server::Context ->)
4 | @path = "/livereload"
5 | @public_path = File.expand_path(public_path)
6 | # @watcher = Watcher.new()
7 |
8 | # proc = ->(socket : HTTP::WebSocket, HTTP::Server::Context ->) {
9 | # puts typeof(socket)
10 |
11 | # # watch_changes(socket)
12 |
13 | # # socket.on_close do
14 | # # puts "Server: Closing socket"
15 | # # end
16 | # }
17 |
18 | # sssproc = ->(){}
19 | # super(@path, )
20 | end
21 |
22 | def call(context)
23 | original_path = context.request.path.not_nil!
24 | is_dir_path = original_path.ends_with?("/")
25 | request_path = URI.unescape(original_path)
26 |
27 | # Load livereload js
28 | if request_path == "/livereload.js"
29 | js_file = File.expand_path("../../static/livereload.js", __FILE__)
30 | return File.open(js_file) do |file|
31 | IO.copy(file, context.response)
32 | end
33 | end
34 |
35 | # inject \n