mirror of
https://github.com/facundoolano/jorge.git
synced 2024-12-25 21:58:28 +01:00
Init command and default assets (#5)
* cleanup command arguments * add base init files * implement init command * add some layouts * add index * add blog index * post preview * honor config in file server * sample post * sample markdown * add css style to initfiles * more style tweaks * more style tweaks
This commit is contained in:
parent
79dbf8129e
commit
56a92695f5
15 changed files with 587 additions and 16 deletions
|
@ -1,21 +1,127 @@
|
|||
package commands
|
||||
|
||||
import (
|
||||
"bufio"
|
||||
"fmt"
|
||||
"io"
|
||||
"io/fs"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
|
||||
"embed"
|
||||
|
||||
"github.com/facundoolano/jorge/config"
|
||||
"github.com/facundoolano/jorge/site"
|
||||
)
|
||||
|
||||
func Init() error {
|
||||
// get working directory
|
||||
// default to .
|
||||
// if not exist, create directory
|
||||
// copy over default files
|
||||
fmt.Println("not implemented yet")
|
||||
//go:embed all:initfiles
|
||||
var initfiles embed.FS
|
||||
var initConfig string = `name: "%s"
|
||||
author: "%s"
|
||||
url: "%s"
|
||||
`
|
||||
var initReadme string = `
|
||||
# %s
|
||||
|
||||
A jorge blog by %s.
|
||||
`
|
||||
|
||||
const FILE_RW_MODE = 0777
|
||||
|
||||
func Init(projectDir string) error {
|
||||
if err := ensureEmptyProjectDir(projectDir); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
siteName := prompt("site name")
|
||||
siteUrl := prompt("site url")
|
||||
siteAuthor := prompt("author")
|
||||
|
||||
// creating config and readme files manually, since I want to use the supplied config values in their
|
||||
// contents. (I don't want to render liquid templates in the WalkDir below since some of the initfiles
|
||||
// are actual templates that should be left as is).
|
||||
configFile := fmt.Sprintf(initConfig, siteName, siteAuthor, siteUrl)
|
||||
readmeFile := fmt.Sprintf(initReadme, siteName, siteAuthor)
|
||||
os.WriteFile(filepath.Join(projectDir, "config.yml"), []byte(configFile), site.FILE_RW_MODE)
|
||||
os.WriteFile(filepath.Join(projectDir, "README.md"), []byte(readmeFile), site.FILE_RW_MODE)
|
||||
|
||||
// walk over initfiles fs
|
||||
// copy create directories and copy files at target
|
||||
|
||||
initfilesRoot := "initfiles"
|
||||
return fs.WalkDir(initfiles, initfilesRoot, func(path string, entry fs.DirEntry, err error) error {
|
||||
if path == initfilesRoot {
|
||||
return nil
|
||||
}
|
||||
subpath, _ := filepath.Rel(initfilesRoot, path)
|
||||
targetPath := filepath.Join(projectDir, subpath)
|
||||
|
||||
// if it's a directory create it at the same location
|
||||
if entry.IsDir() {
|
||||
return os.MkdirAll(targetPath, FILE_RW_MODE)
|
||||
}
|
||||
|
||||
// TODO duplicated in site, extract to somewhere else
|
||||
// if its a file, copy it over
|
||||
targetFile, err := os.Create(targetPath)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer targetFile.Close()
|
||||
|
||||
source, err := initfiles.Open(path)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer source.Close()
|
||||
|
||||
_, err = io.Copy(targetFile, source)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return targetFile.Sync()
|
||||
})
|
||||
}
|
||||
|
||||
func ensureEmptyProjectDir(projectDir string) error {
|
||||
if err := os.Mkdir(projectDir, 0777); err != nil {
|
||||
// if it fails with dir already exist, check if it's empty
|
||||
// https://stackoverflow.com/a/30708914/993769
|
||||
if os.IsExist(err) {
|
||||
// check if empty
|
||||
dir, err := os.Open(projectDir)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer dir.Close()
|
||||
|
||||
// if directory is non empty, fail
|
||||
_, err = dir.Readdirnames(1)
|
||||
if err == nil {
|
||||
return fmt.Errorf("non empty directory %s", projectDir)
|
||||
}
|
||||
return err
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// Prompt the user for a string value
|
||||
func prompt(label string) string {
|
||||
// https://dev.to/tidalcloud/interactive-cli-prompts-in-go-3bj9
|
||||
var s string
|
||||
r := bufio.NewReader(os.Stdin)
|
||||
for {
|
||||
fmt.Fprint(os.Stderr, label+": ")
|
||||
s, _ = r.ReadString('\n')
|
||||
if s != "" {
|
||||
break
|
||||
}
|
||||
}
|
||||
return strings.TrimSpace(s)
|
||||
}
|
||||
|
||||
func New() error {
|
||||
// prompt for title
|
||||
// slugify
|
||||
|
|
3
commands/initfiles/.gitignore
vendored
Normal file
3
commands/initfiles/.gitignore
vendored
Normal file
|
@ -0,0 +1,3 @@
|
|||
target
|
||||
.DS_Store
|
||||
|
14
commands/initfiles/includes/post_preview.html
Normal file
14
commands/initfiles/includes/post_preview.html
Normal file
|
@ -0,0 +1,14 @@
|
|||
<article class="post">
|
||||
<span class="date">{{ post.date | date: "%Y-%m-%d" }}</span>
|
||||
<div>
|
||||
<a class="title" href="{{ post.external | default:post.url }}">{{ post.title }}</a>
|
||||
<small>
|
||||
{% if post.favorite %} <a href="/blog/tags#⭐" style="text-decoration:none">⭐</a> {% endif %}
|
||||
<span class="tags hidden-mobile">
|
||||
{% for tag in post.tags %}
|
||||
<a href="/blog/tags#{{tag}}">#{{tag}}</a>
|
||||
{% endfor %}
|
||||
</span>
|
||||
</small>
|
||||
</div>
|
||||
</article>
|
51
commands/initfiles/layouts/base.html
Normal file
51
commands/initfiles/layouts/base.html
Normal file
|
@ -0,0 +1,51 @@
|
|||
---
|
||||
---
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||
<meta charset="utf-8">
|
||||
{% if page.title %}
|
||||
<title>{{page.head_title|default: page.title }} | {{ site.config.name }}</title>
|
||||
{% else %}
|
||||
<title>{{ site.config.name }}</title>
|
||||
{% endif %}
|
||||
<link type="application/atom+xml" rel="alternate" href="/feed.xml" title="{{ site.config.name }}"/>
|
||||
<link rel="stylesheet" href="/assets/css/main.css">
|
||||
|
||||
<meta name="author" content="{{site.config.author}}">
|
||||
<meta property="og:article:author" content="{{ site.config.author }}">
|
||||
<meta property="og:site_name" content="{{ site.config.title }}">
|
||||
|
||||
{% if page.title %}
|
||||
<meta property="og:title" content="{{ page.title|default: site.config.title }}">
|
||||
<meta name="twitter:title" content="{{ page.title|default: site.config.title }}">
|
||||
{% endif %}
|
||||
|
||||
{% if page.excerpt %}
|
||||
<meta name="description" content="{{ page.excerpt | strip_html | xml_escape }}">
|
||||
<meta name="og:description" content="{{ page.excerpt | strip_html | xml_escape }}">
|
||||
<meta name="twitter:description" content="{{ page.excerpt | strip_html | xml_escape }}">
|
||||
{% endif %}
|
||||
|
||||
{% if page.layout == "post" %}
|
||||
<meta property="og:type" content="article">
|
||||
<meta property="og:article:published_time" content="{{ page.date | date_to_xmlschema }}">
|
||||
<meta property="og:url" content="{{ page.url | absolute_url }}">
|
||||
<link rel="canonical" href="{{ page.url | absolute_url }}">
|
||||
{% else %}
|
||||
<meta property="og:type" content="website">
|
||||
<meta property="og:url" content="{{ page.url | absolute_url }}">
|
||||
<link rel="canonical" href="{{ page.url | absolute_url }}">
|
||||
{% endif %}
|
||||
|
||||
{% if page.image != "" %}
|
||||
<meta property="og:image" content="{{ page.image | absolute_url }}">
|
||||
<meta name="twitter:image" content="{{ page.image | absolute_url }}">
|
||||
{% endif %}
|
||||
|
||||
</head>
|
||||
<body>
|
||||
{{ content }}
|
||||
</body>
|
||||
</html>
|
21
commands/initfiles/layouts/default.html
Normal file
21
commands/initfiles/layouts/default.html
Normal file
|
@ -0,0 +1,21 @@
|
|||
---
|
||||
layout: base
|
||||
---
|
||||
<nav>
|
||||
<a href="/">{{ site.config.name }}</a>
|
||||
<a href="/blog/">/blog</a>
|
||||
|
||||
<div class="nav-right hidden-mobile">
|
||||
{% assign sections = page.submenu %}
|
||||
{% for section in sections %}
|
||||
<a href="{{section[1]}}">{{section[0]}}</a>
|
||||
{% endfor %}
|
||||
</div>
|
||||
</nav>
|
||||
|
||||
<div class="content layout-{{ page.layout }}">
|
||||
{% if page.title %}<h2>{{page.title}}</h2>{% endif %}
|
||||
{{ content }}
|
||||
<br/>
|
||||
</div>
|
||||
<p class="footer center-block">powered by <a href="https://jorge.olano.dev">jorge</a></p>
|
32
commands/initfiles/layouts/post.html
Normal file
32
commands/initfiles/layouts/post.html
Normal file
|
@ -0,0 +1,32 @@
|
|||
---
|
||||
layout: base
|
||||
---
|
||||
<nav>
|
||||
<a href="/">{{ site.config.name }}</a>
|
||||
<a href="/blog">/blog</a>
|
||||
</nav>
|
||||
|
||||
<div class="content layout-{{ page.layout }}" lang="{{ page.lang | default:site.config.lang | default:'en' }}">
|
||||
<header {% if page.cover-img %}class="with-cover"{% endif %}>
|
||||
<h1 class="title">{{ page.title }}</h1>
|
||||
{% if page.subtitle %}<h3 class="subtitle">{{ page.subtitle }}</h3>{% endif %}
|
||||
{% if page.cover-img %}
|
||||
<img class="cover-img" src="{{ page.cover-img | absolute_url }}">
|
||||
{% endif %}
|
||||
</header>
|
||||
{{ content }}
|
||||
</div>
|
||||
|
||||
<br/>
|
||||
<br/>
|
||||
|
||||
<p class="center-block footer">
|
||||
<span class="date">{{ page.date | date: "%d/%m/%Y" }}</span>
|
||||
<span class="tags">
|
||||
{% for tag in page.tags %}
|
||||
<a href="/blog/tags#{{tag}}">#{{tag}}</a>
|
||||
{% endfor %}
|
||||
</span>
|
||||
<br/>
|
||||
powered by <a href="https://jorge.olano.dev">jorge</a>
|
||||
</p>
|
223
commands/initfiles/src/assets/css/main.css
Normal file
223
commands/initfiles/src/assets/css/main.css
Normal file
|
@ -0,0 +1,223 @@
|
|||
html {
|
||||
color-scheme: light dark;
|
||||
overflow-y: scroll;
|
||||
}
|
||||
|
||||
body {
|
||||
max-width: 45em;
|
||||
margin: 0 auto;
|
||||
padding: 0 1rem;
|
||||
width: auto;
|
||||
font-family: Tahoma, Verdana, Arial, sans-serif;
|
||||
line-height: 1.5;
|
||||
|
||||
height: 100vh;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
}
|
||||
|
||||
.footer {
|
||||
padding-top: 2rem;
|
||||
margin-top: auto;
|
||||
}
|
||||
|
||||
@media screen and (max-width: 480px) {
|
||||
.hidden-mobile {
|
||||
display: none
|
||||
}
|
||||
}
|
||||
|
||||
nav {
|
||||
margin: 1rem 0;
|
||||
line-height: 1.8;
|
||||
border-bottom: 1px solid;
|
||||
display: flex;
|
||||
}
|
||||
.nav-right {
|
||||
margin-left: auto;
|
||||
}
|
||||
nav a:not(:last-child) {
|
||||
margin-right: 1rem
|
||||
}
|
||||
nav a:hover {
|
||||
text-decoration: none;
|
||||
}
|
||||
|
||||
li {
|
||||
line-height: 1.6;
|
||||
}
|
||||
|
||||
li:not(:last-child) {
|
||||
padding-bottom: .75rem;
|
||||
}
|
||||
|
||||
a, a:visited {
|
||||
color: LinkText;
|
||||
text-decoration: none;
|
||||
}
|
||||
a:hover {
|
||||
text-decoration: underline
|
||||
}
|
||||
|
||||
a.title {
|
||||
color: unset!important;
|
||||
padding-right: .25rem;
|
||||
}
|
||||
|
||||
.footer, .footer a, .date, .tags, .tags a {
|
||||
color: silver;
|
||||
}
|
||||
.date {
|
||||
padding-right: .5rem;
|
||||
white-space: nowrap;
|
||||
}
|
||||
.tags {
|
||||
display: inline-block;
|
||||
}
|
||||
|
||||
article.post {
|
||||
display: flex;
|
||||
padding-bottom: .5rem;
|
||||
}
|
||||
|
||||
.center-block {
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
hr {
|
||||
border: 0;
|
||||
border-top:1px solid;
|
||||
}
|
||||
|
||||
/* tweaks for post content style */
|
||||
.layout-post img {
|
||||
max-width: 75%;
|
||||
max-height: 400px;
|
||||
}
|
||||
|
||||
img.cover-img {
|
||||
width: 100%;
|
||||
max-width: 100%;
|
||||
max-height: 200px;
|
||||
object-fit: cover;
|
||||
margin-bottom: -2rem;
|
||||
}
|
||||
|
||||
.layout-post hr {
|
||||
border: 1px solid silver;
|
||||
}
|
||||
|
||||
.layout-post {
|
||||
hyphens: auto;
|
||||
text-align: justify;
|
||||
font-size: 1.15rem;
|
||||
line-height: 1.6;
|
||||
|
||||
-moz-osx-font-smoothing: grayscale;
|
||||
-webkit-font-smoothing: antialiased !important;
|
||||
-moz-font-smoothing: antialiased !important;
|
||||
text-rendering: optimizelegibility !important;
|
||||
letter-spacing: .03em;
|
||||
}
|
||||
|
||||
.layout-post .title {
|
||||
hyphens:none;
|
||||
}
|
||||
|
||||
.layout-post header {
|
||||
margin: 3rem 0 5rem;
|
||||
text-align: left;
|
||||
}
|
||||
|
||||
.layout-post header.with-cover {
|
||||
margin-top: -0.5rem;
|
||||
}
|
||||
|
||||
.src pre {
|
||||
font-size: 1rem;
|
||||
overflow-x: auto;
|
||||
line-height: 1.5;
|
||||
padding-left: 1rem
|
||||
}
|
||||
|
||||
blockquote {
|
||||
border-left: 2px solid whitesmoke;
|
||||
padding-left: 1rem;
|
||||
}
|
||||
|
||||
.layout-post p.verse {
|
||||
text-align: right;
|
||||
}
|
||||
|
||||
.layout-post p {
|
||||
line-height: 1.8;
|
||||
margin-bottom: 1.5rem;
|
||||
}
|
||||
|
||||
.layout-post p + h2 {
|
||||
padding-top: 1.5rem;
|
||||
}
|
||||
|
||||
.layout-post .center-block {
|
||||
margin: 2rem 0;
|
||||
}
|
||||
|
||||
/* override in mobile devices for more compact text */
|
||||
@media screen and (max-width: 768px) {
|
||||
.layout-post {
|
||||
font-size: 1rem;
|
||||
line-height: 1.2;
|
||||
letter-spacing: unset;
|
||||
hyphens: none;
|
||||
text-align: left;
|
||||
}
|
||||
|
||||
.layout-post p {
|
||||
margin: 0 0 1rem 0;
|
||||
line-height: 1.7;
|
||||
}
|
||||
|
||||
.layout-post p + p {
|
||||
text-indent: 0;
|
||||
}
|
||||
|
||||
.layout-post img {
|
||||
max-width: 100%;
|
||||
}
|
||||
}
|
||||
|
||||
hr.footnotes-separatator {
|
||||
display:none;
|
||||
}
|
||||
|
||||
/* makes footnote number and text display in the same line */
|
||||
.footnote-definition {
|
||||
display: block;
|
||||
vertical-align: top;
|
||||
margin-bottom: .4rem;
|
||||
}
|
||||
.footnote-body, .footnote-body p {
|
||||
display: inline;
|
||||
}
|
||||
|
||||
/* These control the expand/collapse behavior of the tags page */
|
||||
details summary {
|
||||
list-style: none;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
details summary h3::after {
|
||||
content: "[+]";
|
||||
font-size: small;
|
||||
font-weight: normal;
|
||||
font-family: monospace;
|
||||
vertical-align: middle;
|
||||
}
|
||||
|
||||
details[open] summary h3::after {
|
||||
content: "[-]"
|
||||
}
|
||||
|
||||
details > summary::-webkit-details-marker {
|
||||
display: none;
|
||||
}
|
13
commands/initfiles/src/blog/goodbye-markdown.md
Normal file
13
commands/initfiles/src/blog/goodbye-markdown.md
Normal file
|
@ -0,0 +1,13 @@
|
|||
---
|
||||
title: Goodbye Markdown...
|
||||
tags: [blog]
|
||||
date: 2024-02-16
|
||||
layout: post
|
||||
---
|
||||
|
||||
## For the record
|
||||
|
||||
For the record, even though it has *org* in the name, jorge can also render markdown,
|
||||
thanks to [goldmark](https://github.com/yuin/goldmark/).
|
||||
|
||||
[Next time](./hello-org), I'll talk about org-mode posts.
|
22
commands/initfiles/src/blog/hello-org.org
Normal file
22
commands/initfiles/src/blog/hello-org.org
Normal file
|
@ -0,0 +1,22 @@
|
|||
---
|
||||
title: Hello Org!
|
||||
subtitle: Writing posts with org-mode syntax
|
||||
tags: [blog, emacs]
|
||||
date: 2024-02-17
|
||||
layout: post
|
||||
---
|
||||
#+OPTIONS: toc:nil num:nil
|
||||
|
||||
** Indeed
|
||||
|
||||
This post was originally written with org-mode syntax, instead of [[file:goodbye-markdown][markdown]].
|
||||
|
||||
As you can see, /italics/ and *bold* render as expected, and you can even use footnotes[fn:1].
|
||||
|
||||
All of this is powered by [[https://github.com/niklasfasching/go-org][go-org]], btw[fn:2].
|
||||
|
||||
** Notes
|
||||
|
||||
[fn:1] See?
|
||||
|
||||
[fn:2] And another one footnote, to stay on the safe side.
|
15
commands/initfiles/src/blog/index.html
Normal file
15
commands/initfiles/src/blog/index.html
Normal file
|
@ -0,0 +1,15 @@
|
|||
---
|
||||
layout: default
|
||||
submenu: [["/feed", "/feed.xml"], ["/tags", "/blog/tags"]]
|
||||
title: Blog
|
||||
---
|
||||
|
||||
{% assign posts_by_year = site.posts | group_by_exp:"post", "post.date | date: '%Y'" %}
|
||||
{% for year in posts_by_year %}
|
||||
{% unless forloop.first%}
|
||||
<br/>
|
||||
{% endunless %}
|
||||
{% for post in year.items %}
|
||||
{% include post_preview.html %}
|
||||
{% endfor %}
|
||||
{% endfor %}
|
16
commands/initfiles/src/blog/tags.html
Normal file
16
commands/initfiles/src/blog/tags.html
Normal file
|
@ -0,0 +1,16 @@
|
|||
---
|
||||
layout: default
|
||||
title: Tags
|
||||
---
|
||||
|
||||
{% for tag in site.tags %}
|
||||
<details>
|
||||
<summary>
|
||||
<h3><a href="#{{tag[0]}}" class="title" id="{{tag[0]}}">#{{tag[0]}}</a></h3>
|
||||
</summary>
|
||||
|
||||
{% for post in tag[1] %}
|
||||
{% include post_preview.html %}
|
||||
{% endfor %}
|
||||
</details>
|
||||
{% endfor %}
|
35
commands/initfiles/src/feed.xml
Normal file
35
commands/initfiles/src/feed.xml
Normal file
|
@ -0,0 +1,35 @@
|
|||
---
|
||||
---
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<feed xmlns="http://www.w3.org/2005/Atom" {% if site.config.lang %}xml:lang="{{ site.lang }}"{% endif %}>
|
||||
<generator uri="https://jorge.olano.dev/" version="0.0.1">jorge</generator>
|
||||
<link href="{{ page.url | absolute_url}}" rel="self" type="application/atom+xml"/>
|
||||
<link href="{{ site.config.url }}" rel="alternate" type="text/html"/>
|
||||
<updated>{{ "now" | date: "%Y-%m-%d %H:%M" }}</updated>
|
||||
<id>{{ page.url | absolute_url}}</id>
|
||||
<title type="html">{{ site.config.name }}</title>
|
||||
<author>
|
||||
<name>{{ site.config.author }}</name>
|
||||
</author>
|
||||
{% for post in site.posts limit:10 %}
|
||||
<entry {% if post.lang %}xml:lang="{{post.lang}}"{% endif %}>
|
||||
{% assign post_title = post.title | strip_html | normalize_whitespace | xml_escape %}
|
||||
<title type="html">{{ post.title }}</title>
|
||||
<link href="{{ post.url | absolute_url }}" rel="alternate" type="text/html" title="{{ post.title }}"/>
|
||||
<published>{{ post.date | date: "%Y-%m-%d %H:%M" }}</published>
|
||||
<updated>{{ post.date | date: "%Y-%m-%d %H:%M" }}</updated>
|
||||
<id>{{ post.url | absolute_url }}</id>
|
||||
<author>
|
||||
<name>{{ post.author | default:site.config.author }}</name>
|
||||
</author>
|
||||
{% for tag in post.tags %}
|
||||
<category term="{{ tag }}"/>
|
||||
{% endfor %}
|
||||
<summary type="html"><![CDATA[{{ post.excerpt | strip_html | normalize_whitespace }}]]></summary>
|
||||
{% if post.image %}
|
||||
<media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" url="{{ post.image | absolute_url }}"/>
|
||||
<media:content medium="image" url="{{ post.image | absolute_url }}" xmlns:media="http://search.yahoo.com/mrss/"/>
|
||||
{% endif %}
|
||||
</entry>
|
||||
{% endfor %}
|
||||
</feed>
|
21
commands/initfiles/src/index.html
Normal file
21
commands/initfiles/src/index.html
Normal file
|
@ -0,0 +1,21 @@
|
|||
---
|
||||
layout: default
|
||||
---
|
||||
<h2><a href="#about" class="title" id="about">About</a></h2>
|
||||
<p>Welcome to {{ site.config.name }} by {{ site.config.author }}.</p>
|
||||
<br/>
|
||||
|
||||
<h2><a href="#latest" class="title" id="latest">Latest posts</a></h2>
|
||||
|
||||
{% for post in site.posts limit:3 %}
|
||||
{% include post_preview.html %}
|
||||
{% endfor %}
|
||||
|
||||
<p>See the full <a href="/blog">blog archive</a> or subscribe to the <a href="/feed.xml">feed</a>.</p>
|
||||
<br/>
|
||||
|
||||
<h2><a href="#contact" class="title" id="contact">Contact</a></h2>
|
||||
<ul>
|
||||
<li><a href="mailto:some@email.com">@email</a></li>
|
||||
<li><a href="https://some.site/profile">@somewhere else</a></li>
|
||||
</ul>
|
|
@ -32,12 +32,12 @@ func Serve(rootDir string) error {
|
|||
defer watcher.Close()
|
||||
|
||||
// serve the target dir with a file server
|
||||
fs := http.FileServer(HTMLDir{http.Dir("target/")})
|
||||
fs := http.FileServer(HTMLDir{http.Dir(config.TargetDir)})
|
||||
http.Handle("/", http.StripPrefix("/", fs))
|
||||
fmt.Println("server listening at http://localhost:4001/")
|
||||
http.ListenAndServe(":4001", nil)
|
||||
|
||||
return nil
|
||||
addr := fmt.Sprintf("%s:%d", config.ServerHost, config.ServerPort)
|
||||
fmt.Printf("server listening at http://%s\n", addr)
|
||||
return http.ListenAndServe(addr, nil)
|
||||
}
|
||||
|
||||
func rebuild(config *config.Config) error {
|
||||
|
|
11
main.go
11
main.go
|
@ -2,7 +2,6 @@ package main
|
|||
|
||||
import (
|
||||
"errors"
|
||||
"flag"
|
||||
"fmt"
|
||||
"os"
|
||||
|
||||
|
@ -21,8 +20,6 @@ func main() {
|
|||
func run(args []string) error {
|
||||
// TODO consider using cobra or something else to make cli more declarative
|
||||
// and get a better ux out of the box
|
||||
initCmd := flag.NewFlagSet("init", flag.ExitOnError)
|
||||
newCmd := flag.NewFlagSet("new", flag.ExitOnError)
|
||||
|
||||
if len(os.Args) < 2 {
|
||||
// TODO print usage
|
||||
|
@ -31,8 +28,11 @@ func run(args []string) error {
|
|||
|
||||
switch os.Args[1] {
|
||||
case "init":
|
||||
initCmd.Parse(os.Args[2:])
|
||||
return commands.Init()
|
||||
if len(os.Args) < 3 {
|
||||
return errors.New("project directory missing")
|
||||
}
|
||||
rootDir := os.Args[2]
|
||||
return commands.Init(rootDir)
|
||||
case "build":
|
||||
rootDir := "."
|
||||
if len(os.Args) > 2 {
|
||||
|
@ -40,7 +40,6 @@ func run(args []string) error {
|
|||
}
|
||||
return commands.Build(rootDir)
|
||||
case "new":
|
||||
newCmd.Parse(os.Args[2:])
|
||||
return commands.New()
|
||||
case "serve":
|
||||
rootDir := "."
|
||||
|
|
Loading…
Reference in a new issue